Skip to content

Commit

Permalink
add lavalyrics live lyrics support
Browse files Browse the repository at this point in the history
  • Loading branch information
topi314 committed May 14, 2024
1 parent 3c07fa5 commit dff524c
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 79 deletions.
52 changes: 52 additions & 0 deletions commands/lyrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,55 @@ func (c *Commands) Lyrics(data discord.SlashCommandInteractionData, e *handler.C
})
return err
}

func (c *Commands) LiveLyricsSubscribe(_ discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
player := c.Lavalink.ExistingPlayer(*e.GuildID())
if player == nil {
return e.CreateMessage(discord.MessageCreate{
Content: "No player found",
Flags: discord.MessageFlagEphemeral,
})
}

if err := e.DeferCreateMessage(false); err != nil {
return err
}

if err := lavalyrics.SubscribeLyrics(e.Ctx, player.Node().Rest(), player.Node().SessionID(), *e.GuildID()); err != nil {
_, err = e.UpdateInteractionResponse(discord.MessageUpdate{
Content: json.Ptr(fmt.Sprintf("failed to get subscribe to live lyrics: %s", err)),
})
return err
}

_, err := e.UpdateInteractionResponse(discord.MessageUpdate{
Content: json.Ptr(fmt.Sprintf("subscribed to live lyrics")),
})
return err
}

func (c *Commands) LiveLyricsUnsubscribe(_ discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
player := c.Lavalink.ExistingPlayer(*e.GuildID())
if player == nil {
return e.CreateMessage(discord.MessageCreate{
Content: "No player found",
Flags: discord.MessageFlagEphemeral,
})
}

if err := e.DeferCreateMessage(false); err != nil {
return err
}

if err := lavalyrics.UnsubscribeLyrics(e.Ctx, player.Node().Rest(), player.Node().SessionID(), *e.GuildID()); err != nil {
_, err = e.UpdateInteractionResponse(discord.MessageUpdate{
Content: json.Ptr(fmt.Sprintf("failed to unsubscribe from live lyrics: %s", err)),
})
return err
}

_, err := e.UpdateInteractionResponse(discord.MessageUpdate{
Content: json.Ptr(fmt.Sprintf("unsubscribed from live lyrics")),
})
return err
}
14 changes: 14 additions & 0 deletions commands/music.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,20 @@ var music = discord.SlashCommandCreate{
},
},
},
discord.ApplicationCommandOptionSubCommandGroup{
Name: "live-lyrics",
Description: "Subscribes or unsubscribes to live lyrics",
Options: []discord.ApplicationCommandOptionSubCommand{
{
Name: "subscribe",
Description: "Subscribes to live lyrics",
},
{
Name: "unsubscribe",
Description: "Unsubscribes from live lyrics",
},
},
},
},
}

Expand Down
6 changes: 4 additions & 2 deletions commands/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ func (c *Commands) Play(data discord.SlashCommandInteractionData, e *handler.Com
track, tracks = tracks[0], tracks[1:]
}

c.MusicQueue.Add(*e.GuildID(), e.Channel().ID(), tracks...)

playCtx, playCancel := context.WithTimeout(e.Ctx, 10*time.Second)
defer playCancel()
if err = player.Update(playCtx, lavalink.WithTrack(track)); err != nil {
Expand All @@ -267,9 +269,9 @@ func (c *Commands) Play(data discord.SlashCommandInteractionData, e *handler.Com
})
return err
}
}
if len(tracks) > 0 {
} else {
c.MusicQueue.Add(*e.GuildID(), e.Channel().ID(), tracks...)
}

return nil
}
73 changes: 0 additions & 73 deletions handlers/lavalink.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgolink/v3/disgolink"
"github.com/disgoorg/disgolink/v3/lavalink"
"github.com/disgoorg/sponsorblock-plugin"
"github.com/lavalink-devs/lavalink-bot/commands"
"github.com/lavalink-devs/lavalink-bot/internal/res"
"github.com/topi314/tint"
Expand Down Expand Up @@ -139,75 +138,3 @@ func (h *Handlers) OnUnknownEvent(p disgolink.Player, event lavalink.UnknownEven
func (h *Handlers) OnUnknownMessage(p disgolink.Player, event lavalink.UnknownMessage) {
slog.Info("unknown message", slog.String("op", string(event.Op())), slog.String("data", string(event.Data)))
}

func (h *Handlers) OnSegmentsLoaded(p disgolink.Player, event sponsorblock.SegmentsLoadedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

content := "Segments loaded:\n"
for i, segment := range event.Segments {
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, segment.Category, res.FormatDuration(segment.Start), res.FormatDuration(segment.End))
if len(content)+len(line) > 2000 {
content += "..."
break
}
content += line
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: content,
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

func (h *Handlers) OndSegmentSkipped(p disgolink.Player, event sponsorblock.SegmentSkippedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: fmt.Sprintf("Segment skipped: %s: %s - %s", event.Segment.Category, res.FormatDuration(event.Segment.Start), res.FormatDuration(event.Segment.End)),
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

func (h *Handlers) OnChaptersLoaded(p disgolink.Player, event sponsorblock.ChaptersLoadedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

content := "Chapters loaded:\n"
for i, chapter := range event.Chapters {
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, chapter.Name, res.FormatDuration(chapter.Start), res.FormatDuration(chapter.End))
if len(content)+len(line) > 2000 {
content += "..."
break
}
content += line
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: content,
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

func (h *Handlers) OnChapterStarted(p disgolink.Player, event sponsorblock.ChapterStartedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: fmt.Sprintf("Chapter started: %s: %s - %s", event.Chapter.Name, res.FormatDuration(event.Chapter.Start), res.FormatDuration(event.Chapter.End)),
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}
82 changes: 82 additions & 0 deletions handlers/lavalyrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package handlers

import (
"fmt"
"log/slog"

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgolink/v3/disgolink"
"github.com/disgoorg/json"
"github.com/disgoorg/lavalyrics-plugin"
"github.com/topi314/tint"
)

func (h *Handlers) OnLyricsFound(p disgolink.Player, event lavalyrics.LyricsFoundEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

lyricsMessageID := h.MusicQueue.LyricsMessageID(p.GuildID())
if lyricsMessageID == 0 {
msg, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: "Lyrics found",
})
if err != nil {
slog.Error("failed to send message", tint.Err(err))
return
}
h.MusicQueue.SetLyricsMessageID(p.GuildID(), msg.ID)
return
}

if _, err := h.Client.Rest().UpdateMessage(channelID, lyricsMessageID, discord.MessageUpdate{
Content: json.Ptr("Lyrics found"),
}); err != nil {
slog.Error("failed to update message", tint.Err(err))
}
}

func (h *Handlers) OnLyricsNotFound(p disgolink.Player, event lavalyrics.LyricsNotFoundEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

lyricsMessageID := h.MusicQueue.LyricsMessageID(p.GuildID())
if lyricsMessageID == 0 {
msg, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: "Lyrics not found",
})
if err != nil {
slog.Error("failed to send message", tint.Err(err))
return
}
h.MusicQueue.SetLyricsMessageID(p.GuildID(), msg.ID)
return
}

if _, err := h.Client.Rest().UpdateMessage(channelID, lyricsMessageID, discord.MessageUpdate{
Content: json.Ptr("Lyrics not found"),
}); err != nil {
slog.Error("failed to update message", tint.Err(err))
}
}

func (h *Handlers) OnLyricsLine(p disgolink.Player, event lavalyrics.LyricsLineEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

lyricsMessageID := h.MusicQueue.LyricsMessageID(p.GuildID())
if lyricsMessageID == 0 {
return
}

if _, err := h.Client.Rest().UpdateMessage(channelID, lyricsMessageID, discord.MessageUpdate{
Content: json.Ptr(fmt.Sprintf("Line(`%s`): %s", event.Line.Timestamp, event.Line.Line)),
}); err != nil {
slog.Error("failed to update message", tint.Err(err))
}
}
85 changes: 85 additions & 0 deletions handlers/sponsorblock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package handlers

import (
"fmt"
"log/slog"

"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgolink/v3/disgolink"
"github.com/disgoorg/sponsorblock-plugin"
"github.com/lavalink-devs/lavalink-bot/internal/res"
"github.com/topi314/tint"
)

func (h *Handlers) OnSegmentsLoaded(p disgolink.Player, event sponsorblock.SegmentsLoadedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

content := "Segments loaded:\n"
for i, segment := range event.Segments {
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, segment.Category, res.FormatDuration(segment.Start), res.FormatDuration(segment.End))
if len(content)+len(line) > 2000 {
content += "..."
break
}
content += line
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: content,
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

func (h *Handlers) OndSegmentSkipped(p disgolink.Player, event sponsorblock.SegmentSkippedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: fmt.Sprintf("Segment skipped: %s: %s - %s", event.Segment.Category, res.FormatDuration(event.Segment.Start), res.FormatDuration(event.Segment.End)),
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

func (h *Handlers) OnChaptersLoaded(p disgolink.Player, event sponsorblock.ChaptersLoadedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}

content := "Chapters loaded:\n"
for i, chapter := range event.Chapters {
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, chapter.Name, res.FormatDuration(chapter.Start), res.FormatDuration(chapter.End))
if len(content)+len(line) > 2000 {
content += "..."
break
}
content += line
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: content,
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

func (h *Handlers) OnChapterStarted(p disgolink.Player, event sponsorblock.ChapterStartedEvent) {
channelID := h.MusicQueue.ChannelID(p.GuildID())
if channelID == 0 {
return
}
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
Content: fmt.Sprintf("Chapter started: %s: %s - %s", event.Chapter.Name, res.FormatDuration(event.Chapter.Start), res.FormatDuration(event.Chapter.End)),
AllowedMentions: &discord.AllowedMentions{},
}); err != nil {
slog.Error("failed to send message", tint.Err(err))
}
}

29 changes: 26 additions & 3 deletions lavalinkbot/queue.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ type PlayerManager struct {
}

type queue struct {
tracks []lavalink.Track
mode RepeatMode
channelID snowflake.ID
tracks []lavalink.Track
mode RepeatMode
channelID snowflake.ID
lyricsMessageID snowflake.ID
}

func (q *PlayerManager) Get(guildID snowflake.ID) (RepeatMode, []lavalink.Track) {
Expand Down Expand Up @@ -62,6 +63,28 @@ func (q *PlayerManager) ChannelID(guildID snowflake.ID) snowflake.ID {
return qu.channelID
}

func (q *PlayerManager) SetLyricsMessageID(guildID snowflake.ID, messageID snowflake.ID) {
q.mu.Lock()
defer q.mu.Unlock()

qu, ok := q.queues[guildID]
if !ok {
return
}
qu.lyricsMessageID = messageID
}

func (q *PlayerManager) LyricsMessageID(guildID snowflake.ID) snowflake.ID {
q.mu.Lock()
defer q.mu.Unlock()

qu, ok := q.queues[guildID]
if !ok {
return 0
}
return qu.lyricsMessageID
}

func (q *PlayerManager) Add(guildID snowflake.ID, channelID snowflake.ID, tracks ...lavalink.Track) {
q.mu.Lock()
defer q.mu.Unlock()
Expand Down
Loading

0 comments on commit dff524c

Please sign in to comment.