Skip to content

Commit 38b58ef

Browse files
committed
add lavalyrics live lyrics support
1 parent a8e535a commit 38b58ef

File tree

8 files changed

+276
-79
lines changed

8 files changed

+276
-79
lines changed

commands/lyrics.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,55 @@ func (c *Commands) Lyrics(data discord.SlashCommandInteractionData, e *handler.C
8888
})
8989
return err
9090
}
91+
92+
func (c *Commands) LiveLyricsSubscribe(_ discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
93+
player := c.Lavalink.ExistingPlayer(*e.GuildID())
94+
if player == nil {
95+
return e.CreateMessage(discord.MessageCreate{
96+
Content: "No player found",
97+
Flags: discord.MessageFlagEphemeral,
98+
})
99+
}
100+
101+
if err := e.DeferCreateMessage(false); err != nil {
102+
return err
103+
}
104+
105+
if err := lavalyrics.SubscribeLyrics(e.Ctx, player.Node().Rest(), player.Node().SessionID(), *e.GuildID()); err != nil {
106+
_, err = e.UpdateInteractionResponse(discord.MessageUpdate{
107+
Content: json.Ptr(fmt.Sprintf("failed to get subscribe to live lyrics: %s", err)),
108+
})
109+
return err
110+
}
111+
112+
_, err := e.UpdateInteractionResponse(discord.MessageUpdate{
113+
Content: json.Ptr(fmt.Sprintf("subscribed to live lyrics")),
114+
})
115+
return err
116+
}
117+
118+
func (c *Commands) LiveLyricsUnsubscribe(_ discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
119+
player := c.Lavalink.ExistingPlayer(*e.GuildID())
120+
if player == nil {
121+
return e.CreateMessage(discord.MessageCreate{
122+
Content: "No player found",
123+
Flags: discord.MessageFlagEphemeral,
124+
})
125+
}
126+
127+
if err := e.DeferCreateMessage(false); err != nil {
128+
return err
129+
}
130+
131+
if err := lavalyrics.UnsubscribeLyrics(e.Ctx, player.Node().Rest(), player.Node().SessionID(), *e.GuildID()); err != nil {
132+
_, err = e.UpdateInteractionResponse(discord.MessageUpdate{
133+
Content: json.Ptr(fmt.Sprintf("failed to unsubscribe from live lyrics: %s", err)),
134+
})
135+
return err
136+
}
137+
138+
_, err := e.UpdateInteractionResponse(discord.MessageUpdate{
139+
Content: json.Ptr(fmt.Sprintf("unsubscribed from live lyrics")),
140+
})
141+
return err
142+
}

commands/music.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,20 @@ var music = discord.SlashCommandCreate{
463463
},
464464
},
465465
},
466+
discord.ApplicationCommandOptionSubCommandGroup{
467+
Name: "live-lyrics",
468+
Description: "Subscribes or unsubscribes to live lyrics",
469+
Options: []discord.ApplicationCommandOptionSubCommand{
470+
{
471+
Name: "subscribe",
472+
Description: "Subscribes to live lyrics",
473+
},
474+
{
475+
Name: "unsubscribe",
476+
Description: "Unsubscribes from live lyrics",
477+
},
478+
},
479+
},
466480
},
467481
}
468482

commands/play.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@ func (c *Commands) Play(data discord.SlashCommandInteractionData, e *handler.Com
259259
track, tracks = tracks[0], tracks[1:]
260260
}
261261

262+
c.MusicQueue.Add(*e.GuildID(), e.Channel().ID(), tracks...)
263+
262264
playCtx, playCancel := context.WithTimeout(e.Ctx, 10*time.Second)
263265
defer playCancel()
264266
if err = player.Update(playCtx, lavalink.WithTrack(track)); err != nil {
@@ -267,9 +269,9 @@ func (c *Commands) Play(data discord.SlashCommandInteractionData, e *handler.Com
267269
})
268270
return err
269271
}
270-
}
271-
if len(tracks) > 0 {
272+
} else {
272273
c.MusicQueue.Add(*e.GuildID(), e.Channel().ID(), tracks...)
273274
}
275+
274276
return nil
275277
}

handlers/lavalink.go

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/disgoorg/disgo/events"
1111
"github.com/disgoorg/disgolink/v3/disgolink"
1212
"github.com/disgoorg/disgolink/v3/lavalink"
13-
"github.com/disgoorg/sponsorblock-plugin"
1413
"github.com/lavalink-devs/lavalink-bot/commands"
1514
"github.com/lavalink-devs/lavalink-bot/internal/res"
1615
"github.com/topi314/tint"
@@ -139,75 +138,3 @@ func (h *Handlers) OnUnknownEvent(p disgolink.Player, event lavalink.UnknownEven
139138
func (h *Handlers) OnUnknownMessage(p disgolink.Player, event lavalink.UnknownMessage) {
140139
slog.Info("unknown message", slog.String("op", string(event.Op())), slog.String("data", string(event.Data)))
141140
}
142-
143-
func (h *Handlers) OnSegmentsLoaded(p disgolink.Player, event sponsorblock.SegmentsLoadedEvent) {
144-
channelID := h.MusicQueue.ChannelID(p.GuildID())
145-
if channelID == 0 {
146-
return
147-
}
148-
149-
content := "Segments loaded:\n"
150-
for i, segment := range event.Segments {
151-
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, segment.Category, res.FormatDuration(segment.Start), res.FormatDuration(segment.End))
152-
if len(content)+len(line) > 2000 {
153-
content += "..."
154-
break
155-
}
156-
content += line
157-
}
158-
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
159-
Content: content,
160-
AllowedMentions: &discord.AllowedMentions{},
161-
}); err != nil {
162-
slog.Error("failed to send message", tint.Err(err))
163-
}
164-
}
165-
166-
func (h *Handlers) OndSegmentSkipped(p disgolink.Player, event sponsorblock.SegmentSkippedEvent) {
167-
channelID := h.MusicQueue.ChannelID(p.GuildID())
168-
if channelID == 0 {
169-
return
170-
}
171-
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
172-
Content: fmt.Sprintf("Segment skipped: %s: %s - %s", event.Segment.Category, res.FormatDuration(event.Segment.Start), res.FormatDuration(event.Segment.End)),
173-
AllowedMentions: &discord.AllowedMentions{},
174-
}); err != nil {
175-
slog.Error("failed to send message", tint.Err(err))
176-
}
177-
}
178-
179-
func (h *Handlers) OnChaptersLoaded(p disgolink.Player, event sponsorblock.ChaptersLoadedEvent) {
180-
channelID := h.MusicQueue.ChannelID(p.GuildID())
181-
if channelID == 0 {
182-
return
183-
}
184-
185-
content := "Chapters loaded:\n"
186-
for i, chapter := range event.Chapters {
187-
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, chapter.Name, res.FormatDuration(chapter.Start), res.FormatDuration(chapter.End))
188-
if len(content)+len(line) > 2000 {
189-
content += "..."
190-
break
191-
}
192-
content += line
193-
}
194-
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
195-
Content: content,
196-
AllowedMentions: &discord.AllowedMentions{},
197-
}); err != nil {
198-
slog.Error("failed to send message", tint.Err(err))
199-
}
200-
}
201-
202-
func (h *Handlers) OnChapterStarted(p disgolink.Player, event sponsorblock.ChapterStartedEvent) {
203-
channelID := h.MusicQueue.ChannelID(p.GuildID())
204-
if channelID == 0 {
205-
return
206-
}
207-
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
208-
Content: fmt.Sprintf("Chapter started: %s: %s - %s", event.Chapter.Name, res.FormatDuration(event.Chapter.Start), res.FormatDuration(event.Chapter.End)),
209-
AllowedMentions: &discord.AllowedMentions{},
210-
}); err != nil {
211-
slog.Error("failed to send message", tint.Err(err))
212-
}
213-
}

