feature: add player
This commit is contained in:
parent
9922b2d8db
commit
9b3baebda1
22
lavalink.go
22
lavalink.go
|
@ -11,14 +11,17 @@ type Lavalink struct {
|
|||
// UserID is the Discord User ID of the bot
|
||||
UserID int
|
||||
|
||||
nodes []Node
|
||||
nodes []Node
|
||||
players map[string]*Player
|
||||
}
|
||||
|
||||
var (
|
||||
errNoNodes = errors.New("No nodes present")
|
||||
errNodeNotFound = errors.New("Couldn't find that node")
|
||||
errInvalidVersion = errors.New("This library requires Lavalink >= 3")
|
||||
errUnknownPayload = errors.New("Lavalink sent an unknown payload")
|
||||
errNoNodes = errors.New("No nodes present")
|
||||
errNodeNotFound = errors.New("Couldn't find that node")
|
||||
errPlayerNotFound = errors.New("Couldn't find a player for that guild")
|
||||
errVolumeOutOfRange = errors.New("Volume is out of range, must be within [0, 1000]")
|
||||
errInvalidVersion = errors.New("This library requires Lavalink >= 3")
|
||||
errUnknownPayload = errors.New("Lavalink sent an unknown payload")
|
||||
)
|
||||
|
||||
// NewLavalink creates a new Lavalink manager
|
||||
|
@ -74,3 +77,12 @@ func (lavalink *Lavalink) BestNode() (*Node, error) {
|
|||
// TODO: lookup latency
|
||||
return &lavalink.nodes[0], nil
|
||||
}
|
||||
|
||||
// GetPlayer gets a player for a guild
|
||||
func (lavalink *Lavalink) GetPlayer(guild string) (*Player, error) {
|
||||
p, ok := lavalink.players[guild]
|
||||
if !ok {
|
||||
return nil, errPlayerNotFound
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
|
43
model.go
43
model.go
|
@ -51,3 +51,46 @@ type TrackInfo struct {
|
|||
Length int `json:"length"`
|
||||
Position int `json:"position"`
|
||||
}
|
||||
|
||||
const (
|
||||
opVoiceUpdate = "voiceUpdate"
|
||||
opPlay = "play"
|
||||
opStop = "stop"
|
||||
opPause = "pause"
|
||||
opSeek = "seek"
|
||||
opVolume = "volume"
|
||||
opDestroy = "destroy"
|
||||
opPlayerUpdate = "playerUpdate"
|
||||
opEvent = "event"
|
||||
)
|
||||
|
||||
type message struct {
|
||||
Op string `json:"op"`
|
||||
GuildID string `json:"guildId,omitempty"`
|
||||
SessionID string `json:"sessionId,omitempty"`
|
||||
Event *VoiceServerUpdate `json:"event,omitempty"`
|
||||
Track string `json:"track,omitempty"`
|
||||
StartTime string `json:"startTime,omitempty"`
|
||||
EndTime string `json:"endTime,omitempty"`
|
||||
Pause bool `json:"pause,omitempty"`
|
||||
Position int `json:"position,omitempty"`
|
||||
Volume int `json:"volume,omitempty"`
|
||||
State *state `json:"state,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
ThresholdMs int `json:"thresholdMs,omitempty"`
|
||||
// TODO: stats
|
||||
}
|
||||
|
||||
type state struct {
|
||||
Time int `json:"time"`
|
||||
Position int `json:"position"`
|
||||
}
|
||||
|
||||
// VoiceServerUpdate is a raw Discord VOICE_SERVER_UPDATE event
|
||||
type VoiceServerUpdate struct {
|
||||
GuildID int `json:"guild_id"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
|
41
node.go
41
node.go
|
@ -93,9 +93,50 @@ func (node *Node) onEvent(msgType int, msg []byte) error {
|
|||
if msgType != websocket.TextMessage {
|
||||
return errUnknownPayload
|
||||
}
|
||||
|
||||
m := message{}
|
||||
err := json.Unmarshal(msg, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch m.Op {
|
||||
case opPlayerUpdate:
|
||||
// todo
|
||||
case opEvent:
|
||||
// todo
|
||||
default:
|
||||
return errUnknownPayload
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePlayer creates an audio player on this node
|
||||
func (node *Node) CreatePlayer(guildID string, sessionID string, event VoiceServerUpdate) (*Player, error) {
|
||||
msg := message{
|
||||
Op: opVoiceUpdate,
|
||||
GuildID: guildID,
|
||||
SessionID: sessionID,
|
||||
Event: &event,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
player := &Player{
|
||||
guildID: guildID,
|
||||
manager: node.manager,
|
||||
node: node,
|
||||
}
|
||||
node.manager.players[guildID] = player
|
||||
return player, nil
|
||||
}
|
||||
|
||||
// LoadTracks queries lavalink to return a Tracks object
|
||||
//
|
||||
// query should be a valid Lavaplayer query, including but not limited to:
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package gavalink
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// Player is a Lavalink player
|
||||
type Player struct {
|
||||
guildID string
|
||||
manager *Lavalink
|
||||
node *Node
|
||||
}
|
||||
|
||||
// Play will play the given track completely
|
||||
func (player *Player) Play(track string) error {
|
||||
return player.PlayAt(track, 0, 0)
|
||||
}
|
||||
|
||||
// PlayAt will play the given track at the specified start and end times
|
||||
//
|
||||
// Setting a time to 0 will omit it.
|
||||
func (player *Player) PlayAt(track string, startTime int, endTime int) error {
|
||||
start := strconv.Itoa(startTime)
|
||||
end := strconv.Itoa(endTime)
|
||||
|
||||
msg := message{
|
||||
Op: opPlay,
|
||||
Track: track,
|
||||
StartTime: start,
|
||||
EndTime: end,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop will stop the currently playing track
|
||||
func (player *Player) Stop() error {
|
||||
msg := message{
|
||||
Op: opStop,
|
||||
GuildID: player.guildID,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Pause will pause or resume the player, depending on the pause parameter
|
||||
func (player *Player) Pause(pause bool) error {
|
||||
msg := message{
|
||||
Op: opPause,
|
||||
GuildID: player.guildID,
|
||||
Pause: pause,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Seek will seek the player to the speicifed position, in millis
|
||||
func (player *Player) Seek(position int) error {
|
||||
msg := message{
|
||||
Op: opSeek,
|
||||
GuildID: player.guildID,
|
||||
Position: position,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Volume will set the player's volume to the specified value
|
||||
//
|
||||
// volume must be within [0, 1000]
|
||||
func (player *Player) Volume(volume int) error {
|
||||
if volume < 0 || volume > 1000 {
|
||||
return errVolumeOutOfRange
|
||||
}
|
||||
|
||||
msg := message{
|
||||
Op: opVolume,
|
||||
GuildID: player.guildID,
|
||||
Volume: volume,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Destroy will destroy this player
|
||||
func (player *Player) Destroy() error {
|
||||
msg := message{
|
||||
Op: opDestroy,
|
||||
GuildID: player.guildID,
|
||||
}
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = player.node.wsConn.WriteMessage(websocket.TextMessage, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
player.manager.players[player.guildID] = nil
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue