Skip to content

Commit 6389f9d

Browse files
committed
add lavalyrics live lyrics support
1 parent 6811d44 commit 6389f9d

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

commands/music.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,20 @@ var music = discord.SlashCommandCreate{
478478
},
479479
},
480480
},
481+
discord.ApplicationCommandOptionSubCommandGroup{
482+
Name: "live-lyrics",
483+
Description: "Subscribes or unsubscribes to live lyrics",
484+
Options: []discord.ApplicationCommandOptionSubCommand{
485+
{
486+
Name: "subscribe",
487+
Description: "Subscribes to live lyrics",
488+
},
489+
{
490+
Name: "unsubscribe",
491+
Description: "Unsubscribes from live lyrics",
492+
},
493+
},
494+
},
481495
},
482496
}
483497

commands/play.go

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

295+
c.MusicQueue.Add(*e.GuildID(), e.Channel().ID(), tracks...)
296+
295297
playCtx, playCancel := context.WithTimeout(e.Ctx, 10*time.Second)
296298
defer playCancel()
297299
if err = player.Update(playCtx, lavalink.WithTrack(track)); err != nil {
@@ -300,10 +302,10 @@ func (c *Commands) Play(data discord.SlashCommandInteractionData, e *handler.Com
300302
})
301303
return err
302304
}
303-
}
304-
if len(tracks) > 0 {
305+
} else {
305306
c.MusicQueue.Add(*e.GuildID(), e.Channel().ID(), tracks...)
306307
}
308+
307309
return nil
308310
}
309311

handlers/lavalink.go

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

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)