Better event handling via DiscordGo's handlers
This commit is contained in:
parent
ba67bde994
commit
c5910e7f5d
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"github.com/valyala/fastjson"
|
||||
"io"
|
||||
"time"
|
||||
|
@ -144,3 +145,7 @@ func jsonStringValue(v *fastjson.Value, keys ...string) string {
|
|||
|
||||
return string(strB)
|
||||
}
|
||||
|
||||
func jsonUnmarshal(v *fastjson.Value, dst interface{}) error {
|
||||
return json.Unmarshal(v.MarshalTo(nil), dst)
|
||||
}
|
||||
|
|
189
event.go
189
event.go
|
@ -1,32 +1,183 @@
|
|||
package gavalink
|
||||
|
||||
// EventHandler defines events that Lavalink may send to a player
|
||||
// EventHandler is an interface for Lavalink events.
|
||||
type EventHandler interface {
|
||||
OnTrackEnd(player *Player, track string, reason string) error
|
||||
OnTrackException(player *Player, track string, reason string) error
|
||||
OnTrackStuck(player *Player, track string, threshold int) error
|
||||
OnVoiceProcessed(player *Player, data *VoiceProcessingData, hotword, override bool) error
|
||||
// Type returns the type of event this handler belongs to.
|
||||
Type() string
|
||||
|
||||
// Handle is called whenever an event of Type() happens.
|
||||
// It is the receivers responsibility to type assert that the interface
|
||||
// is the expected struct.
|
||||
Handle(*Player, interface{})
|
||||
}
|
||||
|
||||
// DummyEventHandler provides an empty event handler for users who
|
||||
// wish to drop events outright. This is not recommended.
|
||||
type DummyEventHandler struct{}
|
||||
// EventInterfaceProvider is an interface for providing empty interfaces for
|
||||
// Lavalink events.
|
||||
type EventInterfaceProvider interface {
|
||||
// Type is the type of event this handler belongs to.
|
||||
Type() string
|
||||
|
||||
// OnTrackEnd is raised when a track ends
|
||||
func (d DummyEventHandler) OnTrackEnd(player *Player, track string, reason string) error {
|
||||
return nil
|
||||
// New returns a new instance of the struct this event handler handles.
|
||||
// This is called once per event.
|
||||
// The struct is provided to all handlers of the same Type().
|
||||
New() interface{}
|
||||
}
|
||||
|
||||
// OnTrackException is raised when a track throws an exception
|
||||
func (d DummyEventHandler) OnTrackException(player *Player, track string, reason string) error {
|
||||
return nil
|
||||
// interfaceEventType is the event handler type for interface{} events.
|
||||
const interfaceEventType = "__INTERFACE__"
|
||||
|
||||
// interfaceEventHandler is an event handler for interface{} events.
|
||||
type interfaceEventHandler func(*Player, interface{})
|
||||
|
||||
// Type returns the event type for interface{} events.
|
||||
func (eh interfaceEventHandler) Type() string {
|
||||
return interfaceEventType
|
||||
}
|
||||
|
||||
// OnTrackStuck is raised when a track gets stuck
|
||||
func (d DummyEventHandler) OnTrackStuck(player *Player, track string, threshold int) error {
|
||||
return nil
|
||||
// Handle is the handler for an interface{} event.
|
||||
func (eh interfaceEventHandler) Handle(p *Player, i interface{}) {
|
||||
eh(p, i)
|
||||
}
|
||||
|
||||
func (d DummyEventHandler) OnVoiceProcessed(player *Player, data *VoiceProcessingData, hotword, override bool) error {
|
||||
return nil
|
||||
var registeredInterfaceProviders = map[string]EventInterfaceProvider{}
|
||||
|
||||
// registerInterfaceProvider registers a provider so that Gavalink can
|
||||
// access it's New() method.
|
||||
func registerInterfaceProvider(eh EventInterfaceProvider) {
|
||||
if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
|
||||
return
|
||||
// XXX:
|
||||
// if we should error here, we need to do something with it.
|
||||
// fmt.Errorf("event %s already registered", eh.Type())
|
||||
}
|
||||
registeredInterfaceProviders[eh.Type()] = eh
|
||||
return
|
||||
}
|
||||
|
||||
// eventHandlerInstance is a wrapper around an event handler, as functions
|
||||
// cannot be compared directly.
|
||||
type eventHandlerInstance struct {
|
||||
eventHandler EventHandler
|
||||
}
|
||||
|
||||
// addEventHandler adds an event handler that will be fired anytime
|
||||
// the Lavalink event matching eventHandler.Type() fires.
|
||||
func (l *Lavalink) addEventHandler(eventHandler EventHandler) func() {
|
||||
l.handlersMu.Lock()
|
||||
defer l.handlersMu.Unlock()
|
||||
|
||||
if l.handlers == nil {
|
||||
l.handlers = map[string][]*eventHandlerInstance{}
|
||||
}
|
||||
|
||||
ehi := &eventHandlerInstance{eventHandler}
|
||||
l.handlers[eventHandler.Type()] = append(l.handlers[eventHandler.Type()], ehi)
|
||||
|
||||
return func() {
|
||||
l.removeEventHandlerInstance(eventHandler.Type(), ehi)
|
||||
}
|
||||
}
|
||||
|
||||
// addEventHandler adds an event handler that will be fired the next time
|
||||
// the Lavalink event matching eventHandler.Type() fires.
|
||||
func (l *Lavalink) addEventHandlerOnce(eventHandler EventHandler) func() {
|
||||
l.handlersMu.Lock()
|
||||
defer l.handlersMu.Unlock()
|
||||
|
||||
if l.onceHandlers == nil {
|
||||
l.onceHandlers = map[string][]*eventHandlerInstance{}
|
||||
}
|
||||
|
||||
ehi := &eventHandlerInstance{eventHandler}
|
||||
l.onceHandlers[eventHandler.Type()] = append(l.onceHandlers[eventHandler.Type()], ehi)
|
||||
|
||||
return func() {
|
||||
l.removeEventHandlerInstance(eventHandler.Type(), ehi)
|
||||
}
|
||||
}
|
||||
|
||||
// AddHandler allows you to add an event handler that will be fired anytime
|
||||
// the Lavalink event that matches the function fires.
|
||||
// The first parameter is a *Session, and the second parameter is a pointer
|
||||
// to a struct corresponding to the event for which you want to listen.
|
||||
//
|
||||
// eg:
|
||||
// Player.AddHandler(func(s *gavalink.Player, m *gavalink.TrackStart) {
|
||||
// })
|
||||
//
|
||||
// or:
|
||||
// Player.AddHandler(func(s *gavalink.Player, m *gavalink.TrackEnd) {
|
||||
// })
|
||||
//
|
||||
//
|
||||
// The return value of this method is a function, that when called will remove the
|
||||
// event handler.
|
||||
func (l *Lavalink) AddHandler(handler interface{}) func() {
|
||||
eh := handlerForInterface(handler)
|
||||
|
||||
if eh == nil {
|
||||
return func() {}
|
||||
}
|
||||
|
||||
return l.addEventHandler(eh)
|
||||
}
|
||||
|
||||
// AddHandlerOnce allows you to add an event handler that will be fired the next time
|
||||
// the Lavalink event that matches the function fires.
|
||||
// See AddHandler for more details.
|
||||
func (l *Lavalink) AddHandlerOnce(handler interface{}) func() {
|
||||
eh := handlerForInterface(handler)
|
||||
|
||||
if eh == nil {
|
||||
return func() {}
|
||||
}
|
||||
|
||||
return l.addEventHandlerOnce(eh)
|
||||
}
|
||||
|
||||
// removeEventHandler instance removes an event handler instance.
|
||||
func (l *Lavalink) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
|
||||
l.handlersMu.Lock()
|
||||
defer l.handlersMu.Unlock()
|
||||
|
||||
handlers := l.handlers[t]
|
||||
for i := range handlers {
|
||||
if handlers[i] == ehi {
|
||||
l.handlers[t] = append(handlers[:i], handlers[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
onceHandlers := l.onceHandlers[t]
|
||||
for i := range onceHandlers {
|
||||
if onceHandlers[i] == ehi {
|
||||
l.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles calling permanent and once handlers for an event type.
|
||||
func (l *Lavalink) handle(p *Player, t string, i interface{}) {
|
||||
for _, eh := range l.handlers[t] {
|
||||
go eh.eventHandler.Handle(p, i)
|
||||
}
|
||||
|
||||
if len(l.onceHandlers[t]) > 0 {
|
||||
for _, eh := range l.onceHandlers[t] {
|
||||
go eh.eventHandler.Handle(p, i)
|
||||
}
|
||||
l.onceHandlers[t] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Handles an event type by calling internal methods, firing handlers and firing the
|
||||
// interface{} event.
|
||||
func (l *Lavalink) handleEvent(p *Player, t string, i interface{}) {
|
||||
l.handlersMu.RLock()
|
||||
defer l.handlersMu.RUnlock()
|
||||
|
||||
// Then they are dispatched to anyone handling interface{} events.
|
||||
l.handle(p, interfaceEventType, i)
|
||||
|
||||
// Finally they are dispatched to any typed handlers.
|
||||
l.handle(p, t, i)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
// Code generated by "eventhandlers"; DO NOT EDIT
|
||||
// See events.go
|
||||
|
||||
package gavalink
|
||||
|
||||
// Following are all the event types.
|
||||
// Event type values are used to match the events returned by Lavalink.
|
||||
const (
|
||||
trackEndEventType = "TrackEndEvent"
|
||||
trackExceptionEventType = "TrackExceptionEvent"
|
||||
trackStartEventType = "TrackStartEvent"
|
||||
trackStuckEventType = "TrackStuckEvent"
|
||||
voiceProcessedEventType = "VoiceProcessedEvent"
|
||||
webSocketClosedEventType = "WebSocketClosedEvent"
|
||||
)
|
||||
|
||||
// trackEndEventHandler is an event handler for TrackEnd events.
|
||||
type trackEndEventHandler func(*Player, *TrackEnd)
|
||||
|
||||
// Type returns the event type for TrackEnd events.
|
||||
func (eh trackEndEventHandler) Type() string {
|
||||
return trackEndEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of TrackEnd.
|
||||
func (eh trackEndEventHandler) New() interface{} {
|
||||
return &TrackEnd{}
|
||||
}
|
||||
|
||||
// Handle is the handler for TrackEnd events.
|
||||
func (eh trackEndEventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*TrackEnd); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
// trackExceptionEventHandler is an event handler for TrackException events.
|
||||
type trackExceptionEventHandler func(*Player, *TrackException)
|
||||
|
||||
// Type returns the event type for TrackException events.
|
||||
func (eh trackExceptionEventHandler) Type() string {
|
||||
return trackExceptionEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of TrackException.
|
||||
func (eh trackExceptionEventHandler) New() interface{} {
|
||||
return &TrackException{}
|
||||
}
|
||||
|
||||
// Handle is the handler for TrackException events.
|
||||
func (eh trackExceptionEventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*TrackException); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
// trackStartEventHandler is an event handler for TrackStart events.
|
||||
type trackStartEventHandler func(*Player, *TrackStart)
|
||||
|
||||
// Type returns the event type for TrackStart events.
|
||||
func (eh trackStartEventHandler) Type() string {
|
||||
return trackStartEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of TrackStart.
|
||||
func (eh trackStartEventHandler) New() interface{} {
|
||||
return &TrackStart{}
|
||||
}
|
||||
|
||||
// Handle is the handler for TrackStart events.
|
||||
func (eh trackStartEventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*TrackStart); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
// trackStuckEventHandler is an event handler for TrackStuck events.
|
||||
type trackStuckEventHandler func(*Player, *TrackStuck)
|
||||
|
||||
// Type returns the event type for TrackStuck events.
|
||||
func (eh trackStuckEventHandler) Type() string {
|
||||
return trackStuckEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of TrackStuck.
|
||||
func (eh trackStuckEventHandler) New() interface{} {
|
||||
return &TrackStuck{}
|
||||
}
|
||||
|
||||
// Handle is the handler for TrackStuck events.
|
||||
func (eh trackStuckEventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*TrackStuck); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
// voiceProcessedEventHandler is an event handler for VoiceProcessed events.
|
||||
type voiceProcessedEventHandler func(*Player, *VoiceProcessed)
|
||||
|
||||
// Type returns the event type for VoiceProcessed events.
|
||||
func (eh voiceProcessedEventHandler) Type() string {
|
||||
return voiceProcessedEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of VoiceProcessed.
|
||||
func (eh voiceProcessedEventHandler) New() interface{} {
|
||||
return &VoiceProcessed{}
|
||||
}
|
||||
|
||||
// Handle is the handler for VoiceProcessed events.
|
||||
func (eh voiceProcessedEventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*VoiceProcessed); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
// webSocketClosedEventHandler is an event handler for WebSocketClosed events.
|
||||
type webSocketClosedEventHandler func(*Player, *WebSocketClosed)
|
||||
|
||||
// Type returns the event type for WebSocketClosed events.
|
||||
func (eh webSocketClosedEventHandler) Type() string {
|
||||
return webSocketClosedEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of WebSocketClosed.
|
||||
func (eh webSocketClosedEventHandler) New() interface{} {
|
||||
return &WebSocketClosed{}
|
||||
}
|
||||
|
||||
// Handle is the handler for WebSocketClosed events.
|
||||
func (eh webSocketClosedEventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*WebSocketClosed); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
func handlerForInterface(handler interface{}) EventHandler {
|
||||
switch v := handler.(type) {
|
||||
case func(*Player, interface{}):
|
||||
return interfaceEventHandler(v)
|
||||
case func(*Player, *TrackEnd):
|
||||
return trackEndEventHandler(v)
|
||||
case func(*Player, *TrackException):
|
||||
return trackExceptionEventHandler(v)
|
||||
case func(*Player, *TrackStart):
|
||||
return trackStartEventHandler(v)
|
||||
case func(*Player, *TrackStuck):
|
||||
return trackStuckEventHandler(v)
|
||||
case func(*Player, *VoiceProcessed):
|
||||
return voiceProcessedEventHandler(v)
|
||||
case func(*Player, *WebSocketClosed):
|
||||
return webSocketClosedEventHandler(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
registerInterfaceProvider(trackEndEventHandler(nil))
|
||||
registerInterfaceProvider(trackExceptionEventHandler(nil))
|
||||
registerInterfaceProvider(trackStartEventHandler(nil))
|
||||
registerInterfaceProvider(trackStuckEventHandler(nil))
|
||||
registerInterfaceProvider(voiceProcessedEventHandler(nil))
|
||||
registerInterfaceProvider(webSocketClosedEventHandler(nil))
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package gavalink
|
||||
|
||||
import "time"
|
||||
|
||||
// Event for when a track starts playing
|
||||
type TrackStart struct {
|
||||
Track string
|
||||
}
|
||||
|
||||
// Event for when a track ends.
|
||||
type TrackEnd struct {
|
||||
Track string
|
||||
Reason string
|
||||
}
|
||||
|
||||
// Event for when a track encounters an error in playback.
|
||||
type TrackException struct {
|
||||
Track string
|
||||
Error string
|
||||
Exception Exception
|
||||
}
|
||||
|
||||
// Event when a track gets stuck
|
||||
type TrackStuck struct {
|
||||
Track string
|
||||
Threshold time.Duration
|
||||
}
|
||||
|
||||
// Event for when voice is processed and sent back to the client.
|
||||
type VoiceProcessed struct {
|
||||
Data *VoiceProcessingData
|
||||
Hotword bool
|
||||
Override bool
|
||||
}
|
||||
|
||||
// Event fired when the websocket is closed.
|
||||
type WebSocketClosed struct {
|
||||
Code int
|
||||
Reason string
|
||||
ByRemote bool
|
||||
}
|
|
@ -10,7 +10,7 @@ import (
|
|||
"syscall"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/foxbot/gavalink"
|
||||
"meow.tf/astra/gavalink"
|
||||
)
|
||||
|
||||
var token string
|
||||
|
@ -162,8 +162,7 @@ func voiceServerUpdate(s *discordgo.Session, event *discordgo.VoiceServerUpdate)
|
|||
return
|
||||
}
|
||||
|
||||
handler := new(gavalink.DummyEventHandler)
|
||||
player, err = node.CreatePlayer(event.GuildID, s.State.SessionID, vsu, handler)
|
||||
player, err = node.CreatePlayer(event.GuildID, s.State.SessionID, vsu)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
|
|
42
lavalink.go
42
lavalink.go
|
@ -5,6 +5,7 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -23,6 +24,11 @@ type Lavalink struct {
|
|||
nodes []*Node
|
||||
players map[string]*Player
|
||||
|
||||
// Event handlers
|
||||
handlersMu sync.RWMutex
|
||||
handlers map[string][]*eventHandlerInstance
|
||||
onceHandlers map[string][]*eventHandlerInstance
|
||||
|
||||
capabilities map[string]interface{}
|
||||
|
||||
BestNodeFunc func([]*Node) (*Node, error)
|
||||
|
@ -50,7 +56,7 @@ func NewLavalink(shards string, userID string) *Lavalink {
|
|||
}
|
||||
|
||||
// AddNodes adds a node to the Lavalink manager
|
||||
func (lavalink *Lavalink) AddNodes(nodeConfigs ...NodeConfig) error {
|
||||
func (l *Lavalink) AddNodes(nodeConfigs ...NodeConfig) error {
|
||||
nodes := make([]*Node, len(nodeConfigs))
|
||||
|
||||
client := &http.Client{
|
||||
|
@ -60,7 +66,7 @@ func (lavalink *Lavalink) AddNodes(nodeConfigs ...NodeConfig) error {
|
|||
for i, c := range nodeConfigs {
|
||||
n := &Node{
|
||||
config: c,
|
||||
manager: lavalink,
|
||||
manager: l,
|
||||
client: client,
|
||||
}
|
||||
|
||||
|
@ -73,15 +79,15 @@ func (lavalink *Lavalink) AddNodes(nodeConfigs ...NodeConfig) error {
|
|||
nodes[i] = n
|
||||
}
|
||||
|
||||
lavalink.nodes = append(lavalink.nodes, nodes...)
|
||||
l.nodes = append(l.nodes, nodes...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveNode removes a node from the manager
|
||||
func (lavalink *Lavalink) removeNode(node *Node) error {
|
||||
func (l *Lavalink) removeNode(node *Node) error {
|
||||
idx := -1
|
||||
for i, n := range lavalink.nodes {
|
||||
for i, n := range l.nodes {
|
||||
if n == node {
|
||||
idx = i
|
||||
break
|
||||
|
@ -93,9 +99,9 @@ func (lavalink *Lavalink) removeNode(node *Node) error {
|
|||
|
||||
node.stop()
|
||||
|
||||
for _, player := range lavalink.players {
|
||||
for _, player := range l.players {
|
||||
if player.node == node {
|
||||
n, err := lavalink.BestNode()
|
||||
n, err := l.BestNode()
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
|
@ -106,28 +112,28 @@ func (lavalink *Lavalink) removeNode(node *Node) error {
|
|||
}
|
||||
|
||||
// temp var for easier reading
|
||||
n := lavalink.nodes
|
||||
n := l.nodes
|
||||
z := len(n) - 1
|
||||
|
||||
n[idx] = n[z] // swap idx with last
|
||||
n = n[:z]
|
||||
|
||||
lavalink.nodes = n
|
||||
l.nodes = n
|
||||
return nil
|
||||
}
|
||||
|
||||
// BestNode returns the Node with the lowest latency
|
||||
func (lavalink *Lavalink) BestNode() (*Node, error) {
|
||||
if len(lavalink.nodes) < 1 {
|
||||
func (l *Lavalink) BestNode() (*Node, error) {
|
||||
if len(l.nodes) < 1 {
|
||||
return nil, errNoNodes
|
||||
}
|
||||
|
||||
return lavalink.BestNodeFunc(lavalink.nodes)
|
||||
return l.BestNodeFunc(l.nodes)
|
||||
}
|
||||
|
||||
// GetPlayer gets a player for a guild
|
||||
func (lavalink *Lavalink) GetPlayer(guild string) (*Player, error) {
|
||||
p, ok := lavalink.players[guild]
|
||||
func (l *Lavalink) GetPlayer(guild string) (*Player, error) {
|
||||
p, ok := l.players[guild]
|
||||
|
||||
if !ok {
|
||||
return nil, errPlayerNotFound
|
||||
|
@ -137,10 +143,10 @@ func (lavalink *Lavalink) GetPlayer(guild string) (*Player, error) {
|
|||
}
|
||||
|
||||
// Add capabilities mappings to the client, letting the server know what we support
|
||||
func (lavalink *Lavalink) AddCapability(key string, i interface{}) {
|
||||
if lavalink.capabilities == nil {
|
||||
lavalink.capabilities = make(map[string]interface{})
|
||||
func (l *Lavalink) AddCapability(key string, i interface{}) {
|
||||
if l.capabilities == nil {
|
||||
l.capabilities = make(map[string]interface{})
|
||||
}
|
||||
|
||||
lavalink.capabilities[key] = i
|
||||
l.capabilities[key] = i
|
||||
}
|
||||
|
|
9
model.go
9
model.go
|
@ -88,7 +88,6 @@ func (t *TrackInfo) UnmarshalJSON(data []byte) error {
|
|||
|
||||
const (
|
||||
opVoiceUpdate = "voiceUpdate"
|
||||
opVoiceProcessed = "voiceProcessed"
|
||||
opUserJoin = "userJoin"
|
||||
opUserLeave = "userLeave"
|
||||
opUserListen = "userListen"
|
||||
|
@ -105,8 +104,16 @@ const (
|
|||
eventTrackEnd = "TrackEndEvent"
|
||||
eventTrackException = "TrackExceptionEvent"
|
||||
eventTrackStuck = "TrackStuckEvent"
|
||||
eventVoiceProcessed = "VoiceProcessedEvent"
|
||||
)
|
||||
|
||||
// Exception is a message from the Lavalink server
|
||||
type Exception struct {
|
||||
Message string
|
||||
Severity string
|
||||
Cause string
|
||||
}
|
||||
|
||||
// VoiceServerUpdate is a raw Discord VOICE_SERVER_UPDATE event
|
||||
type VoiceServerUpdate struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
|
|
66
node.go
66
node.go
|
@ -8,6 +8,7 @@ import (
|
|||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
@ -189,33 +190,53 @@ func (node *Node) onEvent(v *fastjson.Value, msg []byte) error {
|
|||
track := jsonStringValue(v, "track")
|
||||
|
||||
switch jsonStringValue(v, "type") {
|
||||
case eventTrackStart:
|
||||
player.track = track
|
||||
|
||||
player.handle(eventTrackStart, &TrackStart{
|
||||
Track: track,
|
||||
})
|
||||
case eventTrackEnd:
|
||||
player.track = ""
|
||||
err = player.handler.OnTrackEnd(player, track, jsonStringValue(v, "reason"))
|
||||
|
||||
player.handle(eventTrackEnd, &TrackEnd{
|
||||
Track: track,
|
||||
Reason: jsonStringValue(v, "reason"),
|
||||
})
|
||||
case eventTrackException:
|
||||
err = player.handler.OnTrackException(player, track, jsonStringValue(v, "reason"))
|
||||
ex := &TrackException{
|
||||
Track: track,
|
||||
Error: jsonStringValue(v, "error"),
|
||||
}
|
||||
|
||||
if obj := v.Get("exception"); obj != nil {
|
||||
var exception Exception
|
||||
jsonUnmarshal(obj, &exception)
|
||||
ex.Exception = exception
|
||||
}
|
||||
|
||||
player.handle(eventTrackException, ex)
|
||||
case eventTrackStuck:
|
||||
err = player.handler.OnTrackStuck(player, track, v.GetInt("thresholdMs"))
|
||||
player.handle(eventTrackStuck, &TrackStuck{
|
||||
Track: track,
|
||||
Threshold: time.Duration(v.GetInt("thresholdMs")) * time.Millisecond,
|
||||
})
|
||||
case eventVoiceProcessed:
|
||||
data := &VoiceProcessingData{
|
||||
node: node,
|
||||
UserID: jsonStringValue(v, "userId"),
|
||||
URL: fmt.Sprintf("%s/audio/%s", node.config.REST, track),
|
||||
File: track,
|
||||
}
|
||||
|
||||
player.handle(eventVoiceProcessed, &VoiceProcessed{
|
||||
Data: data,
|
||||
Hotword: v.GetBool("hotword"),
|
||||
Override: v.GetBool("override"),
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
case opVoiceProcessed:
|
||||
player, err := node.manager.GetPlayer(jsonStringValue(v, "guildId"))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
track := jsonStringValue(v, "track")
|
||||
|
||||
data := &VoiceProcessingData{
|
||||
node: node,
|
||||
UserID: jsonStringValue(v, "userId"),
|
||||
URL: fmt.Sprintf("%s/audio/%s", node.config.REST, track),
|
||||
File: track,
|
||||
}
|
||||
|
||||
return player.handler.OnVoiceProcessed(player, data, v.GetBool("hotword"), v.GetBool("override"))
|
||||
return nil
|
||||
default:
|
||||
return errUnknownPayload
|
||||
}
|
||||
|
@ -224,7 +245,7 @@ func (node *Node) onEvent(v *fastjson.Value, msg []byte) error {
|
|||
}
|
||||
|
||||
// CreatePlayer creates an audio player on this node
|
||||
func (node *Node) CreatePlayer(guildID string, sessionID string, event VoiceServerUpdate, handler EventHandler) (*Player, error) {
|
||||
func (node *Node) CreatePlayer(guildID string, sessionID string, event VoiceServerUpdate) (*Player, error) {
|
||||
msg := voiceUpdateMessage{
|
||||
Op: opVoiceUpdate,
|
||||
GuildID: guildID,
|
||||
|
@ -243,7 +264,6 @@ func (node *Node) CreatePlayer(guildID string, sessionID string, event VoiceServ
|
|||
sessionID: sessionID,
|
||||
manager: node.manager,
|
||||
node: node,
|
||||
handler: handler,
|
||||
vol: 100,
|
||||
lastVoiceServerUpdate: event,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package gavalink
|
|||
|
||||
import (
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Player is a Lavalink player
|
||||
|
@ -15,7 +16,6 @@ type Player struct {
|
|||
track string
|
||||
manager *Lavalink
|
||||
node *Node
|
||||
handler EventHandler
|
||||
lastVoiceServerUpdate VoiceServerUpdate
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var eventHandlerTmpl = template.Must(template.New("eventHandler").Funcs(template.FuncMap{
|
||||
"constName": constName,
|
||||
"privateName": privateName,
|
||||
}).Parse(`// Code generated by "eventhandlers"; DO NOT EDIT
|
||||
// See events.go
|
||||
|
||||
package gavalink
|
||||
|
||||
// Following are all the event types.
|
||||
// Event type values are used to match the events returned by Lavalink.
|
||||
const ({{range .}}
|
||||
{{privateName .}}EventType = "{{constName .}}"{{end}}
|
||||
)
|
||||
{{range .}}
|
||||
// {{privateName .}}EventHandler is an event handler for {{.}} events.
|
||||
type {{privateName .}}EventHandler func(*Player, *{{.}})
|
||||
|
||||
// Type returns the event type for {{.}} events.
|
||||
func (eh {{privateName .}}EventHandler) Type() string {
|
||||
return {{privateName .}}EventType
|
||||
}
|
||||
// New returns a new instance of {{.}}.
|
||||
func (eh {{privateName .}}EventHandler) New() interface{} {
|
||||
return &{{.}}{}
|
||||
}
|
||||
// Handle is the handler for {{.}} events.
|
||||
func (eh {{privateName .}}EventHandler) Handle(p *Player, i interface{}) {
|
||||
if t, ok := i.(*{{.}}); ok {
|
||||
eh(p, t)
|
||||
}
|
||||
}
|
||||
|
||||
{{end}}
|
||||
func handlerForInterface(handler interface{}) EventHandler {
|
||||
switch v := handler.(type) {
|
||||
case func(*Player, interface{}):
|
||||
return interfaceEventHandler(v){{range .}}
|
||||
case func(*Player, *{{.}}):
|
||||
return {{privateName .}}EventHandler(v){{end}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() { {{range .}}
|
||||
registerInterfaceProvider({{privateName .}}EventHandler(nil)){{end}}
|
||||
}
|
||||
`))
|
||||
|
||||
func main() {
|
||||
var buf bytes.Buffer
|
||||
dir := filepath.Dir(".")
|
||||
|
||||
fs := token.NewFileSet()
|
||||
parsedFile, err := parser.ParseFile(fs, "events.go", nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("warning: internal error: could not parse events.go: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
for object := range parsedFile.Scope.Objects {
|
||||
names = append(names, object)
|
||||
}
|
||||
sort.Strings(names)
|
||||
eventHandlerTmpl.Execute(&buf, names)
|
||||
|
||||
src, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Println("warning: internal error: invalid Go generated:", err)
|
||||
src = buf.Bytes()
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(dir, strings.ToLower("eventhandlers.go")), src, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(buf, "writing output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func constName(name string) string {
|
||||
return name + "Event"
|
||||
}
|
||||
|
||||
func privateName(name string) string {
|
||||
return strings.ToLower(string(name[0])) + name[1:]
|
||||
}
|
Loading…
Reference in New Issue