Initial commit
This commit is contained in:
commit
79e7cffb4e
49 changed files with 6399 additions and 0 deletions
309
Entities/GuildMusic.cs
Normal file
309
Entities/GuildMusic.cs
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
// Project Makoto Example Plugin
|
||||
// Copyright (C) 2023 Fortunevale
|
||||
// This code is licensed under MIT license (see 'LICENSE'-file for details)
|
||||
|
||||
using System.Linq;
|
||||
using DisCatSharp.Lavalink.Entities;
|
||||
using DisCatSharp.Lavalink.Enums;
|
||||
using Newtonsoft.Json;
|
||||
using ProjectMakoto.Database;
|
||||
using ProjectMakoto.Enums;
|
||||
using Xorog.UniversalExtensions;
|
||||
|
||||
namespace ProjectMakoto.Plugins.Music.Entities;
|
||||
|
||||
|
||||
[TableName("guilds")]
|
||||
public class GuildMusic : PluginDatabaseTable
|
||||
{
|
||||
public GuildMusic(BasePlugin plugin, ulong identifierValue) : base(plugin, identifierValue)
|
||||
{
|
||||
this.Id = identifierValue;
|
||||
}
|
||||
|
||||
[ColumnName("GuildId"), ColumnType(ColumnTypes.BigInt), Primary]
|
||||
internal ulong Id { get; init; }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.SongQueue = [];
|
||||
this.ChannelId = 0;
|
||||
this.CurrentVideo = null;
|
||||
this.CurrentVideoPosition = -1;
|
||||
this.Repeat = false;
|
||||
this.Shuffle = false;
|
||||
this.IsPaused = false;
|
||||
|
||||
this.Disposed = false;
|
||||
}
|
||||
|
||||
private DiscordGuild Guild { get; set; }
|
||||
|
||||
public List<ulong> collectedSkips = [];
|
||||
public List<ulong> collectedDisconnectVotes = [];
|
||||
public List<ulong> collectedClearQueueVotes = [];
|
||||
|
||||
[ColumnName("SongQueue"), ColumnType(ColumnTypes.LongText), Default("[]")]
|
||||
public QueueInfo[] SongQueue
|
||||
{
|
||||
get => JsonConvert.DeserializeObject<QueueInfo[]>(this.GetValue<string>(this.Id, "SongQueue")) ?? [];
|
||||
set => _ = this.SetValue(this.Id, "SongQueue", JsonConvert.SerializeObject(value));
|
||||
}
|
||||
|
||||
[ColumnName("Channel"), ColumnType(ColumnTypes.BigInt), Default("0")]
|
||||
public ulong ChannelId
|
||||
{
|
||||
get => this.GetValue<ulong>(this.Id, "Channel");
|
||||
set => _ = this.SetValue(this.Id, "Channel", value);
|
||||
}
|
||||
|
||||
[ColumnName("CurrentVideo"), ColumnType(ColumnTypes.Text), Nullable]
|
||||
public string? CurrentVideo
|
||||
{
|
||||
get => this.GetValue<string>(this.Id, "CurrentVideo");
|
||||
set => _ = this.SetValue(this.Id, "CurrentVideo", value ?? string.Empty);
|
||||
}
|
||||
|
||||
[ColumnName("CurrentPosition"), ColumnType(ColumnTypes.BigInt), Default("-1")]
|
||||
public long CurrentVideoPosition
|
||||
{
|
||||
get => this.GetValue<long>(this.Id, "CurrentPosition");
|
||||
set => _ = this.SetValue(this.Id, "CurrentPosition", value);
|
||||
}
|
||||
|
||||
[ColumnName("Repeat"), ColumnType(ColumnTypes.TinyInt), Default("0")]
|
||||
public bool Repeat
|
||||
{
|
||||
get => this.GetValue<bool>(this.Id, "Repeat");
|
||||
set => _ = this.SetValue(this.Id, "Repeat", value);
|
||||
}
|
||||
|
||||
[ColumnName("Shuffle"), ColumnType(ColumnTypes.TinyInt), Default("0")]
|
||||
public bool Shuffle
|
||||
{
|
||||
get => this.GetValue<bool>(this.Id, "Shuffle");
|
||||
set => _ = this.SetValue(this.Id, "Shuffle", value);
|
||||
}
|
||||
|
||||
[ColumnName("Paused"), ColumnType(ColumnTypes.TinyInt), Default("0")]
|
||||
public bool IsPaused
|
||||
{
|
||||
get => this.GetValue<bool>(this.Id, "Paused");
|
||||
set => _ = this.SetValue(this.Id, "Paused", value);
|
||||
}
|
||||
|
||||
public sealed class QueueInfo(string VideoTitle, string Url, TimeSpan length, ulong? guild, ulong? user)
|
||||
{
|
||||
public string UUID { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
public string VideoTitle { get; set; } = VideoTitle;
|
||||
public string Url { get; set; } = Url;
|
||||
|
||||
public TimeSpan Length { get; set; } = length;
|
||||
|
||||
public ulong GuildId = guild ?? 0;
|
||||
public ulong UserId = user ?? 0;
|
||||
}
|
||||
|
||||
public bool Disposed { private set; get; } = false;
|
||||
public bool Initialized { private set; get; } = false;
|
||||
|
||||
public void Dispose(Bot _bot, ulong Id, string reason)
|
||||
{
|
||||
this.Disposed = true;
|
||||
|
||||
MusicPlugin.Plugin!._logger.LogDebug("Disposed Player for {Id}. ({reason})", Id, reason);
|
||||
|
||||
MusicPlugin.Plugin.Guilds![Id].Reset();
|
||||
}
|
||||
|
||||
public void QueueHandler(Bot _bot, DiscordClient sender, LavalinkSession session, LavalinkGuildPlayer guildPlayer)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.Initialized || this.Disposed)
|
||||
return;
|
||||
|
||||
this.Initialized = true;
|
||||
this.Guild = guildPlayer.Guild;
|
||||
|
||||
MusicPlugin.Plugin!._logger.LogDebug("Initializing Player for {Guild}..", this.Guild.Id);
|
||||
|
||||
var UserAmount = guildPlayer.Channel.Users.Count;
|
||||
CancellationTokenSource VoiceUpdateTokenSource = new();
|
||||
Task VoiceStateUpdated(DiscordClient s, VoiceStateUpdateEventArgs e)
|
||||
{
|
||||
if (e.Guild is null || e.Guild?.Id != this.Guild?.Id)
|
||||
return Task.CompletedTask;
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
if (e.Channel?.Id == guildPlayer.Channel?.Id || e.Before?.Channel?.Id == guildPlayer.Channel?.Id)
|
||||
{
|
||||
VoiceUpdateTokenSource.Cancel();
|
||||
VoiceUpdateTokenSource = new();
|
||||
|
||||
UserAmount = e.Channel is not null ? e.Channel.Users.Count : e.Guild!.Channels.First(x => x.Key == e.Before?.Channel?.Id).Value.Users.Count;
|
||||
|
||||
MusicPlugin.Plugin!._logger.LogTrace("UserAmount updated to {UserAmount} for {Guild}", UserAmount, this.Guild!.Id);
|
||||
|
||||
if (UserAmount <= 1)
|
||||
_ = Task.Delay(30000, VoiceUpdateTokenSource.Token).ContinueWith(x =>
|
||||
{
|
||||
if (!x.IsCompletedSuccessfully)
|
||||
return;
|
||||
|
||||
if (this.Disposed)
|
||||
return;
|
||||
|
||||
if (UserAmount <= 1)
|
||||
{
|
||||
MusicPlugin.Plugin.Guilds![this.Id].Dispose(_bot, e.Guild!.Id, "No users");
|
||||
MusicPlugin.Plugin.Guilds![this.Id].Reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}).Add(_bot);
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
if (e.User.Id == sender.CurrentUser.Id)
|
||||
{
|
||||
if (e.After is null || e.After.Channel is null)
|
||||
{
|
||||
_ = guildPlayer.DisconnectAsync();
|
||||
this.Dispose(_bot, e.Guild!.Id, "Disconnected");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}).Add(_bot);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Task StateUpdated(LavalinkGuildPlayer sender, LavalinkPlayerStateUpdateEventArgs e)
|
||||
{
|
||||
this.CurrentVideo = (sender.CurrentTrack?.Info?.Uri ?? new UriBuilder().Uri).ToString();
|
||||
this.CurrentVideoPosition = (Convert.ToInt64(e.State?.Position.TotalSeconds ?? -1d));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
MusicPlugin.Plugin!._logger.LogDebug("Initializing VoiceStateUpdated Event for {Guild}..", this.Guild.Id);
|
||||
sender.VoiceStateUpdated += VoiceStateUpdated;
|
||||
|
||||
MusicPlugin.Plugin!._logger.LogDebug("Initializing PlayerUpdated Event for {Guild}..", this.Guild.Id);
|
||||
guildPlayer.StateUpdated += StateUpdated;
|
||||
|
||||
QueueInfo? LastPlayedTrack = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var WaitSeconds = 30;
|
||||
|
||||
while ((guildPlayer!.CurrentTrack is not null || MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.Length <= 0) && !this.Disposed)
|
||||
{
|
||||
if (guildPlayer.CurrentTrack is null && MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.Length <= 0)
|
||||
{
|
||||
WaitSeconds--;
|
||||
|
||||
if (WaitSeconds <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
if (this.Disposed)
|
||||
{
|
||||
sender.VoiceStateUpdated -= VoiceStateUpdated;
|
||||
guildPlayer.StateUpdated -= StateUpdated;
|
||||
|
||||
_ = guildPlayer.DisconnectAsync();
|
||||
|
||||
this.Dispose(this.Bot, this.Id, "Graceful Disconnect");
|
||||
return;
|
||||
}
|
||||
|
||||
if (WaitSeconds <= 0)
|
||||
this.Dispose(_bot, this.Guild.Id, "Time out, nothing playing");
|
||||
|
||||
QueueInfo Track;
|
||||
|
||||
var skipSongs = 0;
|
||||
|
||||
if (LastPlayedTrack is not null &&
|
||||
MusicPlugin.Plugin.Guilds![this.Guild.Id].Repeat &&
|
||||
MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.IsNotNullAndNotEmpty() &&
|
||||
MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.Contains(LastPlayedTrack))
|
||||
{
|
||||
skipSongs = Array.IndexOf(MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue, LastPlayedTrack) + 1;
|
||||
|
||||
if (skipSongs >= MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.Length)
|
||||
skipSongs = 0;
|
||||
}
|
||||
|
||||
if (this.SongQueue.Length <= 0)
|
||||
{
|
||||
this.Dispose(_bot, this.Guild.Id, "Queue empty");
|
||||
continue;
|
||||
}
|
||||
|
||||
Track = MusicPlugin.Plugin.Guilds![this.Guild.Id].Shuffle
|
||||
? MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.OrderBy(_ => Guid.NewGuid()).ToList().First()
|
||||
: MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.ToList().Skip(skipSongs).First();
|
||||
|
||||
LastPlayedTrack = Track;
|
||||
|
||||
MusicPlugin.Plugin.Guilds![this.Guild.Id].collectedSkips.Clear();
|
||||
|
||||
var loadResult = await session.LoadTracksAsync(LavalinkSearchType.Plain, Track.Url);
|
||||
|
||||
if (loadResult.LoadType is LavalinkLoadResultType.Error or LavalinkLoadResultType.Empty)
|
||||
{
|
||||
MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue = MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.Remove(x => x.UUID, Track);
|
||||
continue;
|
||||
}
|
||||
|
||||
var loadedTrack = loadResult.LoadType switch
|
||||
{
|
||||
LavalinkLoadResultType.Track => loadResult.GetResultAs<LavalinkTrack>(),
|
||||
LavalinkLoadResultType.Playlist => loadResult.GetResultAs<LavalinkPlaylist>().Tracks.First(),
|
||||
LavalinkLoadResultType.Search => loadResult.GetResultAs<List<LavalinkTrack>>().First(),
|
||||
_ => throw new InvalidOperationException("Unexpected load result type.")
|
||||
};
|
||||
|
||||
guildPlayer = session.GetGuildPlayer(this.Guild) ?? throw new NullReferenceException();
|
||||
this.ChannelId = guildPlayer.Channel.Id;
|
||||
|
||||
if (guildPlayer is not null)
|
||||
{
|
||||
_ = await guildPlayer.PlayAsync(loadedTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Dispose(_bot, this.Guild.Id, "guildConnection is null");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!MusicPlugin.Plugin.Guilds![this.Guild.Id].Repeat)
|
||||
MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue = MusicPlugin.Plugin.Guilds![this.Guild.Id].SongQueue.Remove(x => x.UUID, Track);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MusicPlugin.Plugin!._logger.LogError(ex, "An exception occurred while trying to handle music Channel");
|
||||
|
||||
_ = guildPlayer.DisconnectAsync();
|
||||
this.Dispose(_bot, this.Guild.Id, "Exception");
|
||||
throw;
|
||||
}
|
||||
}).Add(_bot);
|
||||
}
|
||||
}
|
||||
26
Entities/PlaylistEntry.cs
Normal file
26
Entities/PlaylistEntry.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Project Makoto
|
||||
// Copyright (C) 2024 Fortunevale
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY
|
||||
|
||||
namespace ProjectMakoto.Plugins.Music.Entities;
|
||||
|
||||
public sealed class PlaylistEntry
|
||||
{
|
||||
private string _Title { get; set; }
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public string Title { get => this._Title; set => this._Title = value.TruncateWithIndication(100); }
|
||||
|
||||
private TimeSpan? _Length { get; set; }
|
||||
public TimeSpan? Length { get => this._Length; set => this._Length = value; }
|
||||
|
||||
private string _Url { get; set; }
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public string Url { get => this._Url; set => this._Url = value.TruncateWithIndication(2048); }
|
||||
|
||||
public DateTime AddedTime { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
12
Entities/PluginConfig.cs
Normal file
12
Entities/PluginConfig.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
// Project Makoto Example Plugin
|
||||
// Copyright (C) 2023 Fortunevale
|
||||
// This code is licensed under MIT license (see 'LICENSE'-file for details)
|
||||
|
||||
namespace ProjectMakoto.Plugins.Music.Entities;
|
||||
|
||||
public class PluginConfig
|
||||
{
|
||||
public string Host = "127.0.0.1";
|
||||
public int Port = 2333;
|
||||
public string Password = "youshallnotpass";
|
||||
}
|
||||
240
Entities/Translations.cs
Normal file
240
Entities/Translations.cs
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
// Project Makoto
|
||||
// Copyright (C) 2023 Fortunevale
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY
|
||||
namespace ProjectMakoto.Plugins.Music.Entities;
|
||||
#pragma warning disable CS8981
|
||||
#pragma warning disable CS8618
|
||||
#pragma warning disable IDE1006
|
||||
public class Translations : ITranslations
|
||||
{
|
||||
public Dictionary<string, int> Progress = new();
|
||||
public CommandTranslation[] CommandList { get; set; }
|
||||
#region AutoGenerated
|
||||
public commands Commands;
|
||||
public sealed class commands
|
||||
{
|
||||
public music Music;
|
||||
public sealed class music
|
||||
{
|
||||
public playlists Playlists;
|
||||
public sealed class playlists
|
||||
{
|
||||
public modify Modify;
|
||||
public sealed class modify
|
||||
{
|
||||
public SingleTranslationKey DeleteNote;
|
||||
public SingleTranslationKey HexHelp;
|
||||
public SingleTranslationKey NewPlaylistColorPrompt;
|
||||
public SingleTranslationKey NewPlaylistColor;
|
||||
public SingleTranslationKey ThumbnailError;
|
||||
public SingleTranslationKey ThumbnailSizeError;
|
||||
public SingleTranslationKey ImportingThumbnail;
|
||||
public SingleTranslationKey UploadThumbnail;
|
||||
public SingleTranslationKey AddSong;
|
||||
public SingleTranslationKey TrackLimit;
|
||||
public SingleTranslationKey ModifyingPlaylist;
|
||||
public SingleTranslationKey Track;
|
||||
public SingleTranslationKey CurrentTrackCount;
|
||||
public SingleTranslationKey RemoveDuplicates;
|
||||
public SingleTranslationKey RemoveTracks;
|
||||
public SingleTranslationKey AddTracks;
|
||||
public SingleTranslationKey ChangeThumbnail;
|
||||
public SingleTranslationKey ChangeColor;
|
||||
public SingleTranslationKey ChangeName;
|
||||
}
|
||||
public createPlaylist CreatePlaylist;
|
||||
public sealed class createPlaylist
|
||||
{
|
||||
public SingleTranslationKey Created;
|
||||
public SingleTranslationKey Creating;
|
||||
public SingleTranslationKey SupportedAddType;
|
||||
public SingleTranslationKey SetFirstTracks;
|
||||
public SingleTranslationKey SetPlaylistName;
|
||||
public SingleTranslationKey FirstTracks;
|
||||
public SingleTranslationKey PlaylistName;
|
||||
public SingleTranslationKey CreatePlaylist;
|
||||
public SingleTranslationKey ChangeTracks;
|
||||
public SingleTranslationKey ChangeName;
|
||||
}
|
||||
public import Import;
|
||||
public sealed class import
|
||||
{
|
||||
public SingleTranslationKey ImportFailed;
|
||||
public SingleTranslationKey Importing;
|
||||
public SingleTranslationKey UploadExport;
|
||||
public SingleTranslationKey Created;
|
||||
public SingleTranslationKey Creating;
|
||||
public SingleTranslationKey NotLoaded;
|
||||
public SingleTranslationKey PlaylistUrl;
|
||||
public SingleTranslationKey ImportPlaylist;
|
||||
public SingleTranslationKey ImportMethod;
|
||||
public SingleTranslationKey ExportedPlaylist;
|
||||
public SingleTranslationKey Link;
|
||||
}
|
||||
public delete Delete;
|
||||
public sealed class delete
|
||||
{
|
||||
public SingleTranslationKey Deleted;
|
||||
public SingleTranslationKey Deleting;
|
||||
}
|
||||
public export Export;
|
||||
public sealed class export
|
||||
{
|
||||
public SingleTranslationKey Exported;
|
||||
}
|
||||
public share Share;
|
||||
public sealed class share
|
||||
{
|
||||
public MultiTranslationKey Shared;
|
||||
}
|
||||
public addToQueue AddToQueue;
|
||||
public sealed class addToQueue
|
||||
{
|
||||
public SingleTranslationKey Adding;
|
||||
}
|
||||
public manage Manage;
|
||||
public sealed class manage
|
||||
{
|
||||
public SingleTranslationKey PlaylistSelectorDelete;
|
||||
public SingleTranslationKey PlaylistSelectorModify;
|
||||
public SingleTranslationKey PlaylistSelectorExport;
|
||||
public SingleTranslationKey PlaylistSelectorShare;
|
||||
public SingleTranslationKey PlaylistSelectorQueue;
|
||||
public SingleTranslationKey DeleteButton;
|
||||
public SingleTranslationKey ModifyButton;
|
||||
public SingleTranslationKey CreateNewButton;
|
||||
public SingleTranslationKey SaveCurrentButton;
|
||||
public SingleTranslationKey ImportButton;
|
||||
public SingleTranslationKey ExportButton;
|
||||
public SingleTranslationKey ShareButton;
|
||||
public SingleTranslationKey AddToQueueButton;
|
||||
public SingleTranslationKey NoPlaylists;
|
||||
}
|
||||
public loadShare LoadShare;
|
||||
public sealed class loadShare
|
||||
{
|
||||
public SingleTranslationKey Imported;
|
||||
public SingleTranslationKey Importing;
|
||||
public SingleTranslationKey ImportButton;
|
||||
public SingleTranslationKey CreatedBy;
|
||||
public SingleTranslationKey PlaylistName;
|
||||
public SingleTranslationKey Found;
|
||||
public SingleTranslationKey NotFound;
|
||||
public SingleTranslationKey Loading;
|
||||
}
|
||||
public SingleTranslationKey ThumbnailModerationNote;
|
||||
public SingleTranslationKey NameModerationNote;
|
||||
public SingleTranslationKey Tracks;
|
||||
public SingleTranslationKey NoPlaylist;
|
||||
public SingleTranslationKey PlayListLimit;
|
||||
public SingleTranslationKey Title;
|
||||
}
|
||||
public skip Skip;
|
||||
public sealed class skip
|
||||
{
|
||||
public SingleTranslationKey VoteButton;
|
||||
public SingleTranslationKey VoteStarted;
|
||||
public SingleTranslationKey Skipped;
|
||||
public SingleTranslationKey AlreadyVoted;
|
||||
}
|
||||
public shuffle Shuffle;
|
||||
public sealed class shuffle
|
||||
{
|
||||
public SingleTranslationKey Off;
|
||||
public SingleTranslationKey On;
|
||||
}
|
||||
public repeat Repeat;
|
||||
public sealed class repeat
|
||||
{
|
||||
public SingleTranslationKey Off;
|
||||
public SingleTranslationKey On;
|
||||
}
|
||||
public removeQueue RemoveQueue;
|
||||
public sealed class removeQueue
|
||||
{
|
||||
public SingleTranslationKey Removed;
|
||||
public SingleTranslationKey NoSong;
|
||||
public SingleTranslationKey OutOfRange;
|
||||
}
|
||||
public join Join;
|
||||
public sealed class join
|
||||
{
|
||||
public SingleTranslationKey AlreadyUsed;
|
||||
public SingleTranslationKey Joined;
|
||||
}
|
||||
public queue Queue;
|
||||
public sealed class queue
|
||||
{
|
||||
public SingleTranslationKey Play;
|
||||
public SingleTranslationKey Repeat;
|
||||
public SingleTranslationKey Shuffle;
|
||||
public SingleTranslationKey NoSong;
|
||||
public SingleTranslationKey CurrentlyPlaying;
|
||||
public SingleTranslationKey Track;
|
||||
public SingleTranslationKey QueueCount;
|
||||
}
|
||||
public play Play;
|
||||
public sealed class play
|
||||
{
|
||||
public SingleTranslationKey SearchSuccess;
|
||||
public SingleTranslationKey NoMatches;
|
||||
public SingleTranslationKey FailedToLoad;
|
||||
public SingleTranslationKey PlatformSelect;
|
||||
public SingleTranslationKey LookingForPlatform;
|
||||
public SingleTranslationKey LookingFor;
|
||||
public SingleTranslationKey Duration;
|
||||
public SingleTranslationKey Uploader;
|
||||
public SingleTranslationKey QueuePosition;
|
||||
public SingleTranslationKey QueuePositions;
|
||||
public SingleTranslationKey QueuedSingle;
|
||||
public SingleTranslationKey QueuedMultiple;
|
||||
public SingleTranslationKey Preparing;
|
||||
}
|
||||
public pause Pause;
|
||||
public sealed class pause
|
||||
{
|
||||
public SingleTranslationKey Resumed;
|
||||
public SingleTranslationKey Paused;
|
||||
}
|
||||
public forceSkip ForceSkip;
|
||||
public sealed class forceSkip
|
||||
{
|
||||
public SingleTranslationKey Skipped;
|
||||
}
|
||||
public forceDisconnect ForceDisconnect;
|
||||
public sealed class forceDisconnect
|
||||
{
|
||||
public SingleTranslationKey Disconnected;
|
||||
}
|
||||
public forceClearQueue ForceClearQueue;
|
||||
public sealed class forceClearQueue
|
||||
{
|
||||
public SingleTranslationKey Cleared;
|
||||
}
|
||||
public disconnect Disconnect;
|
||||
public sealed class disconnect
|
||||
{
|
||||
public SingleTranslationKey VoteButton;
|
||||
public SingleTranslationKey VoteStarted;
|
||||
public SingleTranslationKey Disconnected;
|
||||
public SingleTranslationKey AlreadyVoted;
|
||||
}
|
||||
public clearQueue ClearQueue;
|
||||
public sealed class clearQueue
|
||||
{
|
||||
public SingleTranslationKey VoteButton;
|
||||
public SingleTranslationKey VoteStarted;
|
||||
public SingleTranslationKey Cleared;
|
||||
public SingleTranslationKey AlreadyVoted;
|
||||
}
|
||||
public SingleTranslationKey DjRole;
|
||||
public SingleTranslationKey NotSameChannel;
|
||||
}
|
||||
}
|
||||
#endregion AutoGenerated
|
||||
}
|
||||
40
Entities/UserMusic.cs
Normal file
40
Entities/UserMusic.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// Project Makoto Example Plugin
|
||||
// Copyright (C) 2023 Fortunevale
|
||||
// This code is licensed under MIT license (see 'LICENSE'-file for details)
|
||||
|
||||
using System.Linq;
|
||||
using DisCatSharp.Lavalink.Entities;
|
||||
using DisCatSharp.Lavalink.Enums;
|
||||
using Newtonsoft.Json;
|
||||
using ProjectMakoto.Database;
|
||||
using ProjectMakoto.Enums;
|
||||
using Xorog.UniversalExtensions;
|
||||
|
||||
namespace ProjectMakoto.Plugins.Music.Entities;
|
||||
|
||||
|
||||
[TableName("users")]
|
||||
public class UserMusic : PluginDatabaseTable
|
||||
{
|
||||
public UserMusic(BasePlugin plugin, ulong identifierValue) : base(plugin, identifierValue)
|
||||
{
|
||||
this.Id = identifierValue;
|
||||
}
|
||||
|
||||
[ColumnName("UserId"), ColumnType(ColumnTypes.BigInt), Primary]
|
||||
internal ulong Id { get; init; }
|
||||
|
||||
[ColumnName("Playlists"), ColumnType(ColumnTypes.LongText), Default("[]")]
|
||||
public UserPlaylist[] Playlists
|
||||
{
|
||||
get => (JsonConvert.DeserializeObject<UserPlaylist[]>(this.GetValue<string>(this.Id, "Playlists")) ?? [])
|
||||
.Select(x =>
|
||||
{
|
||||
x.Bot = this.Bot;
|
||||
x.Parent = this;
|
||||
|
||||
return x;
|
||||
}).ToArray();
|
||||
set => this.SetValue(this.Id, "Playlists", JsonConvert.SerializeObject(value));
|
||||
}
|
||||
}
|
||||
77
Entities/UserPlaylist.cs
Normal file
77
Entities/UserPlaylist.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// Project Makoto
|
||||
// Copyright (C) 2024 Fortunevale
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY
|
||||
|
||||
namespace ProjectMakoto.Plugins.Music.Entities;
|
||||
|
||||
public sealed class UserPlaylist
|
||||
{
|
||||
[JsonIgnore]
|
||||
public Bot Bot { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public UserMusic Parent { get; set; }
|
||||
|
||||
public string PlaylistId { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
private string _PlaylistName { get; set; } = "";
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public string PlaylistName
|
||||
{
|
||||
get => this._PlaylistName;
|
||||
set
|
||||
{
|
||||
this._PlaylistName = value.TruncateWithIndication(256);
|
||||
this.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private string _PlaylistColor { get; set; } = "#FFFFFF";
|
||||
public string PlaylistColor
|
||||
{
|
||||
get => this._PlaylistColor;
|
||||
set
|
||||
{
|
||||
this._PlaylistColor = value.Truncate(7).IsValidHexColor();
|
||||
this.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private string _PlaylistThumbnail { get; set; } = "";
|
||||
public string PlaylistThumbnail
|
||||
{
|
||||
get => this._PlaylistThumbnail;
|
||||
set
|
||||
{
|
||||
this._PlaylistThumbnail = value.Truncate(2048);
|
||||
this.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private PlaylistEntry[] _List = Array.Empty<PlaylistEntry>();
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public PlaylistEntry[] List
|
||||
{
|
||||
get => this._List;
|
||||
set
|
||||
{
|
||||
this._List = value.Take(250).ToArray();
|
||||
this.Update();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (this.Bot is null || this.Parent is null)
|
||||
return;
|
||||
|
||||
this.Parent.Playlists = this.Parent.Playlists.Update(x => x.PlaylistId, this);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue