forked from EllieBotDevs/elliebot
245 lines
No EOL
8 KiB
C#
245 lines
No EOL
8 KiB
C#
#nullable disable
|
|
using LinqToDB;
|
|
using EllieBot.Modules.Music.Services;
|
|
using EllieBot.Db.Models;
|
|
|
|
namespace EllieBot.Modules.Music;
|
|
|
|
public sealed partial class Music
|
|
{
|
|
[Group]
|
|
public sealed partial class PlaylistCommands : EllieModule<IMusicService>
|
|
{
|
|
private static readonly SemaphoreSlim _playlistLock = new(1, 1);
|
|
private readonly DbService _db;
|
|
private readonly IBotCreds _creds;
|
|
|
|
public PlaylistCommands(DbService db, IBotCreds creds)
|
|
{
|
|
_db = db;
|
|
_creds = creds;
|
|
}
|
|
|
|
private async Task EnsureBotInVoiceChannelAsync(ulong voiceChannelId, IGuildUser botUser = null)
|
|
{
|
|
botUser ??= await ctx.Guild.GetCurrentUserAsync();
|
|
await _voiceChannelLock.WaitAsync();
|
|
try
|
|
{
|
|
if (botUser.VoiceChannel?.Id is null || !_service.TryGetMusicPlayer(ctx.Guild.Id, out _))
|
|
await _service.JoinVoiceChannelAsync(ctx.Guild.Id, voiceChannelId);
|
|
}
|
|
finally
|
|
{
|
|
_voiceChannelLock.Release();
|
|
}
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task Playlists([Leftover] int num = 1)
|
|
{
|
|
if (num <= 0)
|
|
return;
|
|
|
|
List<MusicPlaylist> playlists;
|
|
|
|
await using (var uow = _db.GetDbContext())
|
|
{
|
|
playlists = uow.Set<MusicPlaylist>().GetPlaylistsOnPage(num);
|
|
}
|
|
|
|
var embed = CreateEmbed()
|
|
.WithAuthor(GetText(strs.playlists_page(num)), MUSIC_ICON_URL)
|
|
.WithDescription(string.Join("\n",
|
|
playlists.Select(r => GetText(strs.playlists(r.Id, r.Name, r.Author, r.Songs.Count)))))
|
|
.WithOkColor();
|
|
|
|
await Response().Embed(embed).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task DeletePlaylist([Leftover] int id)
|
|
{
|
|
var success = false;
|
|
try
|
|
{
|
|
await using var uow = _db.GetDbContext();
|
|
var pl = uow.Set<MusicPlaylist>().FirstOrDefault(x => x.Id == id);
|
|
|
|
if (pl is not null)
|
|
{
|
|
if (_creds.IsOwner(ctx.User) || pl.AuthorId == ctx.User.Id)
|
|
{
|
|
uow.Set<MusicPlaylist>().Remove(pl);
|
|
await uow.SaveChangesAsync();
|
|
success = true;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log.Warning(ex, "Error deleting playlist");
|
|
}
|
|
|
|
if (!success)
|
|
await Response().Error(strs.playlist_delete_fail).SendAsync();
|
|
else
|
|
await Response().Confirm(strs.playlist_deleted).SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task PlaylistShow(int id, int page = 1)
|
|
{
|
|
if (page-- < 1)
|
|
return;
|
|
|
|
MusicPlaylist mpl;
|
|
await using (var uow = _db.GetDbContext())
|
|
{
|
|
mpl = uow.Set<MusicPlaylist>().GetWithSongs(id);
|
|
}
|
|
|
|
await Response()
|
|
.Paginated()
|
|
.Items(mpl.Songs)
|
|
.PageSize(20)
|
|
.CurrentPage(page)
|
|
.Page((items, _) =>
|
|
{
|
|
var i = 0;
|
|
var str = string.Join("\n",
|
|
items
|
|
.Select(x => $"`{++i}.` [{x.Title.TrimTo(45)}]({x.Query}) `{x.Provider}`"));
|
|
return CreateEmbed().WithTitle($"\"{mpl.Name}\" by {mpl.Author}")
|
|
.WithOkColor()
|
|
.WithDescription(str);
|
|
})
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task Save([Leftover] string name)
|
|
{
|
|
if (!_service.TryGetMusicPlayer(ctx.Guild.Id, out var mp))
|
|
{
|
|
await Response().Error(strs.no_player).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var songs = mp.GetQueuedTracks()
|
|
.Select(s => new PlaylistSong
|
|
{
|
|
Provider = s.Platform.ToString(),
|
|
ProviderType = (MusicType)s.Platform,
|
|
Title = s.Title,
|
|
Query = s.Url
|
|
})
|
|
.ToList();
|
|
|
|
MusicPlaylist playlist;
|
|
await using (var uow = _db.GetDbContext())
|
|
{
|
|
playlist = new()
|
|
{
|
|
Name = name,
|
|
Author = ctx.User.Username,
|
|
AuthorId = ctx.User.Id,
|
|
Songs = songs.ToList()
|
|
};
|
|
uow.Set<MusicPlaylist>().Add(playlist);
|
|
await uow.SaveChangesAsync();
|
|
}
|
|
|
|
await Response()
|
|
.Embed(CreateEmbed()
|
|
.WithOkColor()
|
|
.WithTitle(GetText(strs.playlist_saved))
|
|
.AddField(GetText(strs.name), name)
|
|
.AddField(GetText(strs.id), playlist.Id.ToString()))
|
|
.SendAsync();
|
|
}
|
|
|
|
[Cmd]
|
|
[RequireContext(ContextType.Guild)]
|
|
public async Task Load([Leftover] int id)
|
|
{
|
|
// expensive action, 1 at a time
|
|
await _playlistLock.WaitAsync();
|
|
try
|
|
{
|
|
var user = (IGuildUser)ctx.User;
|
|
var voiceChannelId = user.VoiceChannel?.Id;
|
|
|
|
if (voiceChannelId is null)
|
|
{
|
|
await Response().Error(strs.must_be_in_voice).SendAsync();
|
|
return;
|
|
}
|
|
|
|
_ = ctx.Channel.TriggerTypingAsync();
|
|
|
|
var botUser = await ctx.Guild.GetCurrentUserAsync();
|
|
await EnsureBotInVoiceChannelAsync(voiceChannelId!.Value, botUser);
|
|
|
|
if (botUser.VoiceChannel?.Id != voiceChannelId)
|
|
{
|
|
await Response().Error(strs.not_with_bot_in_voice).SendAsync();
|
|
return;
|
|
}
|
|
|
|
var mp = await _service.GetOrCreateMusicPlayerAsync((ITextChannel)ctx.Channel);
|
|
if (mp is null)
|
|
{
|
|
await Response().Error(strs.no_player).SendAsync();
|
|
return;
|
|
}
|
|
|
|
MusicPlaylist mpl;
|
|
await using (var uow = _db.GetDbContext())
|
|
{
|
|
mpl = uow.Set<MusicPlaylist>().GetWithSongs(id);
|
|
}
|
|
|
|
if (mpl is null)
|
|
{
|
|
await Response().Error(strs.playlist_id_not_found).SendAsync();
|
|
return;
|
|
}
|
|
|
|
IUserMessage msg = null;
|
|
try
|
|
{
|
|
msg = await Response()
|
|
.Pending(strs.attempting_to_queue(Format.Bold(mpl.Songs.Count.ToString())))
|
|
.SendAsync();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
|
|
await mp.EnqueueManyAsync(mpl.Songs.Select(x => (x.Query, (MusicPlatform)x.ProviderType)),
|
|
ctx.User.ToString());
|
|
|
|
if (msg is not null)
|
|
await msg.ModifyAsync(m => m.Content = GetText(strs.playlist_queue_complete));
|
|
}
|
|
finally
|
|
{
|
|
_playlistLock.Release();
|
|
}
|
|
}
|
|
|
|
[Cmd]
|
|
[OwnerOnly]
|
|
public async Task DeletePlaylists()
|
|
{
|
|
await using var uow = _db.GetDbContext();
|
|
await uow.Set<MusicPlaylist>().DeleteAsync();
|
|
await uow.SaveChangesAsync();
|
|
}
|
|
}
|
|
} |