handlers/lavalyrics.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package handlers
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
7+
"github.com/disgoorg/disgo/discord"
8+
"github.com/disgoorg/disgolink/v3/disgolink"
9+
"github.com/disgoorg/json"
10+
"github.com/disgoorg/lavalyrics-plugin"
11+
"github.com/topi314/tint"
12+
)
13+
14+
func (h *Handlers) OnLyricsFound(p disgolink.Player, event lavalyrics.LyricsFoundEvent) {
15+
channelID := h.MusicQueue.ChannelID(p.GuildID())
16+
if channelID == 0 {
17+
return
18+
}
19+
20+
lyricsMessageID := h.MusicQueue.LyricsMessageID(p.GuildID())
21+
if lyricsMessageID == 0 {
22+
msg, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
23+
Content: "Lyrics found",
24+
})
25+
if err != nil {
26+
slog.Error("failed to send message", tint.Err(err))
27+
return
28+
}
29+
h.MusicQueue.SetLyricsMessageID(p.GuildID(), msg.ID)
30+
return
31+
}
32+
33+
if _, err := h.Client.Rest().UpdateMessage(channelID, lyricsMessageID, discord.MessageUpdate{
34+
Content: json.Ptr("Lyrics found"),
35+
}); err != nil {
36+
slog.Error("failed to update message", tint.Err(err))
37+
}
38+
}
39+
40+
func (h *Handlers) OnLyricsNotFound(p disgolink.Player, event lavalyrics.LyricsNotFoundEvent) {
41+
channelID := h.MusicQueue.ChannelID(p.GuildID())
42+
if channelID == 0 {
43+
return
44+
}
45+
46+
lyricsMessageID := h.MusicQueue.LyricsMessageID(p.GuildID())
47+
if lyricsMessageID == 0 {
48+
msg, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
49+
Content: "Lyrics not found",
50+
})
51+
if err != nil {
52+
slog.Error("failed to send message", tint.Err(err))
53+
return
54+
}
55+
h.MusicQueue.SetLyricsMessageID(p.GuildID(), msg.ID)
56+
return
57+
}
58+
59+
if _, err := h.Client.Rest().UpdateMessage(channelID, lyricsMessageID, discord.MessageUpdate{
60+
Content: json.Ptr("Lyrics not found"),
61+
}); err != nil {
62+
slog.Error("failed to update message", tint.Err(err))
63+
}
64+
}
65+
66+
func (h *Handlers) OnLyricsLine(p disgolink.Player, event lavalyrics.LyricsLineEvent) {
67+
channelID := h.MusicQueue.ChannelID(p.GuildID())
68+
if channelID == 0 {
69+
return
70+
}
71+
72+
lyricsMessageID := h.MusicQueue.LyricsMessageID(p.GuildID())
73+
if lyricsMessageID == 0 {
74+
return
75+
}
76+
77+
if _, err := h.Client.Rest().UpdateMessage(channelID, lyricsMessageID, discord.MessageUpdate{
78+
Content: json.Ptr(fmt.Sprintf("Line(`%s`): %s", event.Line.Timestamp, event.Line.Line)),
79+
}); err != nil {
80+
slog.Error("failed to update message", tint.Err(err))
81+
}
82+
}

handlers/sponsorblock.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package handlers
2+
3+
import (
4+
"fmt"
5+
"log/slog"
6+
7+
"github.com/disgoorg/disgo/discord"
8+
"github.com/disgoorg/disgolink/v3/disgolink"
9+
"github.com/disgoorg/sponsorblock-plugin"
10+
"github.com/lavalink-devs/lavalink-bot/internal/res"
11+
"github.com/topi314/tint"
12+
)
13+
14+
func (h *Handlers) OnSegmentsLoaded(p disgolink.Player, event sponsorblock.SegmentsLoadedEvent) {
15+
channelID := h.MusicQueue.ChannelID(p.GuildID())
16+
if channelID == 0 {
17+
return
18+
}
19+
20+
content := "Segments loaded:\n"
21+
for i, segment := range event.Segments {
22+
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, segment.Category, res.FormatDuration(segment.Start), res.FormatDuration(segment.End))
23+
if len(content)+len(line) > 2000 {
24+
content += "..."
25+
break
26+
}
27+
content += line
28+
}
29+
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
30+
Content: content,
31+
AllowedMentions: &discord.AllowedMentions{},
32+
}); err != nil {
33+
slog.Error("failed to send message", tint.Err(err))
34+
}
35+
}
36+
37+
func (h *Handlers) OndSegmentSkipped(p disgolink.Player, event sponsorblock.SegmentSkippedEvent) {
38+
channelID := h.MusicQueue.ChannelID(p.GuildID())
39+
if channelID == 0 {
40+
return
41+
}
42+
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
43+
Content: fmt.Sprintf("Segment skipped: %s: %s - %s", event.Segment.Category, res.FormatDuration(event.Segment.Start), res.FormatDuration(event.Segment.End)),
44+
AllowedMentions: &discord.AllowedMentions{},
45+
}); err != nil {
46+
slog.Error("failed to send message", tint.Err(err))
47+
}
48+
}
49+
50+
func (h *Handlers) OnChaptersLoaded(p disgolink.Player, event sponsorblock.ChaptersLoadedEvent) {
51+
channelID := h.MusicQueue.ChannelID(p.GuildID())
52+
if channelID == 0 {
53+
return
54+
}
55+
56+
content := "Chapters loaded:\n"
57+
for i, chapter := range event.Chapters {
58+
line := fmt.Sprintf("%d. %s: %s - %s\n", i+1, chapter.Name, res.FormatDuration(chapter.Start), res.FormatDuration(chapter.End))
59+
if len(content)+len(line) > 2000 {
60+
content += "..."
61+
break
62+
}
63+
content += line
64+
}
65+
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
66+
Content: content,
67+
AllowedMentions: &discord.AllowedMentions{},
68+
}); err != nil {
69+
slog.Error("failed to send message", tint.Err(err))
70+
}
71+
}
72+
73+
func (h *Handlers) OnChapterStarted(p disgolink.Player, event sponsorblock.ChapterStartedEvent) {
74+
channelID := h.MusicQueue.ChannelID(p.GuildID())
75+
if channelID == 0 {
76+
return
77+
}
78+
if _, err := h.Client.Rest().CreateMessage(channelID, discord.MessageCreate{
79+
Content: fmt.Sprintf("Chapter started: %s: %s - %s", event.Chapter.Name, res.FormatDuration(event.Chapter.Start), res.FormatDuration(event.Chapter.End)),
80+
AllowedMentions: &discord.AllowedMentions{},
81+
}); err != nil {
82+
slog.Error("failed to send message", tint.Err(err))
83+
}
84+
}
85+

lavalinkbot/queue.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ type PlayerManager struct {
2828
}
2929

3030
type queue struct {
31-
tracks []lavalink.Track
32-
mode RepeatMode
33-
channelID snowflake.ID
31+
tracks []lavalink.Track
32+
mode RepeatMode
33+
channelID snowflake.ID
34+
lyricsMessageID snowflake.ID
3435
}
3536

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

66+
func (q *PlayerManager) SetLyricsMessageID(guildID snowflake.ID, messageID snowflake.ID) {
67+
q.mu.Lock()
68+
defer q.mu.Unlock()
69+
70+
qu, ok := q.queues[guildID]
71+
if !ok {
72+
return
73+
}
74+
qu.lyricsMessageID = messageID
75+
}
76+
77+
func (q *PlayerManager) LyricsMessageID(guildID snowflake.ID) snowflake.ID {
78+
q.mu.Lock()
79+
defer q.mu.Unlock()
80+
81+
qu, ok := q.queues[guildID]
82+
if !ok {
83+
return 0
84+
}
85+
return qu.lyricsMessageID
86+
}
87+
6588
func (q *PlayerManager) Add(guildID snowflake.ID, channelID snowflake.ID, tracks ...lavalink.Track) {
6689
q.mu.Lock()
6790
defer q.mu.Unlock()

0 commit comments

Comments
 (0)