From 8ddfdcfa90b6261449fcde50a6190c835b305dff Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 28 Dec 2023 16:03:08 +0100 Subject: [PATCH 001/113] feat: system service --- build_with_dotnet_publish.sh | 2 ++ hatsune-miku.service | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 build_with_dotnet_publish.sh create mode 100644 hatsune-miku.service diff --git a/build_with_dotnet_publish.sh b/build_with_dotnet_publish.sh new file mode 100644 index 00000000..88cb3a2c --- /dev/null +++ b/build_with_dotnet_publish.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet publish -c Release -r linux-x64 --self-contained=true -p:PublishSingleFile=false -p:GenerateRuntimeConfigurationFiles=true \ No newline at end of file diff --git a/hatsune-miku.service b/hatsune-miku.service new file mode 100644 index 00000000..13450947 --- /dev/null +++ b/hatsune-miku.service @@ -0,0 +1,19 @@ +[Unit] +Description=Hatsune Miku Bot +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart="/root/mikuBotNet8/MikuSharp/MikuSharp/bin/Release/net7.0/linux-x64/publish/MikuSharp" +WorkingDirectory=/root/mikuBotNet8/MikuSharp/MikuSharp/bin/Release/net7.0/linux-x64/publish +Restart=always +RestartSec=10 +KillSignal=SIGINT +Environment=ASPNETCORE_ENVIRONMENT=Production +Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false +SyslogIdentifier=HastuneMikuBot +User=root +Environment=DOTNET_ROOT=/usr/bin/dotnet + +[Install] +WantedBy=multi-user.target \ No newline at end of file From e847d68373b39ceb2bb0835aa57c4a67910eceb4 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 28 Dec 2023 19:11:26 +0100 Subject: [PATCH 002/113] fix miku --- MikuSharp/MikuBot.cs | 10 +++++++++- MikuSharp/MikuSharp.csproj | 15 +++++---------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 22215f4a..a7150110 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -170,7 +170,15 @@ internal static async Task RegisterEvents() discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => { - sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", sender.Client.ShardId); + sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", args.ShardId); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleReady += (sender, args) => + { + sender.Client.Logger.LogInformation("Application commands module is ready"); + args.Handled = true; return Task.CompletedTask; }; diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index fcc476dd..d8cf0d74 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -46,16 +46,11 @@ - - - - - - + + + + + From 010370e33d0c035292886ed1a5f7ce016bde185d Mon Sep 17 00:00:00 2001 From: root Date: Thu, 28 Dec 2023 19:12:00 +0100 Subject: [PATCH 003/113] chmod --- build_with_dotnet_publish.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build_with_dotnet_publish.sh diff --git a/build_with_dotnet_publish.sh b/build_with_dotnet_publish.sh old mode 100644 new mode 100755 From 6720337e6f6b661601533135bbcaee9ae75e830f Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 29 Dec 2023 17:58:03 +0100 Subject: [PATCH 004/113] mew --- MikuSharp/Commands/NSFW.cs | 2 +- MikuSharp/MikuBot.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index 7c47e271..b0db28b7 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -9,7 +9,7 @@ namespace MikuSharp.Commands { [RequireNsfw] - class NSFW : BaseCommandModule + public class NSFW : BaseCommandModule { [Command("4k")] [Description("lewd")] diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index a7150110..843d5651 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -133,7 +133,7 @@ internal MikuBot() CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() { - CaseSensitive = true, + CaseSensitive = false, EnableMentionPrefix = true, DmHelp = false, EnableDefaultHelp = true, From 593df1464687ccfc1b05a9fb68270e14d2a765e3 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 30 Dec 2023 18:43:54 +0100 Subject: [PATCH 005/113] bump important dcs fix --- MikuSharp/MikuSharp.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index d8cf0d74..87d578de 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -46,11 +46,11 @@ - - - - - + + + + + From dd599fc0bf04e18cd451fc14efe5bfc3fc68c359 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 30 Dec 2023 19:45:17 +0100 Subject: [PATCH 006/113] Update NicoNicoNii --- NicoNicoNii | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NicoNicoNii b/NicoNicoNii index f3b1aab6..2ddd0c18 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit f3b1aab659dc729baca3b240df5b003f58979fea +Subproject commit 2ddd0c18f15404dcbf82add884c8aea2256ef746 From b7fe8c86f89d08ca2358f32bfd880849f6620363 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 4 Jan 2024 03:05:26 +0100 Subject: [PATCH 007/113] fix critical dcs bug --- MikuSharp/MikuSharp.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 87d578de..ff64e13b 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -46,11 +46,11 @@ - - - - - + + + + + @@ -67,7 +67,7 @@ - + From 83ec8e237e2563b0c9021faff54a9c458088d5b2 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 4 Jan 2024 03:06:25 +0100 Subject: [PATCH 008/113] Update MikuBot.cs --- MikuSharp/MikuBot.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 843d5651..49618625 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -127,7 +127,6 @@ internal MikuBot() EnableDefaultHelp = true, DebugStartup = true, EnableLocalization = false, - ManualOverride = true, GenerateTranslationFilesOnly = false }).Result; From a3c957400c6f50d9eb3957c45095158c1beec76a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 17 Jan 2024 21:01:06 +0100 Subject: [PATCH 009/113] chore(deps): bump packages --- MikuSharp/MikuSharp.csproj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index ff64e13b..8095b8f6 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -46,15 +46,15 @@ - - - - - + + + + + - - + + From d3c33aced4d677d56deab4f456ce61f5225b535b Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 17 Jan 2024 21:26:19 +0100 Subject: [PATCH 010/113] chore: consume discordTokenDev if DEBUG build --- MikuSharp/Entities/BotConfig.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index d30aaf35..3a9c4c35 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -4,7 +4,11 @@ namespace MikuSharp.Entities; public partial class BotConfig { +#if DEBUG + [JsonProperty("discordTokenDev")] +#else [JsonProperty("discordToken")] +#endif public string DiscordToken { get; set; } [JsonProperty("discordBotListToken")] From 832142799a64feeae7279291e0cba966e9d4440c Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:03:40 +0100 Subject: [PATCH 011/113] nom --- MikuSharp/Attributes/CustomMikuAttributes.cs | 8 +- MikuSharp/Commands/About.cs | 84 +++---- MikuSharp/Commands/Action.cs | 26 +-- MikuSharp/Commands/Developer.cs | 53 +++-- MikuSharp/Commands/Fun.cs | 34 +-- MikuSharp/Commands/MikuGuild.cs | 4 +- MikuSharp/Commands/Moderation.cs | 16 +- MikuSharp/Commands/Music.cs | 15 +- MikuSharp/Commands/NSFW.cs | 212 +++++++++--------- MikuSharp/Commands/Playlist.cs | 33 +-- MikuSharp/Commands/Utility.cs | 96 ++++---- MikuSharp/Commands/Weeb.cs | 74 +++--- MikuSharp/Entities/BiliJson.cs | 2 +- MikuSharp/Entities/BiliPlayInfo.cs | 3 +- MikuSharp/Entities/BotConfig.cs | 56 ++--- MikuSharp/Entities/DogCeo.cs | 2 +- MikuSharp/Entities/Entry.cs | 3 +- MikuSharp/Entities/Guild.cs | 3 +- MikuSharp/Entities/Img_Data.cs | 2 +- MikuSharp/Entities/KsoftSiRanImg.cs | 2 +- MikuSharp/Entities/MeekMoe.cs | 2 +- MikuSharp/Entities/MusicInstance.cs | 5 +- MikuSharp/Entities/Nekobot.cs | 2 +- MikuSharp/Entities/Nekos_Life.cs | 2 +- MikuSharp/Entities/Playlist.cs | 3 +- MikuSharp/Entities/PlaylistEntry.cs | 3 +- MikuSharp/Entities/QueueEntry.cs | 3 +- MikuSharp/Entities/Random_D.cs | 2 +- MikuSharp/Entities/TrackResult.cs | 3 +- MikuSharp/Entities/WeebSh.cs | 2 +- MikuSharp/Enums/ExtService.cs | 2 +- MikuSharp/Enums/Playing.cs | 4 +- MikuSharp/Events/Lavalink.cs | 3 +- MikuSharp/Events/MikuGuildJoin.cs | 9 +- MikuSharp/Events/VoiceChat.cs | 3 +- MikuSharp/GlobalSuppressions.cs | 8 +- MikuSharp/MikuBot.cs | 80 ++++--- MikuSharp/Program.cs | 7 +- MikuSharp/Utilities/Bilibili.cs | 15 +- MikuSharp/Utilities/Database.cs | 3 +- MikuSharp/Utilities/DiscordOptionProviders.cs | 8 +- MikuSharp/Utilities/Music.cs | 3 +- MikuSharp/Utilities/NND.cs | 3 +- MikuSharp/Utilities/Other.cs | 12 +- MikuSharp/Utilities/PlaylistDB.cs | 3 +- MikuSharp/Utilities/Web.cs | 14 +- MikuSharp/config.example.json | 50 ++--- NicoNicoNii | 2 +- 48 files changed, 464 insertions(+), 520 deletions(-) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 0db8c3ae..97bdf24c 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -29,7 +29,7 @@ public override Task ExecuteChecksAsync(BaseContext ctx) [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute { - public override async Task ExecuteChecksAsync(BaseContext ctx) + public async override Task ExecuteChecksAsync(BaseContext ctx) { var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); if (ctx.Member.VoiceState?.Channel != null && bot.VoiceState?.Channel != null) @@ -42,8 +42,6 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class, AllowMultiple = false)] public sealed class NotStaffAttribute : CheckBaseAttribute { - public override Task ExecuteCheckAsync(CommandContext ctx, bool help) - { - return Task.FromResult(!ctx.User.IsStaff); - } + public override Task ExecuteCheckAsync(CommandContext ctx, bool help) => + Task.FromResult(!ctx.User.IsStaff); } \ No newline at end of file diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index fbac0b12..d0f85cdb 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -19,46 +19,35 @@ namespace MikuSharp.Commands; internal class About : ApplicationCommandsModule { [SlashCommand("donate", "Financial support information")] - public static async Task DonateAsync(InteractionContext ctx) + public async static Task DonateAsync(InteractionContext ctx) { var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl). - WithTitle("Donate Page!"). - WithAuthor("Miku MikuBot uwu"). - WithUrl("https://meek.moe/"). - WithColor(new DiscordColor("#348573")). - WithDescription("Thank you for your interest in supporting the bot's development!\n" + - "Here are some links that may interest you"). - AddField(new DiscordEmbedField("Patreon", "[Link](https://patreon.com/sekoree)", true)). - AddField(new DiscordEmbedField("PayPal", "[Link](https://paypal.me/speyd3r)", true)); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) + .AddField(new("PayPal", "[Link](https://paypal.me/speyd3r)", true)); await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); } [SlashCommand("bot", "About the bot")] - public static async Task BotAsync(InteractionContext ctx) + public async static Task BotAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl). - WithTitle($"About {ctx.Client.CurrentUser.UsernameWithDiscriminator}!"). - WithAuthor("Miku MikuBot uwu"). - WithUrl("https://meek.moe/"). - WithColor(new DiscordColor("#348573")). - WithDescription(ctx.Client.CurrentApplication.Description); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithDiscriminator}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription(ctx.Client.CurrentApplication.Description); foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new DiscordEmbedField(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id ? "Owner" : "Developer", member.User.UsernameWithDiscriminator)); + emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id ? "Owner" : "Developer", member.User.UsernameWithDiscriminator)); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] - public static async Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") + public async static Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != ctx.Guild.OwnerId) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; } + var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); var f = await announcementChannel.FollowAsync(channel); await Task.Delay(5000); @@ -73,16 +62,16 @@ public static async Task FollowNewsAsync(InteractionContext ctx, [Option("target await stream.CopyToAsync(memoryStream); memoryStream.Position = 0; await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"News setup complete {DiscordEmoji.FromGuildEmote(client: MikuBot.ShardedClient.GetShard(483279257431441410), id: 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); } [SlashCommand("feedback", "Send feedback!")] - public static async Task FeedbackAsync(InteractionContext ctx) + public async static Task FeedbackAsync(InteractionContext ctx) { DiscordInteractionModalBuilder modalBuilder = new(); modalBuilder.WithTitle("Feedback modal"); - modalBuilder.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); - modalBuilder.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20, null, true, null)); + modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); + modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20, null, true, null)); await ctx.CreateModalResponseAsync(modalBuilder); var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); @@ -91,13 +80,11 @@ public static async Task FeedbackAsync(InteractionContext ctx) await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(id: 483279257431441410); + var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var emb = new DiscordEmbedBuilder(); - emb.WithAuthor($"{ctx.User.UsernameWithDiscriminator}", iconUrl: ctx.User.AvatarUrl). - WithTitle(title). - WithDescription(body); + emb.WithAuthor($"{ctx.User.UsernameWithDiscriminator}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); if (ctx.Guild != null) - emb.AddField(new DiscordEmbedField("Guild", $"{ctx.Guild.Id}", true)); + emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); var forum = guild.GetChannel(1020433162662322257); List tags = new(); if (ctx.Guild != null) @@ -109,28 +96,26 @@ public static async Task FeedbackAsync(InteractionContext ctx) await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); - } else await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"You were too slow :(\nThe time limit is one minute.").AsEphemeral()); - } [SlashCommand("ping", "Current ping to discord's services")] - public static async Task PingAsync(InteractionContext ctx) + public async static Task PingAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); [SlashCommand("which_shard", "What shard am I on?")] - public static async Task GetExecutingShardAsync(InteractionContext ctx) + public async static Task GetExecutingShardAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); [SlashCommand("stats", "Some stats of the MikuBot!")] - public static async Task StatsAsync(InteractionContext ctx) + public async static Task StatsAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - int GuildCount = 0; - int UserCount = 0; - int ChannelCount = 0; + var GuildCount = 0; + var UserCount = 0; + var ChannelCount = 0; foreach (var client in MikuBot.ShardedClient.ShardClients) { GuildCount += client.Value.Guilds.Count; @@ -140,28 +125,19 @@ public static async Task StatsAsync(InteractionContext ctx) ChannelCount += guild.Value.Channels.Count; } } - var emb = new DiscordEmbedBuilder(). - WithTitle("Stats"). - WithDescription("Some stats of the MikuBot!"). - AddField(new DiscordEmbedField("Guilds", GuildCount.ToString(), true)). - AddField(new DiscordEmbedField("Users", UserCount.ToString(), true)). - AddField(new DiscordEmbedField("Channels", ChannelCount.ToString(), true)). - AddField(new DiscordEmbedField("Ping", ctx.Client.Ping.ToString(), true)). - AddField(new DiscordEmbedField("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)). - WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + + var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", GuildCount.ToString(), true)).AddField(new("Users", UserCount.ToString(), true)).AddField(new("Channels", ChannelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) + .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } [SlashCommand("support", "Link to my support server")] - public static async Task SupportAsybc(InteractionContext ctx) + public async static Task SupportAsybc(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(id: 483279257431441410); + var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var widget = await guild.GetWidgetAsync(); - var emb = new DiscordEmbedBuilder(). - WithTitle("Support Server"). - WithDescription("Need help or is something broken?"). - WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new DiscordComponentEmoji(704733597655105634)))); + var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 1dd6ad65..ee594671 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -17,9 +17,9 @@ namespace MikuSharp.Commands; internal class Action : ApplicationCommandsModule { [SlashCommand("hug", "Hug someone!")] - public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public async static Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var WSH = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); WSH.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); @@ -30,9 +30,9 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u } [SlashCommand("kiss", "Kiss someone!")] - public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public async static Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var WSH = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); WSH.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); @@ -43,9 +43,9 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The } [SlashCommand("lick", "Lick someone!")] - public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public async static Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var WSH = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); WSH.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); @@ -56,9 +56,9 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The } [SlashCommand("pat", "Pat someone!")] - public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public async static Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var weeurl = await MikuBot._weebClient.GetRandomAsync("pat", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); @@ -73,9 +73,9 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u } [SlashCommand("poke", "Poke someone!")] - public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var weeurl = await MikuBot._weebClient.GetRandomAsync("poke", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); @@ -90,9 +90,9 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The } [SlashCommand("slap", "Slap someone!")] - public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public async static Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var weeurl = await MikuBot._weebClient.GetRandomAsync("slap", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); @@ -105,4 +105,4 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 8894d6b3..9569eec7 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -26,23 +26,21 @@ namespace MikuSharp.Commands; public class Developer : ApplicationCommandsModule { [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) + public async static Task TestAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); } [SlashCommand("guild_shard_test", "Testing")] - public static async Task GuildTestAsync(InteractionContext ctx) + public async static Task GuildTestAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) - { await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); - } } [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] - public static async Task DeleteMessageAsync(ContextMenuContext ctx) + public async static Task DeleteMessageAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) @@ -50,6 +48,7 @@ public static async Task DeleteMessageAsync(ContextMenuContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; } + await ctx.TargetMessage.DeleteAsync(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); } @@ -59,7 +58,7 @@ public static async Task DeleteMessageAsync(ContextMenuContext ctx) /// /// The interaction context. [SlashCommand("dbg", "Get the logs of today")] - public static async Task GetDebugLogAsync(InteractionContext ctx) + public async static Task GetDebugLogAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) @@ -69,7 +68,7 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) } await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); - DateTime now = DateTime.Now; + var now = DateTime.Now; var target_file = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; if (!File.Exists(target_file)) { @@ -77,21 +76,19 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) return; } else - { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {Formatter.Bold(target_file)}")); - } + try { if (!File.Exists($"temp-{target_file}")) - { File.Copy(target_file, $"temp-{target_file}"); - } else { File.Delete($"temp-{target_file}"); File.Copy(target_file, $"temp-{target_file}"); } - FileStream log = new(path: $"temp-{target_file}", FileMode.Open, FileAccess.Read); + + FileStream log = new($"temp-{target_file}", FileMode.Open, FileAccess.Read); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(target_file, log, true).WithContent($"Log {Formatter.Bold(target_file)}").AsEphemeral()); log.Close(); log.Dispose(); @@ -102,6 +99,7 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); } @@ -110,7 +108,7 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public static async Task EvalCSAsync(ContextMenuContext ctx) + public async static Task EvalCSAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) @@ -132,10 +130,10 @@ public static async Task EvalCSAsync(ContextMenuContext ctx) return; } - string cs = code[cs1..cs2]; + var cs = code[cs1..cs2]; await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() - .WithColor(new DiscordColor("#FF007F")) + .WithColor(new("#FF007F")) .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") .Build())).ConfigureAwait(false); msg = await ctx.GetOriginalResponseAsync(); @@ -144,7 +142,8 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe var globals = new SGTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); var sopts = ScriptOptions.Default; - sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities", "DisCatSharp.Lavalink"); + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities", + "DisCatSharp.Lavalink"); sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); var script = CSharpScript.Create(cs, sopts, typeof(SGTestVariables)); @@ -210,19 +209,19 @@ public class SGTestVariables /// The context menu context. public SGTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) { - Client = client; - ShardClient = shard; - - Message = msg; - Channel = ctx.Channel; - Guild = ctx.Guild; - User = ctx.User; - Member = ctx.Member; - Context = ctx; - Inter = Client.GetInteractivity(); + this.Client = client; + this.ShardClient = shard; + + this.Message = msg; + this.Channel = ctx.Channel; + this.Guild = ctx.Guild; + this.User = ctx.User; + this.Member = ctx.Member; + this.Context = ctx; + this.Inter = this.Client.GetInteractivity(); } public DiscordShardedClient ShardClient { get; set; } public DiscordClient Client { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 20e8e99c..d02e6e6b 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -22,17 +22,21 @@ namespace MikuSharp.Commands; internal class Fun : ApplicationCommandsModule { [SlashCommand("8ball", "Yes? No? Maybe?")] - public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + public async static Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); - var responses = new[] { "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No." }; + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var responses = new[] + { + "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", + "Outlook not so good.", "Very doubtful.", "No." + }; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); } [SlashCommand("cat", "Get a random cat image!")] - public static async Task CatAsync(InteractionContext ctx) + public async static Task CatAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var ImgURL = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); DiscordWebhookBuilder builder = new(); @@ -42,9 +46,9 @@ public static async Task CatAsync(InteractionContext ctx) } [SlashCommand("clyde", "Say something as clyde bot")] - public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + public async static Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); @@ -54,17 +58,17 @@ public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Tex } [SlashCommand("coinflip", "Flip a coin lol")] - public static async Task CoinflipAsync(InteractionContext ctx) + public async static Task CoinflipAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); } [SlashCommand("dog", "Random Dog Image")] - public static async Task DogAsync(InteractionContext ctx) + public async static Task DogAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); var em = new DiscordEmbedBuilder(); @@ -106,9 +110,9 @@ public static async Task Lion(InteractionContext ctx) }*/ [SlashCommand("lizard", "Get a random lizard image")] - public static async Task LizardAsync(InteractionContext ctx) + public async static Task LizardAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(get.Url))); @@ -152,9 +156,9 @@ public static async Task RedPandaAsync(InteractionContext ctx) }*/ [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RPSAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) + public async static Task RPSAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder()); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); } diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuild.cs index f44d4875..551b7791 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuild.cs @@ -13,7 +13,7 @@ namespace MikuSharp.Commands; public class MikuGuild : ApplicationCommandsModule { [SlashCommand("smolcar", "#SmolArmy")] - public static async Task SmolCarAsync(InteractionContext ctx) + public async static Task SmolCarAsync(InteractionContext ctx) { if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) { @@ -26,4 +26,4 @@ public static async Task SmolCarAsync(InteractionContext ctx) await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); } } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index c0b63a74..62928d6f 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -13,11 +13,11 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("mod", "Moderation", defaultMemberPermissions: (long)Permissions.BanMembers, dmPermission: false)] +[SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, dmPermission: false)] internal class Moderation : ApplicationCommandsModule { [SlashCommand("disable_invites", "Disable invites usage for guild")] - public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + public async static Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -32,7 +32,7 @@ public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("re } [SlashCommand("enable_invites", "Enable invites usage for guild")] - public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + public async static Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -47,7 +47,7 @@ public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("rea } [SlashCommand("ban", "Ban someone")] - public static async Task BanAsync(InteractionContext ctx, [Option("user", "User to ban")] DiscordUser user, [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, [Option("reason", "Auditlog reason")] string? reason = null) + public async static Task BanAsync(InteractionContext ctx, [Option("user", "User to ban")] DiscordUser user, [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -62,7 +62,7 @@ public static async Task BanAsync(InteractionContext ctx, [Option("user", "User } [SlashCommand("unban", "Unban someone")] - public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) + public async static Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); var userId = Convert.ToUInt64(id); @@ -72,7 +72,7 @@ public static async Task UnbanAsync(InteractionContext ctx, [Option("username", } [SlashCommand("kick", "Kick someone")] - public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) + public async static Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -88,7 +88,7 @@ public static async Task KickAsync(InteractionContext ctx, [Option("user", "User } [SlashCommand("purge", "Delete a large amount of messages fast")] - public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) + public async static Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(true); try @@ -104,4 +104,4 @@ public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "A await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(Formatter.BlockCode(ex.JsonMessage, "json"))); } } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs index 71eeb8c2..ea96ef99 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Music.cs @@ -131,8 +131,8 @@ public static async Task SeekAsync(InteractionContext ctx, [Option("position", " [SlashCommand("play", "Play or queue a song")] [RequireUserVoicechatConnection] - public static async Task PlayAsync(InteractionContext ctx, - [Option("song", "Song name or url to play")] string name_or_url = null, + public static async Task PlayAsync(InteractionContext ctx, + [Option("song", "Song name or url to play")] string name_or_url = null, [Option("music_file", "Music file to play")] DiscordAttachment music_file = null ) { @@ -174,7 +174,7 @@ public static async Task PlayAsync(InteractionContext ctx, await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cleared").AddComponents(buttons)); } } - + await g.ConditionalConnect(ctx); if (music_file == null && name_or_url == null) @@ -212,7 +212,7 @@ public static async Task PlayAsync(InteractionContext ctx, await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Playing").Build()).AsEphemeral()); } } - + [SlashCommand("insert", "Queue a song at a specific position!")] [RequireUserVoicechatConnection] public static async Task InsertToQueueAsync(InteractionContext ctx, @@ -266,7 +266,7 @@ public static async Task InsertToQueueAsync(InteractionContext ctx, await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } } - + [SlashCommand("skip", "Skip the current song")] [RequireUserAndBotVoicechatConnection] public static async Task SkipSongAsync(InteractionContext ctx) @@ -320,7 +320,7 @@ public static async Task StopAsync(InteractionContext ctx) [SlashCommand("volume", "Change the music volume")] [RequireUserAndBotVoicechatConnection] - public static async Task ModifyVolumeAsync(InteractionContext ctx, + public static async Task ModifyVolumeAsync(InteractionContext ctx, [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] int vol = 100 ) { @@ -692,4 +692,5 @@ public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) } } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index b0db28b7..69454931 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -6,117 +6,107 @@ using System.Threading.Tasks; -namespace MikuSharp.Commands +namespace MikuSharp.Commands; + +[RequireNsfw] +public class NSFW : BaseCommandModule { - [RequireNsfw] - public class NSFW : BaseCommandModule + [Command("4k"), Description("lewd")] + public async Task FourK(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("anal"), Description("lewd")] + public async Task Anal(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("ass"), Description("lewd")] + public async Task Ass(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("gonewild"), Description("lewd")] + public async Task Gonewild(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdkitsune"), Description("lewd")] + public async Task LewdKitsune(CommandContext ctx) { - [Command("4k")] - [Description("lewd")] - public async Task FourK(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("anal")] - [Description("lewd")] - public async Task Anal(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("ass")] - [Description("lewd")] - public async Task Ass(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("gonewild")] - [Description("lewd")] - public async Task Gonewild(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdkitsune")] - [Description("lewd")] - public async Task LewdKitsune(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdneko")] - [Description("lewd")] - public async Task LewdNeko(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - [Command("porngif")] - [Description("lewd")] - public async Task PornGif(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("pussy")] - [Description("lewd")] - public async Task Pussy(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("thighs")] - [Aliases("thigh")] - [Description("lewd")] - public async Task Thighs(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdneko"), Description("lewd")] + public async Task LewdNeko(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("porngif"), Description("lewd")] + public async Task PornGif(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("pussy"), Description("lewd")] + public async Task Pussy(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("thighs"), Aliases("thigh"), Description("lewd")] + public async Task Thighs(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Playlist.cs b/MikuSharp/Commands/Playlist.cs index 591b2f8d..2e308b63 100644 --- a/MikuSharp/Commands/Playlist.cs +++ b/MikuSharp/Commands/Playlist.cs @@ -28,7 +28,7 @@ public class PlaylistCreation : ApplicationCommandsModule { [SlashCommand("copy_queue", "Copy the current queue to a playlist!")] [RequireUserAndBotVoicechatConnection] - public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, + public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name ) { @@ -54,7 +54,7 @@ public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, } [SlashCommand("create", "Create a playlist")] - public static async Task CreatePlaylistAsync(InteractionContext ctx, + public static async Task CreatePlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name ) { @@ -70,7 +70,7 @@ public static async Task CreatePlaylistAsync(InteractionContext ctx, } [SlashCommand("create_fixed", "Create a fixed playlist (linked to a Youtube or Soundcloud playlist)")] - public static async Task CreateFixedPlaylistAsync(InteractionContext ctx, + public static async Task CreateFixedPlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name, [Option("link", "Link to playlist")] string link ) @@ -186,7 +186,7 @@ public static async Task ListPlaylistsAsync(InteractionContext ctx) } [SlashCommand("show", "Show the contents of a playlist")] - public static async Task ShowPlaylistAsync(InteractionContext ctx, + public static async Task ShowPlaylistAsync(InteractionContext ctx, [Option("playlist", "Name of playlist to show", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist ) { @@ -260,7 +260,7 @@ public static async Task ShowPlaylistAsync(InteractionContext ctx, } [SlashCommand("delete", "Delete a playlist")] - public static async Task DeletePlaylistAsync(InteractionContext ctx, + public static async Task DeletePlaylistAsync(InteractionContext ctx, [Option("playlist", "Name of playlist to delete", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist ) { @@ -282,7 +282,7 @@ public static async Task DeletePlaylistAsync(InteractionContext ctx, [SlashCommand("rename", "Rename a playlist")] public static async Task RenamePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to rename", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("playlist", "Name of playlist to rename", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, [Option("name", "New name for playlist")] string name ) { @@ -300,13 +300,13 @@ public static async Task RenamePlaylistAsync(InteractionContext ctx, } var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); await pls.GetEntries(); - + await PlaylistDB.RenameList(playlist, ctx.Member.Id, name); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription($"Renamed Playlist to {playlist} -> {name}!").Build())); } [SlashCommand("clear", "Clear all entries from a playlist")] - public static async Task ClearPlaylistAsync(InteractionContext ctx, + public static async Task ClearPlaylistAsync(InteractionContext ctx, [Option("playlist", "Name of playlist to clear", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist ) { @@ -360,7 +360,7 @@ public static async Task PlayPlaylistAsync(InteractionContext ctx, public class SongOperations : ApplicationCommandsModule { [SlashCommand("add", "Add a song to a playlist")] - public static async Task AddSongAsync(InteractionContext ctx, + public static async Task AddSongAsync(InteractionContext ctx, [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, [Option("url_or_search", "Url or name of song to add")] string song ) @@ -391,8 +391,8 @@ public static async Task AddSongAsync(InteractionContext ctx, } [SlashCommand("insert_at", "Insert a song into a playlist at a choosen position")] - public static async Task InsertAtAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + public static async Task InsertAtAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, [Option("position", "Position to insert song at" ,true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi, [Option("url_or_search", "Url or name of song to add")] string song ) @@ -426,8 +426,8 @@ public static async Task InsertAtAsync(InteractionContext ctx, [SlashCommand("move", "Move a song to a specific position in your playlist")] public static async Task MoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string oldposi, + [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string oldposi, [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string newposi ) { @@ -459,8 +459,8 @@ public static async Task MoveSongAsync(InteractionContext ctx, } [SlashCommand("remove", "Remove a song from a playlist")] - public static async Task RemoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + public static async Task RemoveSongAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi ) { @@ -492,4 +492,5 @@ public static bool CheckError(string playlist) return false; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index c6a7d064..0dafec3d 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -28,7 +28,7 @@ internal class Utility : ApplicationCommandsModule internal class AnimeMangaUtility : ApplicationCommandsModule { [SlashCommand("anime_search", "Search for an anime")] - public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) + public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try @@ -40,37 +40,39 @@ public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("searc List ress = new(); foreach (var aa in a.Data) { - emb.WithColor(new DiscordColor(0212255)); + emb.WithColor(new(0212255)); emb.WithTitle(aa.Attributes.Titles.EnJp); if (aa.Attributes.Synopsis.Length != 0) emb.WithDescription(aa.Attributes.Synopsis); if (aa.Attributes.Subtype.Length != 0) - emb.AddField(new DiscordEmbedField("Type", $"{aa.Attributes.Subtype}", true)); + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); if (aa.Attributes.EpisodeCount != null) - emb.AddField(new DiscordEmbedField("Episodes", $"{aa.Attributes.EpisodeCount}", true)); + emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); if (aa.Attributes.EpisodeLength != null) - emb.AddField(new DiscordEmbedField("Length", $"{aa.Attributes.EpisodeLength}", true)); + emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); if (aa.Attributes.StartDate != null) - emb.AddField(new DiscordEmbedField("Start Date", $"{aa.Attributes.StartDate}", true)); + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); if (aa.Attributes.EndDate != null) - emb.AddField(new DiscordEmbedField("End Date", $"{aa.Attributes.EndDate}", true)); + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); if (aa.Attributes.AgeRating != null) - emb.AddField(new DiscordEmbedField("Age Rating", $"{aa.Attributes.AgeRating}", true)); + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); if (aa.Attributes.AverageRating != null) - emb.AddField(new DiscordEmbedField("Score", $"{aa.Attributes.AverageRating}", true)); - emb.AddField(new DiscordEmbedField("NSFW", $"{aa.Attributes.Nsfw}", true)); + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); res.Add(emb); - emb = new DiscordEmbedBuilder(); + emb = new(); } + res.Sort((x, y) => x.Title.CompareTo(y.Title)); - int i = 1; + var i = 1; foreach (var aa in res) { aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new Page(embed: aa)); + ress.Add(new(embed: aa)); i++; } + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); } catch (Exception ex) @@ -82,7 +84,7 @@ public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("searc } [SlashCommand("manga_search", "Search for an manga")] - public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) + public async static Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try @@ -94,34 +96,36 @@ public static async Task SearchMangaAsync(InteractionContext ctx, [Option("searc List ress = new(); foreach (var aa in a.Data) { - emb.WithColor(new DiscordColor(0212255)); + emb.WithColor(new(0212255)); emb.WithTitle(aa.Attributes.Titles.EnJp); if (aa.Attributes.Synopsis != null) emb.WithDescription(aa.Attributes.Synopsis); if (aa.Attributes.Subtype != null) - emb.AddField(new DiscordEmbedField("Type", $"{aa.Attributes.Subtype}", true)); + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); if (aa.Attributes.StartDate != null) - emb.AddField(new DiscordEmbedField("Start Date", $"{aa.Attributes.StartDate}", true)); + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); if (aa.Attributes.EndDate != null) - emb.AddField(new DiscordEmbedField("End Date", $"{aa.Attributes.EndDate}", true)); + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); if (aa.Attributes.AgeRating != null) - emb.AddField(new DiscordEmbedField("Age Rating", $"{aa.Attributes.AgeRating}", true)); + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); if (aa.Attributes.AverageRating != null) - emb.AddField(new DiscordEmbedField("Score", $"{aa.Attributes.AverageRating}", true)); + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); res.Add(emb); - emb = new DiscordEmbedBuilder(); + emb = new(); } + res.Sort((x, y) => x.Title.CompareTo(y.Title)); - int i = 1; + var i = 1; foreach (var aa in res) { aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new Page(embed: aa)); + ress.Add(new(embed: aa)); i++; } + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); } catch (Exception ex) @@ -137,11 +141,11 @@ public static async Task SearchMangaAsync(InteractionContext ctx, [Option("searc internal class DiscordUtility : ApplicationCommandsModule { [SlashCommand("avatar", "Get the avatar of someone or yourself")] - public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) + public async static Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null ? user.AvatarUrl : ctx.User.AvatarUrl).Build())); [SlashCommand("server_info", "Get information about the server")] - public static async Task GuildInfoAsync(InteractionContext ctx) + public async static Task GuildInfoAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); @@ -156,20 +160,20 @@ public static async Task GuildInfoAsync(InteractionContext ctx) var emb = new DiscordEmbedBuilder(); emb.WithTitle(ctx.Guild.Name); - emb.WithColor(new DiscordColor(0212255)); + emb.WithColor(new(0212255)); emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new DiscordEmbedField("Owner", ctx.Guild.Owner.Mention, true)); - emb.AddField(new DiscordEmbedField("Language", ctx.Guild.PreferredLocale, true)); - emb.AddField(new DiscordEmbedField("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new DiscordEmbedField("Created At", Formatter.Timestamp(ctx.Guild.CreationTimestamp, TimestampFormat.LongDateTime), true)); - emb.AddField(new DiscordEmbedField("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); - emb.AddField(new DiscordEmbedField("Members (Bots)", $"{members.Count} ({bots})", true)); + emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); + emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); + emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); + emb.AddField(new("Created At", Formatter.Timestamp(ctx.Guild.CreationTimestamp, TimestampFormat.LongDateTime), true)); + emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); + emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } [SlashCommand("user_info", "Get information about a user")] - public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + public async static Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); @@ -178,43 +182,41 @@ public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", " DiscordMember? member = null; if (ctx.Guild != null) - { try { member = await user.ConvertToMember(ctx.Guild); } - catch (NotFoundException) { } - } + catch (NotFoundException) + { } var emb = new DiscordEmbedBuilder(); - emb.WithColor(new DiscordColor(0212255)); + emb.WithColor(new(0212255)); emb.WithTitle("User Info"); - emb.AddField(new DiscordEmbedField("Username", $"{user.Username}#{user.Discriminator}", true)); + emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); if (member != null) if (member.DisplayName != user.Username) - emb.AddField(new DiscordEmbedField("Nickname", $"{member.DisplayName}", true)); - emb.AddField(new DiscordEmbedField("ID", $"{user.Id}", true)); - emb.AddField(new DiscordEmbedField("Account Creation", $"{Formatter.Timestamp(user.CreationTimestamp)}", true)); + emb.AddField(new("Nickname", $"{member.DisplayName}", true)); + emb.AddField(new("ID", $"{user.Id}", true)); + emb.AddField(new("Account Creation", $"{Formatter.Timestamp(user.CreationTimestamp)}", true)); if (member != null) - emb.AddField(new DiscordEmbedField("Join Date", $"{Formatter.Timestamp(member.JoinedAt)}", true)); + emb.AddField(new("Join Date", $"{Formatter.Timestamp(member.JoinedAt)}", true)); emb.WithThumbnail(user.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public static async Task EmojiListAsync(InteractionContext ctx) + public async static Task EmojiListAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - string wat = "You have to execute this command in a server!"; + var wat = "You have to execute this command in a server!"; if (ctx.Guild != null && ctx.Guild.Emojis.Any()) { wat = "**Emojies:** "; foreach (var em in ctx.Guild.Emojis.Values) - { wat += em + " "; - } } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); } } -} +} \ No newline at end of file diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 2f5a97ac..311df04a 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -21,7 +21,7 @@ namespace MikuSharp.Commands; internal class Weeb : ApplicationCommandsModule { [SlashCommand("awooify", "Awooify your or someones avatar!")] - public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) + public async static Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) { await ctx.DeferAsync(false); var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; @@ -30,7 +30,7 @@ public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "U } [SlashCommand("diva", "Radnom PJD Loading image")] - public static async Task DivaPic(InteractionContext ctx) + public async static Task DivaPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/diva")); @@ -43,7 +43,7 @@ public static async Task DivaPic(InteractionContext ctx) Description = $"[Full Source Image Link]({res.url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); @@ -54,7 +54,7 @@ public static async Task DivaPic(InteractionContext ctx) } [SlashCommand("gumi", "Random Gumi image")] - public static async Task GumiPic(InteractionContext ctx) + public async static Task GumiPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/gumi")); @@ -68,10 +68,8 @@ public static async Task GumiPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -81,7 +79,7 @@ public static async Task GumiPic(InteractionContext ctx) } [SlashCommand("kaito", "Random Kaito image")] - public static async Task KaitoPic(InteractionContext ctx) + public async static Task KaitoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/kaito")); @@ -95,10 +93,8 @@ public static async Task KaitoPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -108,7 +104,7 @@ public static async Task KaitoPic(InteractionContext ctx) } [SlashCommand("len", "Random Len image")] - public static async Task KLenPic(InteractionContext ctx) + public async static Task KLenPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/len")); @@ -122,10 +118,8 @@ public static async Task KLenPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -135,7 +129,7 @@ public static async Task KLenPic(InteractionContext ctx) } [SlashCommand("luka", "Random Luka image")] - public static async Task LukaPic(InteractionContext ctx) + public async static Task LukaPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/luka")); @@ -149,10 +143,8 @@ public static async Task LukaPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -162,7 +154,7 @@ public static async Task LukaPic(InteractionContext ctx) } [SlashCommand("meiko", "Random Meiko image")] - public static async Task MeikoPic(InteractionContext ctx) + public async static Task MeikoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/meiko")); @@ -176,10 +168,8 @@ public static async Task MeikoPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -189,7 +179,7 @@ public static async Task MeikoPic(InteractionContext ctx) } [SlashCommand("miku", "Random Miku image")] - public static async Task HMikuPic(InteractionContext ctx) + public async static Task HMikuPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/miku")); @@ -203,10 +193,8 @@ public static async Task HMikuPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -216,7 +204,7 @@ public static async Task HMikuPic(InteractionContext ctx) } [SlashCommand("neko", "Get a random neko image")] - public static async Task Cat(InteractionContext ctx) + public async static Task Cat(InteractionContext ctx) { await ctx.DeferAsync(false); var ImgURL = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); @@ -232,7 +220,7 @@ public static async Task Cat(InteractionContext ctx) } [SlashCommand("rin", "Random Rin image")] - public static async Task KRinPic(InteractionContext ctx) + public async static Task KRinPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/rin")); @@ -246,10 +234,8 @@ public static async Task KRinPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -259,7 +245,7 @@ public static async Task KRinPic(InteractionContext ctx) } [SlashCommand("teto", "Random Teto image")] - public static async Task KTetoPic(InteractionContext ctx) + public async static Task KTetoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/teto")); @@ -273,10 +259,8 @@ public static async Task KTetoPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; if (res.creator.Length != 0) - { - emim.AddField(new DiscordEmbedField("Creator", res.creator)); - } - emim.WithAuthor(name: "via api.meek.moe", url: "https://api.meek.moe/"); + emim.AddField(new("Creator", res.creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); @@ -284,4 +268,4 @@ public static async Task KTetoPic(InteractionContext ctx) builder.AddEmbed(emim.Build()); await ctx.EditResponseAsync(builder); } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/BiliJson.cs b/MikuSharp/Entities/BiliJson.cs index d945420b..f9c95543 100644 --- a/MikuSharp/Entities/BiliJson.cs +++ b/MikuSharp/Entities/BiliJson.cs @@ -27,4 +27,4 @@ public class BiliJson public string seek_param { get; set; } public string seek_type { get; set; } public List durl { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/BiliPlayInfo.cs b/MikuSharp/Entities/BiliPlayInfo.cs index 4cd13f0b..d58678b0 100644 --- a/MikuSharp/Entities/BiliPlayInfo.cs +++ b/MikuSharp/Entities/BiliPlayInfo.cs @@ -41,5 +41,4 @@ public class Data } public class VideoFrame -{ -} +{ } \ No newline at end of file diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index 3a9c4c35..ae5157c3 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -11,66 +11,48 @@ public partial class BotConfig #endif public string DiscordToken { get; set; } - [JsonProperty("discordBotListToken")] - public string DiscordBotListToken { get; set; } + [JsonProperty("discordBotListToken")] public string DiscordBotListToken { get; set; } - [JsonProperty("weebShToken")] - public string WeebShToken { get; set; } + [JsonProperty("weebShToken")] public string WeebShToken { get; set; } - [JsonProperty("youtubeApiToken")] - public string YoutubeApiToken { get; set; } + [JsonProperty("youtubeApiToken")] public string YoutubeApiToken { get; set; } - [JsonProperty("ksoftSiToken")] - public string KsoftSiToken { get; set; } + [JsonProperty("ksoftSiToken")] public string KsoftSiToken { get; set; } - [JsonIgnore] - public string DbConnectString { get; set; } + [JsonIgnore] public string DbConnectString { get; set; } - [JsonProperty("dbConfig")] - public DatabaseConfig DbConfig { get; set; } + [JsonProperty("dbConfig")] public DatabaseConfig DbConfig { get; set; } - [JsonProperty("lavaConfig")] - public LavalinkConfig LavaConfig { get; set; } + [JsonProperty("lavaConfig")] public LavalinkConfig LavaConfig { get; set; } - [JsonProperty("nndConfig")] - public NndConfig NndConfig { get; set; } + [JsonProperty("nndConfig")] public NndConfig NndConfig { get; set; } } public partial class DatabaseConfig { - [JsonProperty("hostname")] - public string Hostname { get; set; } + [JsonProperty("hostname")] public string Hostname { get; set; } - [JsonProperty("user")] - public string User { get; set; } + [JsonProperty("user")] public string User { get; set; } - [JsonProperty("password")] - public string Password { get; set; } + [JsonProperty("password")] public string Password { get; set; } - [JsonProperty("database")] - public string Database { get; set; } + [JsonProperty("database")] public string Database { get; set; } } public partial class LavalinkConfig { - [JsonProperty("hostname")] - public string Hostname { get; set; } + [JsonProperty("hostname")] public string Hostname { get; set; } - [JsonProperty("password")] - public string Password { get; set; } + [JsonProperty("password")] public string Password { get; set; } - [JsonProperty("port")] - public int Port { get; set; } + [JsonProperty("port")] public int Port { get; set; } } public partial class NndConfig { - [JsonProperty("mail")] - public string Mail { get; set; } + [JsonProperty("mail")] public string Mail { get; set; } - [JsonProperty("password")] - public string Password { get; set; } + [JsonProperty("password")] public string Password { get; set; } - [JsonProperty("ftpConfig")] - public DatabaseConfig FtpConfig { get; set; } -} + [JsonProperty("ftpConfig")] public DatabaseConfig FtpConfig { get; set; } +} \ No newline at end of file diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index df9eddc3..187f9d17 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -4,4 +4,4 @@ public class DogCeo { public string status { get; set; } public string message { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/Entry.cs b/MikuSharp/Entities/Entry.cs index 2ed6c68e..47472ac0 100644 --- a/MikuSharp/Entities/Entry.cs +++ b/MikuSharp/Entities/Entry.cs @@ -14,4 +14,5 @@ public Entry(LavalinkTrack t, DateTimeOffset addtime) additionDate = addtime; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/Guild.cs b/MikuSharp/Entities/Guild.cs index 3067ad66..7c0ed63b 100644 --- a/MikuSharp/Entities/Guild.cs +++ b/MikuSharp/Entities/Guild.cs @@ -32,4 +32,5 @@ public async Task CheckAlone() } } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs index 2d306247..803f57ee 100644 --- a/MikuSharp/Entities/Img_Data.cs +++ b/MikuSharp/Entities/Img_Data.cs @@ -10,4 +10,4 @@ public class Img_Data public string Filetype { get; set; } public DiscordEmbed Embed { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs index adb3f0d1..f2c7d05c 100644 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ b/MikuSharp/Entities/KsoftSiRanImg.cs @@ -6,4 +6,4 @@ public class KsoftSiRanImg : Img_Data public string snowflake { get; set; } public bool nsfw { get; set; } public string tag { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs index 63e72ec8..016d1703 100644 --- a/MikuSharp/Entities/MeekMoe.cs +++ b/MikuSharp/Entities/MeekMoe.cs @@ -4,4 +4,4 @@ public class MeekMoe { public string url { get; set; } public string creator { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/MusicInstance.cs b/MikuSharp/Entities/MusicInstance.cs index 4e00e928..f0cc6755 100644 --- a/MikuSharp/Entities/MusicInstance.cs +++ b/MikuSharp/Entities/MusicInstance.cs @@ -126,7 +126,7 @@ public async Task QueueSong(string n, InteractionContext ctx, int p var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); if (pos == -1) await Database.AddToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString); - else + else await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString, pos); if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); @@ -365,4 +365,5 @@ public async Task PlaySong() } // B/S(`・ω・´) ❤️ (´ω`)U/C -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs index 623ea038..64d22c0a 100644 --- a/MikuSharp/Entities/Nekobot.cs +++ b/MikuSharp/Entities/Nekobot.cs @@ -5,4 +5,4 @@ public class NekoBot : Img_Data public string message { get; set; } public int status { get; set; } public bool success { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs index f8053577..911ee8b6 100644 --- a/MikuSharp/Entities/Nekos_Life.cs +++ b/MikuSharp/Entities/Nekos_Life.cs @@ -3,4 +3,4 @@ public class Nekos_Life : Img_Data { public string Url { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/Playlist.cs b/MikuSharp/Entities/Playlist.cs index e9760877..e5264503 100644 --- a/MikuSharp/Entities/Playlist.cs +++ b/MikuSharp/Entities/Playlist.cs @@ -65,4 +65,5 @@ public async Task> GetEntries() return Entries; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/PlaylistEntry.cs b/MikuSharp/Entities/PlaylistEntry.cs index d570f129..fb962032 100644 --- a/MikuSharp/Entities/PlaylistEntry.cs +++ b/MikuSharp/Entities/PlaylistEntry.cs @@ -14,4 +14,5 @@ public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset mod Position = pos; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/QueueEntry.cs b/MikuSharp/Entities/QueueEntry.cs index 04069ec1..10707fa1 100644 --- a/MikuSharp/Entities/QueueEntry.cs +++ b/MikuSharp/Entities/QueueEntry.cs @@ -14,4 +14,5 @@ public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : b addedBy = m; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/Random_D.cs b/MikuSharp/Entities/Random_D.cs index 3a7d24a9..d329b4fd 100644 --- a/MikuSharp/Entities/Random_D.cs +++ b/MikuSharp/Entities/Random_D.cs @@ -4,4 +4,4 @@ public class Random_D { public string url { get; set; } public string message { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Entities/TrackResult.cs b/MikuSharp/Entities/TrackResult.cs index 557a7279..06a85788 100644 --- a/MikuSharp/Entities/TrackResult.cs +++ b/MikuSharp/Entities/TrackResult.cs @@ -23,4 +23,5 @@ public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) }; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Entities/WeebSh.cs b/MikuSharp/Entities/WeebSh.cs index 054a4c59..b351d5c8 100644 --- a/MikuSharp/Entities/WeebSh.cs +++ b/MikuSharp/Entities/WeebSh.cs @@ -9,4 +9,4 @@ public class WeebSh public MemoryStream ImgData { get; set; } public string Extension { get; set; } public DiscordEmbedBuilder Embed { get; set; } -} +} \ No newline at end of file diff --git a/MikuSharp/Enums/ExtService.cs b/MikuSharp/Enums/ExtService.cs index edb4309c..3f107c7b 100644 --- a/MikuSharp/Enums/ExtService.cs +++ b/MikuSharp/Enums/ExtService.cs @@ -5,4 +5,4 @@ public enum ExtService : int None = 0, Youtube = 1, Soundcloud = 2 -} +} \ No newline at end of file diff --git a/MikuSharp/Enums/Playing.cs b/MikuSharp/Enums/Playing.cs index 77fc0e19..23314462 100644 --- a/MikuSharp/Enums/Playing.cs +++ b/MikuSharp/Enums/Playing.cs @@ -7,14 +7,16 @@ public enum Playstate : int Paused = 2, Stopped = 3 } + public enum RepeatMode : int { Off = 0, On = 1, All = 2 } + public enum ShuffleMode : int { Off = 0, On = 1 -} +} \ No newline at end of file diff --git a/MikuSharp/Events/Lavalink.cs b/MikuSharp/Events/Lavalink.cs index 91ec1069..a1e8e068 100644 --- a/MikuSharp/Events/Lavalink.cs +++ b/MikuSharp/Events/Lavalink.cs @@ -87,4 +87,5 @@ public static async Task LavalinkTrackFinish(LavalinkGuildConnection lava, Track } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index 6b67b421..700cf1cd 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -15,7 +15,7 @@ public class MikuGuild /// /// The client. /// The event args. - public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) + public async static Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) { await Task.FromResult(true); } @@ -25,17 +25,16 @@ public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventAr /// /// The discord client. /// The event args. - public static async Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) + public async static Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) { if (args.PendingBefore.HasValue && args.PendingBefore == true) - { if (args.PendingAfter.HasValue && args.PendingAfter == false) { ulong member_role_id = 483280207927574528; var member_role = args.Guild.GetRole(member_role_id); await args.Member.GrantRoleAsync(member_role); } - } + await Task.FromResult(true); } -} +} \ No newline at end of file diff --git a/MikuSharp/Events/VoiceChat.cs b/MikuSharp/Events/VoiceChat.cs index 8740d8f1..3b4896a4 100644 --- a/MikuSharp/Events/VoiceChat.cs +++ b/MikuSharp/Events/VoiceChat.cs @@ -68,4 +68,5 @@ public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventAr } } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/GlobalSuppressions.cs b/MikuSharp/GlobalSuppressions.cs index 44e3a523..da5448c3 100644 --- a/MikuSharp/GlobalSuppressions.cs +++ b/MikuSharp/GlobalSuppressions.cs @@ -325,6 +325,10 @@ [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.RegisterEvents~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] -[assembly: SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] +[assembly: + SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] \ No newline at end of file diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 49618625..314c314d 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -62,7 +62,6 @@ internal class MikuBot : IDisposable internal static Playstate ps = Playstate.Playing; internal static Stopwatch psc = new(); - internal MikuBot() { var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException("config.json is null or missing"); @@ -72,16 +71,16 @@ internal MikuBot() throw new ArgumentNullException("config.json is null"); Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - _cts = new CancellationTokenSource(); + _cts = new(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) - .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Debug, outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") + .WriteTo.Console(LogEventLevel.Debug, "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") .CreateLogger(); Log.Logger.Information("Starting up!"); - ShardedClient = new DiscordShardedClient(new() + ShardedClient = new(new() { Token = Config.DiscordToken, TokenType = DisCatSharp.Enums.TokenType.Bot, @@ -102,7 +101,7 @@ internal MikuBot() ReconnectIndefinitely = true }); - InteractivityModules = ShardedClient.UseInteractivityAsync(new() + this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() { Timeout = TimeSpan.FromMinutes(2), PaginationBehaviour = PaginationBehaviour.WrapAround, @@ -110,19 +109,19 @@ internal MikuBot() PollBehaviour = PollBehaviour.DeleteEmojis, AckPaginationButtons = true, ButtonBehavior = ButtonPaginationBehavior.Disable, - PaginationButtons = new PaginationButtons() + PaginationButtons = new() { - SkipLeft = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-skip-left", "First", false, new DiscordComponentEmoji("⏮️")), - Left = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-left", "Previous", false, new DiscordComponentEmoji("◀️")), - Stop = new DiscordButtonComponent(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new DiscordComponentEmoji("⏹️")), - Right = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-right", "Next", false, new DiscordComponentEmoji("▶️")), - SkipRight = new DiscordButtonComponent(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new DiscordComponentEmoji("⏭️")) + SkipLeft = new(ButtonStyle.Primary, "pgb-skip-left", "First", false, new("⏮️")), + Left = new(ButtonStyle.Primary, "pgb-left", "Previous", false, new("◀️")), + Stop = new(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new("⏹️")), + Right = new(ButtonStyle.Primary, "pgb-right", "Next", false, new("▶️")), + SkipRight = new(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new("⏭️")) }, ResponseMessage = "Something went wrong.", ResponseBehavior = InteractionResponseBehavior.Ignore }).Result; - ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() + this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() { EnableDefaultHelp = true, DebugStartup = true, @@ -130,16 +129,16 @@ internal MikuBot() GenerateTranslationFilesOnly = false }).Result; - CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() + this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() { CaseSensitive = false, EnableMentionPrefix = true, DmHelp = false, EnableDefaultHelp = true, IgnoreExtraArguments = true, - StringPrefixes = new List(), + StringPrefixes = new(), UseDefaultCommandHandler = true, - DefaultHelpChecks = new List(1) { new Attributes.NotStaffAttribute() } + DefaultHelpChecks = new(1) { new Attributes.NotStaffAttribute() } }).Result; /*LavalinkConfig = new() @@ -152,7 +151,7 @@ internal MikuBot() LavalinkModules = ShardedClient.UseLavalinkAsync().Result;*/ } - internal static async Task RegisterEvents() + internal async static Task RegisterEvents() { ShardedClient.ClientErrored += (sender, args) => { @@ -185,13 +184,13 @@ internal static async Task RegisterEvents() { if (sender.CurrentApplication.Team.Members.Where(x => x.User.Id == args.Member.Id).Any()) { - var text = $"Heywo <:MikuWave:655783221942026271>!" + - $"\n\nOne of my developers joined your server!" + - $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + - $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + - $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + - $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithDiscriminator}!" + - $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; + var text = $"Heywo <:MikuWave:655783221942026271>!" + + $"\n\nOne of my developers joined your server!" + + $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + + $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + + $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + + $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithDiscriminator}!" + + $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; var message = await args.Guild.Owner.SendMessageAsync(text); sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithDiscriminator); sender.Logger.LogInformation("Message content: {content}", message.Content); @@ -210,6 +209,7 @@ internal static async Task RegisterEvents() discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); } } + /* internal async Task ShowConnections() { @@ -221,13 +221,13 @@ internal async Task ShowConnections() } } */ - internal static async Task UpdateBotList() + internal async static Task UpdateBotList() { await Task.Delay(15000); while (true) { var me = await DiscordBotListApi.GetMeAsync(); - int[] count = Array.Empty(); + var count = Array.Empty(); var clients = ShardedClient.ShardClients.Values; foreach (var client in clients) count = count.Append(client.Guilds.Count).ToArray(); @@ -245,21 +245,21 @@ internal async Task SetActivity() Name = "I'm using slash commands now!", ActivityType = ActivityType.Playing }; - await ShardedClient.UpdateStatusAsync(activity: test, userStatus: UserStatus.Online); + await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); DiscordActivity test2 = new() { Name = "Mention me with help for nsfw commands!", ActivityType = ActivityType.Playing }; - await ShardedClient.UpdateStatusAsync(activity: test2, userStatus: UserStatus.Online); + await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); DiscordActivity test3 = new() { Name = "Full NND support!", ActivityType = ActivityType.Playing }; - await ShardedClient.UpdateStatusAsync(activity: test3, userStatus: UserStatus.Online); + await ShardedClient.UpdateStatusAsync(test3, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); } } @@ -267,20 +267,20 @@ internal async Task SetActivity() internal void RegisterCommands() { // Nsfw stuff needs to be hidden, that's why we use commands next - CommandsNextModules.RegisterCommands(); + this.CommandsNextModules.RegisterCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); //ApplicationCommandsModules.RegisterGlobalCommands(); //ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); - ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); // Smolcar command, only guild command - ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); + this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); } internal async Task RunAsync() @@ -293,14 +293,12 @@ internal async Task RunAsync() var LCon = await lavalinkShard.Value.ConnectAsync(LavalinkConfig); LavalinkNodeConnections.Add(lavalinkShard.Key, LCon); }*/ - GameSetThread = Task.Run(SetActivity); + this.GameSetThread = Task.Run(this.SetActivity); //StatusThread = Task.Run(ShowConnections); //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); //BotListThread = Task.Run(UpdateBotList); while (!_cts.IsCancellationRequested) - { await Task.Delay(25); - } await ShardedClient.StopAsync(); } @@ -308,4 +306,4 @@ public void Dispose() { GC.SuppressFinalize(this); } -} +} \ No newline at end of file diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index 885998a9..8c46d55e 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -2,9 +2,9 @@ namespace MikuSharp; -class Program +internal class Program { - static void Main(string[] args) + private static void Main(string[] args) { using (var bot = new MikuBot()) { @@ -13,6 +13,7 @@ static void Main(string[] args) bot.RunAsync().Wait(); bot.Dispose(); } + Log.Logger.Information("Shutdown!"); } -} +} \ No newline at end of file diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index cc048e67..54bdb23d 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -13,7 +13,7 @@ namespace MikuSharp.Utilities; public static class Bilibili { - public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msg_id) + public async static Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msg_id) { try { @@ -25,14 +25,8 @@ public static async Task GetBilibiliAsync(this InteractionContext youtubeDl.Options.PostProcessingOptions.AudioFormat = NYoutubeDL.Helpers.Enums.AudioFormat.mp3; youtubeDl.Options.PostProcessingOptions.AddMetadata = true; youtubeDl.Options.PostProcessingOptions.KeepVideo = false; - youtubeDl.StandardOutputEvent += (e, f) => - { - ctx.Client.Logger.LogDebug("{data}", f); - }; - youtubeDl.StandardErrorEvent += (e, f) => - { - ctx.Client.Logger.LogDebug("{data}", f); - }; + youtubeDl.StandardOutputEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; + youtubeDl.StandardErrorEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; await youtubeDl.DownloadAsync(); var ms = new MemoryStream(); @@ -44,6 +38,7 @@ public static async Task GetBilibiliAsync(this InteractionContext song.Close(); File.Delete($@"{s}.mp3"); } + return ms; } catch (Exception ex) @@ -53,4 +48,4 @@ public static async Task GetBilibiliAsync(this InteractionContext return null; } } -} +} \ No newline at end of file diff --git a/MikuSharp/Utilities/Database.cs b/MikuSharp/Utilities/Database.cs index ff0e53ee..ab85182a 100644 --- a/MikuSharp/Utilities/Database.cs +++ b/MikuSharp/Utilities/Database.cs @@ -332,4 +332,5 @@ public static async Task> GetLastPlayingListAsync(DiscordGuild g) return queue; } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 922a0db8..2c439a49 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -20,9 +20,9 @@ public override Task> Provide { var list = new List(3) { - new DiscordApplicationCommandOptionChoice("Off", $"{(int)RepeatMode.Off}"), - new DiscordApplicationCommandOptionChoice("On", $"{(int)RepeatMode.On}"), - new DiscordApplicationCommandOptionChoice("All", $"{(int)RepeatMode.All}"), + new("Off", $"{(int)RepeatMode.Off}"), + new("On", $"{(int)RepeatMode.On}"), + new("All", $"{(int)RepeatMode.All}") }; return Task.FromResult>(list); } @@ -105,4 +105,4 @@ public async Task> Prov return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.position}: {x.track.Title}", x.position.ToString())); } }*/ -} +} \ No newline at end of file diff --git a/MikuSharp/Utilities/Music.cs b/MikuSharp/Utilities/Music.cs index 1df08fff..8d453f8e 100644 --- a/MikuSharp/Utilities/Music.cs +++ b/MikuSharp/Utilities/Music.cs @@ -215,4 +215,5 @@ public static async Task SendPlayingInformationAsync(this InteractionContext ctx await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs index d10115bd..9cbca917 100644 --- a/MikuSharp/Utilities/NND.cs +++ b/MikuSharp/Utilities/NND.cs @@ -64,4 +64,5 @@ public static async Task GetNNDAsync(this InteractionContext ctx,s } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 9040a225..02378056 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -8,18 +8,14 @@ namespace MikuSharp.Utilities; public static class Other { - public static string resizeLink(string url) - { - return $"https://api.meek.moe/im/?image={url}&resize=500"; - } + public static string resizeLink(string url) => + $"https://api.meek.moe/im/?image={url}&resize=500"; - public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) + public async static Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) { var builder = new DiscordInteractionResponseBuilder(); if (ephemeral) - { builder.AsEphemeral(); - } await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); } -} +} \ No newline at end of file diff --git a/MikuSharp/Utilities/PlaylistDB.cs b/MikuSharp/Utilities/PlaylistDB.cs index 24cafd4f..ace168ae 100644 --- a/MikuSharp/Utilities/PlaylistDB.cs +++ b/MikuSharp/Utilities/PlaylistDB.cs @@ -620,4 +620,5 @@ await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmb } } } -*/ \ No newline at end of file +*/ + diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index 86e2a5e8..81b9eda9 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -17,7 +17,7 @@ namespace MikuSharp.Utilities; public static class Web { - public static async Task GetNekosLifeAsync(this HttpClient client, string url) + public async static Task GetNekosLifeAsync(this HttpClient client, string url) { var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); MemoryStream str = new(await client.GetByteArrayAsync(Other.resizeLink(dl.Url))) @@ -33,9 +33,9 @@ public static async Task GetNekosLifeAsync(this HttpClient client, s return dl; } - public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) + public async static Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) { - client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", MikuBot.Config.KsoftSiToken); + client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); MemoryStream img = new(await client.GetByteArrayAsync(Other.resizeLink(v.url))); v.Data = img; @@ -47,7 +47,7 @@ public static async Task GetKsoftSiRanImgAsync(this HttpClient cl return v; } - public static async Task GetNekobotAsync(this HttpClient client, string url) + public async static Task GetNekobotAsync(this HttpClient client, string url) { var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); MemoryStream str = new(await client.GetByteArrayAsync(Other.resizeLink(dl.message))) @@ -63,7 +63,7 @@ public static async Task GetNekobotAsync(this HttpClient client, string return dl; } - public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) + public async static Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) { var weeurl = await MikuBot._weebClient.GetRandomAsync(query, tags, nsfw: nsfw); MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) @@ -73,11 +73,11 @@ public static async Task GetWeebShAsync(this HttpClient client, string q var em = new DiscordEmbedBuilder(); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by weeb.sh"); - return new WeebSh + return new() { ImgData = img, Extension = MimeGuesser.GuessExtension(img), Embed = em }; } -} +} \ No newline at end of file diff --git a/MikuSharp/config.example.json b/MikuSharp/config.example.json index f7331582..e8af1858 100644 --- a/MikuSharp/config.example.json +++ b/MikuSharp/config.example.json @@ -1,27 +1,27 @@ { - "discordToken": "", - "discordBotListToken": "", - "weebShToken": "", - "youtubeApiToken": "", - "ksoftSiToken": "", - "dbConfig": { - "hostname": "", - "user": "", - "password": "", - "database": "" - }, - "lavaConfig": { - "hostname": "", - "password": "", - "port": 2333 - }, - "nndConfig": { - "mail": "", - "password": "", - "ftpConfig": { - "hostname": "", - "user": "", - "password": "" - } - } + "discordToken": "", + "discordBotListToken": "", + "weebShToken": "", + "youtubeApiToken": "", + "ksoftSiToken": "", + "dbConfig": { + "hostname": "", + "user": "", + "password": "", + "database": "" + }, + "lavaConfig": { + "hostname": "", + "password": "", + "port": 2333 + }, + "nndConfig": { + "mail": "", + "password": "", + "ftpConfig": { + "hostname": "", + "user": "", + "password": "" + } + } } \ No newline at end of file diff --git a/NicoNicoNii b/NicoNicoNii index 2ddd0c18..22221b12 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 2ddd0c18f15404dcbf82add884c8aea2256ef746 +Subproject commit 22221b12d68685507ee053dccdfe2a5970f8873c From bca3911d0c5acdca6e66ee212ba7cdacd7dfc536 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:12:40 +0100 Subject: [PATCH 012/113] a --- MikuSharp/Commands/About.cs | 2 +- MikuSharp/Commands/Action.cs | 12 ++--- MikuSharp/Commands/Developer.cs | 47 +++++++++---------- MikuSharp/Commands/Moderation.cs | 2 +- MikuSharp/Commands/Utility.cs | 12 ++--- MikuSharp/Entities/BotConfig.cs | 8 ++-- MikuSharp/Entities/DogCeo.cs | 2 +- MikuSharp/Entities/KsoftSiRanImg.cs | 2 +- MikuSharp/Entities/MeekMoe.cs | 2 +- MikuSharp/Entities/Nekobot.cs | 2 +- MikuSharp/Entities/Nekos_Life.cs | 2 +- MikuSharp/Entities/WeebSh.cs | 2 +- MikuSharp/Events/MikuGuildJoin.cs | 4 +- MikuSharp/MikuBot.cs | 14 ++++-- MikuSharp/Utilities/DiscordOptionProviders.cs | 2 +- NicoNicoNii | 2 +- 16 files changed, 60 insertions(+), 57 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index d0f85cdb..19862a9c 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -42,7 +42,7 @@ public async static Task BotAsync(InteractionContext ctx) public async static Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != ctx.Guild.OwnerId) + if (!ctx.Client.CurrentApplication.Team.Members.Any(x => x.User == ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index ee594671..c03fa617 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -19,7 +19,7 @@ internal class Action : ApplicationCommandsModule [SlashCommand("hug", "Hug someone!")] public async static Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); var WSH = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); WSH.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); @@ -32,7 +32,7 @@ public async static Task HugAsync(InteractionContext ctx, [Option("user", "The u [SlashCommand("kiss", "Kiss someone!")] public async static Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); var WSH = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); WSH.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); @@ -45,7 +45,7 @@ public async static Task KissAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("lick", "Lick someone!")] public async static Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); var WSH = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); WSH.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); @@ -58,7 +58,7 @@ public async static Task LickAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("pat", "Pat someone!")] public async static Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); var weeurl = await MikuBot._weebClient.GetRandomAsync("pat", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); @@ -75,7 +75,7 @@ public async static Task PatAsync(InteractionContext ctx, [Option("user", "The u [SlashCommand("poke", "Poke someone!")] public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); var weeurl = await MikuBot._weebClient.GetRandomAsync("poke", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); @@ -92,7 +92,7 @@ public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("slap", "Slap someone!")] public async static Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); var weeurl = await MikuBot._weebClient.GetRandomAsync("slap", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 9569eec7..2f4890d5 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -10,10 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; -using MikuSharp.Entities; - using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -43,7 +40,7 @@ public async static Task GuildTestAsync(InteractionContext ctx) public async static Task DeleteMessageAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; @@ -61,7 +58,7 @@ public async static Task DeleteMessageAsync(ContextMenuContext ctx) public async static Task GetDebugLogAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; @@ -69,30 +66,30 @@ public async static Task GetDebugLogAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); var now = DateTime.Now; - var target_file = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; - if (!File.Exists(target_file)) + var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; + if (!File.Exists(targetFile)) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); return; } else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {Formatter.Bold(target_file)}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); try { - if (!File.Exists($"temp-{target_file}")) - File.Copy(target_file, $"temp-{target_file}"); + if (!File.Exists($"temp-{targetFile}")) + File.Copy(targetFile, $"temp-{targetFile}"); else { - File.Delete($"temp-{target_file}"); - File.Copy(target_file, $"temp-{target_file}"); + File.Delete($"temp-{targetFile}"); + File.Copy(targetFile, $"temp-{targetFile}"); } - FileStream log = new($"temp-{target_file}", FileMode.Open, FileAccess.Read); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(target_file, log, true).WithContent($"Log {Formatter.Bold(target_file)}").AsEphemeral()); + FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); log.Close(); log.Dispose(); - File.Delete($"temp-{target_file}"); + File.Delete($"temp-{targetFile}"); } catch (Exception ex) { @@ -108,10 +105,10 @@ public async static Task GetDebugLogAsync(InteractionContext ctx) /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public async static Task EvalCSAsync(ContextMenuContext ctx) + public async static Task EvalCsAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); - if (!ctx.Client.CurrentApplication.Team.Members.Where(x => x.User == ctx.User).Any() && ctx.User.Id != 856780995629154305) + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; @@ -119,9 +116,9 @@ public async static Task EvalCSAsync(ContextMenuContext ctx) var msg = ctx.TargetMessage; var code = ctx.TargetMessage.Content; - var cs1 = code.IndexOf("```") + 3; + var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; cs1 = code.IndexOf('\n', cs1) + 1; - var cs2 = code.LastIndexOf("```"); + var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); var c = await ctx.Guild.GetActiveThreadsAsync(); if (cs1 == -1 || cs2 == -1) @@ -139,18 +136,18 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe msg = await ctx.GetOriginalResponseAsync(); try { - var globals = new SGTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); + var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); var sopts = ScriptOptions.Default; sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities", "DisCatSharp.Lavalink"); sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); - var script = CSharpScript.Create(cs, sopts, typeof(SGTestVariables)); + var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); script.Compile(); var result = await script.RunAsync(globals).ConfigureAwait(false); - if (result != null && result.ReturnValue != null && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) + if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Result", Description = result.ReturnValue.ToString(), Color = new DiscordColor("#007FFF") }.Build())).ConfigureAwait(false); else await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Successful", Description = "No result was returned.", Color = new DiscordColor("#007FFF") }.Build())).ConfigureAwait(false); @@ -165,7 +162,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe /// /// The test variables. /// -public class SGTestVariables +public sealed class SgTestVariables { /// /// Gets or sets the message. @@ -202,12 +199,12 @@ public class SGTestVariables //public Dictionary Bot = MikuBot.Guilds; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The message. /// The client. /// The context menu context. - public SGTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) + public SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) { this.Client = client; this.ShardClient = shard; diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 62928d6f..491bbb6c 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -101,7 +101,7 @@ public async static Task PurgeAsync(InteractionContext ctx, [Option("amount", "A } catch (DisCatSharp.Exceptions.BadRequestException ex) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(Formatter.BlockCode(ex.JsonMessage, "json"))); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.JsonMessage.BlockCode("json"))); } } } \ No newline at end of file diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index 0dafec3d..54462b73 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -64,7 +64,7 @@ public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("searc emb = new(); } - res.Sort((x, y) => x.Title.CompareTo(y.Title)); + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); var i = 1; foreach (var aa in res) { @@ -117,7 +117,7 @@ public async static Task SearchMangaAsync(InteractionContext ctx, [Option("searc emb = new(); } - res.Sort((x, y) => x.Title.CompareTo(y.Title)); + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); var i = 1; foreach (var aa in res) { @@ -156,7 +156,7 @@ public async static Task GuildInfoAsync(InteractionContext ctx) } var members = await ctx.Guild.GetAllMembersAsync(); - var bots = members.Where(x => x.IsBot).Count(); + var bots = members.Count(x => x.IsBot); var emb = new DiscordEmbedBuilder(); emb.WithTitle(ctx.Guild.Name); @@ -165,7 +165,7 @@ public async static Task GuildInfoAsync(InteractionContext ctx) emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new("Created At", Formatter.Timestamp(ctx.Guild.CreationTimestamp, TimestampFormat.LongDateTime), true)); + emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); @@ -197,9 +197,9 @@ public async static Task UserInfoAsync(InteractionContext ctx, [Option("user", " if (member.DisplayName != user.Username) emb.AddField(new("Nickname", $"{member.DisplayName}", true)); emb.AddField(new("ID", $"{user.Id}", true)); - emb.AddField(new("Account Creation", $"{Formatter.Timestamp(user.CreationTimestamp)}", true)); + emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); if (member != null) - emb.AddField(new("Join Date", $"{Formatter.Timestamp(member.JoinedAt)}", true)); + emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); emb.WithThumbnail(user.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index ae5157c3..8ced761a 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -2,7 +2,7 @@ namespace MikuSharp.Entities; -public partial class BotConfig +public sealed partial class BotConfig { #if DEBUG [JsonProperty("discordTokenDev")] @@ -28,7 +28,7 @@ public partial class BotConfig [JsonProperty("nndConfig")] public NndConfig NndConfig { get; set; } } -public partial class DatabaseConfig +public sealed partial class DatabaseConfig { [JsonProperty("hostname")] public string Hostname { get; set; } @@ -39,7 +39,7 @@ public partial class DatabaseConfig [JsonProperty("database")] public string Database { get; set; } } -public partial class LavalinkConfig +public sealed partial class LavalinkConfig { [JsonProperty("hostname")] public string Hostname { get; set; } @@ -48,7 +48,7 @@ public partial class LavalinkConfig [JsonProperty("port")] public int Port { get; set; } } -public partial class NndConfig +public sealed partial class NndConfig { [JsonProperty("mail")] public string Mail { get; set; } diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index 187f9d17..53302f96 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Entities; -public class DogCeo +public sealed class DogCeo { public string status { get; set; } public string message { get; set; } diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs index f2c7d05c..4e4506c1 100644 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ b/MikuSharp/Entities/KsoftSiRanImg.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Entities; -public class KsoftSiRanImg : Img_Data +public sealed class KsoftSiRanImg : Img_Data { public string url { get; set; } public string snowflake { get; set; } diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs index 016d1703..82ca764f 100644 --- a/MikuSharp/Entities/MeekMoe.cs +++ b/MikuSharp/Entities/MeekMoe.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Entities; -public class MeekMoe +public sealed class MeekMoe { public string url { get; set; } public string creator { get; set; } diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs index 64d22c0a..55f0b9c3 100644 --- a/MikuSharp/Entities/Nekobot.cs +++ b/MikuSharp/Entities/Nekobot.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Entities; -public class NekoBot : Img_Data +public sealed class NekoBot : Img_Data { public string message { get; set; } public int status { get; set; } diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs index 911ee8b6..3deb6d1a 100644 --- a/MikuSharp/Entities/Nekos_Life.cs +++ b/MikuSharp/Entities/Nekos_Life.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Entities; -public class Nekos_Life : Img_Data +public sealed class Nekos_Life : Img_Data { public string Url { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Entities/WeebSh.cs b/MikuSharp/Entities/WeebSh.cs index b351d5c8..6f5aa31c 100644 --- a/MikuSharp/Entities/WeebSh.cs +++ b/MikuSharp/Entities/WeebSh.cs @@ -4,7 +4,7 @@ namespace MikuSharp.Entities; -public class WeebSh +public sealed class WeebSh { public MemoryStream ImgData { get; set; } public string Extension { get; set; } diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index 700cf1cd..cf214e44 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -27,8 +27,8 @@ public async static Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventAr /// The event args. public async static Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) { - if (args.PendingBefore.HasValue && args.PendingBefore == true) - if (args.PendingAfter.HasValue && args.PendingAfter == false) + if (args.PendingBefore is true) + if (args.PendingAfter is false) { ulong member_role_id = 483280207927574528; var member_role = args.Guild.GetRole(member_role_id); diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 314c314d..b3ab8246 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -36,7 +36,7 @@ namespace MikuSharp; -internal class MikuBot : IDisposable +internal sealed class MikuBot : IDisposable { internal static CancellationTokenSource _cts { get; set; } @@ -53,6 +53,7 @@ internal class MikuBot : IDisposable internal IReadOnlyDictionary InteractivityModules { get; set; } internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } + internal IReadOnlyDictionary CommandsNextModules { get; set; } //internal IReadOnlyDictionary LavalinkModules { get; set; } @@ -64,11 +65,11 @@ internal class MikuBot : IDisposable internal MikuBot() { - var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException("config.json is null or missing"); + var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); Config = JsonConvert.DeserializeObject(fileData); if (Config == null) - throw new ArgumentNullException("config.json is null"); + throw new ArgumentNullException(null, "config.json is null"); Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; _cts = new(); @@ -182,7 +183,7 @@ internal async static Task RegisterEvents() discordClientKvp.Value.GuildMemberAdded += async (sender, args) => { - if (sender.CurrentApplication.Team.Members.Where(x => x.User.Id == args.Member.Id).Any()) + if (sender.CurrentApplication.Team.Members.Any(x => x.User.Id == args.Member.Id)) { var text = $"Heywo <:MikuWave:655783221942026271>!" + $"\n\nOne of my developers joined your server!" @@ -302,6 +303,11 @@ internal async Task RunAsync() await ShardedClient.StopAsync(); } + ~MikuBot() + { + this.Dispose(); + } + public void Dispose() { GC.SuppressFinalize(this); diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 2c439a49..3193233c 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -31,7 +31,7 @@ public override Task> Provide internal class AutocompleteProviders { - internal class BanProvider : IAutocompleteProvider + internal sealed class BanProvider : IAutocompleteProvider { public async Task> Provider(AutocompleteContext ctx) { diff --git a/NicoNicoNii b/NicoNicoNii index 22221b12..2caa5bcc 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 22221b12d68685507ee053dccdfe2a5970f8873c +Subproject commit 2caa5bcc45b531407b7fe184eb6bcdd564a210ca From 0723114f55b1f48a2340e488d15af59c7f0d0928 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:14:36 +0100 Subject: [PATCH 013/113] naming! --- MikuSharp/Commands/About.cs | 25 +++++----- MikuSharp/Commands/Action.cs | 36 +++++++-------- MikuSharp/Commands/Fun.cs | 16 +++---- MikuSharp/Commands/NSFW.cs | 2 +- MikuSharp/Commands/Utility.cs | 8 ++-- MikuSharp/Commands/Weeb.cs | 74 +++++++++++++++--------------- MikuSharp/Entities/BiliJson.cs | 38 +++++++-------- MikuSharp/Entities/BiliPlayInfo.cs | 52 ++++++++++----------- MikuSharp/Entities/Img_Data.cs | 2 +- MikuSharp/Entities/Random_D.cs | 6 +-- MikuSharp/Events/MikuGuildJoin.cs | 13 +++--- MikuSharp/MikuBot.cs | 16 +++---- MikuSharp/Utilities/Bilibili.cs | 4 +- MikuSharp/Utilities/Other.cs | 2 +- MikuSharp/Utilities/Web.cs | 12 ++--- NicoNicoNii | 2 +- 16 files changed, 153 insertions(+), 155 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 19862a9c..d5f908bb 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -42,7 +42,7 @@ public async static Task BotAsync(InteractionContext ctx) public async static Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - if (!ctx.Client.CurrentApplication.Team.Members.Any(x => x.User == ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); return; @@ -86,11 +86,10 @@ public async static Task FeedbackAsync(InteractionContext ctx) if (ctx.Guild != null) emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); var forum = guild.GetChannel(1020433162662322257); - List tags = new(); - if (ctx.Guild != null) - tags.Add(forum.AvailableTags.First(x => x.Id == 1020434799493648404)); - else - tags.Add(forum.AvailableTags.First(x => x.Id == 1020434935502360576)); + List tags = new() + { + ctx.Guild != null ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) : forum.AvailableTags.First(x => x.Id == 1020434935502360576) + }; var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithDiscriminator}"), null, tags, "Feedback"); var msg = await thread.GetMessageAsync(thread.Id); await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); @@ -113,20 +112,20 @@ public async static Task GetExecutingShardAsync(InteractionContext ctx) public async static Task StatsAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var GuildCount = 0; - var UserCount = 0; - var ChannelCount = 0; + var guildCount = 0; + var userCount = 0; + var channelCount = 0; foreach (var client in MikuBot.ShardedClient.ShardClients) { - GuildCount += client.Value.Guilds.Count; + guildCount += client.Value.Guilds.Count; foreach (var guild in client.Value.Guilds) { - UserCount += guild.Value.MemberCount; - ChannelCount += guild.Value.Channels.Count; + userCount += guild.Value.MemberCount; + channelCount += guild.Value.Channels.Count; } } - var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", GuildCount.ToString(), true)).AddField(new("Users", UserCount.ToString(), true)).AddField(new("Channels", ChannelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) + var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", guildCount.ToString(), true)).AddField(new("Users", userCount.ToString(), true)).AddField(new("Channels", channelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index c03fa617..5d747abe 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -20,12 +20,12 @@ internal class Action : ApplicationCommandsModule public async static Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); - var WSH = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); - WSH.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); + wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{WSH.Extension}", WSH.ImgData); - builder.AddEmbed(WSH.Embed.Build()); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); } @@ -33,12 +33,12 @@ public async static Task HugAsync(InteractionContext ctx, [Option("user", "The u public async static Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); - var WSH = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); - WSH.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); + wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{WSH.Extension}", WSH.ImgData); - builder.AddEmbed(WSH.Embed.Build()); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); } @@ -46,12 +46,12 @@ public async static Task KissAsync(InteractionContext ctx, [Option("user", "The public async static Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); - var WSH = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); - WSH.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); + wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{WSH.Extension}", WSH.ImgData); - builder.AddEmbed(WSH.Embed.Build()); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); } @@ -59,8 +59,8 @@ public async static Task LickAsync(InteractionContext ctx, [Option("user", "The public async static Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); - var weeurl = await MikuBot._weebClient.GetRandomAsync("pat", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", new[] { "" }); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -76,8 +76,8 @@ public async static Task PatAsync(InteractionContext ctx, [Option("user", "The u public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); - var weeurl = await MikuBot._weebClient.GetRandomAsync("poke", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", new[] { "" }); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -93,8 +93,8 @@ public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The public async static Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); - var weeurl = await MikuBot._weebClient.GetRandomAsync("slap", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", new[] { "" }); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index d02e6e6b..dbcf7232 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -37,11 +37,11 @@ public async static Task EightBallAsync(InteractionContext ctx, [Option("text", public async static Task CatAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var ImgURL = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{ImgURL.Filetype}", ImgURL.Data); - builder.AddEmbed(ImgURL.Embed); + builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); + builder.AddEmbed(imgUrl.Embed); await ctx.EditResponseAsync(builder); } @@ -50,7 +50,7 @@ public async static Task ClydeAsync(InteractionContext ctx, [Option("text", "Tex { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); DiscordWebhookBuilder builder = new(); builder.AddFile($"clyde.png", img); @@ -70,11 +70,11 @@ public async static Task DogAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); var em = new DiscordEmbedBuilder(); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); - em.WithDescription($"[Full Image]({dc.message})"); + em.WithDescription($"[Full Image]({dc.Message})"); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -114,7 +114,7 @@ public async static Task LizardAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(get.Url))); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -156,7 +156,7 @@ public static async Task RedPandaAsync(InteractionContext ctx) }*/ [SlashCommand("rps", "Play rock paper scissors!")] - public async static Task RPSAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) + public async static Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index 69454931..3cc77963 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -9,7 +9,7 @@ namespace MikuSharp.Commands; [RequireNsfw] -public class NSFW : BaseCommandModule +public class Nsfw : BaseCommandModule { [Command("4k"), Description("lewd")] public async Task FourK(CommandContext ctx) diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index 54462b73..906747f0 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -28,13 +28,13 @@ internal class Utility : ApplicationCommandsModule internal class AnimeMangaUtility : ApplicationCommandsModule { [SlashCommand("anime_search", "Search for an anime")] - public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) + public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try { var ine = ctx.Client.GetInteractivity(); - var a = await Anime.GetAnimeAsync(search_query); + var a = await Anime.GetAnimeAsync(searchQuery); var emb = new DiscordEmbedBuilder(); List res = new(); List ress = new(); @@ -84,13 +84,13 @@ public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("searc } [SlashCommand("manga_search", "Search for an manga")] - public async static Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string search_query) + public async static Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try { var ine = ctx.Client.GetInteractivity(); - var a = await Manga.GetMangaAsync(search_query); + var a = await Manga.GetMangaAsync(searchQuery); var emb = new DiscordEmbedBuilder(); List res = new(); List ress = new(); diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 311df04a..1d07522b 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -26,7 +26,7 @@ public async static Task AwooifyAsync(InteractionContext ctx, [Option("user", "U await ctx.DeferAsync(false); var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.message).Build())); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); } [SlashCommand("diva", "Radnom PJD Loading image")] @@ -34,13 +34,13 @@ public async static Task DivaPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/diva")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); @@ -58,17 +58,17 @@ public async static Task GumiPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/gumi")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -83,17 +83,17 @@ public async static Task KaitoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/kaito")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -108,17 +108,17 @@ public async static Task KLenPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/len")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -133,17 +133,17 @@ public async static Task LukaPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/luka")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -158,17 +158,17 @@ public async static Task MeikoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/meiko")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -183,17 +183,17 @@ public async static Task HMikuPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/miku")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -207,8 +207,8 @@ public async static Task HMikuPic(InteractionContext ctx) public async static Task Cat(InteractionContext ctx) { await ctx.DeferAsync(false); - var ImgURL = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgURL.Url))); + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); var em = new DiscordEmbedBuilder(); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); @@ -224,17 +224,17 @@ public async static Task KRinPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/rin")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); @@ -249,17 +249,17 @@ public async static Task KTetoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/teto")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(res.url))) + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 }; var emim = new DiscordEmbedBuilder { - Description = $"[Full Source Image Link]({res.url})", + Description = $"[Full Source Image Link]({res.Url})", ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; - if (res.creator.Length != 0) - emim.AddField(new("Creator", res.creator)); + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); diff --git a/MikuSharp/Entities/BiliJson.cs b/MikuSharp/Entities/BiliJson.cs index f9c95543..427f4f2b 100644 --- a/MikuSharp/Entities/BiliJson.cs +++ b/MikuSharp/Entities/BiliJson.cs @@ -4,27 +4,27 @@ namespace MikuSharp.Entities; public class Durl2 { - public int order { get; set; } - public int length { get; set; } - public int size { get; set; } - public string ahead { get; set; } - public string vhead { get; set; } - public string url { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } } public class BiliJson { - public string from { get; set; } - public string result { get; set; } - public int quality { get; set; } - public string format { get; set; } - public int timelength { get; set; } - public string accept_format { get; set; } - public List accept_description { get; set; } - public List accept_quality { get; set; } - public int video_codecid { get; set; } - public bool video_project { get; set; } - public string seek_param { get; set; } - public string seek_type { get; set; } - public List durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public bool VideoProject { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Entities/BiliPlayInfo.cs b/MikuSharp/Entities/BiliPlayInfo.cs index d58678b0..7b304487 100644 --- a/MikuSharp/Entities/BiliPlayInfo.cs +++ b/MikuSharp/Entities/BiliPlayInfo.cs @@ -4,40 +4,40 @@ namespace MikuSharp.Entities; public class BiliPlayinfo { - public int code { get; set; } - public string message { get; set; } - public int ttl { get; set; } - public Data data { get; set; } - public string session { get; set; } - public VideoFrame videoFrame { get; set; } + public int Code { get; set; } + public string Message { get; set; } + public int Ttl { get; set; } + public Data Data { get; set; } + public string Session { get; set; } + public VideoFrame VideoFrame { get; set; } } public class Durl { - public int order { get; set; } - public int length { get; set; } - public int size { get; set; } - public string ahead { get; set; } - public string vhead { get; set; } - public string url { get; set; } - public object backup_url { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } + public object BackupUrl { get; set; } } public class Data { - public string from { get; set; } - public string result { get; set; } - public string message { get; set; } - public int quality { get; set; } - public string format { get; set; } - public int timelength { get; set; } - public string accept_format { get; set; } - public List accept_description { get; set; } - public List accept_quality { get; set; } - public int video_codecid { get; set; } - public string seek_param { get; set; } - public string seek_type { get; set; } - public List durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public string Message { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } public class VideoFrame diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs index 803f57ee..66991b4c 100644 --- a/MikuSharp/Entities/Img_Data.cs +++ b/MikuSharp/Entities/Img_Data.cs @@ -4,7 +4,7 @@ namespace MikuSharp.Entities; -public class Img_Data +public class ImgData { public Stream Data { get; set; } public string Filetype { get; set; } diff --git a/MikuSharp/Entities/Random_D.cs b/MikuSharp/Entities/Random_D.cs index d329b4fd..05fc585b 100644 --- a/MikuSharp/Entities/Random_D.cs +++ b/MikuSharp/Entities/Random_D.cs @@ -1,7 +1,7 @@ namespace MikuSharp.Entities; -public class Random_D +public class RandomD { - public string url { get; set; } - public string message { get; set; } + public string Url { get; set; } + public string Message { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index cf214e44..d8a0db23 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -27,13 +27,12 @@ public async static Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventAr /// The event args. public async static Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) { - if (args.PendingBefore is true) - if (args.PendingAfter is false) - { - ulong member_role_id = 483280207927574528; - var member_role = args.Guild.GetRole(member_role_id); - await args.Member.GrantRoleAsync(member_role); - } + if (args is { PendingBefore: true, PendingAfter: false }) + { + ulong memberRoleId = 483280207927574528; + var memberRole = args.Guild.GetRole(memberRoleId); + await args.Member.GrantRoleAsync(memberRole); + } await Task.FromResult(true); } diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index b3ab8246..1e5edf87 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -38,7 +38,7 @@ namespace MikuSharp; internal sealed class MikuBot : IDisposable { - internal static CancellationTokenSource _cts { get; set; } + internal static CancellationTokenSource Cts { get; set; } internal static BotConfig Config { get; set; } //internal LavalinkConfiguration LavalinkConfig { get; set; } @@ -47,7 +47,7 @@ internal sealed class MikuBot : IDisposable internal Task StatusThread { get; set; } internal Task BotListThread { get; set; } - internal static WeebClient _weebClient = new("Hatsune Miku Bot", "4.0.0"); + internal static WeebClient WeebClient = new("Hatsune Miku Bot", "4.0.0"); internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } internal static DiscordShardedClient ShardedClient { get; set; } @@ -60,8 +60,8 @@ internal sealed class MikuBot : IDisposable //internal static Dictionary LavalinkNodeConnections = new(); //internal static Dictionary Guilds = new(); - internal static Playstate ps = Playstate.Playing; - internal static Stopwatch psc = new(); + internal static Playstate Ps = Playstate.Playing; + internal static Stopwatch Psc = new(); internal MikuBot() { @@ -72,7 +72,7 @@ internal MikuBot() throw new ArgumentNullException(null, "config.json is null"); Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - _cts = new(); + Cts = new(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() @@ -268,7 +268,7 @@ internal async Task SetActivity() internal void RegisterCommands() { // Nsfw stuff needs to be hidden, that's why we use commands next - this.CommandsNextModules.RegisterCommands(); + this.CommandsNextModules.RegisterCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); @@ -286,7 +286,7 @@ internal void RegisterCommands() internal async Task RunAsync() { - await _weebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); + await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); await ShardedClient.StartAsync(); await Task.Delay(5000); /*foreach (var lavalinkShard in LavalinkModules) @@ -298,7 +298,7 @@ internal async Task RunAsync() //StatusThread = Task.Run(ShowConnections); //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); //BotListThread = Task.Run(UpdateBotList); - while (!_cts.IsCancellationRequested) + while (!Cts.IsCancellationRequested) await Task.Delay(25); await ShardedClient.StopAsync(); } diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index 54bdb23d..503e53c6 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -13,11 +13,11 @@ namespace MikuSharp.Utilities; public static class Bilibili { - public async static Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msg_id) + public async static Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) { try { - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); var youtubeDl = new YoutubeDL(@"youtube-dl.exe"); youtubeDl.Options.FilesystemOptions.Output = $@"{s}.mp4"; youtubeDl.Options.PostProcessingOptions.ExtractAudio = true; diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 02378056..81039a31 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -8,7 +8,7 @@ namespace MikuSharp.Utilities; public static class Other { - public static string resizeLink(string url) => + public static string ResizeLink(string url) => $"https://api.meek.moe/im/?image={url}&resize=500"; public async static Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index 81b9eda9..3508b097 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -17,10 +17,10 @@ namespace MikuSharp.Utilities; public static class Web { - public async static Task GetNekosLifeAsync(this HttpClient client, string url) + public async static Task GetNekosLifeAsync(this HttpClient client, string url) { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.resizeLink(dl.Url))) + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) { Position = 0 }; @@ -37,7 +37,7 @@ public async static Task GetKsoftSiRanImgAsync(this HttpClient cl { client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); - MemoryStream img = new(await client.GetByteArrayAsync(Other.resizeLink(v.url))); + MemoryStream img = new(await client.GetByteArrayAsync(Other.ResizeLink(v.Url))); v.Data = img; v.Filetype = MimeGuesser.GuessExtension(img); var em = new DiscordEmbedBuilder(); @@ -50,7 +50,7 @@ public async static Task GetKsoftSiRanImgAsync(this HttpClient cl public async static Task GetNekobotAsync(this HttpClient client, string url) { var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.resizeLink(dl.message))) + MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) { Position = 0 }; @@ -65,7 +65,7 @@ public async static Task GetNekobotAsync(this HttpClient client, string public async static Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) { - var weeurl = await MikuBot._weebClient.GetRandomAsync(query, tags, nsfw: nsfw); + var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) { Position = 0 diff --git a/NicoNicoNii b/NicoNicoNii index 2caa5bcc..1754b846 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 2caa5bcc45b531407b7fe184eb6bcdd564a210ca +Subproject commit 1754b846494a93dce331dd1a56ec4bf0a42086bb From 531dcec57890447604d926e0ad78ec84952239c7 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:17:34 +0100 Subject: [PATCH 014/113] yawn --- MikuSharp/Entities/BotConfig.cs | 8 ++++---- MikuSharp/Entities/DogCeo.cs | 4 ++-- MikuSharp/Entities/KsoftSiRanImg.cs | 10 +++++----- MikuSharp/Entities/MeekMoe.cs | 4 ++-- MikuSharp/Entities/Nekobot.cs | 8 ++++---- MikuSharp/Entities/Nekos_Life.cs | 2 +- MikuSharp/Utilities/DiscordOptionProviders.cs | 5 +---- NicoNicoNii | 2 +- 8 files changed, 20 insertions(+), 23 deletions(-) diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index 8ced761a..d83a152d 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -2,7 +2,7 @@ namespace MikuSharp.Entities; -public sealed partial class BotConfig +public sealed class BotConfig { #if DEBUG [JsonProperty("discordTokenDev")] @@ -28,7 +28,7 @@ public sealed partial class BotConfig [JsonProperty("nndConfig")] public NndConfig NndConfig { get; set; } } -public sealed partial class DatabaseConfig +public sealed class DatabaseConfig { [JsonProperty("hostname")] public string Hostname { get; set; } @@ -39,7 +39,7 @@ public sealed partial class DatabaseConfig [JsonProperty("database")] public string Database { get; set; } } -public sealed partial class LavalinkConfig +public sealed class LavalinkConfig { [JsonProperty("hostname")] public string Hostname { get; set; } @@ -48,7 +48,7 @@ public sealed partial class LavalinkConfig [JsonProperty("port")] public int Port { get; set; } } -public sealed partial class NndConfig +public sealed class NndConfig { [JsonProperty("mail")] public string Mail { get; set; } diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index 53302f96..9f60a118 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -2,6 +2,6 @@ public sealed class DogCeo { - public string status { get; set; } - public string message { get; set; } + public string Status { get; set; } + public string Message { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs index 4e4506c1..df1df905 100644 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ b/MikuSharp/Entities/KsoftSiRanImg.cs @@ -1,9 +1,9 @@ namespace MikuSharp.Entities; -public sealed class KsoftSiRanImg : Img_Data +public sealed class KsoftSiRanImg : ImgData { - public string url { get; set; } - public string snowflake { get; set; } - public bool nsfw { get; set; } - public string tag { get; set; } + public string Url { get; set; } + public string Snowflake { get; set; } + public bool Nsfw { get; set; } + public string Tag { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs index 82ca764f..1eab9207 100644 --- a/MikuSharp/Entities/MeekMoe.cs +++ b/MikuSharp/Entities/MeekMoe.cs @@ -2,6 +2,6 @@ public sealed class MeekMoe { - public string url { get; set; } - public string creator { get; set; } + public string Url { get; set; } + public string Creator { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs index 55f0b9c3..68419bda 100644 --- a/MikuSharp/Entities/Nekobot.cs +++ b/MikuSharp/Entities/Nekobot.cs @@ -1,8 +1,8 @@ namespace MikuSharp.Entities; -public sealed class NekoBot : Img_Data +public sealed class NekoBot : ImgData { - public string message { get; set; } - public int status { get; set; } - public bool success { get; set; } + public string Message { get; set; } + public int Status { get; set; } + public bool Success { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs index 3deb6d1a..513a61d4 100644 --- a/MikuSharp/Entities/Nekos_Life.cs +++ b/MikuSharp/Entities/Nekos_Life.cs @@ -1,6 +1,6 @@ namespace MikuSharp.Entities; -public sealed class Nekos_Life : Img_Data +public sealed class NekosLife : ImgData { public string Url { get; set; } } \ No newline at end of file diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 3193233c..febf614b 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -37,10 +37,7 @@ public async Task> Prov { var bans = await ctx.Guild.GetBansAsync(); List bannedUsers = new(25); - if (ctx.FocusedOption.Value == null) - bannedUsers.AddRange(bans.Take(25)); - else - bannedUsers.AddRange(bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); + bannedUsers.AddRange(ctx.FocusedOption.Value is null ? bans.Take(25) : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithDiscriminator, x.User.Id.ToString())); } diff --git a/NicoNicoNii b/NicoNicoNii index 1754b846..3e5de40c 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 1754b846494a93dce331dd1a56ec4bf0a42086bb +Subproject commit 3e5de40c4ddfe9f372dc014d155fa90a36ef1a75 From a361d0f6fff799da5dd23e71161f92fd60dd8a8d Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:21:08 +0100 Subject: [PATCH 015/113] reeeee --- MikuSharp/Commands/About.cs | 15 ++++---- MikuSharp/Commands/Fun.cs | 2 +- MikuSharp/Commands/Moderation.cs | 18 +++++----- MikuSharp/Commands/Weeb.cs | 36 +++++++++---------- MikuSharp/MikuBot.cs | 6 ++-- MikuSharp/Utilities/DiscordOptionProviders.cs | 2 +- MikuSharp/Utilities/Other.cs | 4 +-- MikuSharp/Utilities/Web.cs | 1 - NicoNicoNii | 2 +- 9 files changed, 41 insertions(+), 45 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index d5f908bb..7938123b 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -1,13 +1,10 @@ -using DisCatSharp; -using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; using DisCatSharp.Interactivity.Extensions; -using Google.Apis.YouTube.v3.Data; - using System; using System.Collections.Generic; using System.Linq; @@ -32,9 +29,9 @@ public async static Task BotAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithDiscriminator}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription(ctx.Client.CurrentApplication.Description); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription(ctx.Client.CurrentApplication.Description); foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id ? "Owner" : "Developer", member.User.UsernameWithDiscriminator)); + emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id ? "Owner" : "Developer", member.User.UsernameWithGlobalName)); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } @@ -82,7 +79,7 @@ public async static Task FeedbackAsync(InteractionContext ctx) var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var emb = new DiscordEmbedBuilder(); - emb.WithAuthor($"{ctx.User.UsernameWithDiscriminator}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); + emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); if (ctx.Guild != null) emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); var forum = guild.GetChannel(1020433162662322257); @@ -90,14 +87,14 @@ public async static Task FeedbackAsync(InteractionContext ctx) { ctx.Guild != null ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) : forum.AvailableTags.First(x => x.Id == 1020434935502360576) }; - var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithDiscriminator}"), null, tags, "Feedback"); + var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); var msg = await thread.GetMessageAsync(thread.Id); await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); } else - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"You were too slow :(\nThe time limit is one minute.").AsEphemeral()); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is one minute.").AsEphemeral()); } [SlashCommand("ping", "Current ping to discord's services")] diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index dbcf7232..13ca9c90 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -53,7 +53,7 @@ public async static Task ClydeAsync(InteractionContext ctx, [Option("text", "Tex Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); DiscordWebhookBuilder builder = new(); - builder.AddFile($"clyde.png", img); + builder.AddFile("clyde.png", img); await ctx.EditResponseAsync(builder); } diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 491bbb6c..50b9f36e 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -23,11 +23,11 @@ public async static Task DisableInvitesAsync(InteractionContext ctx, [Option("re try { await ctx.Guild.DisableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Disabled invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Disabled invites")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not disable invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not disable invites")); } } @@ -38,11 +38,11 @@ public async static Task EnableInvitesAsync(InteractionContext ctx, [Option("rea try { await ctx.Guild.EnableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Enabled invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Enabled invites")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not enable invites")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not enable invites")); } } @@ -53,11 +53,11 @@ public async static Task BanAsync(InteractionContext ctx, [Option("user", "User try { await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithGlobalName}")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithGlobalName}")); } } @@ -68,7 +68,7 @@ public async static Task UnbanAsync(InteractionContext ctx, [Option("username", var userId = Convert.ToUInt64(id); var user = await ctx.Client.GetUserAsync(userId, true); await ctx.Guild.UnbanMemberAsync(user, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithGlobalName}")); } [SlashCommand("kick", "Kick someone")] @@ -79,11 +79,11 @@ public async static Task KickAsync(InteractionContext ctx, [Option("user", "User { var member = await user.ConvertToMember(ctx.Guild); await member.RemoveAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithGlobalName}")); } catch (Exception) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithDiscriminator}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithGlobalName}")); } } diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 1d07522b..86fa709c 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -33,7 +33,7 @@ public async static Task AwooifyAsync(InteractionContext ctx, [Option("user", "U public async static Task DivaPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/diva")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -44,7 +44,7 @@ public async static Task DivaPic(InteractionContext ctx) ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" }; emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); DiscordWebhookBuilder builder = new(); @@ -57,7 +57,7 @@ public async static Task DivaPic(InteractionContext ctx) public async static Task GumiPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/gumi")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -70,7 +70,7 @@ public async static Task GumiPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -82,7 +82,7 @@ public async static Task GumiPic(InteractionContext ctx) public async static Task KaitoPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/kaito")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -95,7 +95,7 @@ public async static Task KaitoPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -107,7 +107,7 @@ public async static Task KaitoPic(InteractionContext ctx) public async static Task KLenPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/len")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -120,7 +120,7 @@ public async static Task KLenPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -132,7 +132,7 @@ public async static Task KLenPic(InteractionContext ctx) public async static Task LukaPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/luka")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -145,7 +145,7 @@ public async static Task LukaPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -157,7 +157,7 @@ public async static Task LukaPic(InteractionContext ctx) public async static Task MeikoPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/meiko")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -170,7 +170,7 @@ public async static Task MeikoPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -182,7 +182,7 @@ public async static Task MeikoPic(InteractionContext ctx) public async static Task HMikuPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/miku")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -195,7 +195,7 @@ public async static Task HMikuPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -223,7 +223,7 @@ public async static Task Cat(InteractionContext ctx) public async static Task KRinPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/rin")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -236,7 +236,7 @@ public async static Task KRinPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); @@ -248,7 +248,7 @@ public async static Task KRinPic(InteractionContext ctx) public async static Task KTetoPic(InteractionContext ctx) { await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://api.meek.moe/teto")); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { Position = 0 @@ -261,7 +261,7 @@ public async static Task KTetoPic(InteractionContext ctx) if (res.Creator.Length != 0) emim.AddField(new("Creator", res.Creator)); emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithDiscriminator, ctx.User.AvatarUrl); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 1e5edf87..022cc6cf 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -47,7 +47,7 @@ internal sealed class MikuBot : IDisposable internal Task StatusThread { get; set; } internal Task BotListThread { get; set; } - internal static WeebClient WeebClient = new("Hatsune Miku Bot", "4.0.0"); + internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "4.0.0"); internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } internal static DiscordShardedClient ShardedClient { get; set; } @@ -190,10 +190,10 @@ internal async static Task RegisterEvents() + $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." - + $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithDiscriminator}!" + + $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithGlobalName}!" + $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; var message = await args.Guild.Owner.SendMessageAsync(text); - sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithDiscriminator); + sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithGlobalName); sender.Logger.LogInformation("Message content: {content}", message.Content); } else diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index febf614b..4bf5aa62 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -39,7 +39,7 @@ public async Task> Prov List bannedUsers = new(25); bannedUsers.AddRange(ctx.FocusedOption.Value is null ? bans.Take(25) : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); - return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithDiscriminator, x.User.Id.ToString())); + return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); } } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 81039a31..ddffeabc 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -8,8 +8,8 @@ namespace MikuSharp.Utilities; public static class Other { - public static string ResizeLink(string url) => - $"https://api.meek.moe/im/?image={url}&resize=500"; + public static string ResizeLink(string url) + => $"https://api.meek.moe/im/?image={url}&resize=500"; public async static Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) { diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index 3508b097..ec3ee758 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -8,7 +8,6 @@ using System.IO; using System.Net.Http; -using System.Net.Http.Headers; using System.Threading.Tasks; using Weeb.net; diff --git a/NicoNicoNii b/NicoNicoNii index 3e5de40c..725dd1b0 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 3e5de40c4ddfe9f372dc014d155fa90a36ef1a75 +Subproject commit 725dd1b091181fd77ccb946dce64bb36b916c0cc From 4053f0184ce076114530e50e817d342a02dc8ccf Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:23:23 +0100 Subject: [PATCH 016/113] mew --- MikuSharp/Attributes/CustomMikuAttributes.cs | 13 ++++-------- MikuSharp/Commands/About.cs | 16 +++++++------- MikuSharp/Commands/Action.cs | 12 +++++------ MikuSharp/Commands/Developer.cs | 14 ++++++------- MikuSharp/Commands/Fun.cs | 14 ++++++------- MikuSharp/Commands/MikuGuild.cs | 2 +- MikuSharp/Commands/Moderation.cs | 12 +++++------ MikuSharp/Commands/Utility.cs | 12 +++++------ MikuSharp/Commands/Weeb.cs | 22 ++++++++++---------- MikuSharp/Events/MikuGuildJoin.cs | 4 ++-- MikuSharp/MikuBot.cs | 4 ++-- MikuSharp/Utilities/Bilibili.cs | 2 +- MikuSharp/Utilities/Other.cs | 2 +- MikuSharp/Utilities/Web.cs | 8 +++---- 14 files changed, 66 insertions(+), 71 deletions(-) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 97bdf24c..99a2cbd8 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -11,22 +11,17 @@ namespace MikuSharp.Attributes; /// /// Defines that usage of this command is restricted to users in a vc. /// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserVoicechatConnection : ApplicationCommandCheckBaseAttribute { public override Task ExecuteChecksAsync(BaseContext ctx) - { - if (ctx.Member.VoiceState?.Channel != null) - return Task.FromResult(true); - - return Task.FromResult(false); - } + => Task.FromResult(ctx.Member.VoiceState?.Channel != null); } /// /// Defines that usage of this command is restricted to users & the bot in a vc. /// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute { public async override Task ExecuteChecksAsync(BaseContext ctx) @@ -39,7 +34,7 @@ public async override Task ExecuteChecksAsync(BaseContext ctx) } } -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class)] public sealed class NotStaffAttribute : CheckBaseAttribute { public override Task ExecuteCheckAsync(CommandContext ctx, bool help) => diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 7938123b..3550706c 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -16,7 +16,7 @@ namespace MikuSharp.Commands; internal class About : ApplicationCommandsModule { [SlashCommand("donate", "Financial support information")] - public async static Task DonateAsync(InteractionContext ctx) + public static async Task DonateAsync(InteractionContext ctx) { var emb = new DiscordEmbedBuilder(); emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) @@ -25,7 +25,7 @@ public async static Task DonateAsync(InteractionContext ctx) } [SlashCommand("bot", "About the bot")] - public async static Task BotAsync(InteractionContext ctx) + public static async Task BotAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var emb = new DiscordEmbedBuilder(); @@ -36,7 +36,7 @@ public async static Task BotAsync(InteractionContext ctx) } [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] - public async static Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") + public static async Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) @@ -63,7 +63,7 @@ public async static Task FollowNewsAsync(InteractionContext ctx, [Option("target } [SlashCommand("feedback", "Send feedback!")] - public async static Task FeedbackAsync(InteractionContext ctx) + public static async Task FeedbackAsync(InteractionContext ctx) { DiscordInteractionModalBuilder modalBuilder = new(); modalBuilder.WithTitle("Feedback modal"); @@ -98,15 +98,15 @@ public async static Task FeedbackAsync(InteractionContext ctx) } [SlashCommand("ping", "Current ping to discord's services")] - public async static Task PingAsync(InteractionContext ctx) + public static async Task PingAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); [SlashCommand("which_shard", "What shard am I on?")] - public async static Task GetExecutingShardAsync(InteractionContext ctx) + public static async Task GetExecutingShardAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); [SlashCommand("stats", "Some stats of the MikuBot!")] - public async static Task StatsAsync(InteractionContext ctx) + public static async Task StatsAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var guildCount = 0; @@ -128,7 +128,7 @@ public async static Task StatsAsync(InteractionContext ctx) } [SlashCommand("support", "Link to my support server")] - public async static Task SupportAsybc(InteractionContext ctx) + public static async Task SupportAsybc(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 5d747abe..cddc5435 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -17,7 +17,7 @@ namespace MikuSharp.Commands; internal class Action : ApplicationCommandsModule { [SlashCommand("hug", "Hug someone!")] - public async static Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); @@ -30,7 +30,7 @@ public async static Task HugAsync(InteractionContext ctx, [Option("user", "The u } [SlashCommand("kiss", "Kiss someone!")] - public async static Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); @@ -43,7 +43,7 @@ public async static Task KissAsync(InteractionContext ctx, [Option("user", "The } [SlashCommand("lick", "Lick someone!")] - public async static Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); @@ -56,7 +56,7 @@ public async static Task LickAsync(InteractionContext ctx, [Option("user", "The } [SlashCommand("pat", "Pat someone!")] - public async static Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", new[] { "" }); @@ -73,7 +73,7 @@ public async static Task PatAsync(InteractionContext ctx, [Option("user", "The u } [SlashCommand("poke", "Poke someone!")] - public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", new[] { "" }); @@ -90,7 +90,7 @@ public async static Task PokeAsync(InteractionContext ctx, [Option("user", "The } [SlashCommand("slap", "Slap someone!")] - public async static Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", new[] { "" }); diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 2f4890d5..a036027a 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -23,13 +23,13 @@ namespace MikuSharp.Commands; public class Developer : ApplicationCommandsModule { [SlashCommand("test", "Testing")] - public async static Task TestAsync(InteractionContext ctx) + public static async Task TestAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); } [SlashCommand("guild_shard_test", "Testing")] - public async static Task GuildTestAsync(InteractionContext ctx) + public static async Task GuildTestAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) @@ -37,7 +37,7 @@ public async static Task GuildTestAsync(InteractionContext ctx) } [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] - public async static Task DeleteMessageAsync(ContextMenuContext ctx) + public static async Task DeleteMessageAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) @@ -55,7 +55,7 @@ public async static Task DeleteMessageAsync(ContextMenuContext ctx) /// /// The interaction context. [SlashCommand("dbg", "Get the logs of today")] - public async static Task GetDebugLogAsync(InteractionContext ctx) + public static async Task GetDebugLogAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) @@ -88,7 +88,7 @@ public async static Task GetDebugLogAsync(InteractionContext ctx) FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); log.Close(); - log.Dispose(); + await log.DisposeAsync(); File.Delete($"temp-{targetFile}"); } catch (Exception ex) @@ -105,7 +105,7 @@ public async static Task GetDebugLogAsync(InteractionContext ctx) /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public async static Task EvalCsAsync(ContextMenuContext ctx) + public static async Task EvalCsAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) @@ -133,7 +133,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe .WithColor(new("#FF007F")) .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") .Build())).ConfigureAwait(false); - msg = await ctx.GetOriginalResponseAsync(); + await ctx.GetOriginalResponseAsync(); try { var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 13ca9c90..1922b759 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -22,7 +22,7 @@ namespace MikuSharp.Commands; internal class Fun : ApplicationCommandsModule { [SlashCommand("8ball", "Yes? No? Maybe?")] - public async static Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var responses = new[] @@ -34,7 +34,7 @@ public async static Task EightBallAsync(InteractionContext ctx, [Option("text", } [SlashCommand("cat", "Get a random cat image!")] - public async static Task CatAsync(InteractionContext ctx) + public static async Task CatAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); @@ -46,7 +46,7 @@ public async static Task CatAsync(InteractionContext ctx) } [SlashCommand("clyde", "Say something as clyde bot")] - public async static Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); @@ -58,7 +58,7 @@ public async static Task ClydeAsync(InteractionContext ctx, [Option("text", "Tex } [SlashCommand("coinflip", "Flip a coin lol")] - public async static Task CoinflipAsync(InteractionContext ctx) + public static async Task CoinflipAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; @@ -66,7 +66,7 @@ public async static Task CoinflipAsync(InteractionContext ctx) } [SlashCommand("dog", "Random Dog Image")] - public async static Task DogAsync(InteractionContext ctx) + public static async Task DogAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); @@ -110,7 +110,7 @@ public static async Task Lion(InteractionContext ctx) }*/ [SlashCommand("lizard", "Get a random lizard image")] - public async static Task LizardAsync(InteractionContext ctx) + public static async Task LizardAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); @@ -156,7 +156,7 @@ public static async Task RedPandaAsync(InteractionContext ctx) }*/ [SlashCommand("rps", "Play rock paper scissors!")] - public async static Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuild.cs index 551b7791..707cab3d 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuild.cs @@ -13,7 +13,7 @@ namespace MikuSharp.Commands; public class MikuGuild : ApplicationCommandsModule { [SlashCommand("smolcar", "#SmolArmy")] - public async static Task SmolCarAsync(InteractionContext ctx) + public static async Task SmolCarAsync(InteractionContext ctx) { if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) { diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 50b9f36e..69a6e917 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -17,7 +17,7 @@ namespace MikuSharp.Commands; internal class Moderation : ApplicationCommandsModule { [SlashCommand("disable_invites", "Disable invites usage for guild")] - public async static Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -32,7 +32,7 @@ public async static Task DisableInvitesAsync(InteractionContext ctx, [Option("re } [SlashCommand("enable_invites", "Enable invites usage for guild")] - public async static Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -47,7 +47,7 @@ public async static Task EnableInvitesAsync(InteractionContext ctx, [Option("rea } [SlashCommand("ban", "Ban someone")] - public async static Task BanAsync(InteractionContext ctx, [Option("user", "User to ban")] DiscordUser user, [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task BanAsync(InteractionContext ctx, [Option("user", "User to ban")] DiscordUser user, [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -62,7 +62,7 @@ public async static Task BanAsync(InteractionContext ctx, [Option("user", "User } [SlashCommand("unban", "Unban someone")] - public async static Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); var userId = Convert.ToUInt64(id); @@ -72,7 +72,7 @@ public async static Task UnbanAsync(InteractionContext ctx, [Option("username", } [SlashCommand("kick", "Kick someone")] - public async static Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(false); try @@ -88,7 +88,7 @@ public async static Task KickAsync(InteractionContext ctx, [Option("user", "User } [SlashCommand("purge", "Delete a large amount of messages fast")] - public async static Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) + public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) { await ctx.DeferAsync(true); try diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index 906747f0..effa1a17 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -28,7 +28,7 @@ internal class Utility : ApplicationCommandsModule internal class AnimeMangaUtility : ApplicationCommandsModule { [SlashCommand("anime_search", "Search for an anime")] - public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try @@ -84,7 +84,7 @@ public async static Task SearchAnimeAsync(InteractionContext ctx, [Option("searc } [SlashCommand("manga_search", "Search for an manga")] - public async static Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); try @@ -141,11 +141,11 @@ public async static Task SearchMangaAsync(InteractionContext ctx, [Option("searc internal class DiscordUtility : ApplicationCommandsModule { [SlashCommand("avatar", "Get the avatar of someone or yourself")] - public async static Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) + public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null ? user.AvatarUrl : ctx.User.AvatarUrl).Build())); [SlashCommand("server_info", "Get information about the server")] - public async static Task GuildInfoAsync(InteractionContext ctx) + public static async Task GuildInfoAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); @@ -173,7 +173,7 @@ public async static Task GuildInfoAsync(InteractionContext ctx) } [SlashCommand("user_info", "Get information about a user")] - public async static Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); @@ -205,7 +205,7 @@ public async static Task UserInfoAsync(InteractionContext ctx, [Option("user", " } [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public async static Task EmojiListAsync(InteractionContext ctx) + public static async Task EmojiListAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var wat = "You have to execute this command in a server!"; diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 86fa709c..51ce08ac 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -21,7 +21,7 @@ namespace MikuSharp.Commands; internal class Weeb : ApplicationCommandsModule { [SlashCommand("awooify", "Awooify your or someones avatar!")] - public async static Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) + public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) { await ctx.DeferAsync(false); var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; @@ -30,7 +30,7 @@ public async static Task AwooifyAsync(InteractionContext ctx, [Option("user", "U } [SlashCommand("diva", "Radnom PJD Loading image")] - public async static Task DivaPic(InteractionContext ctx) + public static async Task DivaPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); @@ -54,7 +54,7 @@ public async static Task DivaPic(InteractionContext ctx) } [SlashCommand("gumi", "Random Gumi image")] - public async static Task GumiPic(InteractionContext ctx) + public static async Task GumiPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); @@ -79,7 +79,7 @@ public async static Task GumiPic(InteractionContext ctx) } [SlashCommand("kaito", "Random Kaito image")] - public async static Task KaitoPic(InteractionContext ctx) + public static async Task KaitoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); @@ -104,7 +104,7 @@ public async static Task KaitoPic(InteractionContext ctx) } [SlashCommand("len", "Random Len image")] - public async static Task KLenPic(InteractionContext ctx) + public static async Task KLenPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); @@ -129,7 +129,7 @@ public async static Task KLenPic(InteractionContext ctx) } [SlashCommand("luka", "Random Luka image")] - public async static Task LukaPic(InteractionContext ctx) + public static async Task LukaPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); @@ -154,7 +154,7 @@ public async static Task LukaPic(InteractionContext ctx) } [SlashCommand("meiko", "Random Meiko image")] - public async static Task MeikoPic(InteractionContext ctx) + public static async Task MeikoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); @@ -179,7 +179,7 @@ public async static Task MeikoPic(InteractionContext ctx) } [SlashCommand("miku", "Random Miku image")] - public async static Task HMikuPic(InteractionContext ctx) + public static async Task HMikuPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); @@ -204,7 +204,7 @@ public async static Task HMikuPic(InteractionContext ctx) } [SlashCommand("neko", "Get a random neko image")] - public async static Task Cat(InteractionContext ctx) + public static async Task Cat(InteractionContext ctx) { await ctx.DeferAsync(false); var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); @@ -220,7 +220,7 @@ public async static Task Cat(InteractionContext ctx) } [SlashCommand("rin", "Random Rin image")] - public async static Task KRinPic(InteractionContext ctx) + public static async Task KRinPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); @@ -245,7 +245,7 @@ public async static Task KRinPic(InteractionContext ctx) } [SlashCommand("teto", "Random Teto image")] - public async static Task KTetoPic(InteractionContext ctx) + public static async Task KTetoPic(InteractionContext ctx) { await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index d8a0db23..9caa355a 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -15,7 +15,7 @@ public class MikuGuild /// /// The client. /// The event args. - public async static Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) + public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) { await Task.FromResult(true); } @@ -25,7 +25,7 @@ public async static Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventAr /// /// The discord client. /// The event args. - public async static Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) + public static async Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) { if (args is { PendingBefore: true, PendingAfter: false }) { diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 022cc6cf..49f30755 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -152,7 +152,7 @@ internal MikuBot() LavalinkModules = ShardedClient.UseLavalinkAsync().Result;*/ } - internal async static Task RegisterEvents() + internal static async Task RegisterEvents() { ShardedClient.ClientErrored += (sender, args) => { @@ -222,7 +222,7 @@ internal async Task ShowConnections() } } */ - internal async static Task UpdateBotList() + internal static async Task UpdateBotList() { await Task.Delay(15000); while (true) diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index 503e53c6..40397b34 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -13,7 +13,7 @@ namespace MikuSharp.Utilities; public static class Bilibili { - public async static Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) + public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) { try { diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index ddffeabc..97933152 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -11,7 +11,7 @@ public static class Other public static string ResizeLink(string url) => $"https://api.meek.moe/im/?image={url}&resize=500"; - public async static Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) + public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) { var builder = new DiscordInteractionResponseBuilder(); if (ephemeral) diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index ec3ee758..a1329313 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -16,7 +16,7 @@ namespace MikuSharp.Utilities; public static class Web { - public async static Task GetNekosLifeAsync(this HttpClient client, string url) + public static async Task GetNekosLifeAsync(this HttpClient client, string url) { var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) @@ -32,7 +32,7 @@ public async static Task GetNekosLifeAsync(this HttpClient client, st return dl; } - public async static Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) + public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) { client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); @@ -46,7 +46,7 @@ public async static Task GetKsoftSiRanImgAsync(this HttpClient cl return v; } - public async static Task GetNekobotAsync(this HttpClient client, string url) + public static async Task GetNekobotAsync(this HttpClient client, string url) { var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) @@ -62,7 +62,7 @@ public async static Task GetNekobotAsync(this HttpClient client, string return dl; } - public async static Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) + public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) { var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) From 5544dfb49e8996b54db189187fc1bc4357658924 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:24:09 +0100 Subject: [PATCH 017/113] Update CustomMikuAttributes.cs --- MikuSharp/Attributes/CustomMikuAttributes.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 99a2cbd8..08c5dd6f 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -27,16 +27,15 @@ public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandChe public async override Task ExecuteChecksAsync(BaseContext ctx) { var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); - if (ctx.Member.VoiceState?.Channel != null && bot.VoiceState?.Channel != null) - return await Task.FromResult(true); - - return await Task.FromResult(false); + return ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null + ? await Task.FromResult(true) + : await Task.FromResult(false); } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class)] public sealed class NotStaffAttribute : CheckBaseAttribute { - public override Task ExecuteCheckAsync(CommandContext ctx, bool help) => - Task.FromResult(!ctx.User.IsStaff); + public override Task ExecuteCheckAsync(CommandContext ctx, bool help) + => Task.FromResult(!ctx.User.IsStaff); } \ No newline at end of file From 8edef8891dfa5fc2a1293f427d637c21a8828c95 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 08:24:49 +0100 Subject: [PATCH 018/113] Update Program.cs --- MikuSharp/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index 8c46d55e..cf09744b 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -11,7 +11,6 @@ private static void Main(string[] args) MikuBot.RegisterEvents().Wait(); bot.RegisterCommands(); bot.RunAsync().Wait(); - bot.Dispose(); } Log.Logger.Information("Shutdown!"); From f2669c1159008061e1f274ce5acb787823ff5047 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 17:40:38 +0100 Subject: [PATCH 019/113] feat: ping user for action --- MikuSharp/Commands/Action.cs | 12 +- MikuSharp/MikuSharp.csproj | 222 +++++++++++++++++------------------ 2 files changed, 117 insertions(+), 117 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index cddc5435..d42c7d4b 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -21,9 +21,9 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); - wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); DiscordWebhookBuilder builder = new(); + builder.WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); @@ -34,9 +34,9 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); - wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); DiscordWebhookBuilder builder = new(); + builder.WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); @@ -47,9 +47,9 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); - wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); DiscordWebhookBuilder builder = new(); + builder.WithContent($"{ctx.User.Mention} licks {user.Mention} owo"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); @@ -62,11 +62,11 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); DiscordWebhookBuilder builder = new(); + builder.WithContent($"{ctx.User.Mention} pats {user.Mention} #w#"); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); @@ -79,11 +79,11 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); DiscordWebhookBuilder builder = new(); + builder.WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); @@ -96,11 +96,11 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); DiscordWebhookBuilder builder = new(); + builder.WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 8095b8f6..2186b6b3 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -1,30 +1,30 @@  - - Exe - net7.0 - MikuSharp.Program - miku.ico - 4.0.0 - MikuSharp Team - https://github.com/Sekoree/MikuSharp/ - git - https://github.com/Sekoree/MikuSharp/ - MIT - Full Hatsune Miku Discord bot C# Rewrite! - enable - Hatsune Miku Discord MikuBot - MikuSharp Team - miku.jpg - README.md - discord bot; discatsharp; hatsune miku; miku; bot - + + Exe + net7.0 + MikuSharp.Program + miku.ico + 4.0.0 + MikuSharp Team + https://github.com/Sekoree/MikuSharp/ + git + https://github.com/Sekoree/MikuSharp/ + MIT + Full Hatsune Miku Discord bot C# Rewrite! + enable + Hatsune Miku Discord MikuBot + MikuSharp Team + miku.jpg + README.md + discord bot; discatsharp; hatsune miku; miku; bot + - - - - - + + + + + 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 @@ -32,97 +32,97 @@ 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 - + - - True - \ - + + True + \ + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - True - \ - - - Always - - - Always - - - Always - - - Always - - - Always - - + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + True + \ + + + Always + + + Always + + + Always + + + Always + + + Always + + - + \ No newline at end of file From fe7236cc1e07fbd5efab2170f37a6fc668867650 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 21 Jan 2024 17:52:20 +0100 Subject: [PATCH 020/113] fix: ping in followup instead --- MikuSharp/Commands/Action.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index d42c7d4b..8435221f 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -21,12 +21,13 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); + wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); DiscordWebhookBuilder builder = new(); - builder.WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); } [SlashCommand("kiss", "Kiss someone!")] @@ -34,12 +35,13 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); + wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); DiscordWebhookBuilder builder = new(); - builder.WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); } [SlashCommand("lick", "Lick someone!")] @@ -47,12 +49,13 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); + wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); DiscordWebhookBuilder builder = new(); - builder.WithContent($"{ctx.User.Mention} licks {user.Mention} owo"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); } [SlashCommand("pat", "Pat someone!")] @@ -62,14 +65,15 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); DiscordWebhookBuilder builder = new(); - builder.WithContent($"{ctx.User.Mention} pats {user.Mention} #w#"); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); } [SlashCommand("poke", "Poke someone!")] @@ -79,14 +83,15 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); DiscordWebhookBuilder builder = new(); - builder.WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); } [SlashCommand("slap", "Slap someone!")] @@ -96,13 +101,14 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", new[] { "" }); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); DiscordWebhookBuilder builder = new(); - builder.WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); } } \ No newline at end of file From a7913f20cdf4763fe67ca9bc7b59f17d699a3a5e Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 29 Mar 2024 03:51:30 +0100 Subject: [PATCH 021/113] fuck me --- MikuSharp/Commands/Developer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index a036027a..f9507c3c 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -139,8 +139,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); var sopts = ScriptOptions.Default; - sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities", - "DisCatSharp.Lavalink"); + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); From d57ffea51f75191391a37857cc455660239be810 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 24 Apr 2024 07:59:14 +0200 Subject: [PATCH 022/113] bleh --- .editorconfig | 4 + MikuSharp.sln | 5 + MikuSharp/Attributes/CustomMikuAttributes.cs | 2 +- MikuSharp/Commands/Music.cs | 527 ++++++++++-------- MikuSharp/Entities/Entry.cs | 15 +- MikuSharp/Entities/Guild.cs | 28 +- MikuSharp/Entities/MusicInstance.cs | 514 +++++++++-------- MikuSharp/Entities/PlaylistEntry.cs | 13 +- MikuSharp/Entities/QueueEntry.cs | 15 +- MikuSharp/Entities/TrackResult.cs | 24 +- MikuSharp/Events/Lavalink.cs | 115 ++-- MikuSharp/Events/VoiceChat.cs | 63 +-- MikuSharp/MikuBot.cs | 48 +- MikuSharp/MikuSharp.csproj | 26 +- MikuSharp/Utilities/Bilibili.cs | 1 + MikuSharp/Utilities/Database.cs | 108 ++-- MikuSharp/Utilities/DiscordOptionProviders.cs | 33 +- MikuSharp/Utilities/Music.cs | 134 ++--- MikuSharp/Utilities/NND.cs | 37 +- MikuSharp/Utilities/PlaylistDB.cs | 6 +- NicoNicoNii | 2 +- 21 files changed, 911 insertions(+), 809 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..653c3205 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# IDE0003: Remove qualification +dotnet_diagnostic.IDE0003.severity = silent diff --git a/MikuSharp.sln b/MikuSharp.sln index 6f07b478..8fa0e00d 100644 --- a/MikuSharp.sln +++ b/MikuSharp.sln @@ -7,6 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MikuSharp", "MikuSharp\Miku EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NicoNicoNii", "NicoNicoNii\NicoNicoNii\NicoNicoNii.csproj", "{38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4CB3E9BD-AA4F-4000-9595-03AC74654E5A}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 08c5dd6f..05b7c478 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -24,7 +24,7 @@ public override Task ExecuteChecksAsync(BaseContext ctx) [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute { - public async override Task ExecuteChecksAsync(BaseContext ctx) + public override async Task ExecuteChecksAsync(BaseContext ctx) { var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); return ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs index ea96ef99..a8f0857e 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Music.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; @@ -28,11 +28,13 @@ namespace MikuSharp.Commands; [SlashCommandGroup("music", "Music commands", dmPermission: false)] public class Music : ApplicationCommandsModule { - private static readonly string[] Units = new[] { "", "ki", "Mi", "Gi" }; + private static readonly string[] Units = { "", "ki", "Mi", "Gi" }; + private static string SizeToString(long l) { double d = l; var u = 0; + while (d >= 900 && u < Units.Length - 2) { u++; @@ -45,60 +47,61 @@ private static string SizeToString(long l) [SlashCommandGroup("base", "Base commands")] public class Base : ApplicationCommandsModule { - [SlashCommand("join", "Joins the voice channel you're in")] - [RequireUserVoicechatConnection] + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] public static async Task JoinAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); + await ctx.DeferAsync(); + if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); + g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); await g.ConditionalConnect(ctx); - g.musicInstance.usedChannel = ctx.Channel; + g.MusicInstance.UsedChannel = ctx.Channel; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); } [SlashCommand("leave", "Leaves the channel")] public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; - if (g.musicInstance == null) + + if (g.MusicInstance == null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not in a voice channel")); return; } - g.musicInstance.playstate = Playstate.NotPlaying; + + g.MusicInstance.Playstate = Playstate.NotPlaying; + try { if (keep) - await g.musicInstance.guildConnection.StopAsync(); - await g.musicInstance.guildConnection.DisconnectAsync(); + await g.MusicInstance.GuildConnection.StopAsync(); + await g.MusicInstance.GuildConnection.DisconnectAsync(); if (!keep) await Database.ClearQueue(ctx.Guild); - g.musicInstance = null; + g.MusicInstance = null; } catch (Exception) { } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); } - - [SlashCommand("lstats", "Displays Lavalink statistics")] - [ApplicationCommandRequireOwner] + [SlashCommand("lstats", "Displays Lavalink statistics"), ApplicationCommandRequireTeamDeveloper] public static async Task GetLavalinkStatsAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); - var stats = MikuBot.LavalinkNodeConnections[ctx.Client.ShardId].Statistics; + await ctx.DeferAsync(); + var stats = MikuBot.LavalinkSessions[ctx.Client.ShardId].Statistics; var sb = new StringBuilder(); sb.Append("Lavalink resources usage statistics: ```") .Append("Uptime: ").Append(stats.Uptime).AppendLine() - .Append("Players: ").AppendFormat("{0} active / {1} total", stats.ActivePlayers, stats.TotalPlayers).AppendLine() - .Append("CPU Cores: ").Append(stats.CpuCoreCount).AppendLine() - .Append("CPU Usage: ").AppendFormat("{0:#,##0.0%} lavalink / {1:#,##0.0%} system", stats.CpuLavalinkLoad, stats.CpuSystemLoad).AppendLine() - .Append("RAM Usage: ").AppendFormat("{0} allocated / {1} used / {2} free / {3} reservable", SizeToString(stats.RamAllocated), SizeToString(stats.RamUsed), SizeToString(stats.RamFree), SizeToString(stats.RamReservable)).AppendLine() - .Append("Audio frames (per minute): ").AppendFormat("{0:#,##0} sent / {1:#,##0} nulled / {2:#,##0} deficit", stats.AverageSentFramesPerMinute, stats.AverageNulledFramesPerMinute, stats.AverageDeficitFramesPerMinute).AppendLine() + .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() + .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() + .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() + .Append("RAM Usage: ").Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() + .Append("Audio frames (per minute): ").Append($"{stats.Frames.Sent:#,##0} sent / {stats.Frames.Nulled:#,##0} nulled / {stats.Frames.Deficit:#,##0} deficit").AppendLine() .Append("```"); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); } @@ -107,51 +110,54 @@ public static async Task GetLavalinkStatsAsync(InteractionContext ctx) [SlashCommandGroup("playback", "Playback controls")] public class Playback : ApplicationCommandsModule { - [SlashCommand("seek", "Seek a song")] - [RequireUserAndBotVoicechatConnection] + [SlashCommand("seek", "Seek a song"), RequireUserAndBotVoicechatConnection] public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) { - await ctx.DeferAsync(true); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); + await ctx.DeferAsync(); + if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - if (g.musicInstance.playstate != Playstate.Playing && g.musicInstance.playstate != Playstate.Paused) + + if (g.MusicInstance.Playstate != Playstate.Playing && g.MusicInstance.Playstate != Playstate.Paused) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); return; } - g.musicInstance.usedChannel = ctx.Channel; + + g.MusicInstance.UsedChannel = ctx.Channel; var ts = TimeSpan.FromSeconds(position); - await g.musicInstance.guildConnection.SeekAsync(ts); + await g.MusicInstance.GuildConnection.SeekAsync(ts); var pos = ts.Hours < 1 ? ts.ToString(@"mm\:ss") : ts.ToString(@"hh\:mm\:ss"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked {g.musicInstance.currentSong.track.Title} to {pos}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked {g.MusicInstance.CurrentSong.Track.Info.Title} to {pos}")); } - [SlashCommand("play", "Play or queue a song")] - [RequireUserVoicechatConnection] - public static async Task PlayAsync(InteractionContext ctx, - [Option("song", "Song name or url to play")] string name_or_url = null, - [Option("music_file", "Music file to play")] DiscordAttachment music_file = null + [SlashCommand("play", "Play or queue a song"), RequireUserVoicechatConnection] + public static async Task PlayAsync( + InteractionContext ctx, + [Option("song", "Song name or url to play")] string nameOrUrl = null, + [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null ) { - await ctx.DeferAsync(true); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); + await ctx.DeferAsync(); + if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); + g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); var curq = await Database.GetQueueAsync(ctx.Guild); - if (curq.Count != 0 && g.musicInstance.playstate == Playstate.NotPlaying) + + if (curq.Count != 0 && g.MusicInstance.Playstate == Playstate.NotPlaying) { var inter = ctx.Client.GetInteractivity(); List buttons = new(2) - { - new DiscordButtonComponent(ButtonStyle.Success, "restore", "Restore old queue"), - new DiscordButtonComponent(ButtonStyle.Danger, "clear", "Clear old queue") - }; + { + new(ButtonStyle.Success, "restore", "Restore old queue"), + new(ButtonStyle.Danger, "clear", "Clear old queue") + }; var msg = await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Recover Queue").WithDescription("The last time the bot disconnected the queue wasnt cleared, do you want to restore and play that old one?").Build()).AddComponents(buttons)); var hmm = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + if (hmm.TimedOut) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Timed out!")); @@ -162,8 +168,8 @@ public static async Task PlayAsync(InteractionContext ctx, await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); buttons.ForEach(x => x.Disable()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Restored").AddComponents(buttons)); - await g.musicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); - await g.musicInstance.PlaySong(); + await g.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); + await g.MusicInstance.PlaySong(); return; } else @@ -177,217 +183,236 @@ public static async Task PlayAsync(InteractionContext ctx, await g.ConditionalConnect(ctx); - if (music_file == null && name_or_url == null) + if (musicFile == null && nameOrUrl == null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); return; } - g.musicInstance.usedChannel = ctx.Channel; - name_or_url = music_file.SearchUrlOrAttachment(name_or_url); - var oldState = g.musicInstance.playstate; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Trying to play/search {name_or_url}...")); - var q = await g.musicInstance.QueueSong(name_or_url, ctx); + + g.MusicInstance.UsedChannel = ctx.Channel; + nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); + var oldState = g.MusicInstance.Playstate; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Trying to play/search {nameOrUrl}...")); + var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx); + if (q == null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); return; } + var emb = new DiscordEmbedBuilder(); + if (oldState == Playstate.Playing) { - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\n" + - $"Requested by {ctx.Member.Mention}")); + emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}")); if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Added").Build()).AsEphemeral()); } else { if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\nRequested by {ctx.Member.Mention}")); + emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); else - emb.AddField(new DiscordEmbedField(q.Tracks[q.PlaylistInfo.SelectedTrack].Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Author}\nRequested by {ctx.Member.Mention}")); + emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + "]", + $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}")); if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Playing").Build()).AsEphemeral()); } } - [SlashCommand("insert", "Queue a song at a specific position!")] - [RequireUserVoicechatConnection] - public static async Task InsertToQueueAsync(InteractionContext ctx, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string posi, - [Option("song", "Song name or url to play")] string name_or_url = null, - [Option("music_file", "Music file to play")] DiscordAttachment music_file = null + [SlashCommand("insert", "Queue a song at a specific position!"), RequireUserVoicechatConnection] + public static async Task InsertToQueueAsync( + InteractionContext ctx, + [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string posi, + [Option("song", "Song name or url to play")] string nameOrUrl = null, + [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null ) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var pos = Convert.ToInt32(posi); var g = MikuBot.Guilds[ctx.Guild.Id]; if (pos < 1) return; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); + + g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); await g.ConditionalConnect(ctx); - if (music_file == null && name_or_url == null) + if (musicFile == null && nameOrUrl == null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); return; } - g.musicInstance.usedChannel = ctx.Channel; - name_or_url = music_file.SearchUrlOrAttachment(name_or_url); - var oldState = g.musicInstance.playstate; - var q = await g.musicInstance.QueueSong(name_or_url, ctx, pos); + + g.MusicInstance.UsedChannel = ctx.Channel; + nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); + var oldState = g.MusicInstance.Playstate; + var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx, pos); + if (q == null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); return; } + var emb = new DiscordEmbedBuilder(); + if (oldState == Playstate.Playing) { - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\n" + - $"Requested by {ctx.Member.Mention}\nAt position: {pos}")); + emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}\nAt position: {pos}")); if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); emb.WithTitle("Playing"); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } else { if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new DiscordEmbedField(q.Tracks.First().Title + "[" + (q.Tracks.First().Length.Hours != 0 ? q.Tracks.First().Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Author}\nRequested by {ctx.Member.Mention}")); + emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); else - emb.AddField(new DiscordEmbedField(q.Tracks[q.PlaylistInfo.SelectedTrack].Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Author}\nRequested by {ctx.Member.Mention}At position: {pos}")); + emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + "]", + $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}At position: {pos}")); if (q.Tracks.Count != 1) - emb.AddField(new DiscordEmbedField("Playlist added:", $"added {q.Tracks.Count - 1} more")); + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); emb.WithTitle("Added"); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } } - [SlashCommand("skip", "Skip the current song")] - [RequireUserAndBotVoicechatConnection] + [SlashCommand("skip", "Skip the current song"), RequireUserAndBotVoicechatConnection] public static async Task SkipSongAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); var queue = await Database.GetQueueAsync(ctx.Guild); if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - g.musicInstance.guildConnection.PlaybackFinished -= Lavalink.LavalinkTrackFinish; - if (g.musicInstance.currentSong != null) + + g.MusicInstance.UsedChannel = ctx.Channel; + g.MusicInstance.GuildConnection.TrackEnded -= Lavalink.LavalinkTrackFinish; + + if (g.MusicInstance.CurrentSong != null) { - if (g.musicInstance.repeatMode != RepeatMode.On && g.musicInstance.repeatMode != RepeatMode.All) - await Database.RemoveFromQueueAsync(g.musicInstance.currentSong.position, ctx.Guild); + if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) + await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, ctx.Guild); if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.musicInstance.currentSong.track.TrackString); - else if (lastPlayedSongs[0]?.track.Uri != g.musicInstance.currentSong.track.Uri) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.musicInstance.currentSong.track.TrackString); + await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); + else if (lastPlayedSongs[0]?.Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) + await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); } + queue = await Database.GetQueueAsync(ctx.Guild); - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + if (queue.Count != 0) - await g.musicInstance.PlaySong(); + await g.MusicInstance.PlaySong(); else { - g.musicInstance.playstate = Playstate.NotPlaying; - await g.musicInstance.guildConnection.StopAsync(); + g.MusicInstance.Playstate = Playstate.NotPlaying; + await g.MusicInstance.GuildConnection.StopAsync(); } - if (g.musicInstance.lastSong != null) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Skipped:**\n{g.musicInstance.lastSong.track.Title}").Build())); + + if (g.MusicInstance.LastSong != null) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Skipped:**\n{g.MusicInstance.LastSong.Track.Info.Title}").Build())); else await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Continued!**").Build())); } - [SlashCommand("stop", "Stop Playback")] - [RequireUserAndBotVoicechatConnection] + [SlashCommand("stop", "Stop Playback"), RequireUserAndBotVoicechatConnection] public static async Task StopAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - await Task.Run(async () => await g.musicInstance.guildConnection.StopAsync()); - var cmd_id = ctx.Client.GetApplicationCommands().GlobalCommands.First(x => x.Name == "music").Id; - await ctx.EditResponseAsync(builder: new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Stopped** (use to start playback again)").Build())); + + g.MusicInstance.UsedChannel = ctx.Channel; + await Task.Run(async () => await g.MusicInstance.GuildConnection.StopAsync()); + var cmdId = ctx.Client.GetApplicationCommands().GlobalCommands.First(x => x.Name == "music").Id; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Stopped** (use to start playback again)").Build())); } - [SlashCommand("volume", "Change the music volume")] - [RequireUserAndBotVoicechatConnection] - public static async Task ModifyVolumeAsync(InteractionContext ctx, - [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] int vol = 100 + [SlashCommand("volume", "Change the music volume"), RequireUserAndBotVoicechatConnection] + public static async Task ModifyVolumeAsync( + InteractionContext ctx, + [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] + int vol = 100 ) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; + + g.MusicInstance.UsedChannel = ctx.Channel; if (vol > 150) vol = 150; - await g.musicInstance.guildConnection.SetVolumeAsync(vol); + await g.MusicInstance.GuildConnection.SetVolumeAsync(vol); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Set volume to {vol}**").Build())); } - [SlashCommand("pause", "Pauses playback")] - [RequireUserAndBotVoicechatConnection] + [SlashCommand("pause", "Pauses playback"), RequireUserAndBotVoicechatConnection] public static async Task PauseAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - if (g.musicInstance.playstate == Playstate.Playing) + + g.MusicInstance.UsedChannel = ctx.Channel; + + if (g.MusicInstance.Playstate == Playstate.Playing) { - await g.musicInstance.guildConnection.PauseAsync(); - g.musicInstance.playstate = Playstate.Paused; + await g.MusicInstance.GuildConnection.PauseAsync(); + g.MusicInstance.Playstate = Playstate.Paused; await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Paused**").Build())); } else await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not playing anything right now")); } - [SlashCommand("resume", "Resumes paused playback")] - [RequireUserAndBotVoicechatConnection] + [SlashCommand("resume", "Resumes paused playback"), RequireUserAndBotVoicechatConnection] public static async Task ResumeAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - if (g.musicInstance.playstate == Playstate.Stopped) + + g.MusicInstance.UsedChannel = ctx.Channel; + + if (g.MusicInstance.Playstate == Playstate.Stopped) { - await g.musicInstance.PlaySong(); + await g.MusicInstance.PlaySong(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Started Playback**").Build())); } else { - await g.musicInstance.guildConnection.ResumeAsync(); - g.musicInstance.playstate = Playstate.Playing; + await g.MusicInstance.GuildConnection.ResumeAsync(); + g.MusicInstance.Playstate = Playstate.Playing; await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Resumed**").Build())); } } } - [SlashCommandGroup("queue", "Queue management")] - [RequireUserAndBotVoicechatConnection] + [SlashCommandGroup("queue", "Queue management"), RequireUserAndBotVoicechatConnection] public class Queue : ApplicationCommandsModule { [SlashCommand("show", "Show the current queue")] public static async Task ShowQueueAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var queue = await Database.GetQueueAsync(ctx.Guild); + try { var g = MikuBot.Guilds[ctx.Guild.Id]; + if (queue.Count == 0) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); @@ -395,107 +420,115 @@ public static async Task ShowQueueAsync(InteractionContext ctx) } var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = queue.Count / 5; - if ((queue.Count % 5) != 0) + var songsPerPage = 0; + var currentPage = 1; + var songAmount = 0; + var totalP = queue.Count / 5; + if (queue.Count % 5 != 0) totalP++; var emb = new DiscordEmbedBuilder(); - List Pages = new(); - if (g.musicInstance.repeatMode == RepeatMode.All) + List pages = new(); + + if (g.MusicInstance.RepeatMode == RepeatMode.All) { - songAmount = g.musicInstance.repeatAllPos; - foreach (var Track in queue) + songAmount = g.MusicInstance.RepeatAllPos; + + foreach (var track in queue) { if (songsPerPage == 0 && currentPage == 1) { emb.WithTitle("Current Queue"); g.GetPlayingState(out var time1, out var time2); - emb.AddField(new DiscordEmbedField($"**{songAmount}.{g.musicInstance.currentSong.track.Title.Replace("*", "").Replace("|", "")}** by {g.musicInstance.currentSong.track.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.musicInstance.currentSong.addedBy}> [Link]({g.musicInstance.currentSong.track.Uri.AbsoluteUri})\nˉˉˉˉˉ")); + emb.AddField(new($"**{songAmount}.{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", + $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); } else { queue.ElementAt(songAmount).GetPlayingState(out var time); - emb.AddField(new DiscordEmbedField($"**{songAmount}.{queue.ElementAt(songAmount).track.Title.Replace("*", "").Replace("|", "")}** by {queue.ElementAt(songAmount).track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{queue.ElementAt(songAmount).addedBy}> [Link]({queue.ElementAt(songAmount).track.Uri.AbsoluteUri})")); + emb.AddField(new($"**{songAmount}.{queue.ElementAt(songAmount).Track.Info.Title.Replace("*", "").Replace("|", "")}** by {queue.ElementAt(songAmount).Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", + $"Requested by <@{queue.ElementAt(songAmount).AddedBy}> [Link]({queue.ElementAt(songAmount).Track.Info.Uri.AbsoluteUri})")); } + songsPerPage++; songAmount++; if (songAmount == queue.Count) songAmount = 0; + if (songsPerPage == 5) { songsPerPage = 0; - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); + pages.Add(new(embed: emb)); emb.ClearFields(); emb.WithTitle("more™"); currentPage++; } - if (songAmount == g.musicInstance.repeatAllPos) + + if (songAmount == g.MusicInstance.RepeatAllPos) { - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); + pages.Add(new(embed: emb)); emb.ClearFields(); } } } else - { - foreach (var Track in queue) + foreach (var track in queue) { if (songsPerPage == 0 && currentPage == 1) { emb.WithTitle("Current Queue"); g.GetPlayingState(out var time1, out var time2); - emb.AddField(new DiscordEmbedField($"**{g.musicInstance.currentSong.track.Title.Replace("*", "").Replace("|", "")}** by {g.musicInstance.currentSong.track.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.musicInstance.currentSong.addedBy}> [Link]({g.musicInstance.currentSong.track.Uri.AbsoluteUri})\nˉˉˉˉˉ")); + emb.AddField(new($"**{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", + $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); } else { - Track.GetPlayingState(out var time); - emb.AddField(new DiscordEmbedField($"**{songAmount}.{Track.track.Title.Replace("*", "").Replace("|", "")}** by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{Track.addedBy}> [Link]({Track.track.Uri.AbsoluteUri})")); + track.GetPlayingState(out var time); + emb.AddField(new($"**{songAmount}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", + $"Requested by <@{track.AddedBy}> [Link]({track.Track.Info.Uri.AbsoluteUri})")); } + songsPerPage++; songAmount++; + if (songsPerPage == 5) { songsPerPage = 0; emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - Pages.Add(new Page(embed: emb)); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + pages.Add(new(embed: emb)); emb.ClearFields(); emb.WithTitle("more™"); currentPage++; } + if (songAmount == queue.Count) { emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - Pages.Add(new Page(embed: emb)); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + pages.Add(new(embed: emb)); emb.ClearFields(); } } - } + if (currentPage == 1) { - emb.AddField(new DiscordEmbedField("Playback options", g.musicInstance.GetPlaybackOptions())); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); return; } else if (currentPage == 2 && songsPerPage == 0) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); return; } - foreach (var eP in Pages.Where(x => !x.Embed.Fields.Any(y => y.Name != "Playback keep")).ToList()) - Pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); + + foreach (var eP in pages.Where(x => x.Embed.Fields.All(y => y.Name == "Playback keep")).ToList()) + pages.Remove(eP); + await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); } catch (Exception ex) { @@ -507,83 +540,96 @@ public static async Task ShowQueueAsync(InteractionContext ctx) [SlashCommand("clear", "Clears the queue")] public static async Task ClearQueueAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; + + g.MusicInstance.UsedChannel = ctx.Channel; await Database.ClearQueue(ctx.Guild); - if (g.musicInstance.currentSong != null) - await Database.AddToQueue(ctx.Guild, g.musicInstance.currentSong.addedBy, g.musicInstance.currentSong.track.TrackString); + if (g.MusicInstance.CurrentSong != null) + await Database.AddToQueue(ctx.Guild, g.MusicInstance.CurrentSong.AddedBy, g.MusicInstance.CurrentSong.Track.Encoded); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Cleared queue!**").Build())); } [SlashCommand("move", "Moves a specific song within the queue")] - public static async Task MoveWithinQueueAsync(InteractionContext ctx, - [Option("song", "Song to move within the queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string old_posi, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string new_posi + public static async Task MoveWithinQueueAsync( + InteractionContext ctx, + [Option("song", "Song to move within the queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string oldPosi, + [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string newPosi ) { - await ctx.DeferAsync(true); - var old_pos = Convert.ToInt32(old_posi); - var new_pos = Convert.ToInt32(new_posi); + await ctx.DeferAsync(); + var oldPos = Convert.ToInt32(oldPosi); + var newPos = Convert.ToInt32(newPosi); var g = MikuBot.Guilds[ctx.Guild.Id]; var queue = await Database.GetQueueAsync(ctx.Guild); if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - if (old_pos < 1 || new_pos < 1 || old_pos == new_pos || new_pos >= queue.Count) + + g.MusicInstance.UsedChannel = ctx.Channel; + if (oldPos < 1 || newPos < 1 || oldPos == newPos || newPos >= queue.Count) return; - var oldSong = queue[old_pos]; - await Database.MoveQueueItems(ctx.Guild, old_pos, new_pos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Moved**:\n **{oldSong.track.Title}**\nby {oldSong.track.Author}\n from position **{old_pos}** to **{new_pos}**!").Build())); + + var oldSong = queue[oldPos]; + await Database.MoveQueueItems(ctx.Guild, oldPos, newPos); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Moved**:\n **{oldSong.Track.Info.Title}**\nby {oldSong.Track.Info.Author}\n from position **{oldPos}** to **{newPos}**!").Build())); } [SlashCommand("remove", "Removes a name_or_url from queue")] - public static async Task RemoveFromQueueAsync(InteractionContext ctx, - [Option("song", "Song to remove from queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] string posi + public static async Task RemoveFromQueueAsync( + InteractionContext ctx, + [Option("song", "Song to remove from queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string posi ) { var position = Convert.ToInt32(posi); - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; var queue = await Database.GetQueueAsync(ctx.Guild); if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; + + g.MusicInstance.UsedChannel = ctx.Channel; var old = queue[position]; await Database.RemoveFromQueueAsync(position, ctx.Guild); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Removed:\n{old.track.Title}**\nby {old.track.Author}").Build())); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Removed:\n{old.Track.Info.Title}**\nby {old.Track.Info.Author}").Build())); } } - [SlashCommandGroup("options", "Playback Options")] - [RequireUserAndBotVoicechatConnection] + [SlashCommandGroup("options", "Playback Options"), RequireUserAndBotVoicechatConnection] public class PlaybackOptions : ApplicationCommandsModule { [SlashCommand("repeat", "Repeat the current song or the entire queue")] - public static async Task RepeatAsync(InteractionContext ctx, - [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] RepeatMode mode) + public static async Task RepeatAsync( + InteractionContext ctx, + [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] + RepeatMode mode + ) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - g.musicInstance.repeatMode = mode; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set repeat mode to:\n **{g.musicInstance.repeatMode}**").Build())); + + g.MusicInstance.UsedChannel = ctx.Channel; + g.MusicInstance.RepeatMode = mode; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set repeat mode to:\n **{g.MusicInstance.RepeatMode}**").Build())); } [SlashCommand("shuffle", "Play the queue in shuffle mode")] public static async Task ShuffleAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; if (await g.IsNotConnected(ctx)) return; - g.musicInstance.usedChannel = ctx.Channel; - g.musicInstance.shuffleMode = g.musicInstance.shuffleMode == ShuffleMode.Off ? ShuffleMode.On : ShuffleMode.Off; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set shuffle mode to:\n**{g.musicInstance.shuffleMode}**").Build())); + + g.MusicInstance.UsedChannel = ctx.Channel; + g.MusicInstance.ShuffleMode = g.MusicInstance.ShuffleMode == ShuffleMode.Off ? ShuffleMode.On : ShuffleMode.Off; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set shuffle mode to:\n**{g.MusicInstance.ShuffleMode}**").Build())); } } @@ -593,27 +639,29 @@ public class PlayingInfo : ApplicationCommandsModule [SlashCommand("now_playing", "Show whats currently playing")] public static async Task ShowNowPlaylingAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var g = MikuBot.Guilds[ctx.Guild.Id]; - g.shardId = ctx.Client.ShardId; + g.ShardId = ctx.Client.ShardId; var eb = new DiscordEmbedBuilder(); eb.WithTitle("Now Playing"); eb.WithDescription("**__Current Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g, null); + await ctx.SendPlayingInformationAsync(eb, g); } [SlashCommand("last_playing", "Show what played before")] public static async Task ShowLastPlaylingAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); + if (!lastPlayedSongs.Any()) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); return; } + var g = MikuBot.Guilds[ctx.Guild.Id]; - g.shardId = ctx.Client.ShardId; + g.ShardId = ctx.Client.ShardId; var eb = new DiscordEmbedBuilder(); eb.WithTitle("Last playing"); eb.WithDescription("**__Previous Song:__**"); @@ -623,66 +671,75 @@ public static async Task ShowLastPlaylingAsync(InteractionContext ctx) [SlashCommand("last_playing_list", "Show what songs were played before")] public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) { - await ctx.DeferAsync(true); + await ctx.DeferAsync(); var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); + if (!lastPlayedSongs.Any()) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); return; } + try { var g = MikuBot.Guilds[ctx.Guild.Id]; + if (lastPlayedSongs.Count == 0) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); return; } + var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = lastPlayedSongs.Count / 10; - if ((lastPlayedSongs.Count % 10) != 0) totalP++; + var songsPerPage = 0; + var currentPage = 1; + var songAmount = 0; + var totalP = lastPlayedSongs.Count / 10; + if (lastPlayedSongs.Count % 10 != 0) + totalP++; var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in lastPlayedSongs) + List pages = new(); + + foreach (var track in lastPlayedSongs) { - Track.GetPlayingState(out var time); - emb.AddField(new DiscordEmbedField($"{songAmount + 1}.{Track.track.Title.Replace("*", "").Replace("|", "")}", $"by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}] [Link]({Track.track.Uri})")); + track.GetPlayingState(out var time); + emb.AddField(new($"{songAmount + 1}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}", $"by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}] [Link]({track.Track.Info.Uri})")); songsPerPage++; songAmount++; + if (songsPerPage == 10) { songsPerPage = 0; emb.WithTitle("Last played songs in this server:\n"); emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); + pages.Add(new(embed: emb)); emb.ClearFields(); emb.WithTitle("more™"); currentPage++; } - if (songAmount == lastPlayedSongs.Count) - { - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; + + if (songAmount != lastPlayedSongs.Count) + continue; + + emb.WithTitle("Last played songs in this server:\n"); + emb.WithFooter($"Page {currentPage}/{totalP}"); + pages.Add(new(embed: emb)); + emb.ClearFields(); } - else if (currentPage == 2 && songsPerPage == 0) + + switch (currentPage) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; + case 1: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); + return; + case 2 when songsPerPage == 0: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); + return; } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - Pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); + + foreach (var eP in pages.Where(x => x.Embed.Fields.Count == 0).ToList()) + pages.Remove(eP); + await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); } catch (Exception ex) { @@ -691,6 +748,4 @@ public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) } } } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Entities/Entry.cs b/MikuSharp/Entities/Entry.cs index 47472ac0..0498bf4d 100644 --- a/MikuSharp/Entities/Entry.cs +++ b/MikuSharp/Entities/Entry.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; using System; @@ -6,13 +6,12 @@ namespace MikuSharp.Entities; public class Entry { - public LavalinkTrack track { get; protected set; } - public DateTimeOffset additionDate { get; protected set; } + public LavalinkTrack Track { get; protected set; } + public DateTimeOffset AdditionDate { get; protected set; } + public Entry(LavalinkTrack t, DateTimeOffset addtime) { - track = t; - additionDate = addtime; + this.Track = t; + this.AdditionDate = addtime; } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Entities/Guild.cs b/MikuSharp/Entities/Guild.cs index 7c0ed63b..c31dcf3f 100644 --- a/MikuSharp/Entities/Guild.cs +++ b/MikuSharp/Entities/Guild.cs @@ -1,36 +1,36 @@ -/*using System; +using System; using System.Threading.Tasks; namespace MikuSharp.Entities; public class Guild { - public int shardId { get; set; } + public int ShardId { get; set; } + //CustomPrefix stuff - public MusicInstance musicInstance { get; set; } + public MusicInstance? MusicInstance { get; set; } public Task AloneCheckThread { get; set; } - public Guild(int id, MusicInstance mi = null) + public Guild(int id, MusicInstance? mi = null) { - shardId = id; - musicInstance = mi; + this.ShardId = id; + this.MusicInstance = mi; } public async Task CheckAlone() { - while (DateTime.UtcNow.Subtract(musicInstance.aloneTime).Minutes != 5 && !musicInstance.aloneCTS.IsCancellationRequested) + while (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes != 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) { await Task.Delay(1000); - if (musicInstance == null || musicInstance.guildConnection == null) + if (this.MusicInstance?.GuildConnection is null) return; } - if (DateTime.UtcNow.Subtract(musicInstance.aloneTime).Minutes == 5 && !musicInstance.aloneCTS.IsCancellationRequested) + + if (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes == 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) { - await Task.Run(async () => await musicInstance.guildConnection.DisconnectAsync()); + await Task.Run(async () => await this.MusicInstance.GuildConnection.DisconnectAsync()); await Task.Delay(500); - musicInstance = null; + this.MusicInstance = null; } } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Entities/MusicInstance.cs b/MikuSharp/Entities/MusicInstance.cs index f0cc6755..1593dfb6 100644 --- a/MikuSharp/Entities/MusicInstance.cs +++ b/MikuSharp/Entities/MusicInstance.cs @@ -1,9 +1,11 @@ -/*using DisCatSharp; +using DisCatSharp; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; using DisCatSharp.Interactivity.Extensions; using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; +using DisCatSharp.Lavalink.Enums; using FluentFTP; @@ -24,235 +26,264 @@ namespace MikuSharp.Entities; public class MusicInstance { - public int shardID { get; set; } - public DiscordChannel usedChannel { get; set; } - public DiscordChannel voiceChannel { get; set; } - public Playstate playstate { get; set; } - public RepeatMode repeatMode { get; set; } - public int repeatAllPos { get; set; } - public ShuffleMode shuffleMode { get; set; } - public DateTime aloneTime { get; set; } - public CancellationTokenSource aloneCTS { get; set; } - public LavalinkNodeConnection nodeConnection { get; set; } - public LavalinkGuildConnection guildConnection { get; set; } - public QueueEntry currentSong { get; set; } - public QueueEntry lastSong { get; set; } - - public MusicInstance(LavalinkNodeConnection node, int shard) + public int ShardId { get; set; } + public DiscordChannel UsedChannel { get; set; } + public DiscordChannel VoiceChannel { get; set; } + public Playstate Playstate { get; set; } + public RepeatMode RepeatMode { get; set; } + public int RepeatAllPos { get; set; } + public ShuffleMode ShuffleMode { get; set; } + public DateTime AloneTime { get; set; } + public CancellationTokenSource AloneCts { get; set; } + public LavalinkSession Session { get; set; } + public LavalinkGuildPlayer GuildConnection { get; set; } + public QueueEntry CurrentSong { get; set; } + public QueueEntry LastSong { get; set; } + + public MusicInstance(LavalinkSession node, int shard) { - shardID = shard; - nodeConnection = node; - usedChannel = null; - playstate = Playstate.NotPlaying; - repeatMode = RepeatMode.Off; - repeatAllPos = 0; - shuffleMode = ShuffleMode.Off; + this.ShardId = shard; + this.Session = node; + this.UsedChannel = null; + this.Playstate = Playstate.NotPlaying; + this.RepeatMode = RepeatMode.Off; + this.RepeatAllPos = 0; + this.ShuffleMode = ShuffleMode.Off; } - public async Task ConnectToChannel(DiscordChannel channel) + public async Task ConnectToChannel(DiscordChannel channel) { switch (channel.Type) { case ChannelType.Voice: - { - guildConnection = await nodeConnection.ConnectAsync(channel); - voiceChannel = channel; - return guildConnection; - } - default: return null; + { + this.GuildConnection = await this.Session.ConnectAsync(channel); + this.VoiceChannel = channel; + return this.GuildConnection; + } + default: + return null; } } + public async Task QueueSong(string n, InteractionContext ctx, int pos = -1) { var queue = await Database.GetQueueAsync(ctx.Guild); var inter = ctx.Client.GetInteractivity(); - if (n.ToLower().StartsWith("http://nicovideo.jp") - || n.ToLower().StartsWith("http://sp.nicovideo.jp") - || n.ToLower().StartsWith("https://nicovideo.jp") - || n.ToLower().StartsWith("https://sp.nicovideo.jp") - || n.ToLower().StartsWith("http://www.nicovideo.jp") - || n.ToLower().StartsWith("https://www.nicovideo.jp")) + + if (n.ToLower().StartsWith("http://nicovideo.jp", StringComparison.Ordinal) + || n.ToLower().StartsWith("http://sp.nicovideo.jp", StringComparison.Ordinal) + || n.ToLower().StartsWith("https://nicovideo.jp", StringComparison.Ordinal) + || n.ToLower().StartsWith("https://sp.nicovideo.jp", StringComparison.Ordinal) + || n.ToLower().StartsWith("http://www.nicovideo.jp", StringComparison.Ordinal) + || n.ToLower().StartsWith("https://www.nicovideo.jp", StringComparison.Ordinal)) { var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); var split = n.Split("/".ToCharArray()); - var nndID = split.First(x => x.StartsWith("sm") || x.StartsWith("nm")).Split("?")[0]; + var nndId = split.First(x => x.StartsWith("sm", StringComparison.Ordinal) || x.StartsWith("nm", StringComparison.Ordinal)).Split("?")[0]; FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); client.Connect(); - if (!client.FileExists($"{nndID}.mp3")) + + if (!client.FileExists($"{nndId}.mp3")) { await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetNNDAsync(n, nndID, msg.Id); + var ex = await ctx.GetNndAsync(n, nndId, msg.Id); + if (ex == null) { await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); return null; } + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading")); - client.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); + client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); } - var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); - if (pos == -1) await Database.AddToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString); - else await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); + + var track = (await this.Session.Rest.LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(track.Info, track.Tracks.First()); } - else if (n.ToLower().StartsWith("https://www.bilibili.com") - || n.ToLower().StartsWith("http://www.bilibili.com")) + else if (n.ToLower().StartsWith("https://www.bilibili.com", StringComparison.Ordinal) + || n.ToLower().StartsWith("http://www.bilibili.com", StringComparison.Ordinal)) { var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing Bilibili Video...").AsEphemeral()); n = n.Replace("https://www.bilibili.com/", ""); n = n.Replace("http://www.bilibili.com/", ""); var split = n.Split("/".ToCharArray()); + if (!split.Contains("video")) { await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Failure")); return null; } - var nndID = split[1].Split("?")[0]; + + var nndId = split[1].Split("?")[0]; FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); client.Connect(); - if (!client.FileExists($"{nndID}.mp3")) + + if (!client.FileExists($"{nndId}.mp3")) { await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetBilibiliAsync(nndID, msg.Id); + var ex = await ctx.GetBilibiliAsync(nndId, msg.Id); + if (ex == null) { await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); return null; } + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); - client.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); + client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); } - var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); + + var track = (await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString); + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, Track.Tracks.First().TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) - await PlaySong(); - return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(track.Info, track.Tracks.First()); } - else if (n.StartsWith("http://") | n.StartsWith("https://")) - { + else if (n.StartsWith("http://", StringComparison.Ordinal) | n.StartsWith("https://", StringComparison.Ordinal)) try { - var s = await nodeConnection.Rest.GetTracksAsync(new Uri(n)); - switch (s.LoadResultType) + var s = await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync(n); + + switch (s.LoadType) { - case LavalinkLoadResultType.LoadFailed: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + - "> Playlist is set to private or unlisted\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); - return null; - }; - case LavalinkLoadResultType.PlaylistLoaded: + case LavalinkLoadResultType.Error: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + "> Playlist is set to private or unlisted\n" + "> The song is unavailable/deleted").Build())); + return null; + } + ; + case LavalinkLoadResultType.Empty: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); + return null; + } + ; + case LavalinkLoadResultType.Playlist: + { + var pl = s.GetResultAs(); + + if (pl.Info.SelectedTrack == -1) { - if (s.PlaylistInfo.SelectedTrack == -1) + List buttons = new(2) { - List buttons = new(2) - { - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add entire playlist"), - new DiscordButtonComponent(ButtonStyle.Primary, "no", "Don't add") + new(ButtonStyle.Success, "yes", "Add entire playlist"), + new(ButtonStyle.Primary, "no", "Don't add") }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Playlist link detected!").AddEmbed(new DiscordEmbedBuilder() - .WithDescription("Choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.ToList()); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Playlist link detected!").AddEmbed(new DiscordEmbedBuilder() + .WithDescription("Choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + else if (resp.Result.Id == "yes") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(pl.Info, pl.Tracks); } else { - List buttons = new(3) + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + else + { + List buttons = new(3) { - new DiscordButtonComponent(ButtonStyle.Primary, "yes", "Add only referred song"), - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add the entire playlist"), - new DiscordButtonComponent(ButtonStyle.Danger, "no", "Cancel") + new(ButtonStyle.Primary, "yes", "Add only referred song"), + new(ButtonStyle.Success, "yes", "Add the entire playlist"), + new(ButtonStyle.Danger, "no", "Cancel") }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Link with Playlist detected!") - .WithDescription("Please choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).Title}")); - if (pos == -1) await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).TrackString); - else await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack)); - } - else if (resp.Result.Id == "all") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {s.PlaylistInfo.Name}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks); - else - { - s.Tracks.Reverse(); - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, s.Tracks, pos); - } - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Link with Playlist detected!") + .WithDescription("Please choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + else if (resp.Result.Id == "yes") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {pl.Tracks.ElementAt(pl.Info.SelectedTrack).Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(pl.Info, pl.Tracks.ElementAt(pl.Info.SelectedTrack)); + } + else if (resp.Result.Id == "all") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {pl.Info.Name}")); + + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); else { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; + pl.Tracks.Reverse(); + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks, pos); } + + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) await this.PlaySong(); + return new(pl.Info, pl.Tracks); } - }; - default: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {s.Tracks.First().Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.First().TrackString); else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, s.Tracks.First().TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) - await PlaySong(); - return new TrackResult(s.PlaylistInfo, s.Tracks.First()); - }; + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + } + ; + default: + { + var p = s.GetResultAs(); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {p.Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p.Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, p.Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(null, p); + } + ; } } catch (Exception ex) @@ -261,109 +292,122 @@ await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmb ctx.Client.Logger.LogError("{ex}", ex.StackTrace); return null; } - } else { var type = LavalinkSearchType.Youtube; - if (n.StartsWith("ytsearch:")) + + if (n.StartsWith("ytsearch:", StringComparison.Ordinal)) { n = n.Replace("ytsearch:", ""); type = LavalinkSearchType.Youtube; } - else if (n.StartsWith("scsearch:")) + else if (n.StartsWith("scsearch:", StringComparison.Ordinal)) { n = n.Replace("ytsearch:", ""); type = LavalinkSearchType.SoundCloud; } - else if (n.StartsWith("spsearch:")) + else if (n.StartsWith("spsearch:", StringComparison.Ordinal)) { n = n.Replace("spsearch:", ""); type = LavalinkSearchType.Spotify; } - else if (n.StartsWith("amsearch:")) + else if (n.StartsWith("amsearch:", StringComparison.Ordinal)) { n = n.Replace("amsearch:", ""); type = LavalinkSearchType.AppleMusic; } - var s = await nodeConnection.Rest.GetTracksAsync(n, type); - switch (s.LoadResultType) + var trackLoadingResult = await this.Session.ConnectedPlayers[ctx.GuildId!.Value].LoadTracksAsync(type, n); + + switch (trackLoadingResult.LoadType) { - case LavalinkLoadResultType.LoadFailed: - { - ctx.Client.Logger.LogDebug("Load failed"); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reason could be:\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - ctx.Client.Logger.LogDebug("No matches"); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); - return null; - }; + case LavalinkLoadResultType.Error: + { + ctx.Client.Logger.LogDebug("Load failed"); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reason could be:\n" + "> The song is unavailable/deleted").Build())); + return null; + } + ; + case LavalinkLoadResultType.Empty: + { + ctx.Client.Logger.LogDebug("No matches"); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); + return null; + } + ; default: + { + var s = trackLoadingResult.GetResultAs>(); + ctx.Client.Logger.LogDebug("Found something"); + var leng = s.Count; + if (leng > 5) leng = 5; + List selectOptions = new(leng); + var em = new DiscordEmbedBuilder() + .WithTitle("Results!") + .WithDescription("Please select a track:\n") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); + + for (var i = 0; i < leng; i++) + { + em.AddField(new($"{i + 1}.{s.ElementAt(i).Info.Title} [{s.ElementAt(i).Info.Length}]", $"by {s.ElementAt(i).Info.Author} [Link]({s.ElementAt(i).Info.Uri})")); + selectOptions.Add(new(s.ElementAt(i).Info.Title, i.ToString(), $"by {s.ElementAt(i).Info.Author}. Length: {s.ElementAt(i).Info.Length}")); + } + + DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); + var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) { - ctx.Client.Logger.LogDebug("Found something"); - int leng = s.Tracks.Count; - if (leng > 5) leng = 5; - List selectOptions = new(leng); - var em = new DiscordEmbedBuilder() - .WithTitle("Results!") - .WithDescription("Please select a track:\n") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); - for (int i = 0; i < leng; i++) - { - em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); - selectOptions.Add(new DiscordStringSelectComponentOption(s.Tracks.ElementAt(i).Title, i.ToString(), $"by {s.Tracks.ElementAt(i).Author}. Length: {s.Tracks.ElementAt(i).Length}")); - } - DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); - var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); - return null; - } - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - var trackSelect = Convert.ToInt32(resp.Result.Values.First()); - var track = s.Tracks.ElementAt(trackSelect); select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Chose {track.Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.TrackString); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.TrackString, pos); - if (guildConnection.IsConnected && (playstate == Playstate.NotPlaying || playstate == Playstate.Stopped)) - await PlaySong(); - return new TrackResult(s.PlaylistInfo, track); - }; + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); + return null; + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + var trackSelect = Convert.ToInt32(resp.Result.Values.First()); + var track = s.ElementAt(trackSelect); + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Chose {track.Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(null, track); + } + ; } } } public async Task PlaySong() { - var queue = await Database.GetQueueAsync(voiceChannel.Guild); - var cur = lastSong; - if (queue.Count != 1 && repeatMode == RepeatMode.All) - repeatAllPos++; - if (repeatAllPos >= queue.Count) - repeatAllPos = 0; - currentSong = shuffleMode == ShuffleMode.Off ? queue[0] : queue[new Random().Next(0, queue.Count)]; - if (repeatMode == RepeatMode.All) - currentSong = queue[repeatAllPos]; - if (repeatMode == RepeatMode.On) - currentSong = cur; - MikuBot.ShardedClient.Logger.LogDebug(currentSong?.track.TrackString); - guildConnection.PlaybackFinished += Lavalink.LavalinkTrackFinish; - playstate = Playstate.Playing; - await Task.Run(async () => await guildConnection.PlayAsync(currentSong.track)); - return currentSong; + var queue = await Database.GetQueueAsync(this.VoiceChannel.Guild); + var cur = this.LastSong; + if (queue.Count != 1 && this.RepeatMode == RepeatMode.All) + this.RepeatAllPos++; + if (this.RepeatAllPos >= queue.Count) + this.RepeatAllPos = 0; + this.CurrentSong = this.ShuffleMode == ShuffleMode.Off ? queue[0] : queue[new Random().Next(0, queue.Count)]; + + switch (this.RepeatMode) + { + case RepeatMode.All: + this.CurrentSong = queue[this.RepeatAllPos]; + break; + case RepeatMode.On: + this.CurrentSong = cur; + break; + } + + MikuBot.ShardedClient.Logger.LogDebug(this.CurrentSong?.Track.Encoded); + this.GuildConnection.TrackEnded += Lavalink.LavalinkTrackFinish; + this.Playstate = Playstate.Playing; + _ = Task.Run(async () => await this.GuildConnection.PlayAsync(this.CurrentSong.Track)); + return this.CurrentSong; } } -// B/S(`・ω・´) ❤️ (´ω`)U/C -*/ - +// B/S(`・ω・´) ❤️ (´ω`)U/C \ No newline at end of file diff --git a/MikuSharp/Entities/PlaylistEntry.cs b/MikuSharp/Entities/PlaylistEntry.cs index fb962032..485623be 100644 --- a/MikuSharp/Entities/PlaylistEntry.cs +++ b/MikuSharp/Entities/PlaylistEntry.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; using System; @@ -6,13 +6,12 @@ namespace MikuSharp.Entities; public class PlaylistEntry : Entry { - public DateTimeOffset modifyDate { get; set; } + public DateTimeOffset ModifyDate { get; set; } public int Position { get; set; } + public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset moddate, int pos) : base(t, addDate) { - modifyDate = moddate; - Position = pos; + this.ModifyDate = moddate; + this.Position = pos; } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Entities/QueueEntry.cs b/MikuSharp/Entities/QueueEntry.cs index 10707fa1..4be3138a 100644 --- a/MikuSharp/Entities/QueueEntry.cs +++ b/MikuSharp/Entities/QueueEntry.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; using System; @@ -6,13 +6,12 @@ namespace MikuSharp.Entities; public class QueueEntry : Entry { - public int position { get; set; } - public ulong addedBy { set; get; } + public int Position { get; set; } + public ulong AddedBy { set; get; } + public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : base(t, adddate) { - position = pos; - addedBy = m; + this.Position = pos; + this.AddedBy = m; } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Entities/TrackResult.cs b/MikuSharp/Entities/TrackResult.cs index 06a85788..f1175bf3 100644 --- a/MikuSharp/Entities/TrackResult.cs +++ b/MikuSharp/Entities/TrackResult.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; using System.Collections.Generic; @@ -8,20 +8,20 @@ public class TrackResult { public LavalinkPlaylistInfo PlaylistInfo { get; set; } public List Tracks { get; set; } + public TrackResult(LavalinkPlaylistInfo pl, IEnumerable tr) { - PlaylistInfo = pl; - Tracks = new List(); - Tracks.AddRange(tr); + this.PlaylistInfo = pl; + this.Tracks = new(); + this.Tracks.AddRange(tr); } + public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) { - PlaylistInfo = pl; - Tracks = new List - { - tr - }; + this.PlaylistInfo = pl; + this.Tracks = new() + { + tr + }; } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Events/Lavalink.cs b/MikuSharp/Events/Lavalink.cs index a1e8e068..1ca24040 100644 --- a/MikuSharp/Events/Lavalink.cs +++ b/MikuSharp/Events/Lavalink.cs @@ -1,5 +1,6 @@ -/*using DisCatSharp.Entities; +using DisCatSharp.Entities; using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Enums; using DisCatSharp.Lavalink.EventArgs; using Microsoft.Extensions.Logging; @@ -14,69 +15,62 @@ namespace MikuSharp.Events; public class Lavalink { - public static async Task LavalinkTrackFinish(LavalinkGuildConnection lava, TrackFinishEventArgs e) + public static async Task LavalinkTrackFinish(LavalinkGuildPlayer lava, LavalinkTrackEndedEventArgs e) { try { - var g = MikuBot.Guilds[e.Player.Guild.Id]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(e.Player.Guild); - if (g.musicInstance == null) + var g = MikuBot.Guilds[e.GuildId]; + var lastPlayedSongs = await Database.GetLastPlayingListAsync(e.Guild); + if (g.MusicInstance == null!) return; + switch (e.Reason) { - case TrackEndReason.Stopped: - { - g.musicInstance.playstate = Playstate.Stopped; - g.musicInstance.guildConnection.PlaybackFinished -= LavalinkTrackFinish; - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - break; - } - case TrackEndReason.Replaced: - { - break; - } - case TrackEndReason.LoadFailed: - { - await g.musicInstance.usedChannel.SendMessageAsync(embed: new DiscordEmbedBuilder().WithTitle("Track failed to play") - .WithDescription($"**{g.musicInstance.currentSong.track.Title}**\nby {g.musicInstance.currentSong.track.Author}\n" + - $"**Failed to load, Skipping to next track**")); - g.musicInstance.guildConnection.PlaybackFinished -= LavalinkTrackFinish; - await Database.RemoveFromQueueAsync(g.musicInstance.currentSong.position, e.Player.Guild); - if (lastPlayedSongs.Count == 0) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - else if (lastPlayedSongs[0].track.Uri != g.musicInstance.currentSong.track.Uri) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - var queue = await Database.GetQueueAsync(e.Player.Guild); - if (queue.Count != 0) await g.musicInstance.PlaySong(); - else g.musicInstance.playstate = Playstate.NotPlaying; - break; - } - case TrackEndReason.Finished: - { - g.musicInstance.guildConnection.PlaybackFinished -= LavalinkTrackFinish; - if (g.musicInstance.repeatMode != RepeatMode.On && g.musicInstance.repeatMode != RepeatMode.All) await Database.RemoveFromQueueAsync(g.musicInstance.currentSong.position, e.Player.Guild); - if (lastPlayedSongs.Count == 0) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - else if (lastPlayedSongs[0].track.Uri != g.musicInstance.currentSong.track.Uri) - { - await Database.AddToLastPlayingListAsync(e.Player.Guild.Id, g.musicInstance.currentSong.track.TrackString); - } - g.musicInstance.lastSong = g.musicInstance.currentSong; - g.musicInstance.currentSong = null; - var queue = await Database.GetQueueAsync(e.Player.Guild); - if (queue.Count != 0) await g.musicInstance.PlaySong(); - else g.musicInstance.playstate = Playstate.NotPlaying; - break; - } + case LavalinkTrackEndReason.Stopped: + { + g.MusicInstance.Playstate = Playstate.Stopped; + g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + break; + } + case LavalinkTrackEndReason.Replaced: + { + break; + } + case LavalinkTrackEndReason.LoadFailed: + { + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithTitle("Track failed to play") + .WithDescription($"**{g.MusicInstance.CurrentSong.Track.Info.Title}**\nby {g.MusicInstance.CurrentSong.Track.Info.Author}\n" + $"**Failed to load, Skipping to next Track**")); + g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; + await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); + if (lastPlayedSongs.Count == 0) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + var queue = await Database.GetQueueAsync(e.Guild); + if (queue.Count != 0) await g.MusicInstance.PlaySong(); + else g.MusicInstance.Playstate = Playstate.NotPlaying; + break; + } + case LavalinkTrackEndReason.Finished: + { + g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; + if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) + await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); + if (lastPlayedSongs.Count == 0) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + var queue = await Database.GetQueueAsync(e.Guild); + if (queue.Count != 0) await g.MusicInstance.PlaySong(); + else g.MusicInstance.Playstate = Playstate.NotPlaying; + break; + } } } catch (Exception ex) @@ -85,7 +79,4 @@ public static async Task LavalinkTrackFinish(LavalinkGuildConnection lava, Track MikuBot.ShardedClient.Logger.LogError(ex.StackTrace); } } - -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Events/VoiceChat.cs b/MikuSharp/Events/VoiceChat.cs index 3b4896a4..e22386a1 100644 --- a/MikuSharp/Events/VoiceChat.cs +++ b/MikuSharp/Events/VoiceChat.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp; +using DisCatSharp; using DisCatSharp.Entities; using DisCatSharp.EventArgs; @@ -8,7 +8,6 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; namespace MikuSharp.Events; @@ -19,47 +18,47 @@ public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventAr { try { - if (!MikuBot.Guilds.Any(x => x.Key == e.Guild.Id)) return; + if (MikuBot.Guilds.All(x => x.Key != e.Guild.Id)) return; + var g = MikuBot.Guilds[e.Guild.Id]; - if (g.musicInstance == null - || g.musicInstance?.guildConnection?.IsConnected == false) return; - if ((e.After?.Channel?.Users.Where(x => !x.IsBot).Count() == 0 - || e.Before?.Channel?.Users.Where(x => !x.IsBot).Count() == 0 - || e.Channel?.Users.Where(x => !x.IsBot).Count() == 0) - && (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true - || e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true - || e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - && g.musicInstance?.guildConnection?.Channel?.Users.Where(x => !x.IsBot).Count() == 0) + if (g.MusicInstance == null + || g.MusicInstance?.GuildConnection?.IsConnected == false) return; + + if (((e.After?.Channel?.Users).Count(x => !x.IsBot) == 0 + || (e.Before?.Channel?.Users).Count(x => !x.IsBot) == 0 + || (e.Channel?.Users).Count(x => !x.IsBot) == 0) + && (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true + || e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true + || e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) + && (g.MusicInstance?.GuildConnection?.Channel?.Users).Count(x => !x.IsBot) == 0) { - if (g.musicInstance.playstate == Playstate.Playing) + if (g.MusicInstance.Playstate == Playstate.Playing) { - await g.musicInstance.guildConnection.PauseAsync(); - g.musicInstance.playstate = Playstate.Paused; + await g.MusicInstance.GuildConnection.PauseAsync(); + g.MusicInstance.Playstate = Playstate.Paused; + try { - await g.musicInstance.usedChannel.SendMessageAsync(embed: new DiscordEmbedBuilder().WithDescription("**Paused** since everyone left the VC, connect back and use m%resume to continue playback otherwise I will disconnect in 5 min").Build()); + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithDescription("**Paused** since everyone left the VC, connect back and use m%resume to continue playback otherwise I will disconnect in 5 min").Build()); } - catch { } + catch + { } } else - { try { - await g.musicInstance.usedChannel.SendMessageAsync(embed: new DiscordEmbedBuilder().WithDescription("Since everyone left the VC I will disconnect too in 5 min").Build()); + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithDescription("Since everyone left the VC I will disconnect too in 5 min").Build()); } - catch { } - } - g.musicInstance.aloneTime = DateTime.UtcNow; - g.musicInstance.aloneCTS = new CancellationTokenSource(); + catch + { } + + g.MusicInstance.AloneTime = DateTime.UtcNow; + g.MusicInstance.AloneCts = new(); g.AloneCheckThread = Task.Run(g.CheckAlone); } - else if (e.After?.Channel?.Users.Where(x => !x.IsBot).Count() != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - { - if (g.musicInstance != null && g.musicInstance?.aloneCTS != null) - { - g.musicInstance.aloneCTS.Cancel(); - } - } + else if ((e.After?.Channel?.Users).Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) + if (g.MusicInstance is { AloneCts: not null }) + g.MusicInstance.AloneCts.Cancel(); } catch (Exception ex) { @@ -67,6 +66,4 @@ public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventAr client.Logger.LogError(ex.StackTrace); } } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 49f30755..10566204 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -8,7 +8,7 @@ using DisCatSharp.Interactivity.Enums; using DisCatSharp.Interactivity.EventHandling; using DisCatSharp.Interactivity.Extensions; -//using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink; using DisCatSharp.Net; using DiscordBotsList.Api; @@ -41,7 +41,7 @@ internal sealed class MikuBot : IDisposable internal static CancellationTokenSource Cts { get; set; } internal static BotConfig Config { get; set; } - //internal LavalinkConfiguration LavalinkConfig { get; set; } + internal LavalinkConfiguration LavalinkConfig { get; set; } internal Task GameSetThread { get; set; } internal Task StatusThread { get; set; } @@ -55,10 +55,10 @@ internal sealed class MikuBot : IDisposable internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } internal IReadOnlyDictionary CommandsNextModules { get; set; } - //internal IReadOnlyDictionary LavalinkModules { get; set; } + internal IReadOnlyDictionary LavalinkModules { get; set; } - //internal static Dictionary LavalinkNodeConnections = new(); - //internal static Dictionary Guilds = new(); + internal static Dictionary LavalinkSessions = new(); + internal static Dictionary Guilds = new(); internal static Playstate Ps = Playstate.Playing; internal static Stopwatch Psc = new(); @@ -142,14 +142,14 @@ internal MikuBot() DefaultHelpChecks = new(1) { new Attributes.NotStaffAttribute() } }).Result; - /*LavalinkConfig = new() + this.LavalinkConfig = new() { - SocketEndpoint = new ConnectionEndpoint { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, - RestEndpoint = new ConnectionEndpoint { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, + SocketEndpoint = new() { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, + RestEndpoint = new() { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, Password = Config.LavaConfig.Password }; - LavalinkModules = ShardedClient.UseLavalinkAsync().Result;*/ + this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; } internal static async Task RegisterEvents() @@ -165,7 +165,7 @@ internal static async Task RegisterEvents() foreach (var discordClientKvp in ShardedClient.ShardClients) { - //discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; + discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => { @@ -211,33 +211,32 @@ internal static async Task RegisterEvents() } } -/* internal async Task ShowConnections() { while (true) { - var al = Guilds.Where(x => x.Value?.musicInstance != null); - ShardedClient.Logger.LogInformation("Voice Connections: " + al.Where(x => x.Value.musicInstance.guildConnection?.IsConnected == true).Count()); + var al = Guilds.Where(x => x.Value?.MusicInstance != null); + ShardedClient.Logger.LogInformation("Voice Connections: " + al.Where(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true).Count()); await Task.Delay(15000); } } -*/ + internal static async Task UpdateBotList() { await Task.Delay(15000); + while (true) { var me = await DiscordBotListApi.GetMeAsync(); var count = Array.Empty(); var clients = ShardedClient.ShardClients.Values; - foreach (var client in clients) - count = count.Append(client.Guilds.Count).ToArray(); + count = clients.Aggregate(count, (current, client) => current.Append(client.Guilds.Count).ToArray()); await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); await Task.Delay(TimeSpan.FromMinutes(15)); } } - internal async Task SetActivity() + internal static async Task SetActivity() { while (true) { @@ -275,7 +274,7 @@ internal void RegisterCommands() this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); - //ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); //ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); @@ -289,17 +288,20 @@ internal async Task RunAsync() await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); await ShardedClient.StartAsync(); await Task.Delay(5000); - /*foreach (var lavalinkShard in LavalinkModules) + + foreach (var lavalinkShard in this.LavalinkModules) { - var LCon = await lavalinkShard.Value.ConnectAsync(LavalinkConfig); - LavalinkNodeConnections.Add(lavalinkShard.Key, LCon); - }*/ - this.GameSetThread = Task.Run(this.SetActivity); + var connection = await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + LavalinkSessions.Add(lavalinkShard.Key, connection); + } + + //this.GameSetThread = Task.Run(SetActivity); //StatusThread = Task.Run(ShowConnections); //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); //BotListThread = Task.Run(UpdateBotList); while (!Cts.IsCancellationRequested) await Task.Delay(25); + _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); await ShardedClient.StopAsync(); } diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 2186b6b3..e22c60fb 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -43,31 +43,31 @@ - - - - - - - - + + + + + + + + - - - + + + - + - + diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index 40397b34..06aff7e8 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -30,6 +30,7 @@ public static async Task GetBilibiliAsync(this InteractionContext youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; await youtubeDl.DownloadAsync(); var ms = new MemoryStream(); + if (File.Exists($@"{s}.mp3")) { var song = File.Open($@"{s}.mp3", FileMode.Open); diff --git a/MikuSharp/Utilities/Database.cs b/MikuSharp/Utilities/Database.cs index ab85182a..e12493d1 100644 --- a/MikuSharp/Utilities/Database.cs +++ b/MikuSharp/Utilities/Database.cs @@ -1,5 +1,6 @@ -/*using DisCatSharp.Entities; +using DisCatSharp.Entities; using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; using MikuSharp.Entities; @@ -7,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; namespace MikuSharp.Utilities; @@ -15,17 +17,15 @@ public class Database { public static async Task AddToLastPlayingListAsync(ulong g, string ts) { - int position = 0; + var position = 0; var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guildId = {g};", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) - { position = reader.GetInt32(0); - } - reader.Close(); + await reader.CloseAsync(); cmd2.Dispose(); var cmd = new NpgsqlCommand("INSERT INTO lastplayedsongs VALUES (@pos,@guild,@ts)", conn); var para = cmd.CreateParameter(); @@ -42,7 +42,7 @@ public static async Task AddToLastPlayingListAsync(ulong g, string ts) cmd.Parameters.Add(para3); await cmd.ExecuteNonQueryAsync(); cmd.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); } @@ -55,16 +55,19 @@ public static async Task ReorderQueue(DiscordGuild g) var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); await cmd.ExecuteNonQueryAsync(); cmd.Dispose(); - int i = 0; - string longcmd = ""; + var i = 0; + var longcmd = ""; + foreach (var qi in queueNow) { - longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.addedBy}','{qi.track.TrackString}',@adddate{i});"; + longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.AddedBy}','{qi.Track.Encoded}',@adddate{i});"; i++; } + var cmd2 = new NpgsqlCommand(); if (string.IsNullOrEmpty(longcmd)) return; + cmd2.CommandText = longcmd; cmd2.Connection = conn; var para = cmd2.CreateParameter(); @@ -72,17 +75,19 @@ public static async Task ReorderQueue(DiscordGuild g) para.Value = Convert.ToInt64(g.Id); cmd2.Parameters.Add(para); i = 0; - foreach(var qi in queueNow) + + foreach (var qi in queueNow) { var para2 = cmd2.CreateParameter(); para2.ParameterName = $"adddate{i}"; - para2.Value = qi.additionDate.UtcDateTime; + para2.Value = qi.AdditionDate.UtcDateTime; cmd2.Parameters.Add(para2); i++; } + await cmd2.ExecuteNonQueryAsync(); cmd2.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); } @@ -95,16 +100,19 @@ public static async Task RebuildQueue(DiscordGuild g, List q) var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); await cmd.ExecuteNonQueryAsync(); cmd.Dispose(); - int i = 0; - string longcmd = ""; + var i = 0; + var longcmd = ""; + foreach (var qi in queueNow) { - longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.addedBy}','{qi.track.TrackString}',@adddate{i});"; + longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.AddedBy}','{qi.Track.Encoded}',@adddate{i});"; i++; } + var cmd2 = new NpgsqlCommand(); if (string.IsNullOrEmpty(longcmd)) return; + cmd2.CommandText = longcmd; cmd2.Connection = conn; var para = cmd2.CreateParameter(); @@ -112,17 +120,19 @@ public static async Task RebuildQueue(DiscordGuild g, List q) para.Value = Convert.ToInt64(g.Id); cmd2.Parameters.Add(para); i = 0; + foreach (var qi in queueNow) { var para2 = cmd2.CreateParameter(); para2.ParameterName = $"adddate{i}"; - para2.Value = qi.additionDate.UtcDateTime; + para2.Value = qi.AdditionDate.UtcDateTime; cmd2.Parameters.Add(para2); i++; } + await cmd2.ExecuteNonQueryAsync(); cmd2.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); } @@ -135,29 +145,25 @@ public static async Task> GetQueueAsync(DiscordGuild g) var reader = await cmd2.ExecuteReaderAsync(); List queue = new(); while (await reader.ReadAsync()) - { - queue.Add(new QueueEntry(LavalinkUtilities.DecodeTrack(Convert.ToString(reader["trackstring"])), Convert.ToUInt64(reader["userid"]), DateTimeOffset.Parse(reader["addtime"].ToString()), Convert.ToInt32(reader["position"]))); - } - reader.Close(); + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(Convert.ToString(reader["trackstring"])), Convert.ToUInt64(reader["userid"]), DateTimeOffset.Parse(reader["addtime"].ToString()), Convert.ToInt32(reader["position"]))); + await reader.CloseAsync(); cmd2.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); return queue; } public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) { - int position = 0; + var position = 0; var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) - { position = reader.GetInt32(0); - } - reader.Close(); + await reader.CloseAsync(); cmd2.Dispose(); var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@pos,@guild,@user,@ts,@time)", conn); var para = cmd.CreateParameter(); @@ -182,30 +188,30 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) cmd.Parameters.Add(para5); await cmd.ExecuteNonQueryAsync(); cmd.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); } public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) { - int position = 0; + var position = 0; var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) - { position = reader.GetInt32(0); - } - reader.Close(); + await reader.CloseAsync(); cmd2.Dispose(); - string longcmd = ""; + var longcmd = ""; + foreach (var tt in ts) { - longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.TrackString}',@time);"; + longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.Encoded}',@time);"; position++; } + var cmd = new NpgsqlCommand(longcmd, conn); var para = cmd.CreateParameter(); para.ParameterName = "guild"; @@ -221,30 +227,30 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, List cmd.Parameters.Add(para3); await cmd.ExecuteNonQueryAsync(); cmd.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); } public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) { - int position = 0; + var position = 0; var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) - { position = reader.GetInt32(0); - } - reader.Close(); + await reader.CloseAsync(); cmd2.Dispose(); - string longcmd = ""; + var longcmd = ""; + foreach (var tt in ts) { - longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.track.TrackString}',@time);"; + longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.Track.Encoded}',@time);"; position++; } + var cmd = new NpgsqlCommand(longcmd, conn); var para = cmd.CreateParameter(); para.ParameterName = "guild"; @@ -260,14 +266,14 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, List cmd.Parameters.Add(para3); await cmd.ExecuteNonQueryAsync(); cmd.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); } public static async Task InsertToQueue(DiscordGuild g, ulong u, string ts, int pos) { var qnow = await GetQueueAsync(g); - qnow.Insert(pos, new QueueEntry(LavalinkUtilities.DecodeTrack(ts), u, DateTimeOffset.UtcNow, pos)); + qnow.Insert(pos, new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(ts), u, DateTimeOffset.UtcNow, pos)); await RebuildQueue(g, qnow); } @@ -275,9 +281,7 @@ public static async Task InsertToQueue(DiscordGuild g, ulong u, List> GetLastPlayingListAsync(DiscordGuild g) var reader = await cmd2.ExecuteReaderAsync(); List queue = new(); while (await reader.ReadAsync()) - queue.Add(new Entry(LavalinkUtilities.DecodeTrack((string)reader["trackstring"]), DateTimeOffset.UtcNow)); - reader.Close(); + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync((string)reader["trackstring"]), DateTimeOffset.UtcNow)); + await reader.CloseAsync(); cmd2.Dispose(); - conn.Close(); + await conn.CloseAsync(); conn.Dispose(); return queue; } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 4bf5aa62..25141597 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -14,7 +14,7 @@ namespace MikuSharp.Utilities; internal class FixedOptionProviders { - internal class RepeatModeProvider : ChoiceProvider + internal sealed class RepeatModeProvider : ChoiceProvider { public override Task> Provider() { @@ -43,7 +43,7 @@ public async Task> Prov } } -/* + /* internal class PlaylistProvider : IAutocompleteProvider { public async Task> Provider(AutocompleteContext ctx) @@ -51,6 +51,7 @@ public async Task> Prov var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); if (plls.Count == 0) return new List() { new("You have no songs", "error") }; + var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); List> playlists = new(25); @@ -62,15 +63,20 @@ public async Task> Prov return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); } } + internal class SongProvider : IAutocompleteProvider { public async Task> Provider(AutocompleteContext ctx) { var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); - if (playlist == null) - return new List() { new("You have no playlist selected", "error") }; - if (playlist == "error") - return new List() { new("You have no valid playlist selected", "error") }; + + switch (playlist) + { + case null: + return new List() { new("You have no playlist selected", "error") }; + case "error": + return new List() { new("You have no valid playlist selected", "error") }; + } var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); var tracks = await pls.GetEntries(); @@ -80,13 +86,14 @@ public async Task> Prov else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); else - songs.AddRange(tracks.Where(x => x.track.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + songs.AddRange(tracks.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.track.Title}", x.Position.ToString())); + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); } } + */ - internal class QueueProvider : IAutocompleteProvider + internal sealed class QueueProvider : IAutocompleteProvider { public async Task> Provider(AutocompleteContext ctx) { @@ -95,11 +102,11 @@ public async Task> Prov if (ctx.FocusedOption.Value == null) songs.AddRange(queue.Take(25)); else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(queue.Where(x => x.position.ToString().StartsWith(pos.ToString())).Take(25)); + songs.AddRange(queue.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); else - songs.AddRange(queue.Where(x => x.track.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + songs.AddRange(queue.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.position}: {x.track.Title}", x.position.ToString())); + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); } - }*/ + } } \ No newline at end of file diff --git a/MikuSharp/Utilities/Music.cs b/MikuSharp/Utilities/Music.cs index 8d453f8e..29fc35d2 100644 --- a/MikuSharp/Utilities/Music.cs +++ b/MikuSharp/Utilities/Music.cs @@ -1,10 +1,9 @@ -/*using AlbumArtExtraction; +using AlbumArtExtraction; using DisCatSharp; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; -using Google.Apis.Services; using Google.Apis.YouTube.v3; using HeyRed.Mime; @@ -36,28 +35,35 @@ public static ExtService GetExtService(string e) public static string GetPlaybackOptions(this MusicInstance instance) { - var opts = ""; - if (instance.repeatMode == RepeatMode.On) - opts += DiscordEmoji.FromUnicode("🔂"); - if (instance.repeatMode == RepeatMode.All) - opts += DiscordEmoji.FromUnicode("🔁"); - if (instance.shuffleMode == ShuffleMode.On) + string opts = null; + + switch (instance.RepeatMode) + { + case RepeatMode.On: + opts += DiscordEmoji.FromUnicode("🔂"); + break; + case RepeatMode.All: + opts += DiscordEmoji.FromUnicode("🔁"); + break; + } + + if (instance.ShuffleMode == ShuffleMode.On) opts += DiscordEmoji.FromUnicode("🔀"); - if (opts == "") - return "None"; - return opts; + + return opts ?? "None"; } public static async Task ConditionalConnect(this Guild guild, InteractionContext ctx) { - if (!guild.musicInstance.guildConnection?.IsConnected != null && !guild.musicInstance.guildConnection.IsConnected) + if (!guild.MusicInstance?.GuildConnection?.IsConnected != null && !guild.MusicInstance.GuildConnection.IsConnected) return; - await guild.musicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); + + await guild.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); } public static async Task IsNotConnected(this Guild guild, InteractionContext ctx) { - if (guild.musicInstance == null || guild.musicInstance?.guildConnection?.IsConnected == false) + if (guild.MusicInstance == null! || guild.MusicInstance?.GuildConnection?.IsConnected == false) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Not connected!")); return true; @@ -66,30 +72,30 @@ public static async Task IsNotConnected(this Guild guild, InteractionConte return false; } - public static string SearchUrlOrAttachment(this DiscordAttachment? attachment, string? search_or_url) - => !string.IsNullOrEmpty(search_or_url) ? search_or_url : attachment?.ProxyUrl; + public static string SearchUrlOrAttachment(this DiscordAttachment? attachment, string? searchOrUrl) + => !string.IsNullOrEmpty(searchOrUrl) ? searchOrUrl : attachment?.ProxyUrl; public static void GetPlayingState(this Guild guild, out string time1, out string time2) { - switch (guild.musicInstance.currentSong.track.Length.Hours) + switch (guild.MusicInstance.CurrentSong.Track.Info.Length.Hours) { case < 1: - time1 = guild.musicInstance.guildConnection.CurrentState.PlaybackPosition.ToString(@"mm\:ss"); - time2 = guild.musicInstance.currentSong.track.Length.ToString(@"mm\:ss"); + time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"mm\:ss"); + time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"mm\:ss"); break; default: - time1 = guild.musicInstance.guildConnection.CurrentState.PlaybackPosition.ToString(@"hh\:mm\:ss"); - time2 = guild.musicInstance.currentSong.track.Length.ToString(@"hh\:mm\:ss"); + time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"hh\:mm\:ss"); + time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"hh\:mm\:ss"); break; } } public static void GetPlayingState(this Entry entry, out string time) { - time = entry.track.Length.Hours switch + time = entry.Track.Info.Length.Hours switch { - < 1 => entry.track.Length.ToString(@"mm\:ss"), - _ => entry.track.Length.ToString(@"hh\:mm\:ss"), + < 1 => entry.Track.Info.Length.ToString(@"mm\:ss"), + _ => entry.Track.Info.Length.ToString(@"hh\:mm\:ss") }; } @@ -97,13 +103,13 @@ public static async Task GetYoutubePlayingInformationAsync( { try { - var youtubeService = new YouTubeService(new BaseClientService.Initializer() + var youtubeService = new YouTubeService(new() { ApiKey = MikuBot.Config.YoutubeApiToken, ApplicationName = typeof(MikuBot).ToString() }); var searchListRequest = youtubeService.Search.List("snippet"); - searchListRequest.Q = lastPlayedSongs != null ? lastPlayedSongs[0].track.Title + " " + lastPlayedSongs[0].track.Author : guild.musicInstance.currentSong.track.Title + " " + guild.musicInstance.currentSong.track.Author; + searchListRequest.Q = lastPlayedSongs != null ? lastPlayedSongs[0].Track.Info.Title + " " + lastPlayedSongs[0].Track.Info.Author : guild.MusicInstance.CurrentSong.Track.Info.Title + " " + guild.MusicInstance.CurrentSong.Track.Info.Author; searchListRequest.MaxResults = 1; searchListRequest.Type = "video"; var searchListResponse = await searchListRequest.ExecuteAsync(); @@ -111,72 +117,72 @@ public static async Task GetYoutubePlayingInformationAsync( if (lastPlayedSongs == null) { guild.GetPlayingState(out var time1, out var time2); - builder.AddField(new DiscordEmbedField($"{guild.musicInstance.currentSong.track.Title} ({time1}/{time2})", $"[Video Link]({guild.musicInstance.currentSong.track.Uri})\n" + - $"[{guild.musicInstance.currentSong.track.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); + builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({time1}/{time2})", $"[Video Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\n" + $"[{guild.MusicInstance.CurrentSong.Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); } else { lastPlayedSongs[0].GetPlayingState(out var time); - builder.AddField(new DiscordEmbedField($"{lastPlayedSongs[0].track.Title} ({time})", $"[Video Link]({lastPlayedSongs[0].track.Uri})\n" + - $"[{lastPlayedSongs[0].track.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); + builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({time})", $"[Video Link]({lastPlayedSongs[0].Track.Info.Uri})\n" + $"[{lastPlayedSongs[0].Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); } - builder.AddField(new DiscordEmbedField("Description", searchListResponse.Items[0].Snippet.Description.Length > 1000 ? - string.Concat(searchListResponse.Items[0].Snippet.Description.AsSpan(0, 1000), "...") : - searchListResponse.Items[0].Snippet.Description + + builder.AddField(new("Description", searchListResponse.Items[0].Snippet.Description.Length > 1000 ? string.Concat(searchListResponse.Items[0].Snippet.Description.AsSpan(0, 1000), "...") : searchListResponse.Items[0].Snippet.Description )); builder.WithImageUrl(searchListResponse.Items[0].Snippet.Thumbnails.High.Url); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); } - catch(Exception) + catch (Exception) { if (builder.Fields.Count != 1) { if (lastPlayedSongs == null) - builder.AddField(new DiscordEmbedField($"{guild.musicInstance.currentSong.track.Title} ({guild.musicInstance.currentSong.track.Length})", $"By {guild.musicInstance.currentSong.track.Author}\n[Link]({guild.musicInstance.currentSong.track.Uri})\nRequested by <@{guild.musicInstance.currentSong.addedBy}>")); + builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({guild.MusicInstance.CurrentSong.Track.Info.Length})", $"By {guild.MusicInstance.CurrentSong.Track.Info.Author}\n[Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\nRequested by <@{guild.MusicInstance.CurrentSong.AddedBy}>")); else - builder.AddField(new DiscordEmbedField($"{lastPlayedSongs[0].track.Title} ({lastPlayedSongs[0].track.Length})", $"By {lastPlayedSongs[0].track.Author}\n[Link]({lastPlayedSongs[0].track.Uri})")); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); + builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({lastPlayedSongs[0].Track.Info.Length})", $"By {lastPlayedSongs[0].Track.Info.Author}\n[Link]({lastPlayedSongs[0].Track.Info.Uri})")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); } } + return builder; } public static async Task GetUrlPlayingInformationAsync(this DiscordEmbedBuilder builder, DiscordClient client, Guild guild, List? lastPlayedSongs) { Stream? img = null; - Entry entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.musicInstance.currentSong; + var entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.MusicInstance.CurrentSong; + try { - MemoryStream d = new(await client.RestClient.GetByteArrayAsync(entry.track.Uri)) + MemoryStream d = new(await client.RestClient.GetByteArrayAsync(entry.Track.Info.Uri)) { Position = 0 }; - FileStream e = File.Create($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); + var e = File.Create($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); await d.CopyToAsync(e); e.Close(); var selector = new Selector(); - var extractor = selector.SelectAlbumArtExtractor($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); - img = extractor.Extract($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); + var extractor = selector.SelectAlbumArtExtractor($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + img = extractor.Extract($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); } catch (Exception ex) { client.Logger.LogDebug(ex.Message); client.Logger.LogDebug(ex.StackTrace); img = null; - File.Delete($@"{entry.track.Uri.ToString().Split('/')[^2]}.{entry.track.Uri.ToString().Split('/').Last()}"); + File.Delete($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); } - builder.AddField(new DiscordEmbedField($"{entry.track.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.track.Author}\n[Link]({entry.track.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.musicInstance.currentSong.addedBy}>" : "")}")); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); + + builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); if (img != null) - builder.WithImageUrl($"attachment://{entry.track.Uri.ToString().Split('/')[^2]}.{MimeGuesser.GuessExtension(img)}"); + builder.WithImageUrl($"attachment://{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{MimeGuesser.GuessExtension(img)}"); return builder; } public static DiscordEmbedBuilder GetOtherPlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) { - Entry entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.musicInstance.currentSong; - builder.AddField(new DiscordEmbedField($"{entry.track.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.track.Author}\n[Link]({entry.track.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.musicInstance.currentSong.addedBy}>" : "")}")); - builder.AddField(new DiscordEmbedField("Playback options", guild.musicInstance.GetPlaybackOptions())); + var entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.MusicInstance.CurrentSong; + builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); return builder; } @@ -185,35 +191,35 @@ public static string GetDynamicPlayingState(this Guild guild, List? lastP switch (lastPlayedSongs) { case null: - { - guild.GetPlayingState(out var time1, out var time2); - return $"{time1}/{time2}"; - } + { + guild.GetPlayingState(out var time1, out var time2); + return $"{time1}/{time2}"; + } default: - { - lastPlayedSongs[0].GetPlayingState(out var time); - return $"{time}"; - } + { + lastPlayedSongs[0].GetPlayingState(out var time); + return $"{time}"; + } } } public static async Task SendPlayingInformationAsync(this InteractionContext ctx, DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) { - Entry entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.musicInstance.currentSong; + var entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.MusicInstance.CurrentSong; + if (entry == null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); return; } - builder = entry.track.Uri.ToString().Contains("youtu") + + builder = entry.Track.Info.Uri.ToString().Contains("youtu") ? await builder.GetYoutubePlayingInformationAsync(guild) - : entry.track.Uri.ToString().StartsWith("https://media.discordapp.net/attachments/") || entry.track.Uri.ToString().StartsWith("https://cdn.discordapp.com/attachments/") + : entry.Track.Info.Uri.ToString().StartsWith("https://media.discordapp.net/attachments/", StringComparison.Ordinal) || entry.Track.Info.Uri.ToString().StartsWith("https://cdn.discordapp.com/attachments/", StringComparison.Ordinal) ? await builder.GetUrlPlayingInformationAsync(ctx.Client, guild, lastPlayedSongs) : builder.GetOtherPlayingInformationAsync(guild, lastPlayedSongs); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); } -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs index 9cbca917..a91e3733 100644 --- a/MikuSharp/Utilities/NND.cs +++ b/MikuSharp/Utilities/NND.cs @@ -1,4 +1,4 @@ -/*using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using Microsoft.Extensions.Logging; @@ -12,40 +12,34 @@ namespace MikuSharp.Utilities; -public static class NND +public static class Nnd { - public static async Task GetNNDAsync(this InteractionContext ctx,string n, string s, ulong msg_id) + public static async Task GetNndAsync(this InteractionContext ctx, string n, string s, ulong msgId) { try { - NNDClient nndClient = new(); + NndClient nndClient = new(); NicoVideoClient videoClient = new(nndClient); var videoPage = await videoClient.GetWatchPageInfoAsync(s); - var download_exe = "nnd.exe"; - var linux_exe = "nndownload.py"; - string cmd = download_exe; - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); + var downloadExe = "nnd.exe"; + var linuxExe = "nndownload.py"; + var cmd = downloadExe; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); if (OperatingSystem.IsLinux()) - cmd = linux_exe; + cmd = linuxExe; Process downloadProcess = new(); downloadProcess.StartInfo.FileName = cmd; downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; - downloadProcess.OutputDataReceived += (d, f) => - { - ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); - }; + downloadProcess.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); }; downloadProcess.Start(); await downloadProcess.WaitForExitAsync(); var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Converting")); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); Process convertProgress = new(); convertProgress.StartInfo.FileName = "ffmpeg"; convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; - convertProgress.OutputDataReceived += (d, f) => - { - ctx.Client.Logger.LogDebug("{data}", f.Data); - }; + convertProgress.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", f.Data); }; convertProgress.Start(); await convertProgress.WaitForExitAsync(); File.Delete($@"{s}.mp4"); @@ -58,11 +52,8 @@ public static async Task GetNNDAsync(this InteractionContext ctx,s { ctx.Client.Logger.LogDebug("{ex}", ex.Message); ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - await ctx.EditFollowupAsync(msg_id, new DiscordWebhookBuilder().WithContent("Encountered error")); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); return null; } } - -} -*/ - +} \ No newline at end of file diff --git a/MikuSharp/Utilities/PlaylistDB.cs b/MikuSharp/Utilities/PlaylistDB.cs index ace168ae..81298997 100644 --- a/MikuSharp/Utilities/PlaylistDB.cs +++ b/MikuSharp/Utilities/PlaylistDB.cs @@ -499,7 +499,7 @@ await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmb var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() .WithTitle("Playlist link detected!") .WithDescription("Choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) .Build()).AddComponents(buttons)); var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); if (resp.TimedOut) @@ -532,7 +532,7 @@ await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmb var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() .WithTitle("Link with Playlist detected!") .WithDescription("Please choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) .Build()).AddComponents(buttons)); var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); if (resp.TimedOut) @@ -596,7 +596,7 @@ await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmb var em = new DiscordEmbedBuilder() .WithTitle("Results!") .WithDescription("Please select a track:\n") - .WithAuthor($"Requested by {ctx.Member.Username}#{ctx.Member.Discriminator} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); for (int i = 0; i < leng; i++) { em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); diff --git a/NicoNicoNii b/NicoNicoNii index 725dd1b0..4abc1356 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 725dd1b091181fd77ccb946dce64bb36b916c0cc +Subproject commit 4abc135610c3c7ab627af5d2686ecfd09880aaeb From 9e2ad2c437db4d4542cacb4499d7eef18af9f0cc Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 25 Apr 2024 00:37:20 +0200 Subject: [PATCH 023/113] aaaaaaa --- MikuSharp/Commands/About.cs | 4 ++ MikuSharp/Commands/Action.cs | 8 ++- MikuSharp/Commands/Fun.cs | 8 ++- MikuSharp/Commands/NSFW.cs | 3 +- MikuSharp/Commands/Weeb.cs | 9 ++- MikuSharp/Events/VoiceChat.cs | 13 ++-- MikuSharp/MikuBot.cs | 2 +- MikuSharp/Utilities/Database.cs | 122 ++++++++++++++++++-------------- 8 files changed, 103 insertions(+), 66 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 3550706c..22f587bf 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -39,6 +39,7 @@ public static async Task BotAsync(InteractionContext ctx) public static async Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") { await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); @@ -72,6 +73,7 @@ public static async Task FeedbackAsync(InteractionContext ctx) await ctx.CreateModalResponseAsync(modalBuilder); var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); + if (!res.TimedOut) { await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); @@ -112,9 +114,11 @@ public static async Task StatsAsync(InteractionContext ctx) var guildCount = 0; var userCount = 0; var channelCount = 0; + foreach (var client in MikuBot.ShardedClient.ShardClients) { guildCount += client.Value.Guilds.Count; + foreach (var guild in client.Value.Guilds) { userCount += guild.Value.MemberCount; diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 8435221f..b299f8d7 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -13,7 +13,13 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("action", "Actions", dmPermission: false)] +[SlashCommandGroup("action", "Actions", false, new[] +{ + InteractionContextType.Guild, InteractionContextType.PrivateChannel +}, new[] +{ + ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall +})] internal class Action : ApplicationCommandsModule { [SlashCommand("hug", "Hug someone!")] diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 1922b759..cdebad69 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -18,7 +18,13 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("fun", "Fun commands")] +[SlashCommandGroup("fun", "Fun commands", false, new[] +{ + InteractionContextType.Guild, InteractionContextType.PrivateChannel +}, new[] +{ + ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall +})] internal class Fun : ApplicationCommandsModule { [SlashCommand("8ball", "Yes? No? Maybe?")] diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index 3cc77963..458b3749 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -2,13 +2,14 @@ using DisCatSharp.CommandsNext.Attributes; using DisCatSharp.Entities; +using MikuSharp.Attributes; using MikuSharp.Utilities; using System.Threading.Tasks; namespace MikuSharp.Commands; -[RequireNsfw] +[RequireNsfw, NotStaff] public class Nsfw : BaseCommandModule { [Command("4k"), Description("lewd")] diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 51ce08ac..9fdd756d 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -2,6 +2,7 @@ using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; +using DisCatSharp.Enums; using HeyRed.Mime; @@ -17,7 +18,13 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("weeb", "Weeb Stuff!", dmPermission: false)] +[SlashCommandGroup("weeb", "Weeb Stuff!", false, new[] +{ + InteractionContextType.Guild, InteractionContextType.PrivateChannel +}, new[] +{ + ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall +})] internal class Weeb : ApplicationCommandsModule { [SlashCommand("awooify", "Awooify your or someones avatar!")] diff --git a/MikuSharp/Events/VoiceChat.cs b/MikuSharp/Events/VoiceChat.cs index e22386a1..d4aaa680 100644 --- a/MikuSharp/Events/VoiceChat.cs +++ b/MikuSharp/Events/VoiceChat.cs @@ -18,19 +18,20 @@ public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventAr { try { - if (MikuBot.Guilds.All(x => x.Key != e.Guild.Id)) return; + if (MikuBot.Guilds.All(x => x.Key != e.Guild.Id)) + return; var g = MikuBot.Guilds[e.Guild.Id]; if (g.MusicInstance == null || g.MusicInstance?.GuildConnection?.IsConnected == false) return; - if (((e.After?.Channel?.Users).Count(x => !x.IsBot) == 0 - || (e.Before?.Channel?.Users).Count(x => !x.IsBot) == 0 - || (e.Channel?.Users).Count(x => !x.IsBot) == 0) + if (((e.After?.Channel?.Users)?.Count(x => !x.IsBot) == 0 + || (e.Before?.Channel?.Users)?.Count(x => !x.IsBot) == 0 + || (e.Channel?.Users)?.Count(x => !x.IsBot) == 0) && (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true || e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true || e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - && (g.MusicInstance?.GuildConnection?.Channel?.Users).Count(x => !x.IsBot) == 0) + && (g.MusicInstance?.GuildConnection?.Channel?.Users)?.Count(x => !x.IsBot) == 0) { if (g.MusicInstance.Playstate == Playstate.Playing) { @@ -56,7 +57,7 @@ public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventAr g.MusicInstance.AloneCts = new(); g.AloneCheckThread = Task.Run(g.CheckAlone); } - else if ((e.After?.Channel?.Users).Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) + else if ((e.After?.Channel?.Users)?.Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) if (g.MusicInstance is { AloneCts: not null }) g.MusicInstance.AloneCts.Cancel(); } diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 10566204..90b4f4cd 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -127,7 +127,7 @@ internal MikuBot() EnableDefaultHelp = true, DebugStartup = true, EnableLocalization = false, - GenerateTranslationFilesOnly = false + GenerateTranslationFilesOnly = false, }).Result; this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() diff --git a/MikuSharp/Utilities/Database.cs b/MikuSharp/Utilities/Database.cs index e12493d1..7db70379 100644 --- a/MikuSharp/Utilities/Database.cs +++ b/MikuSharp/Utilities/Database.cs @@ -21,16 +21,16 @@ public static async Task AddToLastPlayingListAsync(ulong g, string ts) var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guildId = {g};", conn); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guild_id = '{g.ToString()}';", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) position = reader.GetInt32(0); await reader.CloseAsync(); - cmd2.Dispose(); + await cmd2.DisposeAsync(); var cmd = new NpgsqlCommand("INSERT INTO lastplayedsongs VALUES (@pos,@guild,@ts)", conn); var para = cmd.CreateParameter(); para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g); + para.Value = g.ToString(); cmd.Parameters.Add(para); var para2 = cmd.CreateParameter(); para2.ParameterName = "ts"; @@ -41,9 +41,9 @@ public static async Task AddToLastPlayingListAsync(ulong g, string ts) para3.Value = position; cmd.Parameters.Add(para3); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task ReorderQueue(DiscordGuild g) @@ -52,15 +52,15 @@ public static async Task ReorderQueue(DiscordGuild g) var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); var queueNow = await GetQueueAsync(g); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); var i = 0; var longcmd = ""; foreach (var qi in queueNow) { - longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.AddedBy}','{qi.Track.Encoded}',@adddate{i});"; + longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; i++; } @@ -72,7 +72,7 @@ public static async Task ReorderQueue(DiscordGuild g) cmd2.Connection = conn; var para = cmd2.CreateParameter(); para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); + para.Value = g.Id.ToString(); cmd2.Parameters.Add(para); i = 0; @@ -86,9 +86,9 @@ public static async Task ReorderQueue(DiscordGuild g) } await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); + await cmd2.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task RebuildQueue(DiscordGuild g, List q) @@ -97,15 +97,15 @@ public static async Task RebuildQueue(DiscordGuild g, List q) var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); var queueNow = q; - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}'", conn); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); var i = 0; var longcmd = ""; foreach (var qi in queueNow) { - longcmd += $"INSERT INTO queues VALUES ({i},@guild,'{qi.AddedBy}','{qi.Track.Encoded}',@adddate{i});"; + longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; i++; } @@ -117,7 +117,7 @@ public static async Task RebuildQueue(DiscordGuild g, List q) cmd2.Connection = conn; var para = cmd2.CreateParameter(); para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); + para.Value = g.Id.ToString(); cmd2.Parameters.Add(para); i = 0; @@ -131,25 +131,37 @@ public static async Task RebuildQueue(DiscordGuild g, List q) } await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); + await cmd2.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task> GetQueueAsync(DiscordGuild g) { + List queue = new(); + var count = 0; var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM queues WHERE guildId = {g.Id} ORDER BY position ASC;", conn); - var reader = await cmd2.ExecuteReaderAsync(); - List queue = new(); - while (await reader.ReadAsync()) - queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(Convert.ToString(reader["trackstring"])), Convert.ToUInt64(reader["userid"]), DateTimeOffset.Parse(reader["addtime"].ToString()), Convert.ToInt32(reader["position"]))); - await reader.CloseAsync(); - cmd2.Dispose(); + var cmd1 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + count = Convert.ToInt32(await cmd1.ExecuteScalarAsync()); + await cmd1.DisposeAsync(); + + if (count == 0) + { + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } + + var cmd2 = new NpgsqlCommand($"SELECT * FROM queues WHERE guild_id = '{g.Id.ToString()}' ORDER BY position ASC;", conn); + var reader2 = await cmd2.ExecuteReaderAsync(); + while (await reader2.ReadAsync()) + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(Convert.ToString(reader2["track"])), Convert.ToUInt64(reader2["user_id"]), DateTimeOffset.Parse(reader2["added_at"].ToString()), Convert.ToInt32(reader2["position"]))); + await reader2.CloseAsync(); + await cmd2.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); return queue; } @@ -159,20 +171,20 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) position = reader.GetInt32(0); await reader.CloseAsync(); - cmd2.Dispose(); - var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@pos,@guild,@user,@ts,@time)", conn); + await cmd2.DisposeAsync(); + var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@guild,@pos,@user,@ts,@time)", conn); var para = cmd.CreateParameter(); para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); + para.Value = g.Id.ToString(); cmd.Parameters.Add(para); var para2 = cmd.CreateParameter(); para2.ParameterName = "user"; - para2.Value = Convert.ToInt64(u); + para2.Value = u.ToString(); cmd.Parameters.Add(para2); var para3 = cmd.CreateParameter(); para3.ParameterName = "ts"; @@ -187,9 +199,9 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) para5.Value = DateTime.UtcNow; cmd.Parameters.Add(para5); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) @@ -198,37 +210,37 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, List var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) position = reader.GetInt32(0); await reader.CloseAsync(); - cmd2.Dispose(); + await cmd2.DisposeAsync(); var longcmd = ""; foreach (var tt in ts) { - longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.Encoded}',@time);"; + longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Encoded}',@time);"; position++; } var cmd = new NpgsqlCommand(longcmd, conn); var para = cmd.CreateParameter(); para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); + para.Value = g.Id.ToString(); cmd.Parameters.Add(para); var para2 = cmd.CreateParameter(); para2.ParameterName = "user"; - para2.Value = Convert.ToInt64(u); + para2.Value = u.ToString(); cmd.Parameters.Add(para2); var para3 = cmd.CreateParameter(); para3.ParameterName = "time"; para3.Value = DateTime.UtcNow; cmd.Parameters.Add(para3); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) @@ -237,37 +249,37 @@ public static async Task AddToQueue(DiscordGuild g, ulong u, List var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guildId = {g.Id};", conn); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); var reader = await cmd2.ExecuteReaderAsync(); while (await reader.ReadAsync()) position = reader.GetInt32(0); await reader.CloseAsync(); - cmd2.Dispose(); + await cmd2.DisposeAsync(); var longcmd = ""; foreach (var tt in ts) { - longcmd += $"INSERT INTO queues VALUES ({position},@guild,@user,'{tt.Track.Encoded}',@time);"; + longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Track.Encoded}',@time);"; position++; } var cmd = new NpgsqlCommand(longcmd, conn); var para = cmd.CreateParameter(); para.ParameterName = "guild"; - para.Value = Convert.ToInt64(g.Id); + para.Value = g.Id.ToString(); cmd.Parameters.Add(para); var para2 = cmd.CreateParameter(); para2.ParameterName = "user"; - para2.Value = Convert.ToInt64(u); + para2.Value = u.ToString(); cmd.Parameters.Add(para2); var para3 = cmd.CreateParameter(); para3.ParameterName = "time"; para3.Value = DateTime.UtcNow; cmd.Parameters.Add(para3); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task InsertToQueue(DiscordGuild g, ulong u, string ts, int pos) @@ -290,12 +302,12 @@ public static async Task RemoveFromQueueAsync(int position, DiscordGuild g) var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE position = {position} AND guildid = {g.Id};", conn); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE position = {position} AND guild_id = '{g.Id.ToString()}';", conn); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); await ReorderQueue(g); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task ClearQueue(DiscordGuild g) @@ -303,11 +315,11 @@ public static async Task ClearQueue(DiscordGuild g) var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guildid = {g.Id};", conn); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); + await cmd.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); } public static async Task MoveQueueItems(DiscordGuild g, int oldpos, int newpos) @@ -324,15 +336,15 @@ public static async Task> GetLastPlayingListAsync(DiscordGuild g) var connString = MikuBot.Config.DbConnectString; var conn = new NpgsqlConnection(connString); await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM lastplayedsongs WHERE guildId = {g.Id} ORDER BY lastplayedsongs.trackposition DESC LIMIT 1000", conn); + var cmd2 = new NpgsqlCommand($"SELECT * FROM lastplayedsongs WHERE guild_id = '{g.Id.ToString()}' ORDER BY lastplayedsongs.trackposition DESC LIMIT 1000", conn); var reader = await cmd2.ExecuteReaderAsync(); List queue = new(); while (await reader.ReadAsync()) queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync((string)reader["trackstring"]), DateTimeOffset.UtcNow)); await reader.CloseAsync(); - cmd2.Dispose(); + await cmd2.DisposeAsync(); await conn.CloseAsync(); - conn.Dispose(); + await conn.DisposeAsync(); return queue; } } \ No newline at end of file From b90258f7dc059f1a5a210d1f218ef88b5ec3eaa1 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 11 Sep 2024 17:31:44 +0200 Subject: [PATCH 024/113] feat: bite, nom & stare --- MikuSharp/Attributes/CustomMikuAttributes.cs | 36 +- MikuSharp/Commands/About.cs | 277 +-- MikuSharp/Commands/Action.cs | 271 +-- MikuSharp/Commands/Developer.cs | 349 ++-- MikuSharp/Commands/Fun.cs | 216 +-- MikuSharp/Commands/MikuGuild.cs | 37 +- MikuSharp/Commands/Moderation.cs | 189 ++- MikuSharp/Commands/Music.cs | 1496 +++++++++-------- MikuSharp/Commands/NSFW.cs | 202 +-- MikuSharp/Commands/Playlist.cs | 909 +++++----- MikuSharp/Commands/Utility.cs | 414 ++--- MikuSharp/Commands/Weeb.cs | 482 +++--- MikuSharp/Entities/BiliJson.cs | 40 +- MikuSharp/Entities/BiliPlayInfo.cs | 54 +- MikuSharp/Entities/BotConfig.cs | 60 +- MikuSharp/Entities/DogCeo.cs | 6 +- MikuSharp/Entities/Entry.cs | 20 +- MikuSharp/Entities/Guild.cs | 50 +- MikuSharp/Entities/Img_Data.cs | 12 +- MikuSharp/Entities/KsoftSiRanImg.cs | 10 +- MikuSharp/Entities/MeekMoe.cs | 6 +- MikuSharp/Entities/MusicInstance.cs | 775 +++++---- MikuSharp/Entities/Nekobot.cs | 8 +- MikuSharp/Entities/Nekos_Life.cs | 4 +- MikuSharp/Entities/Playlist.cs | 99 +- MikuSharp/Entities/PlaylistEntry.cs | 20 +- MikuSharp/Entities/QueueEntry.cs | 20 +- MikuSharp/Entities/Random_D.cs | 6 +- MikuSharp/Entities/TrackResult.cs | 35 +- MikuSharp/Entities/WeebSh.cs | 12 +- MikuSharp/Enums/ExtService.cs | 10 +- MikuSharp/Enums/Playing.cs | 26 +- MikuSharp/Events/Lavalink.cs | 136 +- MikuSharp/Events/MikuGuildJoin.cs | 38 +- MikuSharp/Events/VoiceChat.cs | 108 +- MikuSharp/GlobalSuppressions.cs | 247 ++- MikuSharp/MikuBot.cs | 589 +++---- MikuSharp/MikuSharp.csproj | 32 +- MikuSharp/Program.cs | 22 +- MikuSharp/Utilities/Bilibili.cs | 80 +- MikuSharp/Utilities/Database.cs | 680 ++++---- MikuSharp/Utilities/DiscordOptionProviders.cs | 198 +-- MikuSharp/Utilities/Music.cs | 431 ++--- MikuSharp/Utilities/NND.cs | 98 +- MikuSharp/Utilities/Other.cs | 26 +- MikuSharp/Utilities/PlaylistDB.cs | 1163 ++++++------- MikuSharp/Utilities/Web.cs | 132 +- NicoNicoNii | 2 +- 48 files changed, 5229 insertions(+), 4904 deletions(-) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 05b7c478..4996e7aa 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -1,41 +1,41 @@ -using DisCatSharp.ApplicationCommands.Attributes; +using System; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.CommandsNext; using DisCatSharp.CommandsNext.Attributes; -using System; -using System.Threading.Tasks; - namespace MikuSharp.Attributes; /// -/// Defines that usage of this command is restricted to users in a vc. +/// Defines that usage of this command is restricted to users in a vc. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserVoicechatConnection : ApplicationCommandCheckBaseAttribute { - public override Task ExecuteChecksAsync(BaseContext ctx) - => Task.FromResult(ctx.Member.VoiceState?.Channel != null); + public override Task ExecuteChecksAsync(BaseContext ctx) + => Task.FromResult(ctx.Member.VoiceState?.Channel != null); } /// -/// Defines that usage of this command is restricted to users & the bot in a vc. +/// Defines that usage of this command is restricted to users & the bot in a vc. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute { - public override async Task ExecuteChecksAsync(BaseContext ctx) - { - var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); - return ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null - ? await Task.FromResult(true) - : await Task.FromResult(false); - } + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); + return ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null + ? await Task.FromResult(true) + : await Task.FromResult(false); + } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class)] public sealed class NotStaffAttribute : CheckBaseAttribute { - public override Task ExecuteCheckAsync(CommandContext ctx, bool help) - => Task.FromResult(!ctx.User.IsStaff); -} \ No newline at end of file + public override Task ExecuteCheckAsync(CommandContext ctx, bool help) + => Task.FromResult(!ctx.User.IsStaff); +} diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 22f587bf..74663d31 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -1,143 +1,156 @@ -using DisCatSharp.ApplicationCommands; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; using DisCatSharp.Interactivity.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; [SlashCommandGroup("about", "About")] internal class About : ApplicationCommandsModule { - [SlashCommand("donate", "Financial support information")] - public static async Task DonateAsync(InteractionContext ctx) - { - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) - .AddField(new("PayPal", "[Link](https://paypal.me/speyd3r)", true)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); - } - - [SlashCommand("bot", "About the bot")] - public static async Task BotAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")).WithDescription(ctx.Client.CurrentApplication.Description); - foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id ? "Owner" : "Developer", member.User.UsernameWithGlobalName)); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] - public static async Task FollowNewsAsync(InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, [Option("name", "Name of webhook")] string name = "Miku Bot Announcements") - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); - var f = await announcementChannel.FollowAsync(channel); - await Task.Delay(5000); - var msgs = await channel.GetMessagesAsync(); - var target = msgs.First(x => x.MessageType == MessageType.ChannelFollowAdd); - await target.DeleteAsync("Message cleanup"); - var webhooks = await channel.GetWebhooksAsync(); - var webhook = webhooks.First(x => x.Id == f.WebhookId); - var selfAvatarUrl = ctx.Client.CurrentUser.AvatarUrl; - var stream = await ctx.Client.RestClient.GetStreamAsync(selfAvatarUrl); - var memoryStream = new System.IO.MemoryStream(); - await stream.CopyToAsync(memoryStream); - memoryStream.Position = 0; - await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); - } - - [SlashCommand("feedback", "Send feedback!")] - public static async Task FeedbackAsync(InteractionContext ctx) - { - DiscordInteractionModalBuilder modalBuilder = new(); - modalBuilder.WithTitle("Feedback modal"); - modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); - modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20, null, true, null)); - await ctx.CreateModalResponseAsync(modalBuilder); - - var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); - - if (!res.TimedOut) - { - await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; - var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); - var emb = new DiscordEmbedBuilder(); - emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); - if (ctx.Guild != null) - emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); - var forum = guild.GetChannel(1020433162662322257); - List tags = new() - { - ctx.Guild != null ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) : forum.AvailableTags.First(x => x.Id == 1020434935502360576) - }; - var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); - var msg = await thread.GetMessageAsync(thread.Id); - await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); - await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); - await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); - } - else - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is one minute.").AsEphemeral()); - } - - [SlashCommand("ping", "Current ping to discord's services")] - public static async Task PingAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); - - [SlashCommand("which_shard", "What shard am I on?")] - public static async Task GetExecutingShardAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); - - [SlashCommand("stats", "Some stats of the MikuBot!")] - public static async Task StatsAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var guildCount = 0; - var userCount = 0; - var channelCount = 0; - - foreach (var client in MikuBot.ShardedClient.ShardClients) - { - guildCount += client.Value.Guilds.Count; - - foreach (var guild in client.Value.Guilds) - { - userCount += guild.Value.MemberCount; - channelCount += guild.Value.Channels.Count; - } - } - - var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", guildCount.ToString(), true)).AddField(new("Users", userCount.ToString(), true)).AddField(new("Channels", channelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) - .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("support", "Link to my support server")] - public static async Task SupportAsybc(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); - var widget = await guild.GetWidgetAsync(); - var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); - } -} \ No newline at end of file + [SlashCommand("donate", "Financial support information")] + public static async Task DonateAsync(InteractionContext ctx) + { + var emb = new DiscordEmbedBuilder(); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) + .WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) + .AddField(new("PayPal", "[Link](https://paypal.me/speyd3r)", true)); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); + } + + [SlashCommand("bot", "About the bot")] + public static async Task BotAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var emb = new DiscordEmbedBuilder(); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) + .WithDescription(ctx.Client.CurrentApplication.Description); + foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) + emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id + ? "Owner" + : "Developer", member.User.UsernameWithGlobalName)); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] + public static async Task FollowNewsAsync( + InteractionContext ctx, + [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, + [Option("name", "Name of webhook")] string name = "Miku Bot Announcements" + ) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); + var f = await announcementChannel.FollowAsync(channel); + await Task.Delay(5000); + var msgs = await channel.GetMessagesAsync(); + var target = msgs.First(x => x.MessageType == MessageType.ChannelFollowAdd); + await target.DeleteAsync("Message cleanup"); + var webhooks = await channel.GetWebhooksAsync(); + var webhook = webhooks.First(x => x.Id == f.WebhookId); + var selfAvatarUrl = ctx.Client.CurrentUser.AvatarUrl; + var stream = await ctx.Client.RestClient.GetStreamAsync(selfAvatarUrl); + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + memoryStream.Position = 0; + await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( + $"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); + } + + [SlashCommand("feedback", "Send feedback!")] + public static async Task FeedbackAsync(InteractionContext ctx) + { + DiscordInteractionModalBuilder modalBuilder = new(); + modalBuilder.WithTitle("Feedback modal"); + modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); + modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20)); + await ctx.CreateModalResponseAsync(modalBuilder); + + var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); + + if (!res.TimedOut) + { + await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; + var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; + var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var emb = new DiscordEmbedBuilder(); + emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); + if (ctx.Guild != null) + emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); + var forum = guild.GetChannel(1020433162662322257); + List tags = + [ + ctx.Guild != null + ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) + : forum.AvailableTags.First(x => x.Id == 1020434935502360576) + ]; + var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); + var msg = await thread.GetMessageAsync(thread.Id); + await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); + await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); + await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); + } + else + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is one minute.").AsEphemeral()); + } + + [SlashCommand("ping", "Current ping to discord's services")] + public static async Task PingAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); + + [SlashCommand("which_shard", "What shard am I on?")] + public static async Task GetExecutingShardAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); + + [SlashCommand("stats", "Some stats of the MikuBot!")] + public static async Task StatsAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var guildCount = 0; + var userCount = 0; + var channelCount = 0; + + foreach (var client in MikuBot.ShardedClient.ShardClients) + { + guildCount += client.Value.Guilds.Count; + + foreach (var guild in client.Value.Guilds) + { + userCount += guild.Value.MemberCount!.Value; + channelCount += guild.Value.Channels.Count; + } + } + + var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", guildCount.ToString(), true)).AddField(new("Users", userCount.ToString(), true)) + .AddField(new("Channels", channelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) + .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("support", "Link to my support server")] + public static async Task SupportAsybc(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var widget = await guild.GetWidgetAsync(); + var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); + } +} diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index b299f8d7..2fba8cee 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -1,4 +1,7 @@ -using DisCatSharp.ApplicationCommands; +using System.IO; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; @@ -8,113 +11,167 @@ using MikuSharp.Utilities; -using System.IO; -using System.Threading.Tasks; - namespace MikuSharp.Commands; -[SlashCommandGroup("action", "Actions", false, new[] -{ - InteractionContextType.Guild, InteractionContextType.PrivateChannel -}, new[] -{ - ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall -})] +[SlashCommandGroup("action", "Actions", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] internal class Action : ApplicationCommandsModule { - [SlashCommand("hug", "Hug someone!")] - public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", new[] { "" }); - wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - await ctx.EditResponseAsync(builder); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); - } - - [SlashCommand("kiss", "Kiss someone!")] - public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", new[] { "" }); - wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - await ctx.EditResponseAsync(builder); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); - } - - [SlashCommand("lick", "Lick someone!")] - public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", new[] { "" }); - wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - await ctx.EditResponseAsync(builder); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); - } - - [SlashCommand("pat", "Pat someone!")] - public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); - } - - [SlashCommand("poke", "Poke someone!")] - public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); - } - - [SlashCommand("slap", "Slap someone!")] - public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", new[] { "" }); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); - } -} \ No newline at end of file + [SlashCommand("hug", "Hug someone!")] + public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", [""]); + wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("kiss", "Kiss someone!")] + public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", [""]); + wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("lick", "Lick someone!")] + public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", [""]); + wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("pat", "Pat someone!")] + public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("poke", "Poke someone!")] + public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("slap", "Slap someone!")] + public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("bite", "Bite someone!")] + public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} bites {user.Mention} x~x"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("nom", "Nom someone!")] + public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} noms {user.Mention} >:3c"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } + + [SlashCommand("stare", "Stare at someone!")] + public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} stares {user.Mention} O.o")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} stares at {user.Mention} O.o"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + } +} diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index f9507c3c..4c16c887 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -1,4 +1,9 @@ -using DisCatSharp; +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; @@ -10,214 +15,230 @@ using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; /// -/// The developer commands. +/// The developer commands. /// public class Developer : ApplicationCommandsModule { - [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - } - - [SlashCommand("guild_shard_test", "Testing")] - public static async Task GuildTestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); - } - - [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] - public static async Task DeleteMessageAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.TargetMessage.DeleteAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } + [SlashCommand("test", "Testing")] + public static async Task TestAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + } + + [SlashCommand("guild_shard_test", "Testing")] + public static async Task GuildTestAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); + } + + [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] + public static async Task DeleteMessageAsync(ContextMenuContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + await ctx.TargetMessage.DeleteAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } /// - /// Gets the debug log. + /// Gets the debug log. /// /// The interaction context. [SlashCommand("dbg", "Get the logs of today")] - public static async Task GetDebugLogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); - var now = DateTime.Now; - var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; - if (!File.Exists(targetFile)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); - return; - } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); - - try - { - if (!File.Exists($"temp-{targetFile}")) - File.Copy(targetFile, $"temp-{targetFile}"); - else - { - File.Delete($"temp-{targetFile}"); - File.Copy(targetFile, $"temp-{targetFile}"); - } - - FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); - log.Close(); - await log.DisposeAsync(); - File.Delete($"temp-{targetFile}"); - } - catch (Exception ex) - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } + public static async Task GetDebugLogAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); + var now = DateTime.Now; + var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; + + if (!File.Exists(targetFile)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); + + try + { + if (!File.Exists($"temp-{targetFile}")) + File.Copy(targetFile, $"temp-{targetFile}"); + else + { + File.Delete($"temp-{targetFile}"); + File.Copy(targetFile, $"temp-{targetFile}"); + } + + FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); + log.Close(); + await log.DisposeAsync(); + File.Delete($"temp-{targetFile}"); + } + catch (Exception ex) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } /// - /// Evals the csharp script. + /// Evals the csharp script. /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public static async Task EvalCsAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - var msg = ctx.TargetMessage; - var code = ctx.TargetMessage.Content; - var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; - cs1 = code.IndexOf('\n', cs1) + 1; - var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); - var c = await ctx.Guild.GetActiveThreadsAsync(); - - if (cs1 == -1 || cs2 == -1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); - return; - } - - var cs = code[cs1..cs2]; - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() - .WithColor(new("#FF007F")) - .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") - .Build())).ConfigureAwait(false); - await ctx.GetOriginalResponseAsync(); - try - { - var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); - - var sopts = ScriptOptions.Default; - sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); - sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); - - var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); - script.Compile(); - var result = await script.RunAsync(globals).ConfigureAwait(false); - - if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Result", Description = result.ReturnValue.ToString(), Color = new DiscordColor("#007FFF") }.Build())).ConfigureAwait(false); - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Successful", Description = "No result was returned.", Color = new DiscordColor("#007FFF") }.Build())).ConfigureAwait(false); - } - catch (Exception ex) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder { Title = "Evaluation Failure", Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), Color = new DiscordColor("#FF0000") }.Build())).ConfigureAwait(false); - } - } + public static async Task EvalCsAsync(ContextMenuContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + var msg = ctx.TargetMessage; + var code = ctx.TargetMessage.Content; + var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; + cs1 = code.IndexOf('\n', cs1) + 1; + var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); + var c = await ctx.Guild.GetActiveThreadsAsync(); + + if (cs1 == -1 || cs2 == -1) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); + return; + } + + var cs = code[cs1..cs2]; + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() + .WithColor(new("#FF007F")) + .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") + .Build())).ConfigureAwait(false); + await ctx.GetOriginalResponseAsync(); + + try + { + var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); + + var sopts = ScriptOptions.Default; + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", + "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); + sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + + var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); + script.Compile(); + var result = await script.RunAsync(globals).ConfigureAwait(false); + + if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Result", + Description = result.ReturnValue.ToString(), + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Successful", + Description = "No result was returned.", + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + } + catch (Exception ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Failure", + Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), + Color = new DiscordColor("#FF0000") + }.Build())).ConfigureAwait(false); + } + } } /// -/// The test variables. +/// The test variables. /// public sealed class SgTestVariables { + //public Dictionary Bot = MikuBot.Guilds; + /// - /// Gets or sets the message. + /// Initializes a new instance of the class. + /// + /// The message. + /// The client. + /// The context menu context. + public SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) + { + this.Client = client; + this.ShardClient = shard; + + this.Message = msg; + this.Channel = ctx.Channel; + this.Guild = ctx.Guild; + this.User = ctx.User; + this.Member = ctx.Member; + this.Context = ctx; + this.Inter = this.Client.GetInteractivity(); + } + + /// + /// Gets or sets the message. /// public DiscordMessage Message { get; set; } - public InteractivityExtension Inter { get; set; } + public InteractivityExtension Inter { get; set; } /// - /// Gets or sets the channel. + /// Gets or sets the channel. /// public DiscordChannel Channel { get; set; } /// - /// Gets or sets the guild. + /// Gets or sets the guild. /// public DiscordGuild Guild { get; set; } /// - /// Gets or sets the user. + /// Gets or sets the user. /// public DiscordUser User { get; set; } /// - /// Gets or sets the member. + /// Gets or sets the member. /// public DiscordMember Member { get; set; } /// - /// Gets or sets the context menu context. + /// Gets or sets the context menu context. /// public ContextMenuContext Context { get; set; } - //public Dictionary Bot = MikuBot.Guilds; + public DiscordShardedClient ShardClient { get; set; } - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The client. - /// The context menu context. - public SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) - { - this.Client = client; - this.ShardClient = shard; - - this.Message = msg; - this.Channel = ctx.Channel; - this.Guild = ctx.Guild; - this.User = ctx.User; - this.Member = ctx.Member; - this.Context = ctx; - this.Inter = this.Client.GetInteractivity(); - } - - public DiscordShardedClient ShardClient { get; set; } - - public DiscordClient Client { get; set; } -} \ No newline at end of file + public DiscordClient Client { get; set; } +} diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index cdebad69..6b18a383 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -1,4 +1,7 @@ -using DisCatSharp; +using System; +using System.IO; +using System.Threading.Tasks; + using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; @@ -12,82 +15,98 @@ using Newtonsoft.Json; -using System; -using System.IO; -using System.Threading.Tasks; - namespace MikuSharp.Commands; -[SlashCommandGroup("fun", "Fun commands", false, new[] -{ - InteractionContextType.Guild, InteractionContextType.PrivateChannel -}, new[] -{ - ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall -})] +[SlashCommandGroup("fun", "Fun commands", false, [ + InteractionContextType.Guild, InteractionContextType.PrivateChannel +], [ + ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall +])] internal class Fun : ApplicationCommandsModule { - [SlashCommand("8ball", "Yes? No? Maybe?")] - public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var responses = new[] - { - "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", - "Outlook not so good.", "Very doubtful.", "No." - }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); - } - - [SlashCommand("cat", "Get a random cat image!")] - public static async Task CatAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); - builder.AddEmbed(imgUrl.Embed); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("clyde", "Say something as clyde bot")] - public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); - - DiscordWebhookBuilder builder = new(); - builder.AddFile("clyde.png", img); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("coinflip", "Flip a coin lol")] - public static async Task CoinflipAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); - } - - [SlashCommand("dog", "Random Dog Image")] - public static async Task DogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); - em.WithDescription($"[Full Image]({dc.Message})"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - /* + [SlashCommand("8ball", "Yes? No? Maybe?")] + public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var responses = new[] + { + "It is certain.", + "It is decidedly so.", + "Without a doubt.", + "Yes - definitely.", + "You may rely on it.", + "As I see it, yes.", + "Most likely.", + "Outlook good.", + "Yes.", + "Signs point to yes.", + "Reply hazy, try again", + "Ask again later.", + "Better not tell you now.", + "Cannot predict now.", + "Concentrate and ask again.", + "Don't count on it.", + "My reply is no.", + "My sources say no.", + "Outlook not so good.", + "Very doubtful.", + "No." + }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); + } + + [SlashCommand("cat", "Get a random cat image!")] + public static async Task CatAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); + builder.AddEmbed(imgUrl.Embed); + await ctx.EditResponseAsync(builder); + } + + [SlashCommand("clyde", "Say something as clyde bot")] + public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); + + DiscordWebhookBuilder builder = new(); + builder.AddFile("clyde.png", img); + await ctx.EditResponseAsync(builder); + } + + [SlashCommand("coinflip", "Flip a coin lol")] + public static async Task CoinflipAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var flip = new[] + { + $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" + }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); + } + + [SlashCommand("dog", "Random Dog Image")] + public static async Task DogAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); + em.WithDescription($"[Full Image]({dc.Message})"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + } + /* [SlashCommand("duck", "Random duck image")] public static async Task DuckAsync(InteractionContext ctx) { @@ -103,7 +122,7 @@ public static async Task DuckAsync(InteractionContext ctx) builder.WithEmbed(em.Build()); await ctx.EditResponseAsync(builder); }*/ - /* + /* [SlashCommand("lion", "Get a random lion image")] public static async Task Lion(InteractionContext ctx) { @@ -115,18 +134,18 @@ public static async Task Lion(InteractionContext ctx) await ctx.EditResponseAsync(builder); }*/ - [SlashCommand("lizard", "Get a random lizard image")] - public static async Task LizardAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - } - /* + [SlashCommand("lizard", "Get a random lizard image")] + public static async Task LizardAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + } + /* [SlashCommand("panda", "Random panda image")] public static async Task PandaAsync(InteractionContext ctx) { @@ -149,7 +168,7 @@ public static async Task PenguinAsync(InteractionContext ctx) await ctx.EditResponseAsync(builder); }*/ - /* + /* [SlashCommand("redpanda", "Random red panda image")] public static async Task RedPandaAsync(InteractionContext ctx) { @@ -161,14 +180,17 @@ public static async Task RedPandaAsync(InteractionContext ctx) await ctx.EditResponseAsync(builder); }*/ - [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); - } - /* + [SlashCommand("rps", "Play rock paper scissors!")] + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var rock = new[] + { + $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" + }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); + } + /* [SlashCommand("tiger", "Random tiger image")] public static async Task TigerAsync(InteractionContext ctx) { @@ -180,7 +202,7 @@ public static async Task TigerAsync(InteractionContext ctx) await ctx.EditResponseAsync(builder); }*/ - /* + /* [SlashCommand("trumptweet", "generate a tweet by Trump")] public static async Task TrumpTweetAsync(InteractionContext ctx, [RemainingText]string text) { @@ -192,4 +214,4 @@ public static async Task TrumpTweetAsync(InteractionContext ctx, [RemainingText] builder.AddFile($"trump.png", img); await ctx.EditResponseAsync(builder); }*/ -} \ No newline at end of file +} diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuild.cs index 707cab3d..f9765386 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuild.cs @@ -1,29 +1,28 @@ -using DisCatSharp; +using System.Linq; +using System.Threading.Tasks; + using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; public class MikuGuild : ApplicationCommandsModule { - [SlashCommand("smolcar", "#SmolArmy")] - public static async Task SmolCarAsync(InteractionContext ctx) - { - if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) - { - await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":(")); - } - else - { - await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); - } - } -} \ No newline at end of file + [SlashCommand("smolcar", "#SmolArmy")] + public static async Task SmolCarAsync(InteractionContext ctx) + { + if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) + { + await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":(")); + } + else + { + await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); + } + } +} diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 69a6e917..43256cc8 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -1,107 +1,118 @@ -using DisCatSharp; +using System; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; +using DisCatSharp.Exceptions; using MikuSharp.Utilities; -using System; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; [SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, dmPermission: false)] internal class Moderation : ApplicationCommandsModule { - [SlashCommand("disable_invites", "Disable invites usage for guild")] - public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); - try - { - await ctx.Guild.DisableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Disabled invites")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not disable invites")); - } - } + [SlashCommand("disable_invites", "Disable invites usage for guild")] + public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); + + try + { + await ctx.Guild.DisableInvitesAsync(reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Disabled invites")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not disable invites")); + } + } + + [SlashCommand("enable_invites", "Enable invites usage for guild")] + public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); + + try + { + await ctx.Guild.EnableInvitesAsync(reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Enabled invites")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not enable invites")); + } + } + + [SlashCommand("ban", "Ban someone")] + public static async Task BanAsync( + InteractionContext ctx, + [Option("user", "User to ban")] DiscordUser user, + [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, + [Option("reason", "Auditlog reason")] string? reason = null + ) + { + await ctx.DeferAsync(false); + + try + { + await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithGlobalName}")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithGlobalName}")); + } + } - [SlashCommand("enable_invites", "Enable invites usage for guild")] - public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); - try - { - await ctx.Guild.EnableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Enabled invites")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not enable invites")); - } - } + [SlashCommand("unban", "Unban someone")] + public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); + var userId = Convert.ToUInt64(id); + var user = await ctx.Client.GetUserAsync(userId, true); + await ctx.Guild.UnbanMemberAsync(user, reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithGlobalName}")); + } - [SlashCommand("ban", "Ban someone")] - public static async Task BanAsync(InteractionContext ctx, [Option("user", "User to ban")] DiscordUser user, [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); - try - { - await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithGlobalName}")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithGlobalName}")); - } - } + [SlashCommand("kick", "Kick someone")] + public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); - [SlashCommand("unban", "Unban someone")] - public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); - var userId = Convert.ToUInt64(id); - var user = await ctx.Client.GetUserAsync(userId, true); - await ctx.Guild.UnbanMemberAsync(user, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithGlobalName}")); - } + try + { + var member = await user.ConvertToMember(ctx.Guild); + await member.RemoveAsync(reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithGlobalName}")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithGlobalName}")); + } + } - [SlashCommand("kick", "Kick someone")] - public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); - try - { - var member = await user.ConvertToMember(ctx.Guild); - await member.RemoveAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithGlobalName}")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithGlobalName}")); - } - } + [SlashCommand("purge", "Delete a large amount of messages fast")] + public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(); - [SlashCommand("purge", "Delete a large amount of messages fast")] - public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(true); - try - { - var msgs = await ctx.Channel.GetMessagesAsync(amount); - var under14DaysOld = msgs.Where(x => (DateTime.Now - x.CreationTimestamp.DateTime).TotalDays < 14).ToList().AsReadOnly(); - if (under14DaysOld.Any()) - await ctx.Channel.DeleteMessagesAsync(under14DaysOld, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Purged {under14DaysOld.Count} messages")); - } - catch (DisCatSharp.Exceptions.BadRequestException ex) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.JsonMessage.BlockCode("json"))); - } - } -} \ No newline at end of file + try + { + var msgs = await ctx.Channel.GetMessagesAsync(amount); + var under14DaysOld = msgs.Where(x => (DateTime.Now - x.CreationTimestamp.DateTime).TotalDays < 14).ToList().AsReadOnly(); + if (under14DaysOld.Any()) + await ctx.Channel.DeleteMessagesAsync(under14DaysOld, reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Purged {under14DaysOld.Count} messages")); + } + catch (BadRequestException ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.JsonMessage.BlockCode("json"))); + } + } +} diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs index a8f0857e..05dda0e6 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Music.cs @@ -1,4 +1,10 @@ -using DisCatSharp.ApplicationCommands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; @@ -9,743 +15,773 @@ using Microsoft.Extensions.Logging; using MikuSharp.Attributes; -using MikuSharp.Entities; using MikuSharp.Enums; using MikuSharp.Events; using MikuSharp.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace MikuSharp.Commands; /// -/// The music commands +/// The music commands /// [SlashCommandGroup("music", "Music commands", dmPermission: false)] public class Music : ApplicationCommandsModule { - private static readonly string[] Units = { "", "ki", "Mi", "Gi" }; - - private static string SizeToString(long l) - { - double d = l; - var u = 0; - - while (d >= 900 && u < Units.Length - 2) - { - u++; - d /= 1024; - } - - return $"{d:#,##0.00} {Units[u]}B"; - } - - [SlashCommandGroup("base", "Base commands")] - public class Base : ApplicationCommandsModule - { - [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] - public static async Task JoinAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); - await g.ConditionalConnect(ctx); - g.MusicInstance.UsedChannel = ctx.Channel; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - } - - [SlashCommand("leave", "Leaves the channel")] - public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - - if (g.MusicInstance == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not in a voice channel")); - return; - } - - g.MusicInstance.Playstate = Playstate.NotPlaying; - - try - { - if (keep) - await g.MusicInstance.GuildConnection.StopAsync(); - await g.MusicInstance.GuildConnection.DisconnectAsync(); - if (!keep) - await Database.ClearQueue(ctx.Guild); - g.MusicInstance = null; - } - catch (Exception) - { } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } - - [SlashCommand("lstats", "Displays Lavalink statistics"), ApplicationCommandRequireTeamDeveloper] - public static async Task GetLavalinkStatsAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var stats = MikuBot.LavalinkSessions[ctx.Client.ShardId].Statistics; - var sb = new StringBuilder(); - sb.Append("Lavalink resources usage statistics: ```") - .Append("Uptime: ").Append(stats.Uptime).AppendLine() - .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() - .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() - .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() - .Append("RAM Usage: ").Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() - .Append("Audio frames (per minute): ").Append($"{stats.Frames.Sent:#,##0} sent / {stats.Frames.Nulled:#,##0} nulled / {stats.Frames.Deficit:#,##0} deficit").AppendLine() - .Append("```"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); - } - } - - [SlashCommandGroup("playback", "Playback controls")] - public class Playback : ApplicationCommandsModule - { - [SlashCommand("seek", "Seek a song"), RequireUserAndBotVoicechatConnection] - public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) - { - await ctx.DeferAsync(); - if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - if (g.MusicInstance.Playstate != Playstate.Playing && g.MusicInstance.Playstate != Playstate.Paused) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); - return; - } - - g.MusicInstance.UsedChannel = ctx.Channel; - var ts = TimeSpan.FromSeconds(position); - await g.MusicInstance.GuildConnection.SeekAsync(ts); - var pos = ts.Hours < 1 ? ts.ToString(@"mm\:ss") : ts.ToString(@"hh\:mm\:ss"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked {g.MusicInstance.CurrentSong.Track.Info.Title} to {pos}")); - } - - [SlashCommand("play", "Play or queue a song"), RequireUserVoicechatConnection] - public static async Task PlayAsync( - InteractionContext ctx, - [Option("song", "Song name or url to play")] string nameOrUrl = null, - [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null - ) - { - await ctx.DeferAsync(); - if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); - var curq = await Database.GetQueueAsync(ctx.Guild); - - if (curq.Count != 0 && g.MusicInstance.Playstate == Playstate.NotPlaying) - { - var inter = ctx.Client.GetInteractivity(); - List buttons = new(2) - { - new(ButtonStyle.Success, "restore", "Restore old queue"), - new(ButtonStyle.Danger, "clear", "Clear old queue") - }; - var msg = await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Recover Queue").WithDescription("The last time the bot disconnected the queue wasnt cleared, do you want to restore and play that old one?").Build()).AddComponents(buttons)); - var hmm = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - - if (hmm.TimedOut) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Timed out!")); - return; - } - else if (hmm.Result.Id == "restore") - { - await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Restored").AddComponents(buttons)); - await g.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); - await g.MusicInstance.PlaySong(); - return; - } - else - { - await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - await Database.ClearQueue(ctx.Guild); - buttons.ForEach(x => x.Disable()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cleared").AddComponents(buttons)); - } - } - - await g.ConditionalConnect(ctx); - - if (musicFile == null && nameOrUrl == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); - return; - } - - g.MusicInstance.UsedChannel = ctx.Channel; - nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); - var oldState = g.MusicInstance.Playstate; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Trying to play/search {nameOrUrl}...")); - var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx); - - if (q == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); - return; - } - - var emb = new DiscordEmbedBuilder(); - - if (oldState == Playstate.Playing) - { - emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Added").Build()).AsEphemeral()); - } - else - { - if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); - else - emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + "]", - $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Playing").Build()).AsEphemeral()); - } - } - - [SlashCommand("insert", "Queue a song at a specific position!"), RequireUserVoicechatConnection] - public static async Task InsertToQueueAsync( - InteractionContext ctx, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string posi, - [Option("song", "Song name or url to play")] string nameOrUrl = null, - [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null - ) - { - await ctx.DeferAsync(); - var pos = Convert.ToInt32(posi); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (pos < 1) - return; - - g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); - - await g.ConditionalConnect(ctx); - - if (musicFile == null && nameOrUrl == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); - return; - } - - g.MusicInstance.UsedChannel = ctx.Channel; - nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); - var oldState = g.MusicInstance.Playstate; - var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx, pos); - - if (q == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); - return; - } - - var emb = new DiscordEmbedBuilder(); - - if (oldState == Playstate.Playing) - { - emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}\nAt position: {pos}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - emb.WithTitle("Playing"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - else - { - if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new(q.Tracks.First().Info.Title + "[" + (q.Tracks.First().Info.Length.Hours != 0 ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); - else - emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + "[" + (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + "]", - $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}At position: {pos}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - emb.WithTitle("Added"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - } - - [SlashCommand("skip", "Skip the current song"), RequireUserAndBotVoicechatConnection] - public static async Task SkipSongAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - g.MusicInstance.GuildConnection.TrackEnded -= Lavalink.LavalinkTrackFinish; - - if (g.MusicInstance.CurrentSong != null) - { - if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) - await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, ctx.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); - else if (lastPlayedSongs[0]?.Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); - } - - queue = await Database.GetQueueAsync(ctx.Guild); - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - - if (queue.Count != 0) - await g.MusicInstance.PlaySong(); - else - { - g.MusicInstance.Playstate = Playstate.NotPlaying; - await g.MusicInstance.GuildConnection.StopAsync(); - } - - if (g.MusicInstance.LastSong != null) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Skipped:**\n{g.MusicInstance.LastSong.Track.Info.Title}").Build())); - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Continued!**").Build())); - } - - [SlashCommand("stop", "Stop Playback"), RequireUserAndBotVoicechatConnection] - public static async Task StopAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - await Task.Run(async () => await g.MusicInstance.GuildConnection.StopAsync()); - var cmdId = ctx.Client.GetApplicationCommands().GlobalCommands.First(x => x.Name == "music").Id; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Stopped** (use to start playback again)").Build())); - } - - [SlashCommand("volume", "Change the music volume"), RequireUserAndBotVoicechatConnection] - public static async Task ModifyVolumeAsync( - InteractionContext ctx, - [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] - int vol = 100 - ) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - if (vol > 150) vol = 150; - await g.MusicInstance.GuildConnection.SetVolumeAsync(vol); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Set volume to {vol}**").Build())); - } - - [SlashCommand("pause", "Pauses playback"), RequireUserAndBotVoicechatConnection] - public static async Task PauseAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - - if (g.MusicInstance.Playstate == Playstate.Playing) - { - await g.MusicInstance.GuildConnection.PauseAsync(); - g.MusicInstance.Playstate = Playstate.Paused; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Paused**").Build())); - } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not playing anything right now")); - } - - [SlashCommand("resume", "Resumes paused playback"), RequireUserAndBotVoicechatConnection] - public static async Task ResumeAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - - if (g.MusicInstance.Playstate == Playstate.Stopped) - { - await g.MusicInstance.PlaySong(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Started Playback**").Build())); - } - else - { - await g.MusicInstance.GuildConnection.ResumeAsync(); - g.MusicInstance.Playstate = Playstate.Playing; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Resumed**").Build())); - } - } - } - - [SlashCommandGroup("queue", "Queue management"), RequireUserAndBotVoicechatConnection] - public class Queue : ApplicationCommandsModule - { - [SlashCommand("show", "Show the current queue")] - public static async Task ShowQueueAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var queue = await Database.GetQueueAsync(ctx.Guild); - - try - { - var g = MikuBot.Guilds[ctx.Guild.Id]; - - if (queue.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); - return; - } - - var inter = ctx.Client.GetInteractivity(); - var songsPerPage = 0; - var currentPage = 1; - var songAmount = 0; - var totalP = queue.Count / 5; - if (queue.Count % 5 != 0) - totalP++; - var emb = new DiscordEmbedBuilder(); - List pages = new(); - - if (g.MusicInstance.RepeatMode == RepeatMode.All) - { - songAmount = g.MusicInstance.RepeatAllPos; - - foreach (var track in queue) - { - if (songsPerPage == 0 && currentPage == 1) - { - emb.WithTitle("Current Queue"); - g.GetPlayingState(out var time1, out var time2); - emb.AddField(new($"**{songAmount}.{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); - } - else - { - queue.ElementAt(songAmount).GetPlayingState(out var time); - emb.AddField(new($"**{songAmount}.{queue.ElementAt(songAmount).Track.Info.Title.Replace("*", "").Replace("|", "")}** by {queue.ElementAt(songAmount).Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{queue.ElementAt(songAmount).AddedBy}> [Link]({queue.ElementAt(songAmount).Track.Info.Uri.AbsoluteUri})")); - } - - songsPerPage++; - songAmount++; - if (songAmount == queue.Count) - songAmount = 0; - - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - - if (songAmount == g.MusicInstance.RepeatAllPos) - { - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - } - } - } - else - foreach (var track in queue) - { - if (songsPerPage == 0 && currentPage == 1) - { - emb.WithTitle("Current Queue"); - g.GetPlayingState(out var time1, out var time2); - emb.AddField(new($"**{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); - } - else - { - track.GetPlayingState(out var time); - emb.AddField(new($"**{songAmount}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{track.AddedBy}> [Link]({track.Track.Info.Uri.AbsoluteUri})")); - } - - songsPerPage++; - songAmount++; - - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - pages.Add(new(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - - if (songAmount == queue.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - pages.Add(new(embed: emb)); - emb.ClearFields(); - } - } - - if (currentPage == 1) - { - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - } - - foreach (var eP in pages.Where(x => x.Embed.Fields.All(y => y.Name == "Playback keep")).ToList()) - pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - } - } - - [SlashCommand("clear", "Clears the queue")] - public static async Task ClearQueueAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - await Database.ClearQueue(ctx.Guild); - if (g.MusicInstance.CurrentSong != null) - await Database.AddToQueue(ctx.Guild, g.MusicInstance.CurrentSong.AddedBy, g.MusicInstance.CurrentSong.Track.Encoded); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Cleared queue!**").Build())); - } - - [SlashCommand("move", "Moves a specific song within the queue")] - public static async Task MoveWithinQueueAsync( - InteractionContext ctx, - [Option("song", "Song to move within the queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string oldPosi, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string newPosi - ) - { - await ctx.DeferAsync(); - var oldPos = Convert.ToInt32(oldPosi); - var newPos = Convert.ToInt32(newPosi); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - if (oldPos < 1 || newPos < 1 || oldPos == newPos || newPos >= queue.Count) - return; - - var oldSong = queue[oldPos]; - await Database.MoveQueueItems(ctx.Guild, oldPos, newPos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Moved**:\n **{oldSong.Track.Info.Title}**\nby {oldSong.Track.Info.Author}\n from position **{oldPos}** to **{newPos}**!").Build())); - } - - [SlashCommand("remove", "Removes a name_or_url from queue")] - public static async Task RemoveFromQueueAsync( - InteractionContext ctx, - [Option("song", "Song to remove from queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string posi - ) - { - var position = Convert.ToInt32(posi); - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - var old = queue[position]; - await Database.RemoveFromQueueAsync(position, ctx.Guild); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Removed:\n{old.Track.Info.Title}**\nby {old.Track.Info.Author}").Build())); - } - } - - [SlashCommandGroup("options", "Playback Options"), RequireUserAndBotVoicechatConnection] - public class PlaybackOptions : ApplicationCommandsModule - { - [SlashCommand("repeat", "Repeat the current song or the entire queue")] - public static async Task RepeatAsync( - InteractionContext ctx, - [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] - RepeatMode mode - ) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - g.MusicInstance.RepeatMode = mode; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set repeat mode to:\n **{g.MusicInstance.RepeatMode}**").Build())); - } - - [SlashCommand("shuffle", "Play the queue in shuffle mode")] - public static async Task ShuffleAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - g.MusicInstance.ShuffleMode = g.MusicInstance.ShuffleMode == ShuffleMode.Off ? ShuffleMode.On : ShuffleMode.Off; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set shuffle mode to:\n**{g.MusicInstance.ShuffleMode}**").Build())); - } - } - - [SlashCommandGroup("info", "Playing info")] - public class PlayingInfo : ApplicationCommandsModule - { - [SlashCommand("now_playing", "Show whats currently playing")] - public static async Task ShowNowPlaylingAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.ShardId = ctx.Client.ShardId; - var eb = new DiscordEmbedBuilder(); - eb.WithTitle("Now Playing"); - eb.WithDescription("**__Current Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g); - } - - [SlashCommand("last_playing", "Show what played before")] - public static async Task ShowLastPlaylingAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - - if (!lastPlayedSongs.Any()) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); - return; - } - - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.ShardId = ctx.Client.ShardId; - var eb = new DiscordEmbedBuilder(); - eb.WithTitle("Last playing"); - eb.WithDescription("**__Previous Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g, lastPlayedSongs); - } - - [SlashCommand("last_playing_list", "Show what songs were played before")] - public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - - if (!lastPlayedSongs.Any()) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); - return; - } - - try - { - var g = MikuBot.Guilds[ctx.Guild.Id]; - - if (lastPlayedSongs.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); - return; - } - - var inter = ctx.Client.GetInteractivity(); - var songsPerPage = 0; - var currentPage = 1; - var songAmount = 0; - var totalP = lastPlayedSongs.Count / 10; - if (lastPlayedSongs.Count % 10 != 0) - totalP++; - var emb = new DiscordEmbedBuilder(); - List pages = new(); - - foreach (var track in lastPlayedSongs) - { - track.GetPlayingState(out var time); - emb.AddField(new($"{songAmount + 1}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}", $"by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}] [Link]({track.Track.Info.Uri})")); - songsPerPage++; - songAmount++; - - if (songsPerPage == 10) - { - songsPerPage = 0; - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - - if (songAmount != lastPlayedSongs.Count) - continue; - - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - } - - switch (currentPage) - { - case 1: - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - case 2 when songsPerPage == 0: - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - } - - foreach (var eP in pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - } - } - } -} \ No newline at end of file + private static readonly string[] Units = + [ + "", "ki", "Mi", "Gi" + ]; + + private static string SizeToString(long l) + { + double d = l; + var u = 0; + + while (d >= 900 && u < Units.Length - 2) + { + u++; + d /= 1024; + } + + return $"{d:#,##0.00} {Units[u]}B"; + } + + [SlashCommandGroup("base", "Base commands")] + public class Base : ApplicationCommandsModule + { + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] + public static async Task JoinAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); + var g = MikuBot.Guilds[ctx.Guild.Id]; + g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); + await g.ConditionalConnect(ctx); + g.MusicInstance.UsedChannel = ctx.Channel; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + } + + [SlashCommand("leave", "Leaves the channel")] + public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + + if (g.MusicInstance == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not in a voice channel")); + return; + } + + g.MusicInstance.Playstate = Playstate.NotPlaying; + + try + { + if (keep) + await g.MusicInstance.GuildConnection.StopAsync(); + await g.MusicInstance.GuildConnection.DisconnectAsync(); + if (!keep) + await Database.ClearQueue(ctx.Guild); + g.MusicInstance = null; + } + catch (Exception) + { } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + } + + [SlashCommand("lstats", "Displays Lavalink statistics"), ApplicationCommandRequireTeamDeveloper] + public static async Task GetLavalinkStatsAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var stats = MikuBot.LavalinkSessions[ctx.Client.ShardId].Statistics; + var sb = new StringBuilder(); + sb.Append("Lavalink resources usage statistics: ```") + .Append("Uptime: ").Append(stats.Uptime).AppendLine() + .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() + .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() + .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() + .Append("RAM Usage: ") + .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() + .Append("Audio frames (per minute): ").Append($"{stats.Frames.Sent:#,##0} sent / {stats.Frames.Nulled:#,##0} nulled / {stats.Frames.Deficit:#,##0} deficit").AppendLine() + .Append("```"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); + } + } + + [SlashCommandGroup("playback", "Playback controls")] + public class Playback : ApplicationCommandsModule + { + [SlashCommand("seek", "Seek a song"), RequireUserAndBotVoicechatConnection] + public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) + { + await ctx.DeferAsync(); + if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + if (g.MusicInstance.Playstate != Playstate.Playing && g.MusicInstance.Playstate != Playstate.Paused) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); + return; + } + + g.MusicInstance.UsedChannel = ctx.Channel; + var ts = TimeSpan.FromSeconds(position); + await g.MusicInstance.GuildConnection.SeekAsync(ts); + var pos = ts.Hours < 1 + ? ts.ToString(@"mm\:ss") + : ts.ToString(@"hh\:mm\:ss"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked {g.MusicInstance.CurrentSong.Track.Info.Title} to {pos}")); + } + + [SlashCommand("play", "Play or queue a song"), RequireUserVoicechatConnection] + public static async Task PlayAsync( + InteractionContext ctx, + [Option("song", "Song name or url to play")] string nameOrUrl = null, + [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null + ) + { + await ctx.DeferAsync(); + if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); + var g = MikuBot.Guilds[ctx.Guild.Id]; + g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); + var curq = await Database.GetQueueAsync(ctx.Guild); + + if (curq.Count != 0 && g.MusicInstance.Playstate == Playstate.NotPlaying) + { + var inter = ctx.Client.GetInteractivity(); + List buttons = [new(ButtonStyle.Success, "restore", "Restore old queue"), new(ButtonStyle.Danger, "clear", "Clear old queue")]; + var msg = await ctx.EditResponseAsync(new DiscordWebhookBuilder() + .AddEmbed(new DiscordEmbedBuilder().WithTitle("Recover Queue").WithDescription("The last time the bot disconnected the queue wasnt cleared, do you want to restore and play that old one?").Build()).AddComponents(buttons)); + var hmm = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (hmm.TimedOut) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Timed out!")); + return; + } + + if (hmm.Result.Id == "restore") + { + await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Restored").AddComponents(buttons)); + await g.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); + await g.MusicInstance.PlaySong(); + return; + } + + await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + await Database.ClearQueue(ctx.Guild); + buttons.ForEach(x => x.Disable()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cleared").AddComponents(buttons)); + } + + await g.ConditionalConnect(ctx); + + if (musicFile == null && nameOrUrl == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); + return; + } + + g.MusicInstance.UsedChannel = ctx.Channel; + nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); + var oldState = g.MusicInstance.Playstate; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Trying to play/search {nameOrUrl}...")); + var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx); + + if (q == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); + return; + } + + var emb = new DiscordEmbedBuilder(); + + if (oldState == Playstate.Playing) + { + emb.AddField(new(q.Tracks.First().Info.Title + + "[" + + (q.Tracks.First().Info.Length.Hours != 0 + ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") + : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + + "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}")); + if (q.Tracks.Count != 1) + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Added").Build()).AsEphemeral()); + } + else + { + if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) + emb.AddField(new(q.Tracks.First().Info.Title + + "[" + + (q.Tracks.First().Info.Length.Hours != 0 + ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") + : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + + "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); + else + emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + + "[" + + (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 + ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") + : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + + "]", + $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}")); + if (q.Tracks.Count != 1) + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Playing").Build()).AsEphemeral()); + } + } + + [SlashCommand("insert", "Queue a song at a specific position!"), RequireUserVoicechatConnection] + public static async Task InsertToQueueAsync( + InteractionContext ctx, + [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string posi, + [Option("song", "Song name or url to play")] string nameOrUrl = null, + [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null + ) + { + await ctx.DeferAsync(); + var pos = Convert.ToInt32(posi); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (pos < 1) + return; + + g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); + + await g.ConditionalConnect(ctx); + + if (musicFile == null && nameOrUrl == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); + return; + } + + g.MusicInstance.UsedChannel = ctx.Channel; + nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); + var oldState = g.MusicInstance.Playstate; + var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx, pos); + + if (q == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); + return; + } + + var emb = new DiscordEmbedBuilder(); + + if (oldState == Playstate.Playing) + { + emb.AddField(new(q.Tracks.First().Info.Title + + "[" + + (q.Tracks.First().Info.Length.Hours != 0 + ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") + : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + + "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}\nAt position: {pos}")); + if (q.Tracks.Count != 1) + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); + emb.WithTitle("Playing"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + else + { + if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) + emb.AddField(new(q.Tracks.First().Info.Title + + "[" + + (q.Tracks.First().Info.Length.Hours != 0 + ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") + : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + + "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); + else + emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + + "[" + + (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 + ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") + : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + + "]", + $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}At position: {pos}")); + if (q.Tracks.Count != 1) + emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); + emb.WithTitle("Added"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + } + + [SlashCommand("skip", "Skip the current song"), RequireUserAndBotVoicechatConnection] + public static async Task SkipSongAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); + var queue = await Database.GetQueueAsync(ctx.Guild); + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + g.MusicInstance.GuildConnection.TrackEnded -= Lavalink.LavalinkTrackFinish; + + if (g.MusicInstance.CurrentSong != null) + { + if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) + await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, ctx.Guild); + if (lastPlayedSongs.Count == 0) + await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); + else if (lastPlayedSongs[0]?.Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) + await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); + } + + queue = await Database.GetQueueAsync(ctx.Guild); + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + + if (queue.Count != 0) + await g.MusicInstance.PlaySong(); + else + { + g.MusicInstance.Playstate = Playstate.NotPlaying; + await g.MusicInstance.GuildConnection.StopAsync(); + } + + if (g.MusicInstance.LastSong != null) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Skipped:**\n{g.MusicInstance.LastSong.Track.Info.Title}").Build())); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Continued!**").Build())); + } + + [SlashCommand("stop", "Stop Playback"), RequireUserAndBotVoicechatConnection] + public static async Task StopAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + await Task.Run(async () => await g.MusicInstance.GuildConnection.StopAsync()); + var cmdId = ctx.Client.GetApplicationCommands().GlobalCommands.First(x => x.Name == "music").Id; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Stopped** (use to start playback again)").Build())); + } + + [SlashCommand("volume", "Change the music volume"), RequireUserAndBotVoicechatConnection] + public static async Task ModifyVolumeAsync( + InteractionContext ctx, + [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] + int vol = 100 + ) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + if (vol > 150) vol = 150; + await g.MusicInstance.GuildConnection.SetVolumeAsync(vol); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Set volume to {vol}**").Build())); + } + + [SlashCommand("pause", "Pauses playback"), RequireUserAndBotVoicechatConnection] + public static async Task PauseAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + + if (g.MusicInstance.Playstate == Playstate.Playing) + { + await g.MusicInstance.GuildConnection.PauseAsync(); + g.MusicInstance.Playstate = Playstate.Paused; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Paused**").Build())); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not playing anything right now")); + } + + [SlashCommand("resume", "Resumes paused playback"), RequireUserAndBotVoicechatConnection] + public static async Task ResumeAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + + if (g.MusicInstance.Playstate == Playstate.Stopped) + { + await g.MusicInstance.PlaySong(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Started Playback**").Build())); + } + else + { + await g.MusicInstance.GuildConnection.ResumeAsync(); + g.MusicInstance.Playstate = Playstate.Playing; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Resumed**").Build())); + } + } + } + + [SlashCommandGroup("queue", "Queue management"), RequireUserAndBotVoicechatConnection] + public class Queue : ApplicationCommandsModule + { + [SlashCommand("show", "Show the current queue")] + public static async Task ShowQueueAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var queue = await Database.GetQueueAsync(ctx.Guild); + + try + { + var g = MikuBot.Guilds[ctx.Guild.Id]; + + if (queue.Count == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); + return; + } + + var inter = ctx.Client.GetInteractivity(); + var songsPerPage = 0; + var currentPage = 1; + var songAmount = 0; + var totalP = queue.Count / 5; + if (queue.Count % 5 != 0) + totalP++; + var emb = new DiscordEmbedBuilder(); + List pages = []; + + if (g.MusicInstance.RepeatMode == RepeatMode.All) + { + songAmount = g.MusicInstance.RepeatAllPos; + + foreach (var track in queue) + { + if (songsPerPage == 0 && currentPage == 1) + { + emb.WithTitle("Current Queue"); + g.GetPlayingState(out var time1, out var time2); + emb.AddField(new( + $"**{songAmount}.{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", + $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); + } + else + { + queue.ElementAt(songAmount).GetPlayingState(out var time); + emb.AddField(new($"**{songAmount}.{queue.ElementAt(songAmount).Track.Info.Title.Replace("*", "").Replace("|", "")}** by {queue.ElementAt(songAmount).Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", + $"Requested by <@{queue.ElementAt(songAmount).AddedBy}> [Link]({queue.ElementAt(songAmount).Track.Info.Uri.AbsoluteUri})")); + } + + songsPerPage++; + songAmount++; + if (songAmount == queue.Count) + songAmount = 0; + + if (songsPerPage == 5) + { + songsPerPage = 0; + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + emb.WithFooter($"Page {currentPage}/{totalP}"); + pages.Add(new(embed: emb)); + emb.ClearFields(); + emb.WithTitle("more™"); + currentPage++; + } + + if (songAmount == g.MusicInstance.RepeatAllPos) + { + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + emb.WithFooter($"Page {currentPage}/{totalP}"); + pages.Add(new(embed: emb)); + emb.ClearFields(); + } + } + } + else + foreach (var track in queue) + { + if (songsPerPage == 0 && currentPage == 1) + { + emb.WithTitle("Current Queue"); + g.GetPlayingState(out var time1, out var time2); + emb.AddField(new($"**{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", + $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); + } + else + { + track.GetPlayingState(out var time); + emb.AddField(new($"**{songAmount}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", + $"Requested by <@{track.AddedBy}> [Link]({track.Track.Info.Uri.AbsoluteUri})")); + } + + songsPerPage++; + songAmount++; + + if (songsPerPage == 5) + { + songsPerPage = 0; + emb.WithFooter($"Page {currentPage}/{totalP}"); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + pages.Add(new(embed: emb)); + emb.ClearFields(); + emb.WithTitle("more™"); + currentPage++; + } + + if (songAmount == queue.Count) + { + emb.WithFooter($"Page {currentPage}/{totalP}"); + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + pages.Add(new(embed: emb)); + emb.ClearFields(); + } + } + + if (currentPage == 1) + { + emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); + return; + } + + if (currentPage == 2 && songsPerPage == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); + return; + } + + foreach (var eP in pages.Where(x => x.Embed.Fields.All(y => y.Name == "Playback keep")).ToList()) + pages.Remove(eP); + await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + } + } + + [SlashCommand("clear", "Clears the queue")] + public static async Task ClearQueueAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + await Database.ClearQueue(ctx.Guild); + if (g.MusicInstance.CurrentSong != null) + await Database.AddToQueue(ctx.Guild, g.MusicInstance.CurrentSong.AddedBy, g.MusicInstance.CurrentSong.Track.Encoded); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Cleared queue!**").Build())); + } + + [SlashCommand("move", "Moves a specific song within the queue")] + public static async Task MoveWithinQueueAsync( + InteractionContext ctx, + [Option("song", "Song to move within the queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string oldPosi, + [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string newPosi + ) + { + await ctx.DeferAsync(); + var oldPos = Convert.ToInt32(oldPosi); + var newPos = Convert.ToInt32(newPosi); + var g = MikuBot.Guilds[ctx.Guild.Id]; + var queue = await Database.GetQueueAsync(ctx.Guild); + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + if (oldPos < 1 || newPos < 1 || oldPos == newPos || newPos >= queue.Count) + return; + + var oldSong = queue[oldPos]; + await Database.MoveQueueItems(ctx.Guild, oldPos, newPos); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() + .WithDescription($"**Moved**:\n **{oldSong.Track.Info.Title}**\nby {oldSong.Track.Info.Author}\n from position **{oldPos}** to **{newPos}**!").Build())); + } + + [SlashCommand("remove", "Removes a name_or_url from queue")] + public static async Task RemoveFromQueueAsync( + InteractionContext ctx, + [Option("song", "Song to remove from queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] + string posi + ) + { + var position = Convert.ToInt32(posi); + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + var queue = await Database.GetQueueAsync(ctx.Guild); + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + var old = queue[position]; + await Database.RemoveFromQueueAsync(position, ctx.Guild); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Removed:\n{old.Track.Info.Title}**\nby {old.Track.Info.Author}").Build())); + } + } + + [SlashCommandGroup("options", "Playback Options"), RequireUserAndBotVoicechatConnection] + public class PlaybackOptions : ApplicationCommandsModule + { + [SlashCommand("repeat", "Repeat the current song or the entire queue")] + public static async Task RepeatAsync( + InteractionContext ctx, + [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] + RepeatMode mode + ) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + g.MusicInstance.RepeatMode = mode; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set repeat mode to:\n **{g.MusicInstance.RepeatMode}**").Build())); + } + + [SlashCommand("shuffle", "Play the queue in shuffle mode")] + public static async Task ShuffleAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + if (await g.IsNotConnected(ctx)) + return; + + g.MusicInstance.UsedChannel = ctx.Channel; + g.MusicInstance.ShuffleMode = g.MusicInstance.ShuffleMode == ShuffleMode.Off + ? ShuffleMode.On + : ShuffleMode.Off; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set shuffle mode to:\n**{g.MusicInstance.ShuffleMode}**").Build())); + } + } + + [SlashCommandGroup("info", "Playing info")] + public class PlayingInfo : ApplicationCommandsModule + { + [SlashCommand("now_playing", "Show whats currently playing")] + public static async Task ShowNowPlaylingAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var g = MikuBot.Guilds[ctx.Guild.Id]; + g.ShardId = ctx.Client.ShardId; + var eb = new DiscordEmbedBuilder(); + eb.WithTitle("Now Playing"); + eb.WithDescription("**__Current Song:__**"); + await ctx.SendPlayingInformationAsync(eb, g); + } + + [SlashCommand("last_playing", "Show what played before")] + public static async Task ShowLastPlaylingAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); + + if (!lastPlayedSongs.Any()) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); + return; + } + + var g = MikuBot.Guilds[ctx.Guild.Id]; + g.ShardId = ctx.Client.ShardId; + var eb = new DiscordEmbedBuilder(); + eb.WithTitle("Last playing"); + eb.WithDescription("**__Previous Song:__**"); + await ctx.SendPlayingInformationAsync(eb, g, lastPlayedSongs); + } + + [SlashCommand("last_playing_list", "Show what songs were played before")] + public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) + { + await ctx.DeferAsync(); + var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); + + if (!lastPlayedSongs.Any()) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); + return; + } + + try + { + var g = MikuBot.Guilds[ctx.Guild.Id]; + + if (lastPlayedSongs.Count == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); + return; + } + + var inter = ctx.Client.GetInteractivity(); + var songsPerPage = 0; + var currentPage = 1; + var songAmount = 0; + var totalP = lastPlayedSongs.Count / 10; + if (lastPlayedSongs.Count % 10 != 0) + totalP++; + var emb = new DiscordEmbedBuilder(); + List pages = []; + + foreach (var track in lastPlayedSongs) + { + track.GetPlayingState(out var time); + emb.AddField(new($"{songAmount + 1}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}", $"by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}] [Link]({track.Track.Info.Uri})")); + songsPerPage++; + songAmount++; + + if (songsPerPage == 10) + { + songsPerPage = 0; + emb.WithTitle("Last played songs in this server:\n"); + emb.WithFooter($"Page {currentPage}/{totalP}"); + pages.Add(new(embed: emb)); + emb.ClearFields(); + emb.WithTitle("more™"); + currentPage++; + } + + if (songAmount != lastPlayedSongs.Count) + continue; + + emb.WithTitle("Last played songs in this server:\n"); + emb.WithFooter($"Page {currentPage}/{totalP}"); + pages.Add(new(embed: emb)); + emb.ClearFields(); + } + + switch (currentPage) + { + case 1: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); + return; + case 2 when songsPerPage == 0: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); + return; + } + + foreach (var eP in pages.Where(x => x.Embed.Fields.Count == 0).ToList()) + pages.Remove(eP); + await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + } + } + } +} diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index 458b3749..249290f5 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -1,3 +1,5 @@ +using System.Threading.Tasks; + using DisCatSharp.CommandsNext; using DisCatSharp.CommandsNext.Attributes; using DisCatSharp.Entities; @@ -5,109 +7,107 @@ using MikuSharp.Attributes; using MikuSharp.Utilities; -using System.Threading.Tasks; - namespace MikuSharp.Commands; [RequireNsfw, NotStaff] public class Nsfw : BaseCommandModule { - [Command("4k"), Description("lewd")] - public async Task FourK(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("anal"), Description("lewd")] - public async Task Anal(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("ass"), Description("lewd")] - public async Task Ass(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("gonewild"), Description("lewd")] - public async Task Gonewild(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdkitsune"), Description("lewd")] - public async Task LewdKitsune(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdneko"), Description("lewd")] - public async Task LewdNeko(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("porngif"), Description("lewd")] - public async Task PornGif(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("pussy"), Description("lewd")] - public async Task Pussy(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("thighs"), Aliases("thigh"), Description("lewd")] - public async Task Thighs(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } -} \ No newline at end of file + [Command("4k"), Description("lewd")] + public async Task FourK(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("anal"), Description("lewd")] + public async Task Anal(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("ass"), Description("lewd")] + public async Task Ass(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("gonewild"), Description("lewd")] + public async Task Gonewild(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdkitsune"), Description("lewd")] + public async Task LewdKitsune(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdneko"), Description("lewd")] + public async Task LewdNeko(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("porngif"), Description("lewd")] + public async Task PornGif(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("pussy"), Description("lewd")] + public async Task Pussy(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("thighs"), Aliases("thigh"), Description("lewd")] + public async Task Thighs(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.WithEmbed(d.Embed); + await ctx.RespondAsync(builder); + } +} diff --git a/MikuSharp/Commands/Playlist.cs b/MikuSharp/Commands/Playlist.cs index 2e308b63..4037ebce 100644 --- a/MikuSharp/Commands/Playlist.cs +++ b/MikuSharp/Commands/Playlist.cs @@ -23,474 +23,475 @@ namespace MikuSharp.Commands; [SlashCommandGroup("playlists", "Manage your playlists", dmPermission: false)] public class Playlists : ApplicationCommandsModule { - [SlashCommandGroup("new", "Playlist creation")] - public class PlaylistCreation : ApplicationCommandsModule - { - [SlashCommand("copy_queue", "Copy the current queue to a playlist!")] - [RequireUserAndBotVoicechatConnection] - public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name - ) - { - await ctx.DeferAsync(true); - var q = await Database.GetQueueAsync(ctx.Guild); - if (q.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Nothing in queue")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Copy Queue").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.AddPlaylist(name, ctx.Member.Id); - foreach (var e in q) - { - await PlaylistDB.AddEntry(name, ctx.Member.Id, e.track.TrackString); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Queue Copy").WithDescription("Queue was saved to new playlist -> " + name).Build())); - } + [SlashCommandGroup("new", "Playlist creation")] + public class PlaylistCreation : ApplicationCommandsModule + { + [SlashCommand("copy_queue", "Copy the current queue to a playlist!")] + [RequireUserAndBotVoicechatConnection] + public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, + [Option("name", "Name of new playlist")] string name + ) + { + await ctx.DeferAsync(true); + var q = await Database.GetQueueAsync(ctx.Guild); + if (q.Count == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Nothing in queue")); + return; + } + var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (pls.Any(x => x == name)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Copy Queue").WithDescription("**Error** You already have a playlist with that playlist!").Build())); + return; + } + await PlaylistDB.AddPlaylist(name, ctx.Member.Id); + foreach (var e in q) + { + await PlaylistDB.AddEntry(name, ctx.Member.Id, e.track.TrackString); + } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Queue Copy").WithDescription("Queue was saved to new playlist -> " + name).Build())); + } - [SlashCommand("create", "Create a playlist")] - public static async Task CreatePlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name - ) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.AddPlaylist(name, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("New Playlist was created -> " + name).Build())); - } + [SlashCommand("create", "Create a playlist")] + public static async Task CreatePlaylistAsync(InteractionContext ctx, + [Option("name", "Name of new playlist")] string name + ) + { + await ctx.DeferAsync(true); + var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (pls.Any(x => x == name)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); + return; + } + await PlaylistDB.AddPlaylist(name, ctx.Member.Id); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("New Playlist was created -> " + name).Build())); + } - [SlashCommand("create_fixed", "Create a fixed playlist (linked to a Youtube or Soundcloud playlist)")] - public static async Task CreateFixedPlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name, - [Option("link", "Link to playlist")] string link - ) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - LavalinkLoadResult s = null; - try - { - s = await MikuBot.LavalinkNodeConnections[ctx.Client.ShardId].Rest.GetTracksAsync(new Uri(link)); - } - catch - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); - return; - } - if (s.LoadResultType != LavalinkLoadResultType.PlaylistLoaded) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); - return; - } - if (link.Contains("youtu") && !link.Contains("soundcloud")) - { - await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Youtube, link); - } - else - { - await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Soundcloud, link); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription($"Fixed playlist created with playlist -> {name} and {s.Tracks.Count} Songs!").Build())); - } - } + [SlashCommand("create_fixed", "Create a fixed playlist (linked to a Youtube or Soundcloud playlist)")] + public static async Task CreateFixedPlaylistAsync(InteractionContext ctx, + [Option("name", "Name of new playlist")] string name, + [Option("link", "Link to playlist")] string link + ) + { + await ctx.DeferAsync(true); + var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (pls.Any(x => x == name)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); + return; + } + LavalinkLoadResult s = null; + try + { + s = await MikuBot.LavalinkNodeConnections[ctx.Client.ShardId].Rest.GetTracksAsync(new Uri(link)); + } + catch + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); + return; + } + if (s.LoadResultType != LavalinkLoadResultType.PlaylistLoaded) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); + return; + } + if (link.Contains("youtu") && !link.Contains("soundcloud")) + { + await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Youtube, link); + } + else + { + await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Soundcloud, link); + } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription($"Fixed playlist created with playlist -> {name} and {s.Tracks.Count} Songs!").Build())); + } + } - [SlashCommandGroup("manage", "Playlist management")] - public class PlaylistManagement : ApplicationCommandsModule - { - [SlashCommand("list", "List all your playlists")] - public static async Task ListPlaylistsAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - if (pls.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You dont have any playlists")); - return; - } - //ctx.Client.Logger.LogDebug(pls.Count.ToString()); - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = pls.Count / 5; - if ((pls.Count % 5) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in pls) - { - //ctx.Client.Logger.LogDebug(Track.Value == null); - //ctx.Client.Logger.LogDebug(Track.Key); - int songam = 0; - var ent = await Track.Value.GetEntries(); - songam = ent.Count; - string sub = ""; - if (Track.Value.ExternalService == ExtService.None) - { - sub = $"Created on: {Track.Value.Creation}\n" + - $"Last modified on: {Track.Value.Modify}"; - } - else - { - sub = $"Created on: {Track.Value.Creation}\n" + - $"{Track.Value.ExternalService} [Link]({Track.Value.Url})"; - } - emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.Key}** ({songam} Songs)", sub)); - songsPerPage++; - songAmount++; - emb.WithTitle($"List Playlists"); - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - currentPage++; - } - if (songAmount == pls.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - { - Pages.Remove(eP); - } - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } + [SlashCommandGroup("manage", "Playlist management")] + public class PlaylistManagement : ApplicationCommandsModule + { + [SlashCommand("list", "List all your playlists")] + public static async Task ListPlaylistsAsync(InteractionContext ctx) + { + await ctx.DeferAsync(true); + var pls = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); + if (pls.Count == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You dont have any playlists")); + return; + } + //ctx.Client.Logger.LogDebug(pls.Count.ToString()); + var inter = ctx.Client.GetInteractivity(); + int songsPerPage = 0; + int currentPage = 1; + int songAmount = 0; + int totalP = pls.Count / 5; + if ((pls.Count % 5) != 0) totalP++; + var emb = new DiscordEmbedBuilder(); + List Pages = new(); + foreach (var Track in pls) + { + //ctx.Client.Logger.LogDebug(Track.Value == null); + //ctx.Client.Logger.LogDebug(Track.Key); + int songam = 0; + var ent = await Track.Value.GetEntries(); + songam = ent.Count; + string sub = ""; + if (Track.Value.ExternalService == ExtService.None) + { + sub = $"Created on: {Track.Value.Creation}\n" + + $"Last modified on: {Track.Value.Modify}"; + } + else + { + sub = $"Created on: {Track.Value.Creation}\n" + + $"{Track.Value.ExternalService} [Link]({Track.Value.Url})"; + } + emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.Key}** ({songam} Songs)", sub)); + songsPerPage++; + songAmount++; + emb.WithTitle($"List Playlists"); + if (songsPerPage == 5) + { + songsPerPage = 0; + emb.WithFooter($"Page {currentPage}/{totalP}"); + Pages.Add(new Page(embed: emb)); + emb.ClearFields(); + currentPage++; + } + if (songAmount == pls.Count) + { + emb.WithFooter($"Page {currentPage}/{totalP}"); + Pages.Add(new Page(embed: emb)); + emb.ClearFields(); + } + } + if (currentPage == 1) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); + return; + } + else if (currentPage == 2 && songsPerPage == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); + return; + } + foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) + { + Pages.Remove(eP); + } + await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); + } - [SlashCommand("show", "Show the contents of a playlist")] - public static async Task ShowPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to show", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!pls.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Show Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var q = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var queue = await q.GetEntries(); - if (queue.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist empty!")); - return; - } - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = queue.Count / 5; - if ((queue.Count % 5) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in queue) - { - string time = ""; - if (Track.track.Length.Hours < 1) time = Track.track.Length.ToString(@"mm\:ss"); - else time = Track.track.Length.ToString(@"hh\:mm\:ss"); - emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.track.Title.Replace("*", "").Replace("|", "")}** by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Added on {Track.additionDate} [Link]({Track.track.Uri.AbsoluteUri})")); - songsPerPage++; - songAmount++; - emb.WithTitle($"Songs in {playlist}"); - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - currentPage++; - } - if (songAmount == queue.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - { - Pages.Remove(eP); - } - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } + [SlashCommand("show", "Show the contents of a playlist")] + public static async Task ShowPlaylistAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to show", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!pls.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Show Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + var q = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + var queue = await q.GetEntries(); + if (queue.Count == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist empty!")); + return; + } + var inter = ctx.Client.GetInteractivity(); + int songsPerPage = 0; + int currentPage = 1; + int songAmount = 0; + int totalP = queue.Count / 5; + if ((queue.Count % 5) != 0) totalP++; + var emb = new DiscordEmbedBuilder(); + List Pages = new(); + foreach (var Track in queue) + { + string time = ""; + if (Track.track.Length.Hours < 1) time = Track.track.Length.ToString(@"mm\:ss"); + else time = Track.track.Length.ToString(@"hh\:mm\:ss"); + emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.track.Title.Replace("*", "").Replace("|", "")}** by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}]", + $"Added on {Track.additionDate} [Link]({Track.track.Uri.AbsoluteUri})")); + songsPerPage++; + songAmount++; + emb.WithTitle($"Songs in {playlist}"); + if (songsPerPage == 5) + { + songsPerPage = 0; + emb.WithFooter($"Page {currentPage}/{totalP}"); + Pages.Add(new Page(embed: emb)); + emb.ClearFields(); + currentPage++; + } + if (songAmount == queue.Count) + { + emb.WithFooter($"Page {currentPage}/{totalP}"); + Pages.Add(new Page(embed: emb)); + emb.ClearFields(); + } + } + if (currentPage == 1) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); + return; + } + else if (currentPage == 2 && songsPerPage == 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); + return; + } + foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) + { + Pages.Remove(eP); + } + await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); + } - [SlashCommand("delete", "Delete a playlist")] - public static async Task DeletePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to delete", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!pls.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.RemovePlaylist(playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("Deleted playlist -> " + playlist).Build())); - } + [SlashCommand("delete", "Delete a playlist")] + public static async Task DeletePlaylistAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to delete", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!pls.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + await PlaylistDB.RemovePlaylist(playlist, ctx.Member.Id); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("Deleted playlist -> " + playlist).Build())); + } - [SlashCommand("rename", "Rename a playlist")] - public static async Task RenamePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to rename", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("name", "New name for playlist")] string name - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription("**Error** You dont have a playlist with that name!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - await pls.GetEntries(); + [SlashCommand("rename", "Rename a playlist")] + public static async Task RenamePlaylistAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to rename", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("name", "New name for playlist")] string name + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!p.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription("**Error** You dont have a playlist with that name!").Build())); + return; + } + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + await pls.GetEntries(); - await PlaylistDB.RenameList(playlist, ctx.Member.Id, name); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription($"Renamed Playlist to {playlist} -> {name}!").Build())); - } + await PlaylistDB.RenameList(playlist, ctx.Member.Id, name); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription($"Renamed Playlist to {playlist} -> {name}!").Build())); + } - [SlashCommand("clear", "Clear all entries from a playlist")] - public static async Task ClearPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to clear", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.ClearList(playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription($"Cleared all songs from playlist -> {playlist}!").Build())); - } + [SlashCommand("clear", "Clear all entries from a playlist")] + public static async Task ClearPlaylistAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to clear", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!p.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + await PlaylistDB.ClearList(playlist, ctx.Member.Id); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription($"Cleared all songs from playlist -> {playlist}!").Build())); + } - [SlashCommand("play", "Play a playlist/Add the songs to the queue")] - [RequireUserVoicechatConnection] - public static async Task PlayPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to play", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - var ps = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - if (!ps.Any(x => x.Key == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var p = await pls.GetEntries(); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - { - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); - } - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); - await g.ConditionalConnect(ctx); - g.musicInstance.usedChannel = ctx.Channel; - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p); - if (g.musicInstance.guildConnection.IsConnected && (g.musicInstance.playstate == Playstate.NotPlaying || g.musicInstance.playstate == Playstate.Stopped)) - await g.musicInstance.PlaySong(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription($"Playing playlist/Added to queue!").Build())); - } - } + [SlashCommand("play", "Play a playlist/Add the songs to the queue")] + [RequireUserVoicechatConnection] + public static async Task PlayPlaylistAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to play", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist + ) + { + await ctx.DeferAsync(true); + var ps = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); + if (!ps.Any(x => x.Key == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + var p = await pls.GetEntries(); + if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) + { + MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); + } + var g = MikuBot.Guilds[ctx.Guild.Id]; + g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); + await g.ConditionalConnect(ctx); + g.musicInstance.usedChannel = ctx.Channel; + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p); + if (g.musicInstance.guildConnection.IsConnected && (g.musicInstance.playstate == Playstate.NotPlaying || g.musicInstance.playstate == Playstate.Stopped)) + await g.musicInstance.PlaySong(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription($"Playing playlist/Added to queue!").Build())); + } + } - [SlashCommandGroup("song", "Song management")] - public class SongOperations : ApplicationCommandsModule - { - [SlashCommand("add", "Add a song to a playlist")] - public static async Task AddSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("url_or_search", "Url or name of song to add")] string song - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); - return; - } - TrackResult got = await PlaylistDB.GetSong(song, ctx); - if (got == null) - return; - await PlaylistDB.AddEntry(playlist, ctx.Member.Id, got.Tracks); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription($"Added entry -> {got.Tracks[0].Title}!").Build())); - } + [SlashCommandGroup("song", "Song management")] + public class SongOperations : ApplicationCommandsModule + { + [SlashCommand("add", "Add a song to a playlist")] + public static async Task AddSongAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("url_or_search", "Url or name of song to add")] string song + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!p.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + if (pls.ExternalService != ExtService.None) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); + return; + } + TrackResult got = await PlaylistDB.GetSong(song, ctx); + if (got == null) + return; + await PlaylistDB.AddEntry(playlist, ctx.Member.Id, got.Tracks); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription($"Added entry -> {got.Tracks[0].Title}!").Build())); + } - [SlashCommand("insert_at", "Insert a song into a playlist at a choosen position")] - public static async Task InsertAtAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("position", "Position to insert song at" ,true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi, - [Option("url_or_search", "Url or name of song to add")] string song - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pos = Convert.ToInt32(posi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); - return; - } - TrackResult got = await PlaylistDB.GetSong(song, ctx); - if (got == null) - return; - got.Tracks.Reverse(); - await PlaylistDB.InsertEntry(ctx.Guild, playlist, ctx.Member.Id, got.Tracks, pos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription($"Inserted entry -> {got.Tracks[0].Title} at {pos}!").Build())); - } + [SlashCommand("insert_at", "Insert a song into a playlist at a choosen position")] + public static async Task InsertAtAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("position", "Position to insert song at" ,true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi, + [Option("url_or_search", "Url or name of song to add")] string song + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var pos = Convert.ToInt32(posi); + var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!p.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + if (pls.ExternalService != ExtService.None) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); + return; + } + TrackResult got = await PlaylistDB.GetSong(song, ctx); + if (got == null) + return; + got.Tracks.Reverse(); + await PlaylistDB.InsertEntry(ctx.Guild, playlist, ctx.Member.Id, got.Tracks, pos); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription($"Inserted entry -> {got.Tracks[0].Title} at {pos}!").Build())); + } - [SlashCommand("move", "Move a song to a specific position in your playlist")] - public static async Task MoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string oldposi, - [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string newposi - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var oldpos = Convert.ToInt32(oldposi); - var newpos = Convert.ToInt32(newposi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant move songs!").Build())); - return; - } - var e = await pls.GetEntries(); - if (e[newpos] == null | e[oldpos] == null) - return; - await PlaylistDB.MoveListItems(ctx.Guild, playlist, ctx.Member.Id, oldpos, newpos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Move Song").WithDescription($"Moved entry -> {e[oldpos].track.Title} to position {newpos}!").Build())); - } + [SlashCommand("move", "Move a song to a specific position in your playlist")] + public static async Task MoveSongAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string oldposi, + [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string newposi + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var oldpos = Convert.ToInt32(oldposi); + var newpos = Convert.ToInt32(newposi); + var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!p.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + if (pls.ExternalService != ExtService.None) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant move songs!").Build())); + return; + } + var e = await pls.GetEntries(); + if (e[newpos] == null | e[oldpos] == null) + return; + await PlaylistDB.MoveListItems(ctx.Guild, playlist, ctx.Member.Id, oldpos, newpos); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Move Song").WithDescription($"Moved entry -> {e[oldpos].track.Title} to position {newpos}!").Build())); + } - [SlashCommand("remove", "Remove a song from a playlist")] - public static async Task RemoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pos = Convert.ToInt32(posi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var ents = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var en = await ents.GetEntries(); - await PlaylistDB.RemoveFromList(ctx.Guild, pos, playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription($"Entry removed! -> {en[pos].track.Title}").Build())); - } - } + [SlashCommand("remove", "Remove a song from a playlist")] + public static async Task RemoveSongAsync(InteractionContext ctx, + [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, + [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi + ) + { + await ctx.DeferAsync(true); + if (CheckError(playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); + return; + } + var pos = Convert.ToInt32(posi); + var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (!p.Any(x => x == playlist)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); + return; + } + var ents = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + var en = await ents.GetEntries(); + await PlaylistDB.RemoveFromList(ctx.Guild, pos, playlist, ctx.Member.Id); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription($"Entry removed! -> {en[pos].track.Title}").Build())); + } + } - public static bool CheckError(string playlist) - { - if (playlist == "error") - return true; - else - return false; - } + public static bool CheckError(string playlist) + { + if (playlist == "error") + return true; + else + return false; + } } */ + diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index effa1a17..f7a05b1f 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -1,4 +1,9 @@ -using DisCatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; @@ -14,209 +19,214 @@ using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Commands; [SlashCommandGroup("utility", "Utilities")] internal class Utility : ApplicationCommandsModule { - [SlashCommandGroup("am", "Anime & Mange")] - internal class AnimeMangaUtility : ApplicationCommandsModule - { - [SlashCommand("anime_search", "Search for an anime")] - public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - try - { - var ine = ctx.Client.GetInteractivity(); - var a = await Anime.GetAnimeAsync(searchQuery); - var emb = new DiscordEmbedBuilder(); - List res = new(); - List ress = new(); - foreach (var aa in a.Data) - { - emb.WithColor(new(0212255)); - emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis.Length != 0) - emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype.Length != 0) - emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.EpisodeCount != null) - emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); - if (aa.Attributes.EpisodeLength != null) - emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); - if (aa.Attributes.StartDate != null) - emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) - emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) - emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) - emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); - emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); - if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); - res.Add(emb); - emb = new(); - } - - res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); - var i = 1; - foreach (var aa in res) - { - aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new(embed: aa)); - i++; - } - - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Anime found!")); - } - } - - [SlashCommand("manga_search", "Search for an manga")] - public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - try - { - var ine = ctx.Client.GetInteractivity(); - var a = await Manga.GetMangaAsync(searchQuery); - var emb = new DiscordEmbedBuilder(); - List res = new(); - List ress = new(); - foreach (var aa in a.Data) - { - emb.WithColor(new(0212255)); - emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis != null) - emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype != null) - emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.StartDate != null) - emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) - emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) - emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) - emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); - if (aa.Attributes.CoverImage?.Small != null) - emb.WithThumbnail(aa.Attributes.CoverImage.Small); - emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - res.Add(emb); - emb = new(); - } - - res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); - var i = 1; - foreach (var aa in res) - { - aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new(embed: aa)); - i++; - } - - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Manga found!")); - } - } - } - - [SlashCommandGroup("discord", "Discord Utilities")] - internal class DiscordUtility : ApplicationCommandsModule - { - [SlashCommand("avatar", "Get the avatar of someone or yourself")] - public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null ? user.AvatarUrl : ctx.User.AvatarUrl).Build())); - - [SlashCommand("server_info", "Get information about the server")] - public static async Task GuildInfoAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (ctx.Guild == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); - return; - } - - var members = await ctx.Guild.GetAllMembersAsync(); - var bots = members.Count(x => x.IsBot); - - var emb = new DiscordEmbedBuilder(); - emb.WithTitle(ctx.Guild.Name); - emb.WithColor(new(0212255)); - emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); - emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); - emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); - emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); - emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("user_info", "Get information about a user")] - public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (user == null) - user = ctx.User; - - DiscordMember? member = null; - if (ctx.Guild != null) - try - { - member = await user.ConvertToMember(ctx.Guild); - } - catch (NotFoundException) - { } - - var emb = new DiscordEmbedBuilder(); - emb.WithColor(new(0212255)); - emb.WithTitle("User Info"); - emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); - if (member != null) - if (member.DisplayName != user.Username) - emb.AddField(new("Nickname", $"{member.DisplayName}", true)); - emb.AddField(new("ID", $"{user.Id}", true)); - emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); - if (member != null) - emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); - emb.WithThumbnail(user.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public static async Task EmojiListAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var wat = "You have to execute this command in a server!"; - if (ctx.Guild != null && ctx.Guild.Emojis.Any()) - { - wat = "**Emojies:** "; - foreach (var em in ctx.Guild.Emojis.Values) - wat += em + " "; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); - } - } -} \ No newline at end of file + [SlashCommandGroup("am", "Anime & Mange")] + internal class AnimeMangaUtility : ApplicationCommandsModule + { + [SlashCommand("anime_search", "Search for an anime")] + public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + try + { + var ine = ctx.Client.GetInteractivity(); + var a = await Anime.GetAnimeAsync(searchQuery); + var emb = new DiscordEmbedBuilder(); + List res = []; + List ress = []; + + foreach (var aa in a.Data) + { + emb.WithColor(new(0212255)); + emb.WithTitle(aa.Attributes.Titles.EnJp); + if (aa.Attributes.Synopsis.Length != 0) + emb.WithDescription(aa.Attributes.Synopsis); + if (aa.Attributes.Subtype.Length != 0) + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); + if (aa.Attributes.EpisodeCount != null) + emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); + if (aa.Attributes.EpisodeLength != null) + emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); + if (aa.Attributes.StartDate != null) + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); + if (aa.Attributes.EndDate != null) + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); + if (aa.Attributes.AgeRating != null) + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); + if (aa.Attributes.AverageRating != null) + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); + if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); + res.Add(emb); + emb = new(); + } + + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); + var i = 1; + + foreach (var aa in res) + { + aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + ress.Add(new(embed: aa)); + i++; + } + + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Anime found!")); + } + } + + [SlashCommand("manga_search", "Search for an manga")] + public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + try + { + var ine = ctx.Client.GetInteractivity(); + var a = await Manga.GetMangaAsync(searchQuery); + var emb = new DiscordEmbedBuilder(); + List res = []; + List ress = []; + + foreach (var aa in a.Data) + { + emb.WithColor(new(0212255)); + emb.WithTitle(aa.Attributes.Titles.EnJp); + if (aa.Attributes.Synopsis != null) + emb.WithDescription(aa.Attributes.Synopsis); + if (aa.Attributes.Subtype != null) + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); + if (aa.Attributes.StartDate != null) + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); + if (aa.Attributes.EndDate != null) + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); + if (aa.Attributes.AgeRating != null) + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); + if (aa.Attributes.AverageRating != null) + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + if (aa.Attributes.CoverImage?.Small != null) + emb.WithThumbnail(aa.Attributes.CoverImage.Small); + emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + res.Add(emb); + emb = new(); + } + + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); + var i = 1; + + foreach (var aa in res) + { + aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + ress.Add(new(embed: aa)); + i++; + } + + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Manga found!")); + } + } + } + + [SlashCommandGroup("discord", "Discord Utilities")] + internal class DiscordUtility : ApplicationCommandsModule + { + [SlashCommand("avatar", "Get the avatar of someone or yourself")] + public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null + ? user.AvatarUrl + : ctx.User.AvatarUrl).Build())); + + [SlashCommand("server_info", "Get information about the server")] + public static async Task GuildInfoAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + if (ctx.Guild == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + var members = await ctx.Guild.GetAllMembersAsync(); + var bots = members.Count(x => x.IsBot); + + var emb = new DiscordEmbedBuilder(); + emb.WithTitle(ctx.Guild.Name); + emb.WithColor(new(0212255)); + emb.WithThumbnail(ctx.Guild.IconUrl); + emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); + emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); + emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); + emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); + emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); + emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("user_info", "Get information about a user")] + public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + if (user == null) + user = ctx.User; + + DiscordMember? member = null; + + if (ctx.Guild != null) + try + { + member = await user.ConvertToMember(ctx.Guild); + } + catch (NotFoundException) + { } + + var emb = new DiscordEmbedBuilder(); + emb.WithColor(new(0212255)); + emb.WithTitle("User Info"); + emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); + if (member != null) + if (member.DisplayName != user.Username) + emb.AddField(new("Nickname", $"{member.DisplayName}", true)); + emb.AddField(new("ID", $"{user.Id}", true)); + emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); + if (member != null) + emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); + emb.WithThumbnail(user.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("emojilist", "Lists all custom emoji on this server")] + public static async Task EmojiListAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var wat = "You have to execute this command in a server!"; + + if (ctx.Guild != null && ctx.Guild.Emojis.Any()) + { + wat = "**Emojies:** "; + foreach (var em in ctx.Guild.Emojis.Values) + wat += em + " "; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); + } + } +} diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 9fdd756d..94c5a153 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -1,4 +1,7 @@ -using DisCatSharp.ApplicationCommands; +using System.IO; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; @@ -6,273 +9,266 @@ using HeyRed.Mime; -using Microsoft.Extensions.Logging; - +using MikuSharp.Entities; using MikuSharp.Utilities; using Newtonsoft.Json; -using System; -using System.IO; -using System.Threading.Tasks; - namespace MikuSharp.Commands; -[SlashCommandGroup("weeb", "Weeb Stuff!", false, new[] -{ - InteractionContextType.Guild, InteractionContextType.PrivateChannel -}, new[] -{ - ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall -})] +[SlashCommandGroup("weeb", "Weeb Stuff!", false, [ + InteractionContextType.Guild, InteractionContextType.PrivateChannel +], [ + ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall +])] internal class Weeb : ApplicationCommandsModule { - [SlashCommand("awooify", "Awooify your or someones avatar!")] - public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) - { - await ctx.DeferAsync(false); - var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); - } + [SlashCommand("awooify", "Awooify your or someones avatar!")] + public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) + { + await ctx.DeferAsync(false); + var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); + } - [SlashCommand("diva", "Radnom PJD Loading image")] - public static async Task DivaPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); + [SlashCommand("diva", "Radnom PJD Loading image")] + public static async Task DivaPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("gumi", "Random Gumi image")] - public static async Task GumiPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("gumi", "Random Gumi image")] + public static async Task GumiPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("kaito", "Random Kaito image")] - public static async Task KaitoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("kaito", "Random Kaito image")] + public static async Task KaitoPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("len", "Random Len image")] - public static async Task KLenPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("len", "Random Len image")] + public static async Task KLenPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("luka", "Random Luka image")] - public static async Task LukaPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("luka", "Random Luka image")] + public static async Task LukaPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("meiko", "Random Meiko image")] - public static async Task MeikoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("meiko", "Random Meiko image")] + public static async Task MeikoPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("miku", "Random Miku image")] - public static async Task HMikuPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("miku", "Random Miku image")] + public static async Task HMikuPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("neko", "Get a random neko image")] - public static async Task Cat(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); + [SlashCommand("neko", "Get a random neko image")] + public static async Task Cat(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("rin", "Random Rin image")] - public static async Task KRinPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("rin", "Random Rin image")] + public static async Task KRinPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("teto", "Random Teto image")] - public static async Task KTetoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("teto", "Random Teto image")] + public static async Task KTetoPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } -} \ No newline at end of file + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } +} diff --git a/MikuSharp/Entities/BiliJson.cs b/MikuSharp/Entities/BiliJson.cs index 427f4f2b..ed822829 100644 --- a/MikuSharp/Entities/BiliJson.cs +++ b/MikuSharp/Entities/BiliJson.cs @@ -4,27 +4,27 @@ namespace MikuSharp.Entities; public class Durl2 { - public int Order { get; set; } - public int Length { get; set; } - public int Size { get; set; } - public string Ahead { get; set; } - public string Vhead { get; set; } - public string Url { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } } public class BiliJson { - public string From { get; set; } - public string Result { get; set; } - public int Quality { get; set; } - public string Format { get; set; } - public int Timelength { get; set; } - public string AcceptFormat { get; set; } - public List AcceptDescription { get; set; } - public List AcceptQuality { get; set; } - public int VideoCodecid { get; set; } - public bool VideoProject { get; set; } - public string SeekParam { get; set; } - public string SeekType { get; set; } - public List Durl { get; set; } -} \ No newline at end of file + public string From { get; set; } + public string Result { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public bool VideoProject { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } +} diff --git a/MikuSharp/Entities/BiliPlayInfo.cs b/MikuSharp/Entities/BiliPlayInfo.cs index 7b304487..85b99891 100644 --- a/MikuSharp/Entities/BiliPlayInfo.cs +++ b/MikuSharp/Entities/BiliPlayInfo.cs @@ -4,41 +4,41 @@ namespace MikuSharp.Entities; public class BiliPlayinfo { - public int Code { get; set; } - public string Message { get; set; } - public int Ttl { get; set; } - public Data Data { get; set; } - public string Session { get; set; } - public VideoFrame VideoFrame { get; set; } + public int Code { get; set; } + public string Message { get; set; } + public int Ttl { get; set; } + public Data Data { get; set; } + public string Session { get; set; } + public VideoFrame VideoFrame { get; set; } } public class Durl { - public int Order { get; set; } - public int Length { get; set; } - public int Size { get; set; } - public string Ahead { get; set; } - public string Vhead { get; set; } - public string Url { get; set; } - public object BackupUrl { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } + public object BackupUrl { get; set; } } public class Data { - public string From { get; set; } - public string Result { get; set; } - public string Message { get; set; } - public int Quality { get; set; } - public string Format { get; set; } - public int Timelength { get; set; } - public string AcceptFormat { get; set; } - public List AcceptDescription { get; set; } - public List AcceptQuality { get; set; } - public int VideoCodecid { get; set; } - public string SeekParam { get; set; } - public string SeekType { get; set; } - public List Durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public string Message { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } public class VideoFrame -{ } \ No newline at end of file +{ } diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index d83a152d..c5508df3 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -5,54 +5,72 @@ namespace MikuSharp.Entities; public sealed class BotConfig { #if DEBUG - [JsonProperty("discordTokenDev")] + [JsonProperty("discordTokenDev")] #else [JsonProperty("discordToken")] #endif - public string DiscordToken { get; set; } + public string DiscordToken { get; set; } - [JsonProperty("discordBotListToken")] public string DiscordBotListToken { get; set; } + [JsonProperty("discordBotListToken")] + public string DiscordBotListToken { get; set; } - [JsonProperty("weebShToken")] public string WeebShToken { get; set; } + [JsonProperty("weebShToken")] + public string WeebShToken { get; set; } - [JsonProperty("youtubeApiToken")] public string YoutubeApiToken { get; set; } + [JsonProperty("youtubeApiToken")] + public string YoutubeApiToken { get; set; } - [JsonProperty("ksoftSiToken")] public string KsoftSiToken { get; set; } + [JsonProperty("ksoftSiToken")] + public string KsoftSiToken { get; set; } - [JsonIgnore] public string DbConnectString { get; set; } + [JsonIgnore] + public string DbConnectString { get; set; } - [JsonProperty("dbConfig")] public DatabaseConfig DbConfig { get; set; } + [JsonProperty("dbConfig")] + public DatabaseConfig DbConfig { get; set; } - [JsonProperty("lavaConfig")] public LavalinkConfig LavaConfig { get; set; } + [JsonProperty("lavaConfig")] + public LavalinkConfig LavaConfig { get; set; } - [JsonProperty("nndConfig")] public NndConfig NndConfig { get; set; } + [JsonProperty("nndConfig")] + public NndConfig NndConfig { get; set; } } public sealed class DatabaseConfig { - [JsonProperty("hostname")] public string Hostname { get; set; } + [JsonProperty("hostname")] + public string Hostname { get; set; } - [JsonProperty("user")] public string User { get; set; } + [JsonProperty("user")] + public string User { get; set; } - [JsonProperty("password")] public string Password { get; set; } + [JsonProperty("password")] + public string Password { get; set; } - [JsonProperty("database")] public string Database { get; set; } + [JsonProperty("database")] + public string Database { get; set; } } public sealed class LavalinkConfig { - [JsonProperty("hostname")] public string Hostname { get; set; } + [JsonProperty("hostname")] + public string Hostname { get; set; } - [JsonProperty("password")] public string Password { get; set; } + [JsonProperty("password")] + public string Password { get; set; } - [JsonProperty("port")] public int Port { get; set; } + [JsonProperty("port")] + public int Port { get; set; } } public sealed class NndConfig { - [JsonProperty("mail")] public string Mail { get; set; } + [JsonProperty("mail")] + public string Mail { get; set; } - [JsonProperty("password")] public string Password { get; set; } + [JsonProperty("password")] + public string Password { get; set; } - [JsonProperty("ftpConfig")] public DatabaseConfig FtpConfig { get; set; } -} \ No newline at end of file + [JsonProperty("ftpConfig")] + public DatabaseConfig FtpConfig { get; set; } +} diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index 9f60a118..a72a3216 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -2,6 +2,6 @@ public sealed class DogCeo { - public string Status { get; set; } - public string Message { get; set; } -} \ No newline at end of file + public string Status { get; set; } + public string Message { get; set; } +} diff --git a/MikuSharp/Entities/Entry.cs b/MikuSharp/Entities/Entry.cs index 0498bf4d..0cae14a4 100644 --- a/MikuSharp/Entities/Entry.cs +++ b/MikuSharp/Entities/Entry.cs @@ -1,17 +1,17 @@ -using DisCatSharp.Lavalink.Entities; +using System; -using System; +using DisCatSharp.Lavalink.Entities; namespace MikuSharp.Entities; public class Entry { - public LavalinkTrack Track { get; protected set; } - public DateTimeOffset AdditionDate { get; protected set; } + public Entry(LavalinkTrack t, DateTimeOffset addtime) + { + this.Track = t; + this.AdditionDate = addtime; + } - public Entry(LavalinkTrack t, DateTimeOffset addtime) - { - this.Track = t; - this.AdditionDate = addtime; - } -} \ No newline at end of file + public LavalinkTrack Track { get; protected set; } + public DateTimeOffset AdditionDate { get; protected set; } +} diff --git a/MikuSharp/Entities/Guild.cs b/MikuSharp/Entities/Guild.cs index c31dcf3f..7e6d81c5 100644 --- a/MikuSharp/Entities/Guild.cs +++ b/MikuSharp/Entities/Guild.cs @@ -5,32 +5,32 @@ namespace MikuSharp.Entities; public class Guild { - public int ShardId { get; set; } + public Guild(int id, MusicInstance? mi = null) + { + this.ShardId = id; + this.MusicInstance = mi; + } - //CustomPrefix stuff - public MusicInstance? MusicInstance { get; set; } - public Task AloneCheckThread { get; set; } + public int ShardId { get; set; } - public Guild(int id, MusicInstance? mi = null) - { - this.ShardId = id; - this.MusicInstance = mi; - } + //CustomPrefix stuff + public MusicInstance? MusicInstance { get; set; } + public Task AloneCheckThread { get; set; } - public async Task CheckAlone() - { - while (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes != 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) - { - await Task.Delay(1000); - if (this.MusicInstance?.GuildConnection is null) - return; - } + public async Task CheckAlone() + { + while (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes != 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) + { + await Task.Delay(1000); + if (this.MusicInstance?.GuildConnection is null) + return; + } - if (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes == 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) - { - await Task.Run(async () => await this.MusicInstance.GuildConnection.DisconnectAsync()); - await Task.Delay(500); - this.MusicInstance = null; - } - } -} \ No newline at end of file + if (DateTime.UtcNow.Subtract(this.MusicInstance.AloneTime).Minutes == 5 && !this.MusicInstance.AloneCts.IsCancellationRequested) + { + await Task.Run(async () => await this.MusicInstance.GuildConnection.DisconnectAsync()); + await Task.Delay(500); + this.MusicInstance = null; + } + } +} diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs index 66991b4c..b6f4322a 100644 --- a/MikuSharp/Entities/Img_Data.cs +++ b/MikuSharp/Entities/Img_Data.cs @@ -1,13 +1,13 @@ -using DisCatSharp.Entities; +using System.IO; -using System.IO; +using DisCatSharp.Entities; namespace MikuSharp.Entities; public class ImgData { - public Stream Data { get; set; } - public string Filetype { get; set; } + public Stream Data { get; set; } + public string Filetype { get; set; } - public DiscordEmbed Embed { get; set; } -} \ No newline at end of file + public DiscordEmbed Embed { get; set; } +} diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs index df1df905..46b85a28 100644 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ b/MikuSharp/Entities/KsoftSiRanImg.cs @@ -2,8 +2,8 @@ public sealed class KsoftSiRanImg : ImgData { - public string Url { get; set; } - public string Snowflake { get; set; } - public bool Nsfw { get; set; } - public string Tag { get; set; } -} \ No newline at end of file + public string Url { get; set; } + public string Snowflake { get; set; } + public bool Nsfw { get; set; } + public string Tag { get; set; } +} diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs index 1eab9207..5a1ddf86 100644 --- a/MikuSharp/Entities/MeekMoe.cs +++ b/MikuSharp/Entities/MeekMoe.cs @@ -2,6 +2,6 @@ public sealed class MeekMoe { - public string Url { get; set; } - public string Creator { get; set; } -} \ No newline at end of file + public string Url { get; set; } + public string Creator { get; set; } +} diff --git a/MikuSharp/Entities/MusicInstance.cs b/MikuSharp/Entities/MusicInstance.cs index 1593dfb6..299a7d54 100644 --- a/MikuSharp/Entities/MusicInstance.cs +++ b/MikuSharp/Entities/MusicInstance.cs @@ -1,4 +1,10 @@ -using DisCatSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; @@ -15,399 +21,386 @@ using MikuSharp.Events; using MikuSharp.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - namespace MikuSharp.Entities; public class MusicInstance { - public int ShardId { get; set; } - public DiscordChannel UsedChannel { get; set; } - public DiscordChannel VoiceChannel { get; set; } - public Playstate Playstate { get; set; } - public RepeatMode RepeatMode { get; set; } - public int RepeatAllPos { get; set; } - public ShuffleMode ShuffleMode { get; set; } - public DateTime AloneTime { get; set; } - public CancellationTokenSource AloneCts { get; set; } - public LavalinkSession Session { get; set; } - public LavalinkGuildPlayer GuildConnection { get; set; } - public QueueEntry CurrentSong { get; set; } - public QueueEntry LastSong { get; set; } - - public MusicInstance(LavalinkSession node, int shard) - { - this.ShardId = shard; - this.Session = node; - this.UsedChannel = null; - this.Playstate = Playstate.NotPlaying; - this.RepeatMode = RepeatMode.Off; - this.RepeatAllPos = 0; - this.ShuffleMode = ShuffleMode.Off; - } - - public async Task ConnectToChannel(DiscordChannel channel) - { - switch (channel.Type) - { - case ChannelType.Voice: - { - this.GuildConnection = await this.Session.ConnectAsync(channel); - this.VoiceChannel = channel; - return this.GuildConnection; - } - default: - return null; - } - } - - public async Task QueueSong(string n, InteractionContext ctx, int pos = -1) - { - var queue = await Database.GetQueueAsync(ctx.Guild); - var inter = ctx.Client.GetInteractivity(); - - if (n.ToLower().StartsWith("http://nicovideo.jp", StringComparison.Ordinal) - || n.ToLower().StartsWith("http://sp.nicovideo.jp", StringComparison.Ordinal) - || n.ToLower().StartsWith("https://nicovideo.jp", StringComparison.Ordinal) - || n.ToLower().StartsWith("https://sp.nicovideo.jp", StringComparison.Ordinal) - || n.ToLower().StartsWith("http://www.nicovideo.jp", StringComparison.Ordinal) - || n.ToLower().StartsWith("https://www.nicovideo.jp", StringComparison.Ordinal)) - { - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); - var split = n.Split("/".ToCharArray()); - var nndId = split.First(x => x.StartsWith("sm", StringComparison.Ordinal) || x.StartsWith("nm", StringComparison.Ordinal)).Split("?")[0]; - FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); - client.Connect(); - - if (!client.FileExists($"{nndId}.mp3")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetNndAsync(n, nndId, msg.Id); - - if (ex == null) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); - return null; - } - - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading")); - client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); - } - - var track = (await this.Session.Rest.LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) - await this.PlaySong(); - return new(track.Info, track.Tracks.First()); - } - else if (n.ToLower().StartsWith("https://www.bilibili.com", StringComparison.Ordinal) - || n.ToLower().StartsWith("http://www.bilibili.com", StringComparison.Ordinal)) - { - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing Bilibili Video...").AsEphemeral()); - n = n.Replace("https://www.bilibili.com/", ""); - n = n.Replace("http://www.bilibili.com/", ""); - var split = n.Split("/".ToCharArray()); - - if (!split.Contains("video")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Failure")); - return null; - } - - var nndId = split[1].Split("?")[0]; - FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); - client.Connect(); - - if (!client.FileExists($"{nndId}.mp3")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetBilibiliAsync(nndId, msg.Id); - - if (ex == null) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); - return null; - } - - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); - client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); - } - - var track = (await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) - await this.PlaySong(); - return new(track.Info, track.Tracks.First()); - } - else if (n.StartsWith("http://", StringComparison.Ordinal) | n.StartsWith("https://", StringComparison.Ordinal)) - try - { - var s = await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync(n); - - switch (s.LoadType) - { - case LavalinkLoadResultType.Error: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + "> Playlist is set to private or unlisted\n" + "> The song is unavailable/deleted").Build())); - return null; - } - ; - case LavalinkLoadResultType.Empty: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); - return null; - } - ; - case LavalinkLoadResultType.Playlist: - { - var pl = s.GetResultAs(); - - if (pl.Info.SelectedTrack == -1) - { - List buttons = new(2) - { - new(ButtonStyle.Success, "yes", "Add entire playlist"), - new(ButtonStyle.Primary, "no", "Don't add") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Playlist link detected!").AddEmbed(new DiscordEmbedBuilder() - .WithDescription("Choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) - await this.PlaySong(); - return new(pl.Info, pl.Tracks); - } - else - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - else - { - List buttons = new(3) - { - new(ButtonStyle.Primary, "yes", "Add only referred song"), - new(ButtonStyle.Success, "yes", "Add the entire playlist"), - new(ButtonStyle.Danger, "no", "Cancel") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Link with Playlist detected!") - .WithDescription("Please choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {pl.Tracks.ElementAt(pl.Info.SelectedTrack).Info.Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded, pos); - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) - await this.PlaySong(); - return new(pl.Info, pl.Tracks.ElementAt(pl.Info.SelectedTrack)); - } - else if (resp.Result.Id == "all") - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {pl.Info.Name}")); - - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); - else - { - pl.Tracks.Reverse(); - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks, pos); - } - - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) await this.PlaySong(); - return new(pl.Info, pl.Tracks); - } - else - { - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - } - ; - default: - { - var p = s.GetResultAs(); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {p.Info.Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p.Encoded); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, p.Encoded, pos); - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) - await this.PlaySong(); - return new(null, p); - } - ; - } - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - return null; - } - else - { - var type = LavalinkSearchType.Youtube; - - if (n.StartsWith("ytsearch:", StringComparison.Ordinal)) - { - n = n.Replace("ytsearch:", ""); - type = LavalinkSearchType.Youtube; - } - else if (n.StartsWith("scsearch:", StringComparison.Ordinal)) - { - n = n.Replace("ytsearch:", ""); - type = LavalinkSearchType.SoundCloud; - } - else if (n.StartsWith("spsearch:", StringComparison.Ordinal)) - { - n = n.Replace("spsearch:", ""); - type = LavalinkSearchType.Spotify; - } - else if (n.StartsWith("amsearch:", StringComparison.Ordinal)) - { - n = n.Replace("amsearch:", ""); - type = LavalinkSearchType.AppleMusic; - } - - var trackLoadingResult = await this.Session.ConnectedPlayers[ctx.GuildId!.Value].LoadTracksAsync(type, n); - - switch (trackLoadingResult.LoadType) - { - case LavalinkLoadResultType.Error: - { - ctx.Client.Logger.LogDebug("Load failed"); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reason could be:\n" + "> The song is unavailable/deleted").Build())); - return null; - } - ; - case LavalinkLoadResultType.Empty: - { - ctx.Client.Logger.LogDebug("No matches"); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); - return null; - } - ; - default: - { - var s = trackLoadingResult.GetResultAs>(); - ctx.Client.Logger.LogDebug("Found something"); - var leng = s.Count; - if (leng > 5) leng = 5; - List selectOptions = new(leng); - var em = new DiscordEmbedBuilder() - .WithTitle("Results!") - .WithDescription("Please select a track:\n") - .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); - - for (var i = 0; i < leng; i++) - { - em.AddField(new($"{i + 1}.{s.ElementAt(i).Info.Title} [{s.ElementAt(i).Info.Length}]", $"by {s.ElementAt(i).Info.Author} [Link]({s.ElementAt(i).Info.Uri})")); - selectOptions.Add(new(s.ElementAt(i).Info.Title, i.ToString(), $"by {s.ElementAt(i).Info.Author}. Length: {s.ElementAt(i).Info.Length}")); - } - - DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); - var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); - - if (resp.TimedOut) - { - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); - return null; - } - - await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - var trackSelect = Convert.ToInt32(resp.Result.Values.First()); - var track = s.ElementAt(trackSelect); - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Chose {track.Info.Title}")); - if (pos == -1) - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Encoded); - else - await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Encoded, pos); - if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) - await this.PlaySong(); - return new(null, track); - } - ; - } - } - } - - public async Task PlaySong() - { - var queue = await Database.GetQueueAsync(this.VoiceChannel.Guild); - var cur = this.LastSong; - if (queue.Count != 1 && this.RepeatMode == RepeatMode.All) - this.RepeatAllPos++; - if (this.RepeatAllPos >= queue.Count) - this.RepeatAllPos = 0; - this.CurrentSong = this.ShuffleMode == ShuffleMode.Off ? queue[0] : queue[new Random().Next(0, queue.Count)]; - - switch (this.RepeatMode) - { - case RepeatMode.All: - this.CurrentSong = queue[this.RepeatAllPos]; - break; - case RepeatMode.On: - this.CurrentSong = cur; - break; - } - - MikuBot.ShardedClient.Logger.LogDebug(this.CurrentSong?.Track.Encoded); - this.GuildConnection.TrackEnded += Lavalink.LavalinkTrackFinish; - this.Playstate = Playstate.Playing; - _ = Task.Run(async () => await this.GuildConnection.PlayAsync(this.CurrentSong.Track)); - return this.CurrentSong; - } + public MusicInstance(LavalinkSession node, int shard) + { + this.ShardId = shard; + this.Session = node; + this.UsedChannel = null; + this.Playstate = Playstate.NotPlaying; + this.RepeatMode = RepeatMode.Off; + this.RepeatAllPos = 0; + this.ShuffleMode = ShuffleMode.Off; + } + + public int ShardId { get; set; } + public DiscordChannel UsedChannel { get; set; } + public DiscordChannel VoiceChannel { get; set; } + public Playstate Playstate { get; set; } + public RepeatMode RepeatMode { get; set; } + public int RepeatAllPos { get; set; } + public ShuffleMode ShuffleMode { get; set; } + public DateTime AloneTime { get; set; } + public CancellationTokenSource AloneCts { get; set; } + public LavalinkSession Session { get; set; } + public LavalinkGuildPlayer GuildConnection { get; set; } + public QueueEntry CurrentSong { get; set; } + public QueueEntry LastSong { get; set; } + + public async Task ConnectToChannel(DiscordChannel channel) + { + switch (channel.Type) + { + case ChannelType.Voice: + { + this.GuildConnection = await this.Session.ConnectAsync(channel); + this.VoiceChannel = channel; + return this.GuildConnection; + } + default: + return null; + } + } + + public async Task QueueSong(string n, InteractionContext ctx, int pos = -1) + { + var queue = await Database.GetQueueAsync(ctx.Guild); + var inter = ctx.Client.GetInteractivity(); + + if (n.ToLower().StartsWith("http://nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("http://sp.nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("https://nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("https://sp.nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("http://www.nicovideo.jp", StringComparison.Ordinal) || + n.ToLower().StartsWith("https://www.nicovideo.jp", StringComparison.Ordinal)) + { + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); + var split = n.Split("/".ToCharArray()); + var nndId = split.First(x => x.StartsWith("sm", StringComparison.Ordinal) || x.StartsWith("nm", StringComparison.Ordinal)).Split("?")[0]; + FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); + client.Connect(); + + if (!client.FileExists($"{nndId}.mp3")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); + var ex = await ctx.GetNndAsync(n, nndId, msg.Id); + + if (ex == null) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); + return null; + } + + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading")); + client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); + } + + var track = (await this.Session.Rest.LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(track.Info, track.Tracks.First()); + } + + if (n.ToLower().StartsWith("https://www.bilibili.com", StringComparison.Ordinal) || n.ToLower().StartsWith("http://www.bilibili.com", StringComparison.Ordinal)) + { + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing Bilibili Video...").AsEphemeral()); + n = n.Replace("https://www.bilibili.com/", ""); + n = n.Replace("http://www.bilibili.com/", ""); + var split = n.Split("/".ToCharArray()); + + if (!split.Contains("video")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Failure")); + return null; + } + + var nndId = split[1].Split("?")[0]; + FtpClient client = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); + client.Connect(); + + if (!client.FileExists($"{nndId}.mp3")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); + var ex = await ctx.GetBilibiliAsync(nndId, msg.Id); + + if (ex == null) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); + return null; + } + + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); + client.UploadStream(ex, $"{nndId}.mp3", FtpRemoteExists.Skip, true); + } + + var track = (await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync($"https://nnd.meek.moe/new/{nndId}.mp3")).GetResultAs(); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Tracks.First().Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(track.Info, track.Tracks.First()); + } + + if (n.StartsWith("http://", StringComparison.Ordinal) | n.StartsWith("https://", StringComparison.Ordinal)) + try + { + var s = await this.Session.ConnectedPlayers[ctx.GuildId.Value].LoadTracksAsync(n); + + switch (s.LoadType) + { + case LavalinkLoadResultType.Error: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load") + .WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + "> Playlist is set to private or unlisted\n" + "> The song is unavailable/deleted").Build())); + return null; + } + ; + case LavalinkLoadResultType.Empty: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral() + .AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); + return null; + } + ; + case LavalinkLoadResultType.Playlist: + { + var pl = s.GetResultAs(); + + if (pl.Info.SelectedTrack == -1) + { + List buttons = [new(ButtonStyle.Success, "yes", "Add entire playlist"), new(ButtonStyle.Primary, "no", "Don't add")]; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Playlist link detected!").AddEmbed(new DiscordEmbedBuilder() + .WithDescription("Choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + + if (resp.Result.Id == "yes") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(pl.Info, pl.Tracks); + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + else + { + List buttons = [new(ButtonStyle.Primary, "yes", "Add only referred song"), new(ButtonStyle.Success, "yes", "Add the entire playlist"), new(ButtonStyle.Danger, "no", "Cancel")]; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Link with Playlist detected!") + .WithDescription("Please choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + + if (resp.Result.Id == "yes") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {pl.Tracks.ElementAt(pl.Info.SelectedTrack).Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks.ElementAt(pl.Info.SelectedTrack).Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(pl.Info, pl.Tracks.ElementAt(pl.Info.SelectedTrack)); + } + + if (resp.Result.Id == "all") + { + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {pl.Info.Name}")); + + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks); + else + { + pl.Tracks.Reverse(); + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, pl.Tracks, pos); + } + + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) await this.PlaySong(); + return new(pl.Info, pl.Tracks); + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + ; + default: + { + var p = s.GetResultAs(); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {p.Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p.Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, p.Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(null, p); + } + ; + } + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + return null; + } + + var type = LavalinkSearchType.Youtube; + + if (n.StartsWith("ytsearch:", StringComparison.Ordinal)) + { + n = n.Replace("ytsearch:", ""); + type = LavalinkSearchType.Youtube; + } + else if (n.StartsWith("scsearch:", StringComparison.Ordinal)) + { + n = n.Replace("ytsearch:", ""); + type = LavalinkSearchType.SoundCloud; + } + else if (n.StartsWith("spsearch:", StringComparison.Ordinal)) + { + n = n.Replace("spsearch:", ""); + type = LavalinkSearchType.Spotify; + } + else if (n.StartsWith("amsearch:", StringComparison.Ordinal)) + { + n = n.Replace("amsearch:", ""); + type = LavalinkSearchType.AppleMusic; + } + + var trackLoadingResult = await this.Session.ConnectedPlayers[ctx.GuildId!.Value].LoadTracksAsync(type, n); + + switch (trackLoadingResult.LoadType) + { + case LavalinkLoadResultType.Error: + { + ctx.Client.Logger.LogDebug("Load failed"); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load") + .WithDescription("Loading this song/playlist failed, please try again, reason could be:\n" + "> The song is unavailable/deleted").Build())); + return null; + } + ; + case LavalinkLoadResultType.Empty: + { + ctx.Client.Logger.LogDebug("No matches"); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); + return null; + } + ; + default: + { + var s = trackLoadingResult.GetResultAs>(); + ctx.Client.Logger.LogDebug("Found something"); + var leng = s.Count; + if (leng > 5) leng = 5; + List selectOptions = new(leng); + var em = new DiscordEmbedBuilder() + .WithTitle("Results!") + .WithDescription("Please select a track:\n") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); + + for (var i = 0; i < leng; i++) + { + em.AddField(new($"{i + 1}.{s.ElementAt(i).Info.Title} [{s.ElementAt(i).Info.Length}]", $"by {s.ElementAt(i).Info.Author} [Link]({s.ElementAt(i).Info.Uri})")); + selectOptions.Add(new(s.ElementAt(i).Info.Title, i.ToString(), $"by {s.ElementAt(i).Info.Author}. Length: {s.ElementAt(i).Info.Length}")); + } + + DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); + var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); + + if (resp.TimedOut) + { + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); + return null; + } + + await resp.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); + var trackSelect = Convert.ToInt32(resp.Result.Values.First()); + var track = s.ElementAt(trackSelect); + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Chose {track.Info.Title}")); + if (pos == -1) + await Database.AddToQueue(ctx.Guild, ctx.Member.Id, track.Encoded); + else + await Database.InsertToQueue(ctx.Guild, ctx.Member.Id, track.Encoded, pos); + if (this.GuildConnection.IsConnected && this.Playstate is Playstate.NotPlaying or Playstate.Stopped) + await this.PlaySong(); + return new(null, track); + } + ; + } + } + + public async Task PlaySong() + { + var queue = await Database.GetQueueAsync(this.VoiceChannel.Guild); + var cur = this.LastSong; + if (queue.Count != 1 && this.RepeatMode == RepeatMode.All) + this.RepeatAllPos++; + if (this.RepeatAllPos >= queue.Count) + this.RepeatAllPos = 0; + this.CurrentSong = this.ShuffleMode == ShuffleMode.Off + ? queue[0] + : queue[new Random().Next(0, queue.Count)]; + + switch (this.RepeatMode) + { + case RepeatMode.All: + this.CurrentSong = queue[this.RepeatAllPos]; + break; + case RepeatMode.On: + this.CurrentSong = cur; + break; + } + + MikuBot.ShardedClient.Logger.LogDebug(this.CurrentSong?.Track.Encoded); + this.GuildConnection.TrackEnded += Lavalink.LavalinkTrackFinish; + this.Playstate = Playstate.Playing; + _ = Task.Run(async () => await this.GuildConnection.PlayAsync(this.CurrentSong.Track)); + return this.CurrentSong; + } } -// B/S(`・ω・´) ❤️ (´ω`)U/C \ No newline at end of file +// B/S(`・ω・´) ❤️ (´ω`)U/C diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs index 68419bda..4c64301c 100644 --- a/MikuSharp/Entities/Nekobot.cs +++ b/MikuSharp/Entities/Nekobot.cs @@ -2,7 +2,7 @@ public sealed class NekoBot : ImgData { - public string Message { get; set; } - public int Status { get; set; } - public bool Success { get; set; } -} \ No newline at end of file + public string Message { get; set; } + public int Status { get; set; } + public bool Success { get; set; } +} diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs index 513a61d4..83cecb3f 100644 --- a/MikuSharp/Entities/Nekos_Life.cs +++ b/MikuSharp/Entities/Nekos_Life.cs @@ -2,5 +2,5 @@ public sealed class NekosLife : ImgData { - public string Url { get; set; } -} \ No newline at end of file + public string Url { get; set; } +} diff --git a/MikuSharp/Entities/Playlist.cs b/MikuSharp/Entities/Playlist.cs index e5264503..11524868 100644 --- a/MikuSharp/Entities/Playlist.cs +++ b/MikuSharp/Entities/Playlist.cs @@ -13,57 +13,58 @@ namespace MikuSharp.Entities; public class Playlist { - public string Name { get; set; } - public ulong UserID { get; set; } - public ExtService ExternalService { get; set; } - public string Url { get; set; } - public int SongCount { get; set; } - public DateTimeOffset Creation { get; set; } - public DateTimeOffset Modify { get; set; } + public string Name { get; set; } + public ulong UserID { get; set; } + public ExtService ExternalService { get; set; } + public string Url { get; set; } + public int SongCount { get; set; } + public DateTimeOffset Creation { get; set; } + public DateTimeOffset Modify { get; set; } - public Playlist(ExtService e, string u, string n, ulong usr, int c, DateTimeOffset crea, DateTimeOffset mody) - { - ExternalService = e; - Url = u; - Name = n; - UserID = usr; - SongCount = c; - Creation = crea; - Modify = mody; - } + public Playlist(ExtService e, string u, string n, ulong usr, int c, DateTimeOffset crea, DateTimeOffset mody) + { + ExternalService = e; + Url = u; + Name = n; + UserID = usr; + SongCount = c; + Creation = crea; + Modify = mody; + } - public async Task> GetEntries() - { - var Entries = new List(SongCount); - if (ExternalService == ExtService.None) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM playlistentries WHERE userid = {UserID} AND playlistname = @pl ORDER BY pos ASC;", conn); - cmd2.Parameters.AddWithValue("pl", Name); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - Entries.Add(new PlaylistEntry(LavalinkUtilities.DecodeTrack(Convert.ToString(reader["trackstring"])), DateTimeOffset.Parse(Convert.ToString(reader["addition"])), DateTimeOffset.Parse(Convert.ToString(reader["changed"])), Convert.ToInt32(reader["pos"]))); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } - else - { - var trs = await MikuBot.LavalinkNodeConnections.First().Value.ConnectedGuilds.First().Value.GetTracksAsync(new Uri(Url)); - int i = 0; - foreach (var t in trs.Tracks) - { - Entries.Add(new PlaylistEntry(t, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, i)); - i++; - } - } - return Entries; - } + public async Task> GetEntries() + { + var Entries = new List(SongCount); + if (ExternalService == ExtService.None) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM playlistentries WHERE userid = {UserID} AND playlistname = @pl ORDER BY pos ASC;", conn); + cmd2.Parameters.AddWithValue("pl", Name); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + Entries.Add(new PlaylistEntry(LavalinkUtilities.DecodeTrack(Convert.ToString(reader["trackstring"])), DateTimeOffset.Parse(Convert.ToString(reader["addition"])), DateTimeOffset.Parse(Convert.ToString(reader["changed"])), Convert.ToInt32(reader["pos"]))); + } + reader.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } + else + { + var trs = await MikuBot.LavalinkNodeConnections.First().Value.ConnectedGuilds.First().Value.GetTracksAsync(new Uri(Url)); + int i = 0; + foreach (var t in trs.Tracks) + { + Entries.Add(new PlaylistEntry(t, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, i)); + i++; + } + } + return Entries; + } } */ + diff --git a/MikuSharp/Entities/PlaylistEntry.cs b/MikuSharp/Entities/PlaylistEntry.cs index 485623be..c853214a 100644 --- a/MikuSharp/Entities/PlaylistEntry.cs +++ b/MikuSharp/Entities/PlaylistEntry.cs @@ -1,17 +1,17 @@ -using DisCatSharp.Lavalink.Entities; +using System; -using System; +using DisCatSharp.Lavalink.Entities; namespace MikuSharp.Entities; public class PlaylistEntry : Entry { - public DateTimeOffset ModifyDate { get; set; } - public int Position { get; set; } + public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset moddate, int pos) : base(t, addDate) + { + this.ModifyDate = moddate; + this.Position = pos; + } - public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset moddate, int pos) : base(t, addDate) - { - this.ModifyDate = moddate; - this.Position = pos; - } -} \ No newline at end of file + public DateTimeOffset ModifyDate { get; set; } + public int Position { get; set; } +} diff --git a/MikuSharp/Entities/QueueEntry.cs b/MikuSharp/Entities/QueueEntry.cs index 4be3138a..f5fdad9f 100644 --- a/MikuSharp/Entities/QueueEntry.cs +++ b/MikuSharp/Entities/QueueEntry.cs @@ -1,17 +1,17 @@ -using DisCatSharp.Lavalink.Entities; +using System; -using System; +using DisCatSharp.Lavalink.Entities; namespace MikuSharp.Entities; public class QueueEntry : Entry { - public int Position { get; set; } - public ulong AddedBy { set; get; } + public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : base(t, adddate) + { + this.Position = pos; + this.AddedBy = m; + } - public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : base(t, adddate) - { - this.Position = pos; - this.AddedBy = m; - } -} \ No newline at end of file + public int Position { get; set; } + public ulong AddedBy { set; get; } +} diff --git a/MikuSharp/Entities/Random_D.cs b/MikuSharp/Entities/Random_D.cs index 05fc585b..349fdfce 100644 --- a/MikuSharp/Entities/Random_D.cs +++ b/MikuSharp/Entities/Random_D.cs @@ -2,6 +2,6 @@ public class RandomD { - public string Url { get; set; } - public string Message { get; set; } -} \ No newline at end of file + public string Url { get; set; } + public string Message { get; set; } +} diff --git a/MikuSharp/Entities/TrackResult.cs b/MikuSharp/Entities/TrackResult.cs index f1175bf3..8dd4f4ef 100644 --- a/MikuSharp/Entities/TrackResult.cs +++ b/MikuSharp/Entities/TrackResult.cs @@ -1,27 +1,24 @@ -using DisCatSharp.Lavalink.Entities; +using System.Collections.Generic; -using System.Collections.Generic; +using DisCatSharp.Lavalink.Entities; namespace MikuSharp.Entities; public class TrackResult { - public LavalinkPlaylistInfo PlaylistInfo { get; set; } - public List Tracks { get; set; } + public TrackResult(LavalinkPlaylistInfo pl, IEnumerable tr) + { + this.PlaylistInfo = pl; + this.Tracks = []; + this.Tracks.AddRange(tr); + } - public TrackResult(LavalinkPlaylistInfo pl, IEnumerable tr) - { - this.PlaylistInfo = pl; - this.Tracks = new(); - this.Tracks.AddRange(tr); - } + public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) + { + this.PlaylistInfo = pl; + this.Tracks = [tr]; + } - public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) - { - this.PlaylistInfo = pl; - this.Tracks = new() - { - tr - }; - } -} \ No newline at end of file + public LavalinkPlaylistInfo PlaylistInfo { get; set; } + public List Tracks { get; set; } +} diff --git a/MikuSharp/Entities/WeebSh.cs b/MikuSharp/Entities/WeebSh.cs index 6f5aa31c..f56a651e 100644 --- a/MikuSharp/Entities/WeebSh.cs +++ b/MikuSharp/Entities/WeebSh.cs @@ -1,12 +1,12 @@ -using DisCatSharp.Entities; +using System.IO; -using System.IO; +using DisCatSharp.Entities; namespace MikuSharp.Entities; public sealed class WeebSh { - public MemoryStream ImgData { get; set; } - public string Extension { get; set; } - public DiscordEmbedBuilder Embed { get; set; } -} \ No newline at end of file + public MemoryStream ImgData { get; set; } + public string Extension { get; set; } + public DiscordEmbedBuilder Embed { get; set; } +} diff --git a/MikuSharp/Enums/ExtService.cs b/MikuSharp/Enums/ExtService.cs index 3f107c7b..835ca48b 100644 --- a/MikuSharp/Enums/ExtService.cs +++ b/MikuSharp/Enums/ExtService.cs @@ -1,8 +1,8 @@ namespace MikuSharp.Enums; -public enum ExtService : int +public enum ExtService { - None = 0, - Youtube = 1, - Soundcloud = 2 -} \ No newline at end of file + None = 0, + Youtube = 1, + Soundcloud = 2 +} diff --git a/MikuSharp/Enums/Playing.cs b/MikuSharp/Enums/Playing.cs index 23314462..ccfbb806 100644 --- a/MikuSharp/Enums/Playing.cs +++ b/MikuSharp/Enums/Playing.cs @@ -1,22 +1,22 @@ namespace MikuSharp.Enums; -public enum Playstate : int +public enum Playstate { - NotPlaying = 0, - Playing = 1, - Paused = 2, - Stopped = 3 + NotPlaying = 0, + Playing = 1, + Paused = 2, + Stopped = 3 } -public enum RepeatMode : int +public enum RepeatMode { - Off = 0, - On = 1, - All = 2 + Off = 0, + On = 1, + All = 2 } -public enum ShuffleMode : int +public enum ShuffleMode { - Off = 0, - On = 1 -} \ No newline at end of file + Off = 0, + On = 1 +} diff --git a/MikuSharp/Events/Lavalink.cs b/MikuSharp/Events/Lavalink.cs index 1ca24040..ac060f99 100644 --- a/MikuSharp/Events/Lavalink.cs +++ b/MikuSharp/Events/Lavalink.cs @@ -1,4 +1,7 @@ -using DisCatSharp.Entities; +using System; +using System.Threading.Tasks; + +using DisCatSharp.Entities; using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Enums; using DisCatSharp.Lavalink.EventArgs; @@ -8,75 +11,72 @@ using MikuSharp.Enums; using MikuSharp.Utilities; -using System; -using System.Threading.Tasks; - namespace MikuSharp.Events; public class Lavalink { - public static async Task LavalinkTrackFinish(LavalinkGuildPlayer lava, LavalinkTrackEndedEventArgs e) - { - try - { - var g = MikuBot.Guilds[e.GuildId]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(e.Guild); - if (g.MusicInstance == null!) - return; + public static async Task LavalinkTrackFinish(LavalinkGuildPlayer lava, LavalinkTrackEndedEventArgs e) + { + try + { + var g = MikuBot.Guilds[e.GuildId]; + var lastPlayedSongs = await Database.GetLastPlayingListAsync(e.Guild); + if (g.MusicInstance == null!) + return; - switch (e.Reason) - { - case LavalinkTrackEndReason.Stopped: - { - g.MusicInstance.Playstate = Playstate.Stopped; - g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - break; - } - case LavalinkTrackEndReason.Replaced: - { - break; - } - case LavalinkTrackEndReason.LoadFailed: - { - await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithTitle("Track failed to play") - .WithDescription($"**{g.MusicInstance.CurrentSong.Track.Info.Title}**\nby {g.MusicInstance.CurrentSong.Track.Info.Author}\n" + $"**Failed to load, Skipping to next Track**")); - g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; - await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - var queue = await Database.GetQueueAsync(e.Guild); - if (queue.Count != 0) await g.MusicInstance.PlaySong(); - else g.MusicInstance.Playstate = Playstate.NotPlaying; - break; - } - case LavalinkTrackEndReason.Finished: - { - g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; - if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) - await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - var queue = await Database.GetQueueAsync(e.Guild); - if (queue.Count != 0) await g.MusicInstance.PlaySong(); - else g.MusicInstance.Playstate = Playstate.NotPlaying; - break; - } - } - } - catch (Exception ex) - { - MikuBot.ShardedClient.Logger.LogError(ex.Message); - MikuBot.ShardedClient.Logger.LogError(ex.StackTrace); - } - } -} \ No newline at end of file + switch (e.Reason) + { + case LavalinkTrackEndReason.Stopped: + { + g.MusicInstance.Playstate = Playstate.Stopped; + g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + break; + } + case LavalinkTrackEndReason.Replaced: + { + break; + } + case LavalinkTrackEndReason.LoadFailed: + { + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithTitle("Track failed to play") + .WithDescription($"**{g.MusicInstance.CurrentSong.Track.Info.Title}**\nby {g.MusicInstance.CurrentSong.Track.Info.Author}\n" + $"**Failed to load, Skipping to next Track**")); + g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; + await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); + if (lastPlayedSongs.Count == 0) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + var queue = await Database.GetQueueAsync(e.Guild); + if (queue.Count != 0) await g.MusicInstance.PlaySong(); + else g.MusicInstance.Playstate = Playstate.NotPlaying; + break; + } + case LavalinkTrackEndReason.Finished: + { + g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; + if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) + await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); + if (lastPlayedSongs.Count == 0) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) + await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); + g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; + g.MusicInstance.CurrentSong = null; + var queue = await Database.GetQueueAsync(e.Guild); + if (queue.Count != 0) await g.MusicInstance.PlaySong(); + else g.MusicInstance.Playstate = Playstate.NotPlaying; + break; + } + } + } + catch (Exception ex) + { + MikuBot.ShardedClient.Logger.LogError(ex.Message); + MikuBot.ShardedClient.Logger.LogError(ex.StackTrace); + } + } +} diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index 9caa355a..fdf2612f 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -1,39 +1,39 @@ -using DisCatSharp; -using DisCatSharp.EventArgs; +using System.Threading.Tasks; -using System.Threading.Tasks; +using DisCatSharp; +using DisCatSharp.EventArgs; namespace MikuSharp.Events; /// -/// Event handler for the miku guild. +/// Event handler for the miku guild. /// public class MikuGuild { /// - /// Fired when a new guild member joins. + /// Fired when a new guild member joins. /// /// The client. /// The event args. public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) - { - await Task.FromResult(true); - } + { + await Task.FromResult(true); + } /// - /// Fired when a guild member is updated. + /// Fired when a guild member is updated. /// /// The discord client. /// The event args. public static async Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) - { - if (args is { PendingBefore: true, PendingAfter: false }) - { - ulong memberRoleId = 483280207927574528; - var memberRole = args.Guild.GetRole(memberRoleId); - await args.Member.GrantRoleAsync(memberRole); - } + { + if (args is { PendingBefore: true, PendingAfter: false }) + { + ulong memberRoleId = 483280207927574528; + var memberRole = args.Guild.GetRole(memberRoleId); + await args.Member.GrantRoleAsync(memberRole); + } - await Task.FromResult(true); - } -} \ No newline at end of file + await Task.FromResult(true); + } +} diff --git a/MikuSharp/Events/VoiceChat.cs b/MikuSharp/Events/VoiceChat.cs index d4aaa680..120a2c1e 100644 --- a/MikuSharp/Events/VoiceChat.cs +++ b/MikuSharp/Events/VoiceChat.cs @@ -1,4 +1,8 @@ -using DisCatSharp; +using System; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp; using DisCatSharp.Entities; using DisCatSharp.EventArgs; @@ -6,65 +10,59 @@ using MikuSharp.Enums; -using System; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Events; public class VoiceChat { - public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventArgs e) - { - try - { - if (MikuBot.Guilds.All(x => x.Key != e.Guild.Id)) - return; + public static async Task LeftAlone(DiscordClient client, VoiceStateUpdateEventArgs e) + { + try + { + if (MikuBot.Guilds.All(x => x.Key != e.Guild.Id)) + return; - var g = MikuBot.Guilds[e.Guild.Id]; - if (g.MusicInstance == null - || g.MusicInstance?.GuildConnection?.IsConnected == false) return; + var g = MikuBot.Guilds[e.Guild.Id]; + if (g.MusicInstance == null || g.MusicInstance?.GuildConnection?.IsConnected == false) return; - if (((e.After?.Channel?.Users)?.Count(x => !x.IsBot) == 0 - || (e.Before?.Channel?.Users)?.Count(x => !x.IsBot) == 0 - || (e.Channel?.Users)?.Count(x => !x.IsBot) == 0) - && (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true - || e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true - || e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - && (g.MusicInstance?.GuildConnection?.Channel?.Users)?.Count(x => !x.IsBot) == 0) - { - if (g.MusicInstance.Playstate == Playstate.Playing) - { - await g.MusicInstance.GuildConnection.PauseAsync(); - g.MusicInstance.Playstate = Playstate.Paused; + if ((e.After?.Channel?.Users?.Count(x => !x.IsBot) == 0 || e.Before?.Channel?.Users?.Count(x => !x.IsBot) == 0 || e.Channel?.Users?.Count(x => !x.IsBot) == 0) && + (e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true || + e.Before?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true || + e.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) && + g.MusicInstance?.GuildConnection?.Channel?.Users?.Count(x => !x.IsBot) == 0) + { + if (g.MusicInstance.Playstate == Playstate.Playing) + { + await g.MusicInstance.GuildConnection.PauseAsync(); + g.MusicInstance.Playstate = Playstate.Paused; - try - { - await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithDescription("**Paused** since everyone left the VC, connect back and use m%resume to continue playback otherwise I will disconnect in 5 min").Build()); - } - catch - { } - } - else - try - { - await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithDescription("Since everyone left the VC I will disconnect too in 5 min").Build()); - } - catch - { } + try + { + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder() + .WithDescription("**Paused** since everyone left the VC, connect back and use m%resume to continue playback otherwise I will disconnect in 5 min").Build()); + } + catch + { } + } + else + try + { + await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithDescription("Since everyone left the VC I will disconnect too in 5 min").Build()); + } + catch + { } - g.MusicInstance.AloneTime = DateTime.UtcNow; - g.MusicInstance.AloneCts = new(); - g.AloneCheckThread = Task.Run(g.CheckAlone); - } - else if ((e.After?.Channel?.Users)?.Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) - if (g.MusicInstance is { AloneCts: not null }) - g.MusicInstance.AloneCts.Cancel(); - } - catch (Exception ex) - { - client.Logger.LogError(ex.Message); - client.Logger.LogError(ex.StackTrace); - } - } -} \ No newline at end of file + g.MusicInstance.AloneTime = DateTime.UtcNow; + g.MusicInstance.AloneCts = new(); + g.AloneCheckThread = Task.Run(g.CheckAlone); + } + else if (e.After?.Channel?.Users?.Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) + if (g.MusicInstance is { AloneCts: not null }) + g.MusicInstance.AloneCts.Cancel(); + } + catch (Exception ex) + { + client.Logger.LogError(ex.Message); + client.Logger.LogError(ex.StackTrace); + } + } +} diff --git a/MikuSharp/GlobalSuppressions.cs b/MikuSharp/GlobalSuppressions.cs index da5448c3..145c54cf 100644 --- a/MikuSharp/GlobalSuppressions.cs +++ b/MikuSharp/GlobalSuppressions.cs @@ -86,59 +86,161 @@ [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.QueueEntry.position")] [assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.#ctor")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.Dispose")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.FourK(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Gonewild(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.LewdKitsune(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.LewdNeko(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Entry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.DateTimeOffset)")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.#ctor(System.Int32,MikuSharp.Entities.MusicInstance)")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.CheckAlone~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.#ctor(DisCatSharp.Lavalink.LavalinkNodeConnection,System.Int32)")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.ConnectToChannel(DisCatSharp.Entities.DiscordChannel)~System.Threading.Tasks.Task{DisCatSharp.Lavalink.LavalinkGuildConnection}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Playlist.#ctor(MikuSharp.Enums.ExtService,System.String,System.String,System.UInt64,System.Int32,System.DateTimeOffset,System.DateTimeOffset)")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Playlist.GetEntries~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry}}")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.QueueEntry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.UInt64,System.DateTimeOffset,System.Int32)")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,DisCatSharp.Lavalink.LavalinkTrack)")] -[assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,System.Collections.Generic.IEnumerable{DisCatSharp.Lavalink.LavalinkTrack})")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.FourK(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Gonewild(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.LewdKitsune(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.LewdNeko(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Entry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.DateTimeOffset)")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Guild.#ctor(System.Int32,MikuSharp.Entities.MusicInstance)")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.CheckAlone~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.#ctor(DisCatSharp.Lavalink.LavalinkNodeConnection,System.Int32)")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.ConnectToChannel(DisCatSharp.Entities.DiscordChannel)~System.Threading.Tasks.Task{DisCatSharp.Lavalink.LavalinkGuildConnection}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Playlist.#ctor(MikuSharp.Enums.ExtService,System.String,System.String,System.UInt64,System.Int32,System.DateTimeOffset,System.DateTimeOffset)")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Playlist.GetEntries~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry}}")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.QueueEntry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.UInt64,System.DateTimeOffset,System.Int32)")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,DisCatSharp.Lavalink.LavalinkTrack)")] +[assembly: + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,System.Collections.Generic.IEnumerable{DisCatSharp.Lavalink.LavalinkTrack})")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToLastPlayingListAsync(System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.ClearQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.GetLastPlayingListAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.Entry}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.GetQueueAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.QueueEntry}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.MoveQueueItems(DisCatSharp.Entities.DiscordGuild,System.Int32,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.RebuildQueue(DisCatSharp.Entities.DiscordGuild,System.Collections.Generic.List{MikuSharp.Entities.QueueEntry})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.RemoveFromQueueAsync(System.Int32,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Database.ReorderQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Other.resizeLink(System.String)~System.String")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.AddPlaylist(System.String,System.UInt64,MikuSharp.Enums.ExtService,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.ClearList(System.String,System.UInt64)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylists(DisCatSharp.Entities.DiscordGuild,System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.Dictionary{System.String,MikuSharp.Entities.Playlist}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylistsSimple(System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.List{System.String}}")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.MoveListItems(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Int32,System.Int32)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RebuildList(System.UInt64,System.String,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RemoveFromList(DisCatSharp.Entities.DiscordGuild,System.Int32,System.String,System.UInt64)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RemovePlaylist(System.String,System.UInt64)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.RenameList(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.ReorderList(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToLastPlayingListAsync(System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.ClearQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.GetLastPlayingListAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.Entry}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.GetQueueAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.QueueEntry}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.MoveQueueItems(DisCatSharp.Entities.DiscordGuild,System.Int32,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.RebuildQueue(DisCatSharp.Entities.DiscordGuild,System.Collections.Generic.List{MikuSharp.Entities.QueueEntry})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.RemoveFromQueueAsync(System.Int32,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.ReorderQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Other.resizeLink(System.String)~System.String")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddPlaylist(System.String,System.UInt64,MikuSharp.Enums.ExtService,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.ClearList(System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylists(DisCatSharp.Entities.DiscordGuild,System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.Dictionary{System.String,MikuSharp.Entities.Playlist}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylistsSimple(System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.List{System.String}}")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.MoveListItems(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Int32,System.Int32)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RebuildList(System.UInt64,System.String,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RemoveFromList(DisCatSharp.Entities.DiscordGuild,System.Int32,System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RemovePlaylist(System.String,System.UInt64)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RenameList(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.ReorderList(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot._cts")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot.GameSetThread")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot.StatusThread")] @@ -298,18 +400,26 @@ [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Other")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.PlaylistDB")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Web")] -[assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] +[assembly: + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuild")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.Random_D")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.#ctor")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] @@ -320,15 +430,24 @@ [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.MikuGuild.OnJoinAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberAddEventArgs)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Events.MikuGuild.OnUpdateAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberUpdateEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.MikuGuild.OnJoinAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberAddEventArgs)~System.Threading.Tasks.Task")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.MikuGuild.OnUpdateAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.RegisterEvents~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] [assembly: - SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] +[assembly: + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = + "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] +[assembly: + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] [assembly: - SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] \ No newline at end of file + SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", + Target = + "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 90b4f4cd..b74fb94b 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -1,20 +1,27 @@ -using DisCatSharp; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using DisCatSharp; using DisCatSharp.ApplicationCommands; using DisCatSharp.CommandsNext; -using DisCatSharp.CommandsNext.Attributes; using DisCatSharp.Entities; using DisCatSharp.Enums; using DisCatSharp.Interactivity; using DisCatSharp.Interactivity.Enums; -using DisCatSharp.Interactivity.EventHandling; using DisCatSharp.Interactivity.Extensions; using DisCatSharp.Lavalink; -using DisCatSharp.Net; using DiscordBotsList.Api; using Microsoft.Extensions.Logging; +using MikuSharp.Attributes; +using MikuSharp.Commands; using MikuSharp.Entities; using MikuSharp.Enums; using MikuSharp.Events; @@ -24,294 +31,298 @@ using Serilog; using Serilog.Events; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - using Weeb.net; +using Action = MikuSharp.Commands.Action; +using MikuGuild = MikuSharp.Events.MikuGuild; +using TokenType = DisCatSharp.Enums.TokenType; + namespace MikuSharp; internal sealed class MikuBot : IDisposable { - internal static CancellationTokenSource Cts { get; set; } - - internal static BotConfig Config { get; set; } - internal LavalinkConfiguration LavalinkConfig { get; set; } - - internal Task GameSetThread { get; set; } - internal Task StatusThread { get; set; } - internal Task BotListThread { get; set; } - - internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "4.0.0"); - internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } - internal static DiscordShardedClient ShardedClient { get; set; } - - internal IReadOnlyDictionary InteractivityModules { get; set; } - internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } - - internal IReadOnlyDictionary CommandsNextModules { get; set; } - internal IReadOnlyDictionary LavalinkModules { get; set; } - - internal static Dictionary LavalinkSessions = new(); - internal static Dictionary Guilds = new(); - - internal static Playstate Ps = Playstate.Playing; - internal static Stopwatch Psc = new(); - - internal MikuBot() - { - var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); - - Config = JsonConvert.DeserializeObject(fileData); - if (Config == null) - throw new ArgumentNullException(null, "config.json is null"); - - Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - Cts = new(); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) - .WriteTo.Console(LogEventLevel.Debug, "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") - .CreateLogger(); - Log.Logger.Information("Starting up!"); - - ShardedClient = new(new() - { - Token = Config.DiscordToken, - TokenType = DisCatSharp.Enums.TokenType.Bot, - MinimumLogLevel = LogLevel.Debug, - AutoReconnect = true, - ApiChannel = ApiChannel.Canary, - HttpTimeout = TimeSpan.FromMinutes(1), - Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers, - MessageCacheSize = 2048, - LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger), - ShowReleaseNotesInUpdateCheck = false, - IncludePrereleaseInUpdateCheck = true, - DisableUpdateCheck = true, - EnableSentry = true, - FeedbackEmail = "aiko@aitsys.dev", - DeveloperUserId = 856780995629154305, - AttachUserInfo = true, - ReconnectIndefinitely = true - }); - - this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() - { - Timeout = TimeSpan.FromMinutes(2), - PaginationBehaviour = PaginationBehaviour.WrapAround, - PaginationDeletion = PaginationDeletion.DeleteEmojis, - PollBehaviour = PollBehaviour.DeleteEmojis, - AckPaginationButtons = true, - ButtonBehavior = ButtonPaginationBehavior.Disable, - PaginationButtons = new() - { - SkipLeft = new(ButtonStyle.Primary, "pgb-skip-left", "First", false, new("⏮️")), - Left = new(ButtonStyle.Primary, "pgb-left", "Previous", false, new("◀️")), - Stop = new(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new("⏹️")), - Right = new(ButtonStyle.Primary, "pgb-right", "Next", false, new("▶️")), - SkipRight = new(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new("⏭️")) - }, - ResponseMessage = "Something went wrong.", - ResponseBehavior = InteractionResponseBehavior.Ignore - }).Result; - - this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() - { - EnableDefaultHelp = true, - DebugStartup = true, - EnableLocalization = false, - GenerateTranslationFilesOnly = false, - }).Result; - - this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() - { - CaseSensitive = false, - EnableMentionPrefix = true, - DmHelp = false, - EnableDefaultHelp = true, - IgnoreExtraArguments = true, - StringPrefixes = new(), - UseDefaultCommandHandler = true, - DefaultHelpChecks = new(1) { new Attributes.NotStaffAttribute() } - }).Result; - - this.LavalinkConfig = new() - { - SocketEndpoint = new() { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, - RestEndpoint = new() { Hostname = Config.LavaConfig.Hostname, Port = Config.LavaConfig.Port }, - Password = Config.LavaConfig.Password - }; - - this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; - } - - internal static async Task RegisterEvents() - { - ShardedClient.ClientErrored += (sender, args) => - { - sender.Logger.LogError(args.Exception.Message); - sender.Logger.LogError(args.Exception.StackTrace); - return Task.CompletedTask; - }; - - await Task.Delay(1); - - foreach (var discordClientKvp in ShardedClient.ShardClients) - { - discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; - - discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => - { - sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", args.ShardId); - args.Handled = true; - return Task.CompletedTask; - }; - - discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleReady += (sender, args) => - { - sender.Client.Logger.LogInformation("Application commands module is ready"); - args.Handled = true; - return Task.CompletedTask; - }; - - discordClientKvp.Value.GuildMemberAdded += async (sender, args) => - { - if (sender.CurrentApplication.Team.Members.Any(x => x.User.Id == args.Member.Id)) - { - var text = $"Heywo <:MikuWave:655783221942026271>!" - + $"\n\nOne of my developers joined your server!" - + $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" - + $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." - + $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." - + $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithGlobalName}!" - + $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; - var message = await args.Guild.Owner.SendMessageAsync(text); - sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithGlobalName); - sender.Logger.LogInformation("Message content: {content}", message.Content); - } - else - await Task.FromResult(true); - }; - discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => - { - if (args.Guild.Id == 483279257431441410) - await MikuGuild.OnUpdateAsync(sender, args); - else - await Task.FromResult(true); - }; - - discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); - } - } - - internal async Task ShowConnections() - { - while (true) - { - var al = Guilds.Where(x => x.Value?.MusicInstance != null); - ShardedClient.Logger.LogInformation("Voice Connections: " + al.Where(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true).Count()); - await Task.Delay(15000); - } - } - - internal static async Task UpdateBotList() - { - await Task.Delay(15000); - - while (true) - { - var me = await DiscordBotListApi.GetMeAsync(); - var count = Array.Empty(); - var clients = ShardedClient.ShardClients.Values; - count = clients.Aggregate(count, (current, client) => current.Append(client.Guilds.Count).ToArray()); - await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); - await Task.Delay(TimeSpan.FromMinutes(15)); - } - } - - internal static async Task SetActivity() - { - while (true) - { - DiscordActivity test = new() - { - Name = "I'm using slash commands now!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test2 = new() - { - Name = "Mention me with help for nsfw commands!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test3 = new() - { - Name = "Full NND support!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(test3, UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - } - } - - internal void RegisterCommands() - { - // Nsfw stuff needs to be hidden, that's why we use commands next - this.CommandsNextModules.RegisterCommands(); - - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - //ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - - // Smolcar command, only guild command - this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); - } - - internal async Task RunAsync() - { - await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); - await ShardedClient.StartAsync(); - await Task.Delay(5000); - - foreach (var lavalinkShard in this.LavalinkModules) - { - var connection = await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); - LavalinkSessions.Add(lavalinkShard.Key, connection); - } - - //this.GameSetThread = Task.Run(SetActivity); - //StatusThread = Task.Run(ShowConnections); - //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); - //BotListThread = Task.Run(UpdateBotList); - while (!Cts.IsCancellationRequested) - await Task.Delay(25); - _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); - await ShardedClient.StopAsync(); - } - - ~MikuBot() - { - this.Dispose(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - } -} \ No newline at end of file + internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "4.0.0"); + + internal static readonly Dictionary LavalinkSessions = []; + internal static readonly Dictionary Guilds = []; + + internal static Playstate Ps = Playstate.Playing; + internal static Stopwatch Psc = new(); + + internal MikuBot() + { + var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); + + Config = JsonConvert.DeserializeObject(fileData); + if (Config == null) + throw new ArgumentNullException(null, "config.json is null"); + + Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; + Cts = new(); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) + .WriteTo.Console(LogEventLevel.Debug) + .CreateLogger(); + Log.Logger.Information("Starting up!"); + + ShardedClient = new(new() + { + Token = Config.DiscordToken, + TokenType = TokenType.Bot, + MinimumLogLevel = LogLevel.Debug, + AutoReconnect = true, + ApiChannel = ApiChannel.Canary, + HttpTimeout = TimeSpan.FromMinutes(1), + Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers, + MessageCacheSize = 2048, + LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger), + ShowReleaseNotesInUpdateCheck = false, + IncludePrereleaseInUpdateCheck = true, + DisableUpdateCheck = true, + EnableSentry = true, + FeedbackEmail = "aiko@aitsys.dev", + DeveloperUserId = 856780995629154305, + AttachUserInfo = true, + ReconnectIndefinitely = true + }); + + this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() + { + Timeout = TimeSpan.FromMinutes(2), + PaginationBehaviour = PaginationBehaviour.WrapAround, + PaginationDeletion = PaginationDeletion.DeleteEmojis, + PollBehaviour = PollBehaviour.DeleteEmojis, + AckPaginationButtons = true, + ButtonBehavior = ButtonPaginationBehavior.Disable, + PaginationButtons = new() + { + SkipLeft = new(ButtonStyle.Primary, "pgb-skip-left", "First", false, new("⏮️")), + Left = new(ButtonStyle.Primary, "pgb-left", "Previous", false, new("◀️")), + Stop = new(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new("⏹️")), + Right = new(ButtonStyle.Primary, "pgb-right", "Next", false, new("▶️")), + SkipRight = new(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new("⏭️")) + }, + ResponseMessage = "Something went wrong.", + ResponseBehavior = InteractionResponseBehavior.Ignore + }).Result; + + this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() + { + EnableDefaultHelp = true, + DebugStartup = true, + EnableLocalization = false, + GenerateTranslationFilesOnly = false + }).Result; + + this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() + { + CaseSensitive = false, + EnableMentionPrefix = true, + DmHelp = false, + EnableDefaultHelp = true, + IgnoreExtraArguments = true, + StringPrefixes = [], + UseDefaultCommandHandler = true, + DefaultHelpChecks = [new NotStaffAttribute()] + }).Result; + + this.LavalinkConfig = new() + { + SocketEndpoint = new() + { + Hostname = Config.LavaConfig.Hostname, + Port = Config.LavaConfig.Port + }, + RestEndpoint = new() + { + Hostname = Config.LavaConfig.Hostname, + Port = Config.LavaConfig.Port + }, + Password = Config.LavaConfig.Password + }; + + this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; + } + + internal static CancellationTokenSource Cts { get; set; } + + internal static BotConfig Config { get; set; } + internal LavalinkConfiguration LavalinkConfig { get; set; } + + internal Task GameSetThread { get; set; } + internal Task StatusThread { get; set; } + internal Task BotListThread { get; set; } + internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } + internal static DiscordShardedClient ShardedClient { get; set; } + + internal IReadOnlyDictionary InteractivityModules { get; set; } + internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } + + internal IReadOnlyDictionary CommandsNextModules { get; set; } + internal IReadOnlyDictionary LavalinkModules { get; set; } + + public void Dispose() + { + GC.SuppressFinalize(this); + } + + internal static async Task RegisterEvents() + { + ShardedClient.ClientErrored += (sender, args) => + { + sender.Logger.LogError(args.Exception.Message); + sender.Logger.LogError(args.Exception.StackTrace); + return Task.CompletedTask; + }; + + await Task.Delay(1); + + foreach (var discordClientKvp in ShardedClient.ShardClients) + { + discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => + { + sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", args.ShardId); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleReady += (sender, args) => + { + sender.Client.Logger.LogInformation("Application commands module is ready"); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GuildMemberAdded += async (sender, args) => + { + if (sender.CurrentApplication.Team.Members.Any(x => x.User.Id == args.Member.Id)) + { + var text = $"Heywo <:MikuWave:655783221942026271>!" + + $"\n\nOne of my developers joined your server!" + + $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + + $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + + $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + + $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithGlobalName}!" + + $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; + var message = await args.Guild.Owner.SendMessageAsync(text); + sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithGlobalName); + sender.Logger.LogInformation("Message content: {content}", message.Content); + } + else + await Task.FromResult(true); + }; + discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => + { + if (args.Guild.Id == 483279257431441410) + await MikuGuild.OnUpdateAsync(sender, args); + else + await Task.FromResult(true); + }; + + discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); + } + } + + internal async Task ShowConnections() + { + while (true) + { + var al = Guilds.Where(x => x.Value?.MusicInstance != null); + ShardedClient.Logger.LogInformation("Voice Connections: " + al.Count(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true)); + await Task.Delay(15000); + } + } + + internal static async Task UpdateBotList() + { + await Task.Delay(15000); + + while (true) + { + var me = await DiscordBotListApi.GetMeAsync(); + var count = Array.Empty(); + var clients = ShardedClient.ShardClients.Values; + count = clients.Aggregate(count, (current, client) => [.. current, client.Guilds.Count]); + await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); + await Task.Delay(TimeSpan.FromMinutes(15)); + } + } + + internal static async Task SetActivity() + { + while (true) + { + DiscordActivity test = new() + { + Name = "I'm using slash commands now!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + DiscordActivity test2 = new() + { + Name = "Mention me with help for nsfw commands!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + DiscordActivity test3 = new() + { + Name = "Full NND support!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(test3, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + } + } + + internal void RegisterCommands() + { + // Nsfw stuff needs to be hidden, that's why we use commands next + this.CommandsNextModules.RegisterCommands(); + + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + //ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + + // Smolcar command, only guild command + this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); + } + + internal async Task RunAsync() + { + await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); + await ShardedClient.StartAsync(); + await Task.Delay(5000); + + /*foreach (var lavalinkShard in this.LavalinkModules) + { + var connection = await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + LavalinkSessions.Add(lavalinkShard.Key, connection); + }*/ + + //this.GameSetThread = Task.Run(SetActivity); + //StatusThread = Task.Run(ShowConnections); + //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); + //BotListThread = Task.Run(UpdateBotList); + while (!Cts.IsCancellationRequested) + await Task.Delay(25); + _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); + await ShardedClient.StopAsync(); + } + + ~MikuBot() + { + this.Dispose(); + } +} diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index e22c60fb..b9ac36e3 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net9.0 MikuSharp.Program miku.ico 4.0.0 @@ -18,6 +18,8 @@ miku.jpg README.md discord bot; discatsharp; hatsune miku; miku; bot + true + false @@ -44,30 +46,30 @@ - - - - - - - + + + + + + + - + - + - + - + - - + + - + diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index cf09744b..20a26501 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -4,15 +4,15 @@ namespace MikuSharp; internal class Program { - private static void Main(string[] args) - { - using (var bot = new MikuBot()) - { - MikuBot.RegisterEvents().Wait(); - bot.RegisterCommands(); - bot.RunAsync().Wait(); - } + private static void Main(string[] args) + { + using (var bot = new MikuBot()) + { + MikuBot.RegisterEvents().Wait(); + bot.RegisterCommands(); + bot.RunAsync().Wait(); + } - Log.Logger.Information("Shutdown!"); - } -} \ No newline at end of file + Log.Logger.Information("Shutdown!"); + } +} diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index 06aff7e8..18163377 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -1,52 +1,52 @@ -using DisCatSharp.ApplicationCommands.Context; +using System; +using System.IO; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using Microsoft.Extensions.Logging; using NYoutubeDL; -using System; -using System.IO; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; public static class Bilibili { - public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) - { - try - { - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); - var youtubeDl = new YoutubeDL(@"youtube-dl.exe"); - youtubeDl.Options.FilesystemOptions.Output = $@"{s}.mp4"; - youtubeDl.Options.PostProcessingOptions.ExtractAudio = true; - youtubeDl.Options.PostProcessingOptions.FfmpegLocation = @"ffmpeg.exe"; - youtubeDl.Options.PostProcessingOptions.AudioFormat = NYoutubeDL.Helpers.Enums.AudioFormat.mp3; - youtubeDl.Options.PostProcessingOptions.AddMetadata = true; - youtubeDl.Options.PostProcessingOptions.KeepVideo = false; - youtubeDl.StandardOutputEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; - youtubeDl.StandardErrorEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; - youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; - await youtubeDl.DownloadAsync(); - var ms = new MemoryStream(); + public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) + { + try + { + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); + var youtubeDl = new YoutubeDL(@"youtube-dl.exe"); + youtubeDl.Options.FilesystemOptions.Output = $@"{s}.mp4"; + youtubeDl.Options.PostProcessingOptions.ExtractAudio = true; + youtubeDl.Options.PostProcessingOptions.FfmpegLocation = @"ffmpeg.exe"; + youtubeDl.Options.PostProcessingOptions.AudioFormat = NYoutubeDL.Helpers.Enums.AudioFormat.mp3; + youtubeDl.Options.PostProcessingOptions.AddMetadata = true; + youtubeDl.Options.PostProcessingOptions.KeepVideo = false; + youtubeDl.StandardOutputEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; + youtubeDl.StandardErrorEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; + youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; + await youtubeDl.DownloadAsync(); + var ms = new MemoryStream(); - if (File.Exists($@"{s}.mp3")) - { - var song = File.Open($@"{s}.mp3", FileMode.Open); - await song.CopyToAsync(ms); - ms.Position = 0; - song.Close(); - File.Delete($@"{s}.mp3"); - } + if (File.Exists($@"{s}.mp3")) + { + var song = File.Open($@"{s}.mp3", FileMode.Open); + await song.CopyToAsync(ms); + ms.Position = 0; + song.Close(); + File.Delete($@"{s}.mp3"); + } - return ms; - } - catch (Exception ex) - { - ctx.Client.Logger.LogDebug("{ex}", ex.Message); - ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - return null; - } - } -} \ No newline at end of file + return ms; + } + catch (Exception ex) + { + ctx.Client.Logger.LogDebug("{ex}", ex.Message); + ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); + return null; + } + } +} diff --git a/MikuSharp/Utilities/Database.cs b/MikuSharp/Utilities/Database.cs index 7db70379..f4fa2585 100644 --- a/MikuSharp/Utilities/Database.cs +++ b/MikuSharp/Utilities/Database.cs @@ -1,350 +1,350 @@ -using DisCatSharp.Entities; -using DisCatSharp.Lavalink; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp.Entities; using DisCatSharp.Lavalink.Entities; using MikuSharp.Entities; using Npgsql; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; public class Database { - public static async Task AddToLastPlayingListAsync(ulong g, string ts) - { - var position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guild_id = '{g.ToString()}';", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - position = reader.GetInt32(0); - await reader.CloseAsync(); - await cmd2.DisposeAsync(); - var cmd = new NpgsqlCommand("INSERT INTO lastplayedsongs VALUES (@pos,@guild,@ts)", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = g.ToString(); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "ts"; - para2.Value = ts; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "pos"; - para3.Value = position; - cmd.Parameters.Add(para3); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task ReorderQueue(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var queueNow = await GetQueueAsync(g); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - var i = 0; - var longcmd = ""; - - foreach (var qi in queueNow) - { - longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; - i++; - } - - var cmd2 = new NpgsqlCommand(); - if (string.IsNullOrEmpty(longcmd)) - return; - - cmd2.CommandText = longcmd; - cmd2.Connection = conn; - var para = cmd2.CreateParameter(); - para.ParameterName = "guild"; - para.Value = g.Id.ToString(); - cmd2.Parameters.Add(para); - i = 0; - - foreach (var qi in queueNow) - { - var para2 = cmd2.CreateParameter(); - para2.ParameterName = $"adddate{i}"; - para2.Value = qi.AdditionDate.UtcDateTime; - cmd2.Parameters.Add(para2); - i++; - } - - await cmd2.ExecuteNonQueryAsync(); - await cmd2.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task RebuildQueue(DiscordGuild g, List q) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var queueNow = q; - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}'", conn); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - var i = 0; - var longcmd = ""; - - foreach (var qi in queueNow) - { - longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; - i++; - } - - var cmd2 = new NpgsqlCommand(); - if (string.IsNullOrEmpty(longcmd)) - return; - - cmd2.CommandText = longcmd; - cmd2.Connection = conn; - var para = cmd2.CreateParameter(); - para.ParameterName = "guild"; - para.Value = g.Id.ToString(); - cmd2.Parameters.Add(para); - i = 0; - - foreach (var qi in queueNow) - { - var para2 = cmd2.CreateParameter(); - para2.ParameterName = $"adddate{i}"; - para2.Value = qi.AdditionDate.UtcDateTime; - cmd2.Parameters.Add(para2); - i++; - } - - await cmd2.ExecuteNonQueryAsync(); - await cmd2.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task> GetQueueAsync(DiscordGuild g) - { - List queue = new(); - var count = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd1 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); - count = Convert.ToInt32(await cmd1.ExecuteScalarAsync()); - await cmd1.DisposeAsync(); - - if (count == 0) - { - await conn.CloseAsync(); - await conn.DisposeAsync(); - return queue; - } - - var cmd2 = new NpgsqlCommand($"SELECT * FROM queues WHERE guild_id = '{g.Id.ToString()}' ORDER BY position ASC;", conn); - var reader2 = await cmd2.ExecuteReaderAsync(); - while (await reader2.ReadAsync()) - queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(Convert.ToString(reader2["track"])), Convert.ToUInt64(reader2["user_id"]), DateTimeOffset.Parse(reader2["added_at"].ToString()), Convert.ToInt32(reader2["position"]))); - await reader2.CloseAsync(); - await cmd2.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - return queue; - } - - public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) - { - var position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - position = reader.GetInt32(0); - await reader.CloseAsync(); - await cmd2.DisposeAsync(); - var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@guild,@pos,@user,@ts,@time)", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = g.Id.ToString(); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "user"; - para2.Value = u.ToString(); - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "ts"; - para3.Value = ts; - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "pos"; - para4.Value = position; - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "time"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) - { - var position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - position = reader.GetInt32(0); - await reader.CloseAsync(); - await cmd2.DisposeAsync(); - var longcmd = ""; - - foreach (var tt in ts) - { - longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Encoded}',@time);"; - position++; - } - - var cmd = new NpgsqlCommand(longcmd, conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = g.Id.ToString(); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "user"; - para2.Value = u.ToString(); - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "time"; - para3.Value = DateTime.UtcNow; - cmd.Parameters.Add(para3); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) - { - var position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - position = reader.GetInt32(0); - await reader.CloseAsync(); - await cmd2.DisposeAsync(); - var longcmd = ""; - - foreach (var tt in ts) - { - longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Track.Encoded}',@time);"; - position++; - } - - var cmd = new NpgsqlCommand(longcmd, conn); - var para = cmd.CreateParameter(); - para.ParameterName = "guild"; - para.Value = g.Id.ToString(); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "user"; - para2.Value = u.ToString(); - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "time"; - para3.Value = DateTime.UtcNow; - cmd.Parameters.Add(para3); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task InsertToQueue(DiscordGuild g, ulong u, string ts, int pos) - { - var qnow = await GetQueueAsync(g); - qnow.Insert(pos, new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(ts), u, DateTimeOffset.UtcNow, pos)); - await RebuildQueue(g, qnow); - } - - public static async Task InsertToQueue(DiscordGuild g, ulong u, List ts, int pos) - { - var qnow = await GetQueueAsync(g); - foreach (var tt in ts) - qnow.Insert(pos, new(tt, u, DateTimeOffset.UtcNow, pos)); - await RebuildQueue(g, qnow); - } - - public static async Task RemoveFromQueueAsync(int position, DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE position = {position} AND guild_id = '{g.Id.ToString()}';", conn); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - await ReorderQueue(g); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task ClearQueue(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); - await cmd.ExecuteNonQueryAsync(); - await cmd.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - } - - public static async Task MoveQueueItems(DiscordGuild g, int oldpos, int newpos) - { - var qnow = await GetQueueAsync(g); - var temp = qnow[oldpos]; - qnow.RemoveAt(oldpos); - qnow.Insert(newpos, temp); - await RebuildQueue(g, qnow); - } - - public static async Task> GetLastPlayingListAsync(DiscordGuild g) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM lastplayedsongs WHERE guild_id = '{g.Id.ToString()}' ORDER BY lastplayedsongs.trackposition DESC LIMIT 1000", conn); - var reader = await cmd2.ExecuteReaderAsync(); - List queue = new(); - while (await reader.ReadAsync()) - queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync((string)reader["trackstring"]), DateTimeOffset.UtcNow)); - await reader.CloseAsync(); - await cmd2.DisposeAsync(); - await conn.CloseAsync(); - await conn.DisposeAsync(); - return queue; - } -} \ No newline at end of file + public static async Task AddToLastPlayingListAsync(ulong g, string ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM lastplayedsongs WHERE guild_id = '{g.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var cmd = new NpgsqlCommand("INSERT INTO lastplayedsongs VALUES (@pos,@guild,@ts)", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "ts"; + para2.Value = ts; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "pos"; + para3.Value = position; + cmd.Parameters.Add(para3); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task ReorderQueue(DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var queueNow = await GetQueueAsync(g); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + var i = 0; + var longcmd = ""; + + foreach (var qi in queueNow) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; + i++; + } + + var cmd2 = new NpgsqlCommand(); + if (string.IsNullOrEmpty(longcmd)) + return; + + cmd2.CommandText = longcmd; + cmd2.Connection = conn; + var para = cmd2.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd2.Parameters.Add(para); + i = 0; + + foreach (var qi in queueNow) + { + var para2 = cmd2.CreateParameter(); + para2.ParameterName = $"adddate{i}"; + para2.Value = qi.AdditionDate.UtcDateTime; + cmd2.Parameters.Add(para2); + i++; + } + + await cmd2.ExecuteNonQueryAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task RebuildQueue(DiscordGuild g, List q) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var queueNow = q; + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}'", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + var i = 0; + var longcmd = ""; + + foreach (var qi in queueNow) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{i},'{qi.AddedBy.ToString()}','{qi.Track.Encoded}',@adddate{i});"; + i++; + } + + var cmd2 = new NpgsqlCommand(); + if (string.IsNullOrEmpty(longcmd)) + return; + + cmd2.CommandText = longcmd; + cmd2.Connection = conn; + var para = cmd2.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd2.Parameters.Add(para); + i = 0; + + foreach (var qi in queueNow) + { + var para2 = cmd2.CreateParameter(); + para2.ParameterName = $"adddate{i}"; + para2.Value = qi.AdditionDate.UtcDateTime; + cmd2.Parameters.Add(para2); + i++; + } + + await cmd2.ExecuteNonQueryAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task> GetQueueAsync(DiscordGuild g) + { + List queue = []; + var count = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd1 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + count = Convert.ToInt32(await cmd1.ExecuteScalarAsync()); + await cmd1.DisposeAsync(); + + if (count == 0) + { + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } + + var cmd2 = new NpgsqlCommand($"SELECT * FROM queues WHERE guild_id = '{g.Id.ToString()}' ORDER BY position ASC;", conn); + var reader2 = await cmd2.ExecuteReaderAsync(); + while (await reader2.ReadAsync()) + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(Convert.ToString(reader2["track"])), Convert.ToUInt64(reader2["user_id"]), DateTimeOffset.Parse(reader2["added_at"].ToString()), + Convert.ToInt32(reader2["position"]))); + await reader2.CloseAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } + + public static async Task AddToQueue(DiscordGuild g, ulong u, string ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var cmd = new NpgsqlCommand("INSERT INTO queues VALUES (@guild,@pos,@user,@ts,@time)", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "user"; + para2.Value = u.ToString(); + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "ts"; + para3.Value = ts; + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "pos"; + para4.Value = position; + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "time"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var longcmd = ""; + + foreach (var tt in ts) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Encoded}',@time);"; + position++; + } + + var cmd = new NpgsqlCommand(longcmd, conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "user"; + para2.Value = u.ToString(); + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "time"; + para3.Value = DateTime.UtcNow; + cmd.Parameters.Add(para3); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task AddToQueue(DiscordGuild g, ulong u, List ts) + { + var position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + position = reader.GetInt32(0); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + var longcmd = ""; + + foreach (var tt in ts) + { + longcmd += $"INSERT INTO queues VALUES (@guild,{position},@user,'{tt.Track.Encoded}',@time);"; + position++; + } + + var cmd = new NpgsqlCommand(longcmd, conn); + var para = cmd.CreateParameter(); + para.ParameterName = "guild"; + para.Value = g.Id.ToString(); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "user"; + para2.Value = u.ToString(); + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "time"; + para3.Value = DateTime.UtcNow; + cmd.Parameters.Add(para3); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task InsertToQueue(DiscordGuild g, ulong u, string ts, int pos) + { + var qnow = await GetQueueAsync(g); + qnow.Insert(pos, new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync(ts), u, DateTimeOffset.UtcNow, pos)); + await RebuildQueue(g, qnow); + } + + public static async Task InsertToQueue(DiscordGuild g, ulong u, List ts, int pos) + { + var qnow = await GetQueueAsync(g); + foreach (var tt in ts) + qnow.Insert(pos, new(tt, u, DateTimeOffset.UtcNow, pos)); + await RebuildQueue(g, qnow); + } + + public static async Task RemoveFromQueueAsync(int position, DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE position = {position} AND guild_id = '{g.Id.ToString()}';", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await ReorderQueue(g); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task ClearQueue(DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM queues WHERE guild_id = '{g.Id.ToString()}';", conn); + await cmd.ExecuteNonQueryAsync(); + await cmd.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + } + + public static async Task MoveQueueItems(DiscordGuild g, int oldpos, int newpos) + { + var qnow = await GetQueueAsync(g); + var temp = qnow[oldpos]; + qnow.RemoveAt(oldpos); + qnow.Insert(newpos, temp); + await RebuildQueue(g, qnow); + } + + public static async Task> GetLastPlayingListAsync(DiscordGuild g) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM lastplayedsongs WHERE guild_id = '{g.Id.ToString()}' ORDER BY lastplayedsongs.trackposition DESC LIMIT 1000", conn); + var reader = await cmd2.ExecuteReaderAsync(); + List queue = []; + while (await reader.ReadAsync()) + queue.Add(new(await MikuBot.LavalinkSessions.First().Value.DecodeTrackAsync((string)reader["trackstring"]), DateTimeOffset.UtcNow)); + await reader.CloseAsync(); + await cmd2.DisposeAsync(); + await conn.CloseAsync(); + await conn.DisposeAsync(); + return queue; + } +} diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 25141597..162b1460 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,112 +1,114 @@ -using DisCatSharp.ApplicationCommands.Attributes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using MikuSharp.Entities; using MikuSharp.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; internal class FixedOptionProviders { - internal sealed class RepeatModeProvider : ChoiceProvider - { - public override Task> Provider() - { - var list = new List(3) - { - new("Off", $"{(int)RepeatMode.Off}"), - new("On", $"{(int)RepeatMode.On}"), - new("All", $"{(int)RepeatMode.All}") - }; - return Task.FromResult>(list); - } - } + internal sealed class RepeatModeProvider : ChoiceProvider + { + public override Task> Provider() + { + var list = new List(3) + { + new("Off", $"{(int)RepeatMode.Off}"), + new("On", $"{(int)RepeatMode.On}"), + new("All", $"{(int)RepeatMode.All}") + }; + return Task.FromResult>(list); + } + } } internal class AutocompleteProviders { - internal sealed class BanProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var bans = await ctx.Guild.GetBansAsync(); - List bannedUsers = new(25); - bannedUsers.AddRange(ctx.FocusedOption.Value is null ? bans.Take(25) : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); - - return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); - } - } - - /* - internal class PlaylistProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (plls.Count == 0) - return new List() { new("You have no songs", "error") }; - - var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - - List> playlists = new(25); - if (ctx.FocusedOption.Value == null) - playlists.AddRange(DbPlaylists.Take(25)); - else - playlists.AddRange(DbPlaylists.Where(x => x.Value.Name.Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); - } - } - - internal class SongProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); - - switch (playlist) - { - case null: - return new List() { new("You have no playlist selected", "error") }; - case "error": - return new List() { new("You have no valid playlist selected", "error") }; - } - - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var tracks = await pls.GetEntries(); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(tracks.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(tracks.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); - } - } - */ - - internal sealed class QueueProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var queue = await Database.GetQueueAsync(ctx.Guild); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(queue.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(queue.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(queue.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); - } - } -} \ No newline at end of file + internal sealed class BanProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var bans = await ctx.Guild.GetBansAsync(); + List bannedUsers = new(25); + bannedUsers.AddRange(ctx.FocusedOption.Value is null + ? bans.Take(25) + : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); + + return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); + } + } + + /* + internal class PlaylistProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (plls.Count == 0) + return new List() { new("You have no songs", "error") }; + + var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); + + List> playlists = new(25); + if (ctx.FocusedOption.Value == null) + playlists.AddRange(DbPlaylists.Take(25)); + else + playlists.AddRange(DbPlaylists.Where(x => x.Value.Name.Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + + return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); + } + } + + internal class SongProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); + + switch (playlist) + { + case null: + return new List() { new("You have no playlist selected", "error") }; + case "error": + return new List() { new("You have no valid playlist selected", "error") }; + } + + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + var tracks = await pls.GetEntries(); + List songs = new(25); + if (ctx.FocusedOption.Value == null) + songs.AddRange(tracks.Take(25)); + else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) + songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); + else + songs.AddRange(tracks.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); + } + } + */ + + internal sealed class QueueProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var queue = await Database.GetQueueAsync(ctx.Guild); + List songs = new(25); + if (ctx.FocusedOption.Value == null) + songs.AddRange(queue.Take(25)); + else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) + songs.AddRange(queue.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); + else + songs.AddRange(queue.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); + } + } +} diff --git a/MikuSharp/Utilities/Music.cs b/MikuSharp/Utilities/Music.cs index 29fc35d2..f60cfe3f 100644 --- a/MikuSharp/Utilities/Music.cs +++ b/MikuSharp/Utilities/Music.cs @@ -1,4 +1,10 @@ -using AlbumArtExtraction; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +using AlbumArtExtraction; using DisCatSharp; using DisCatSharp.ApplicationCommands.Context; @@ -13,213 +19,224 @@ using MikuSharp.Entities; using MikuSharp.Enums; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; public static class Music { - public static ExtService GetExtService(string e) - { - return e switch - { - "Youtube" => ExtService.Youtube, - "Soundcloud" => ExtService.Soundcloud, - _ => ExtService.None - }; - } - - public static string GetPlaybackOptions(this MusicInstance instance) - { - string opts = null; - - switch (instance.RepeatMode) - { - case RepeatMode.On: - opts += DiscordEmoji.FromUnicode("🔂"); - break; - case RepeatMode.All: - opts += DiscordEmoji.FromUnicode("🔁"); - break; - } - - if (instance.ShuffleMode == ShuffleMode.On) - opts += DiscordEmoji.FromUnicode("🔀"); - - return opts ?? "None"; - } - - public static async Task ConditionalConnect(this Guild guild, InteractionContext ctx) - { - if (!guild.MusicInstance?.GuildConnection?.IsConnected != null && !guild.MusicInstance.GuildConnection.IsConnected) - return; - - await guild.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); - } - - public static async Task IsNotConnected(this Guild guild, InteractionContext ctx) - { - if (guild.MusicInstance == null! || guild.MusicInstance?.GuildConnection?.IsConnected == false) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Not connected!")); - return true; - } - else - return false; - } - - public static string SearchUrlOrAttachment(this DiscordAttachment? attachment, string? searchOrUrl) - => !string.IsNullOrEmpty(searchOrUrl) ? searchOrUrl : attachment?.ProxyUrl; - - public static void GetPlayingState(this Guild guild, out string time1, out string time2) - { - switch (guild.MusicInstance.CurrentSong.Track.Info.Length.Hours) - { - case < 1: - time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"mm\:ss"); - time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"mm\:ss"); - break; - default: - time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"hh\:mm\:ss"); - time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"hh\:mm\:ss"); - break; - } - } - - public static void GetPlayingState(this Entry entry, out string time) - { - time = entry.Track.Info.Length.Hours switch - { - < 1 => entry.Track.Info.Length.ToString(@"mm\:ss"), - _ => entry.Track.Info.Length.ToString(@"hh\:mm\:ss") - }; - } - - public static async Task GetYoutubePlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) - { - try - { - var youtubeService = new YouTubeService(new() - { - ApiKey = MikuBot.Config.YoutubeApiToken, - ApplicationName = typeof(MikuBot).ToString() - }); - var searchListRequest = youtubeService.Search.List("snippet"); - searchListRequest.Q = lastPlayedSongs != null ? lastPlayedSongs[0].Track.Info.Title + " " + lastPlayedSongs[0].Track.Info.Author : guild.MusicInstance.CurrentSong.Track.Info.Title + " " + guild.MusicInstance.CurrentSong.Track.Info.Author; - searchListRequest.MaxResults = 1; - searchListRequest.Type = "video"; - var searchListResponse = await searchListRequest.ExecuteAsync(); - - if (lastPlayedSongs == null) - { - guild.GetPlayingState(out var time1, out var time2); - builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({time1}/{time2})", $"[Video Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\n" + $"[{guild.MusicInstance.CurrentSong.Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); - } - else - { - lastPlayedSongs[0].GetPlayingState(out var time); - builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({time})", $"[Video Link]({lastPlayedSongs[0].Track.Info.Uri})\n" + $"[{lastPlayedSongs[0].Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); - } - - builder.AddField(new("Description", searchListResponse.Items[0].Snippet.Description.Length > 1000 ? string.Concat(searchListResponse.Items[0].Snippet.Description.AsSpan(0, 1000), "...") : searchListResponse.Items[0].Snippet.Description - )); - builder.WithImageUrl(searchListResponse.Items[0].Snippet.Thumbnails.High.Url); - builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); - } - catch (Exception) - { - if (builder.Fields.Count != 1) - { - if (lastPlayedSongs == null) - builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({guild.MusicInstance.CurrentSong.Track.Info.Length})", $"By {guild.MusicInstance.CurrentSong.Track.Info.Author}\n[Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\nRequested by <@{guild.MusicInstance.CurrentSong.AddedBy}>")); - else - builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({lastPlayedSongs[0].Track.Info.Length})", $"By {lastPlayedSongs[0].Track.Info.Author}\n[Link]({lastPlayedSongs[0].Track.Info.Uri})")); - builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); - } - } - - return builder; - } - - public static async Task GetUrlPlayingInformationAsync(this DiscordEmbedBuilder builder, DiscordClient client, Guild guild, List? lastPlayedSongs) - { - Stream? img = null; - var entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.MusicInstance.CurrentSong; - - try - { - MemoryStream d = new(await client.RestClient.GetByteArrayAsync(entry.Track.Info.Uri)) - { - Position = 0 - }; - var e = File.Create($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); - await d.CopyToAsync(e); - e.Close(); - var selector = new Selector(); - var extractor = selector.SelectAlbumArtExtractor($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); - img = extractor.Extract($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); - } - catch (Exception ex) - { - client.Logger.LogDebug(ex.Message); - client.Logger.LogDebug(ex.StackTrace); - img = null; - File.Delete($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); - } - - builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); - builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); - if (img != null) - builder.WithImageUrl($"attachment://{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{MimeGuesser.GuessExtension(img)}"); - return builder; - } - - public static DiscordEmbedBuilder GetOtherPlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) - { - var entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.MusicInstance.CurrentSong; - builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); - builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); - return builder; - } - - public static string GetDynamicPlayingState(this Guild guild, List? lastPlayedSongs = null) - { - switch (lastPlayedSongs) - { - case null: - { - guild.GetPlayingState(out var time1, out var time2); - return $"{time1}/{time2}"; - } - - default: - { - lastPlayedSongs[0].GetPlayingState(out var time); - return $"{time}"; - } - } - } - - public static async Task SendPlayingInformationAsync(this InteractionContext ctx, DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) - { - var entry = lastPlayedSongs != null ? lastPlayedSongs[0] : guild.MusicInstance.CurrentSong; - - if (entry == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); - return; - } - - builder = entry.Track.Info.Uri.ToString().Contains("youtu") - ? await builder.GetYoutubePlayingInformationAsync(guild) - : entry.Track.Info.Uri.ToString().StartsWith("https://media.discordapp.net/attachments/", StringComparison.Ordinal) || entry.Track.Info.Uri.ToString().StartsWith("https://cdn.discordapp.com/attachments/", StringComparison.Ordinal) - ? await builder.GetUrlPlayingInformationAsync(ctx.Client, guild, lastPlayedSongs) - : builder.GetOtherPlayingInformationAsync(guild, lastPlayedSongs); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); - } -} \ No newline at end of file + public static ExtService GetExtService(string e) + { + return e switch + { + "Youtube" => ExtService.Youtube, + "Soundcloud" => ExtService.Soundcloud, + _ => ExtService.None + }; + } + + public static string GetPlaybackOptions(this MusicInstance instance) + { + string opts = null; + + switch (instance.RepeatMode) + { + case RepeatMode.On: + opts += DiscordEmoji.FromUnicode("🔂"); + break; + case RepeatMode.All: + opts += DiscordEmoji.FromUnicode("🔁"); + break; + } + + if (instance.ShuffleMode == ShuffleMode.On) + opts += DiscordEmoji.FromUnicode("🔀"); + + return opts ?? "None"; + } + + public static async Task ConditionalConnect(this Guild guild, InteractionContext ctx) + { + if (!guild.MusicInstance?.GuildConnection?.IsConnected != null && !guild.MusicInstance.GuildConnection.IsConnected) + return; + + await guild.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); + } + + public static async Task IsNotConnected(this Guild guild, InteractionContext ctx) + { + if (guild.MusicInstance == null! || guild.MusicInstance?.GuildConnection?.IsConnected == false) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Not connected!")); + return true; + } + + return false; + } + + public static string SearchUrlOrAttachment(this DiscordAttachment? attachment, string? searchOrUrl) + => !string.IsNullOrEmpty(searchOrUrl) + ? searchOrUrl + : attachment?.ProxyUrl; + + public static void GetPlayingState(this Guild guild, out string time1, out string time2) + { + switch (guild.MusicInstance.CurrentSong.Track.Info.Length.Hours) + { + case < 1: + time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"mm\:ss"); + time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"mm\:ss"); + break; + default: + time1 = guild.MusicInstance.GuildConnection.TrackPosition.ToString(@"hh\:mm\:ss"); + time2 = guild.MusicInstance.CurrentSong.Track.Info.Length.ToString(@"hh\:mm\:ss"); + break; + } + } + + public static void GetPlayingState(this Entry entry, out string time) + { + time = entry.Track.Info.Length.Hours switch + { + < 1 => entry.Track.Info.Length.ToString(@"mm\:ss"), + _ => entry.Track.Info.Length.ToString(@"hh\:mm\:ss") + }; + } + + public static async Task GetYoutubePlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) + { + try + { + var youtubeService = new YouTubeService(new() + { + ApiKey = MikuBot.Config.YoutubeApiToken, + ApplicationName = typeof(MikuBot).ToString() + }); + var searchListRequest = youtubeService.Search.List("snippet"); + searchListRequest.Q = lastPlayedSongs != null + ? lastPlayedSongs[0].Track.Info.Title + " " + lastPlayedSongs[0].Track.Info.Author + : guild.MusicInstance.CurrentSong.Track.Info.Title + " " + guild.MusicInstance.CurrentSong.Track.Info.Author; + searchListRequest.MaxResults = 1; + searchListRequest.Type = "video"; + var searchListResponse = await searchListRequest.ExecuteAsync(); + + if (lastPlayedSongs == null) + { + guild.GetPlayingState(out var time1, out var time2); + builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({time1}/{time2})", + $"[Video Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\n" + $"[{guild.MusicInstance.CurrentSong.Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); + } + else + { + lastPlayedSongs[0].GetPlayingState(out var time); + builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({time})", + $"[Video Link]({lastPlayedSongs[0].Track.Info.Uri})\n" + $"[{lastPlayedSongs[0].Track.Info.Author}](https://www.youtube.com/channel/" + searchListResponse.Items[0].Snippet.ChannelId + ")")); + } + + builder.AddField(new("Description", searchListResponse.Items[0].Snippet.Description.Length > 1000 + ? string.Concat(searchListResponse.Items[0].Snippet.Description.AsSpan(0, 1000), "...") + : searchListResponse.Items[0].Snippet.Description + )); + builder.WithImageUrl(searchListResponse.Items[0].Snippet.Thumbnails.High.Url); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + } + catch (Exception) + { + if (builder.Fields.Count != 1) + { + if (lastPlayedSongs == null) + builder.AddField(new($"{guild.MusicInstance.CurrentSong.Track.Info.Title} ({guild.MusicInstance.CurrentSong.Track.Info.Length})", + $"By {guild.MusicInstance.CurrentSong.Track.Info.Author}\n[Link]({guild.MusicInstance.CurrentSong.Track.Info.Uri})\nRequested by <@{guild.MusicInstance.CurrentSong.AddedBy}>")); + else + builder.AddField(new($"{lastPlayedSongs[0].Track.Info.Title} ({lastPlayedSongs[0].Track.Info.Length})", $"By {lastPlayedSongs[0].Track.Info.Author}\n[Link]({lastPlayedSongs[0].Track.Info.Uri})")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + } + } + + return builder; + } + + public static async Task GetUrlPlayingInformationAsync(this DiscordEmbedBuilder builder, DiscordClient client, Guild guild, List? lastPlayedSongs) + { + Stream? img = null; + var entry = lastPlayedSongs != null + ? lastPlayedSongs[0] + : guild.MusicInstance.CurrentSong; + + try + { + MemoryStream d = new(await client.RestClient.GetByteArrayAsync(entry.Track.Info.Uri)) + { + Position = 0 + }; + var e = File.Create($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + await d.CopyToAsync(e); + e.Close(); + var selector = new Selector(); + var extractor = selector.SelectAlbumArtExtractor($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + img = extractor.Extract($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + } + catch (Exception ex) + { + client.Logger.LogDebug(ex.Message); + client.Logger.LogDebug(ex.StackTrace); + img = null; + File.Delete($@"{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{entry.Track.Info.Uri.ToString().Split('/').Last()}"); + } + + builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", + $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + if (img != null) + builder.WithImageUrl($"attachment://{entry.Track.Info.Uri.ToString().Split('/')[^2]}.{MimeGuesser.GuessExtension(img)}"); + return builder; + } + + public static DiscordEmbedBuilder GetOtherPlayingInformationAsync(this DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) + { + var entry = lastPlayedSongs != null + ? lastPlayedSongs[0] + : guild.MusicInstance.CurrentSong; + builder.AddField(new($"{entry.Track.Info.Title} ({guild.GetDynamicPlayingState(lastPlayedSongs)})", + $"By {entry.Track.Info.Author}\n[Link]({entry.Track.Info.Uri})\n{(lastPlayedSongs == null ? $"Requested by <@{guild.MusicInstance.CurrentSong.AddedBy}>" : "")}")); + builder.AddField(new("Playback options", guild.MusicInstance.GetPlaybackOptions())); + return builder; + } + + public static string GetDynamicPlayingState(this Guild guild, List? lastPlayedSongs = null) + { + switch (lastPlayedSongs) + { + case null: + { + guild.GetPlayingState(out var time1, out var time2); + return $"{time1}/{time2}"; + } + + default: + { + lastPlayedSongs[0].GetPlayingState(out var time); + return $"{time}"; + } + } + } + + public static async Task SendPlayingInformationAsync(this InteractionContext ctx, DiscordEmbedBuilder builder, Guild guild, List? lastPlayedSongs = null) + { + var entry = lastPlayedSongs != null + ? lastPlayedSongs[0] + : guild.MusicInstance.CurrentSong; + + if (entry == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); + return; + } + + builder = entry.Track.Info.Uri.ToString().Contains("youtu") + ? await builder.GetYoutubePlayingInformationAsync(guild) + : entry.Track.Info.Uri.ToString().StartsWith("https://media.discordapp.net/attachments/", StringComparison.Ordinal) || entry.Track.Info.Uri.ToString().StartsWith("https://cdn.discordapp.com/attachments/", StringComparison.Ordinal) + ? await builder.GetUrlPlayingInformationAsync(ctx.Client, guild, lastPlayedSongs) + : builder.GetOtherPlayingInformationAsync(guild, lastPlayedSongs); + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); + } +} diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs index a91e3733..a16d145a 100644 --- a/MikuSharp/Utilities/NND.cs +++ b/MikuSharp/Utilities/NND.cs @@ -1,59 +1,59 @@ -using DisCatSharp.ApplicationCommands.Context; +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using Microsoft.Extensions.Logging; using NicoNicoNii; -using System; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; public static class Nnd { - public static async Task GetNndAsync(this InteractionContext ctx, string n, string s, ulong msgId) - { - try - { - NndClient nndClient = new(); - NicoVideoClient videoClient = new(nndClient); - var videoPage = await videoClient.GetWatchPageInfoAsync(s); - var downloadExe = "nnd.exe"; - var linuxExe = "nndownload.py"; - var cmd = downloadExe; - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); - if (OperatingSystem.IsLinux()) - cmd = linuxExe; - Process downloadProcess = new(); - downloadProcess.StartInfo.FileName = cmd; - downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; - downloadProcess.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); }; - downloadProcess.Start(); - await downloadProcess.WaitForExitAsync(); - var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; - var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); - Process convertProgress = new(); - convertProgress.StartInfo.FileName = "ffmpeg"; - convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; - convertProgress.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", f.Data); }; - convertProgress.Start(); - await convertProgress.WaitForExitAsync(); - File.Delete($@"{s}.mp4"); - MemoryStream ms = new(await File.ReadAllBytesAsync($@"{s}.mp3")); - File.Delete($@"{s}.mp3"); - ms.Position = 0; - return ms; - } - catch (Exception ex) - { - ctx.Client.Logger.LogDebug("{ex}", ex.Message); - ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); - return null; - } - } -} \ No newline at end of file + public static async Task GetNndAsync(this InteractionContext ctx, string n, string s, ulong msgId) + { + try + { + NndClient nndClient = new(); + NicoVideoClient videoClient = new(nndClient); + var videoPage = await videoClient.GetWatchPageInfoAsync(s); + var downloadExe = "nnd.exe"; + var linuxExe = "nndownload.py"; + var cmd = downloadExe; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); + if (OperatingSystem.IsLinux()) + cmd = linuxExe; + Process downloadProcess = new(); + downloadProcess.StartInfo.FileName = cmd; + downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; + downloadProcess.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); }; + downloadProcess.Start(); + await downloadProcess.WaitForExitAsync(); + var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; + var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); + Process convertProgress = new(); + convertProgress.StartInfo.FileName = "ffmpeg"; + convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; + convertProgress.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", f.Data); }; + convertProgress.Start(); + await convertProgress.WaitForExitAsync(); + File.Delete($@"{s}.mp4"); + MemoryStream ms = new(await File.ReadAllBytesAsync($@"{s}.mp3")); + File.Delete($@"{s}.mp3"); + ms.Position = 0; + return ms; + } + catch (Exception ex) + { + ctx.Client.Logger.LogDebug("{ex}", ex.Message); + ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); + return null; + } + } +} diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 97933152..e96d24b3 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -1,21 +1,21 @@ -using DisCatSharp.ApplicationCommands.Context; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; -using System.Threading.Tasks; - namespace MikuSharp.Utilities; public static class Other { - public static string ResizeLink(string url) - => $"https://api.meek.moe/im/?image={url}&resize=500"; + public static string ResizeLink(string url) + => $"https://api.meek.moe/im/?image={url}&resize=500"; - public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) - { - var builder = new DiscordInteractionResponseBuilder(); - if (ephemeral) - builder.AsEphemeral(); - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); - } -} \ No newline at end of file + public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) + { + var builder = new DiscordInteractionResponseBuilder(); + if (ephemeral) + builder.AsEphemeral(); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); + } +} diff --git a/MikuSharp/Utilities/PlaylistDB.cs b/MikuSharp/Utilities/PlaylistDB.cs index 81298997..6d051d45 100644 --- a/MikuSharp/Utilities/PlaylistDB.cs +++ b/MikuSharp/Utilities/PlaylistDB.cs @@ -21,604 +21,605 @@ namespace MikuSharp.Utilities; public class PlaylistDB { - public static async Task> GetPlaylists(DiscordGuild guild, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); - var reader = await cmd2.ExecuteReaderAsync(); - Dictionary lists = new(); - while (await reader.ReadAsync()) - { - lists.Add(Convert.ToString(reader["playlistname"]), await GetPlaylist(guild, u, Convert.ToString(reader["playlistname"]))); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - return lists; - } + public static async Task> GetPlaylists(DiscordGuild guild, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); + var reader = await cmd2.ExecuteReaderAsync(); + Dictionary lists = new(); + while (await reader.ReadAsync()) + { + lists.Add(Convert.ToString(reader["playlistname"]), await GetPlaylist(guild, u, Convert.ToString(reader["playlistname"]))); + } + reader.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + return lists; + } - public static async Task> GetPlaylistsSimple(ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); - var reader = await cmd2.ExecuteReaderAsync(); - List lists = new(); - while (await reader.ReadAsync()) - { - lists.Add(Convert.ToString(reader["playlistname"])); - } - reader.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - return lists; - } + public static async Task> GetPlaylistsSimple(ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT * FROM playlists WHERE userid = {u} ORDER BY playlistname ASC;", conn); + var reader = await cmd2.ExecuteReaderAsync(); + List lists = new(); + while (await reader.ReadAsync()) + { + lists.Add(Convert.ToString(reader["playlistname"])); + } + reader.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + return lists; + } - public static async Task GetPlaylist(DiscordGuild guild, ulong u, string p) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - int am = 0; - var cmd = new NpgsqlCommand($"SELECT COUNT(*)" + - $"FROM playlistentries " + - $"WHERE userid = {u} " + - $"AND playlistname = @pl;", conn); - cmd.Parameters.AddWithValue("pl", p); - var reader = await cmd.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - am = Convert.ToInt32(reader["count"]); - } - reader.Close(); - cmd.Dispose(); - var cmd2 = new NpgsqlCommand($"SELECT *" + - $"FROM playlists " + - $"WHERE userid = {u} " + - $"AND playlistname = @pl;", conn); - var para = cmd2.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd2.Parameters.Add(para); - var reader2 = await cmd2.ExecuteReaderAsync(); - Playlist pl = null; - while (await reader2.ReadAsync()) - { - if (Music.GetExtService(Convert.ToString(reader2["extservice"])) != ExtService.None) - { - try - { - var ss = await MikuBot.LavalinkNodeConnections.First().Value.Rest.GetTracksAsync(new Uri(Convert.ToString(reader2["url"]))); - am = ss.Tracks.Count; - } - catch { } - } - pl = new Playlist(Music.GetExtService(Convert.ToString(reader2["extservice"])), Convert.ToString(reader2["url"]), Convert.ToString(reader2["playlistname"]), Convert.ToUInt64(reader2["userid"]), am, DateTimeOffset.Parse(Convert.ToString(reader2["creation"])), DateTimeOffset.Parse(Convert.ToString(reader2["changed"]))); - } - reader2.Close(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - if (pl == null) throw new Exception("Tf is up? " + p); - return pl; - } + public static async Task GetPlaylist(DiscordGuild guild, ulong u, string p) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + int am = 0; + var cmd = new NpgsqlCommand($"SELECT COUNT(*)" + + $"FROM playlistentries " + + $"WHERE userid = {u} " + + $"AND playlistname = @pl;", conn); + cmd.Parameters.AddWithValue("pl", p); + var reader = await cmd.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + am = Convert.ToInt32(reader["count"]); + } + reader.Close(); + cmd.Dispose(); + var cmd2 = new NpgsqlCommand($"SELECT *" + + $"FROM playlists " + + $"WHERE userid = {u} " + + $"AND playlistname = @pl;", conn); + var para = cmd2.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd2.Parameters.Add(para); + var reader2 = await cmd2.ExecuteReaderAsync(); + Playlist pl = null; + while (await reader2.ReadAsync()) + { + if (Music.GetExtService(Convert.ToString(reader2["extservice"])) != ExtService.None) + { + try + { + var ss = await MikuBot.LavalinkNodeConnections.First().Value.Rest.GetTracksAsync(new Uri(Convert.ToString(reader2["url"]))); + am = ss.Tracks.Count; + } + catch { } + } + pl = new Playlist(Music.GetExtService(Convert.ToString(reader2["extservice"])), Convert.ToString(reader2["url"]), Convert.ToString(reader2["playlistname"]), Convert.ToUInt64(reader2["userid"]), am, DateTimeOffset.Parse(Convert.ToString(reader2["creation"])), DateTimeOffset.Parse(Convert.ToString(reader2["changed"]))); + } + reader2.Close(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + if (pl == null) throw new Exception("Tf is up? " + p); + return pl; + } - public static async Task ReorderList(DiscordGuild guild, string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var listNow = await GetPlaylist(guild, u, p); - var ln = await listNow.GetEntries(); - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - int i = 0; - string longcmd = ""; - foreach (var qi in ln) - { - string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; - string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; - longcmd += $"INSERT INTO playlistentries VALUES ({i},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; - i++; - } - var cmd2 = new NpgsqlCommand(longcmd, conn); - var para1 = cmd2.CreateParameter(); - para1.ParameterName = "p"; - para1.Value = p; - cmd2.Parameters.Add(para1); - var para2 = cmd2.CreateParameter(); - para2.ParameterName = "u"; - para2.Value = Convert.ToInt64(u); - cmd2.Parameters.Add(para2); - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task ReorderList(DiscordGuild guild, string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var listNow = await GetPlaylist(guild, u, p); + var ln = await listNow.GetEntries(); + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + int i = 0; + string longcmd = ""; + foreach (var qi in ln) + { + string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; + string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; + longcmd += $"INSERT INTO playlistentries VALUES ({i},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; + i++; + } + var cmd2 = new NpgsqlCommand(longcmd, conn); + var para1 = cmd2.CreateParameter(); + para1.ParameterName = "p"; + para1.Value = p; + cmd2.Parameters.Add(para1); + var para2 = cmd2.CreateParameter(); + para2.ParameterName = "u"; + para2.Value = Convert.ToInt64(u); + cmd2.Parameters.Add(para2); + await cmd2.ExecuteNonQueryAsync(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task RebuildList(ulong u, string p, List q) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var queueNow = q; - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - string longcmd = ""; - foreach (var qi in queueNow) - { - string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; - string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; - longcmd += $"INSERT INTO playlistentries VALUES ({qi.Position},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; - } - var cmd2 = new NpgsqlCommand(longcmd, conn); - var para1 = cmd2.CreateParameter(); - para1.ParameterName = "p"; - para1.Value = p; - cmd2.Parameters.Add(para1); - var para2 = cmd2.CreateParameter(); - para2.ParameterName = "u"; - para2.Value = Convert.ToInt64(u); - cmd2.Parameters.Add(para2); - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task RebuildList(ulong u, string p, List q) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var queueNow = q; + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + string longcmd = ""; + foreach (var qi in queueNow) + { + string adddate = $"{qi.additionDate.UtcDateTime.Year}-{qi.additionDate.UtcDateTime.Month}-{qi.additionDate.UtcDateTime.Day} {qi.additionDate.UtcDateTime.Hour}:{qi.additionDate.UtcDateTime.Minute}:{qi.additionDate.UtcDateTime.Second}"; + string moddate = $"{qi.modifyDate.UtcDateTime.Year}-{qi.modifyDate.UtcDateTime.Month}-{qi.modifyDate.UtcDateTime.Day} {qi.modifyDate.UtcDateTime.Hour}:{qi.modifyDate.UtcDateTime.Minute}:{qi.modifyDate.UtcDateTime.Second}"; + longcmd += $"INSERT INTO playlistentries VALUES ({qi.Position},@p,@u,'{qi.track.TrackString}','{adddate}','{moddate}');"; + } + var cmd2 = new NpgsqlCommand(longcmd, conn); + var para1 = cmd2.CreateParameter(); + para1.ParameterName = "p"; + para1.Value = p; + cmd2.Parameters.Add(para1); + var para2 = cmd2.CreateParameter(); + para2.ParameterName = "u"; + para2.Value = Convert.ToInt64(u); + cmd2.Parameters.Add(para2); + await cmd2.ExecuteNonQueryAsync(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task AddPlaylist(string p, ulong u, ExtService e = ExtService.None, string url = "") - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand("INSERT INTO playlists VALUES (@u,@p,@url,@ext,@cre,@mody)", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "u"; - para.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "p"; - para2.Value = p; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "url"; - para3.Value = url; - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "ext"; - para4.Value = e.ToString(); - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "cre"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - var para6 = cmd.CreateParameter(); - para6.ParameterName = "mody"; - para6.Value = DateTime.UtcNow; - cmd.Parameters.Add(para6); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task AddPlaylist(string p, ulong u, ExtService e = ExtService.None, string url = "") + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand("INSERT INTO playlists VALUES (@u,@p,@url,@ext,@cre,@mody)", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "u"; + para.Value = Convert.ToInt64(u); + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "p"; + para2.Value = p; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "url"; + para3.Value = url; + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "ext"; + para4.Value = e.ToString(); + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "cre"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + var para6 = cmd.CreateParameter(); + para6.ParameterName = "mody"; + para6.Value = DateTime.UtcNow; + cmd.Parameters.Add(para6); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task RemovePlaylist(string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM playlists WHERE playlistname = @pl AND userid = {u};", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - await ClearList(p, u); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task RemovePlaylist(string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM playlists WHERE playlistname = @pl AND userid = {u};", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + await ClearList(p, u); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task AddEntry(string p, ulong u, string ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - cmd2.Parameters.AddWithValue("pl", p); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - var cmd = new NpgsqlCommand("INSERT INTO playlistentries VALUES (@pos,@p,@u,@ts,@add,@mody);UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pos"; - para.Value = position; - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "p"; - para2.Value = p; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "u"; - para3.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "ts"; - para4.Value = ts; - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "add"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - var para6 = cmd.CreateParameter(); - para6.ParameterName = "mody"; - para6.Value = DateTime.UtcNow; - cmd.Parameters.Add(para6); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task AddEntry(string p, ulong u, string ts) + { + int position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + cmd2.Parameters.AddWithValue("pl", p); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + position = reader.GetInt32(0); + } + reader.Close(); + cmd2.Dispose(); + var cmd = new NpgsqlCommand("INSERT INTO playlistentries VALUES (@pos,@p,@u,@ts,@add,@mody);UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pos"; + para.Value = position; + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "p"; + para2.Value = p; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "u"; + para3.Value = Convert.ToInt64(u); + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "ts"; + para4.Value = ts; + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "add"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + var para6 = cmd.CreateParameter(); + para6.ParameterName = "mody"; + para6.Value = DateTime.UtcNow; + cmd.Parameters.Add(para6); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task AddEntry(string p, ulong u, List ts) - { - int position = 0; - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd2.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd2.Parameters.Add(para); - var reader = await cmd2.ExecuteReaderAsync(); - while (await reader.ReadAsync()) - { - position = reader.GetInt32(0); - } - reader.Close(); - cmd2.Dispose(); - string longcmd = "UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;"; - foreach (var tt in ts) - { - longcmd += $"INSERT INTO playlistentries VALUES ({position},@p,@u,'{tt.TrackString}',@add,@mody);"; - position++; - } - var cmd = new NpgsqlCommand(longcmd, conn); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "p"; - para2.Value = p; - cmd.Parameters.Add(para2); - var para3 = cmd.CreateParameter(); - para3.ParameterName = "u"; - para3.Value = Convert.ToInt64(u); - cmd.Parameters.Add(para3); - var para4 = cmd.CreateParameter(); - para4.ParameterName = "add"; - para4.Value = DateTime.UtcNow; - cmd.Parameters.Add(para4); - var para5 = cmd.CreateParameter(); - para5.ParameterName = "mody"; - para5.Value = DateTime.UtcNow; - cmd.Parameters.Add(para5); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task AddEntry(string p, ulong u, List ts) + { + int position = 0; + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd2 = new NpgsqlCommand($"SELECT Count(*) FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd2.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd2.Parameters.Add(para); + var reader = await cmd2.ExecuteReaderAsync(); + while (await reader.ReadAsync()) + { + position = reader.GetInt32(0); + } + reader.Close(); + cmd2.Dispose(); + string longcmd = "UPDATE playlists SET changed=@mody WHERE userid=@u AND playlistname=@p;"; + foreach (var tt in ts) + { + longcmd += $"INSERT INTO playlistentries VALUES ({position},@p,@u,'{tt.TrackString}',@add,@mody);"; + position++; + } + var cmd = new NpgsqlCommand(longcmd, conn); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "p"; + para2.Value = p; + cmd.Parameters.Add(para2); + var para3 = cmd.CreateParameter(); + para3.ParameterName = "u"; + para3.Value = Convert.ToInt64(u); + cmd.Parameters.Add(para3); + var para4 = cmd.CreateParameter(); + para4.ParameterName = "add"; + para4.Value = DateTime.UtcNow; + cmd.Parameters.Add(para4); + var para5 = cmd.CreateParameter(); + para5.ParameterName = "mody"; + para5.Value = DateTime.UtcNow; + cmd.Parameters.Add(para5); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, string ts, int pos) - { - var qnow = await GetPlaylist(guild, u, p); - var q = await qnow.GetEntries(); - q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(ts), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); - await RebuildList(u, p, q); - } + public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, string ts, int pos) + { + var qnow = await GetPlaylist(guild, u, p); + var q = await qnow.GetEntries(); + q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(ts), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); + await RebuildList(u, p, q); + } - public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, List ts, int pos) - { - var qnow = await GetPlaylist(guild, u, p); - var q = await qnow.GetEntries(); - foreach (var tt in ts) - { - q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(tt.TrackString), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); - } - await RebuildList(u, p, q); - } + public static async Task InsertEntry(DiscordGuild guild, string p, ulong u, List ts, int pos) + { + var qnow = await GetPlaylist(guild, u, p); + var q = await qnow.GetEntries(); + foreach (var tt in ts) + { + q.Insert(pos, new PlaylistEntry(LavalinkUtilities.DecodeTrack(tt.TrackString), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, pos)); + } + await RebuildList(u, p, q); + } - public static async Task ClearList(string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task ClearList(string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task MoveListItems(DiscordGuild guild, string p, ulong u, int oldpos, int newpos) - { - var qnow = await GetPlaylist(guild, u, p); - var q = await qnow.GetEntries(); - //(q[newpos], q[oldpos]) = (q[oldpos], q[newpos]); - List tempQ = new(q.Count); - List newQ = new(q.Count); - foreach(var entry in q) - { - if (entry.Position == oldpos) - entry.Position = newpos; - else if (entry.Position == newpos) - entry.Position = oldpos; - else - entry.Position = entry.Position; - tempQ.Add(entry); - } - newQ.AddRange(tempQ.OrderBy(x => x.Position)); - await RebuildList(u, p, newQ); - } + public static async Task MoveListItems(DiscordGuild guild, string p, ulong u, int oldpos, int newpos) + { + var qnow = await GetPlaylist(guild, u, p); + var q = await qnow.GetEntries(); + //(q[newpos], q[oldpos]) = (q[oldpos], q[newpos]); + List tempQ = new(q.Count); + List newQ = new(q.Count); + foreach(var entry in q) + { + if (entry.Position == oldpos) + entry.Position = newpos; + else if (entry.Position == newpos) + entry.Position = oldpos; + else + entry.Position = entry.Position; + tempQ.Add(entry); + } + newQ.AddRange(tempQ.OrderBy(x => x.Position)); + await RebuildList(u, p, newQ); + } - public static async Task RemoveFromList(DiscordGuild guild, int position, string p, ulong u) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE pos = {position} AND userid = {u} AND playlistname = @pl;UPDATE playlists SET changed=@mody WHERE userid= {u} AND playlistname=@pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "mody"; - para2.Value = DateTime.UtcNow; - cmd.Parameters.Add(para2); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - await ReorderList(guild, p, u); - conn.Close(); - conn.Dispose(); - } + public static async Task RemoveFromList(DiscordGuild guild, int position, string p, ulong u) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"DELETE FROM playlistentries WHERE pos = {position} AND userid = {u} AND playlistname = @pl;UPDATE playlists SET changed=@mody WHERE userid= {u} AND playlistname=@pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "mody"; + para2.Value = DateTime.UtcNow; + cmd.Parameters.Add(para2); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + await ReorderList(guild, p, u); + conn.Close(); + conn.Dispose(); + } - public static async Task RenameList(string p, ulong u, string newname) - { - var connString = MikuBot.Config.DbConnectString; - var conn = new NpgsqlConnection(connString); - await conn.OpenAsync(); - var cmd = new NpgsqlCommand($"UPDATE playlists SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;", conn); - var para = cmd.CreateParameter(); - para.ParameterName = "pl"; - para.Value = p; - cmd.Parameters.Add(para); - var para2 = cmd.CreateParameter(); - para2.ParameterName = "newn"; - para2.Value = newname; - cmd.Parameters.Add(para2); - await cmd.ExecuteNonQueryAsync(); - cmd.Dispose(); - var cmd2 = new NpgsqlCommand($"UPDATE playlistentries SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;UPDATE playlists SET changed = @mody WHERE userid= {u} AND playlistname = @newn;", conn); - var para3 = cmd2.CreateParameter(); - para3.ParameterName = "pl"; - para3.Value = p; - cmd2.Parameters.Add(para3); - var para4 = cmd2.CreateParameter(); - para4.ParameterName = "newn"; - para4.Value = newname; - cmd2.Parameters.Add(para4); - var para5 = cmd2.CreateParameter(); - para5.ParameterName = "mody"; - para5.Value = DateTime.UtcNow; - cmd2.Parameters.Add(para5); - await cmd2.ExecuteNonQueryAsync(); - cmd2.Dispose(); - conn.Close(); - conn.Dispose(); - } + public static async Task RenameList(string p, ulong u, string newname) + { + var connString = MikuBot.Config.DbConnectString; + var conn = new NpgsqlConnection(connString); + await conn.OpenAsync(); + var cmd = new NpgsqlCommand($"UPDATE playlists SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;", conn); + var para = cmd.CreateParameter(); + para.ParameterName = "pl"; + para.Value = p; + cmd.Parameters.Add(para); + var para2 = cmd.CreateParameter(); + para2.ParameterName = "newn"; + para2.Value = newname; + cmd.Parameters.Add(para2); + await cmd.ExecuteNonQueryAsync(); + cmd.Dispose(); + var cmd2 = new NpgsqlCommand($"UPDATE playlistentries SET playlistname = @newn WHERE userid = {u} AND playlistname = @pl;UPDATE playlists SET changed = @mody WHERE userid= {u} AND playlistname = @newn;", conn); + var para3 = cmd2.CreateParameter(); + para3.ParameterName = "pl"; + para3.Value = p; + cmd2.Parameters.Add(para3); + var para4 = cmd2.CreateParameter(); + para4.ParameterName = "newn"; + para4.Value = newname; + cmd2.Parameters.Add(para4); + var para5 = cmd2.CreateParameter(); + para5.ParameterName = "mody"; + para5.Value = DateTime.UtcNow; + cmd2.Parameters.Add(para5); + await cmd2.ExecuteNonQueryAsync(); + cmd2.Dispose(); + conn.Close(); + conn.Dispose(); + } - public static async Task GetSong(string n, InteractionContext ctx) - { - var nodeConnection = MikuBot.LavalinkNodeConnections.First().Value; - var inter = ctx.Client.GetInteractivity(); - if (n.ToLower().StartsWith("http://nicovideo.jp") - || n.ToLower().StartsWith("http://sp.nicovideo.jp") - || n.ToLower().StartsWith("https://nicovideo.jp") - || n.ToLower().StartsWith("https://sp.nicovideo.jp") - || n.ToLower().StartsWith("http://www.nicovideo.jp") - || n.ToLower().StartsWith("https://www.nicovideo.jp")) - { - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); - var split = n.Split("/".ToCharArray()); - var nndID = split.First(x => x.StartsWith("sm") || x.StartsWith("nm")).Split("?")[0]; - FtpClient ftpClient = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); - ftpClient.Connect(); - if (!ftpClient.FileExists($"{nndID}.mp3")) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); - var ex = await ctx.GetNNDAsync(n, nndID, msg.Id); - if (ex == null) - { - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); - return null; - } - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); - ftpClient.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); - } - var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); - return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); - } - else if (n.StartsWith("http://") | n.StartsWith("https://")) - { - var s = await nodeConnection.Rest.GetTracksAsync(new Uri(n)); - switch (s.LoadResultType) - { - case LavalinkLoadResultType.LoadFailed: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + - "> Playlist is set to private or unlisted\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); - return null; - }; - case LavalinkLoadResultType.PlaylistLoaded: - { - if (s.PlaylistInfo.SelectedTrack == -1) - { - List buttons = new(2) - { - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add entire playlist"), - new DiscordButtonComponent(ButtonStyle.Primary, "no", "Don't add") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Playlist link detected!") - .WithDescription("Choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - else - { - List buttons = new(3) - { - new DiscordButtonComponent(ButtonStyle.Primary, "yes", "Add only referred song"), - new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add the entire playlist"), - new DiscordButtonComponent(ButtonStyle.Danger, "no", "Cancel") - }; - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() - .WithTitle("Link with Playlist detected!") - .WithDescription("Please choose how to handle the playlist link") - .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) - .Build()).AddComponents(buttons)); - var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); - return null; - } - else if (resp.Result.Id == "yes") - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).Title}")); - return new TrackResult(s.PlaylistInfo, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack)); - } - else if (resp.Result.Id == "all") - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {s.PlaylistInfo.Name}")); - return new TrackResult(s.PlaylistInfo, s.Tracks); - } - else - { - buttons.ForEach(x => x.Disable()); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); - return null; - } - } - }; - default: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {s.Tracks.First().Title}")); - return new TrackResult(s.PlaylistInfo, s.Tracks.First()); - }; - } - } - else - { - var s = await nodeConnection.Rest.GetTracksAsync(n); - switch (s.LoadResultType) - { - case LavalinkLoadResultType.LoadFailed: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + - "> The song is unavailable/deleted").Build())); - return null; - }; - case LavalinkLoadResultType.NoMatches: - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); - return null; - }; - default: - { - int leng = s.Tracks.Count; - if (leng > 5) leng = 5; - List selectOptions = new(leng) - { + public static async Task GetSong(string n, InteractionContext ctx) + { + var nodeConnection = MikuBot.LavalinkNodeConnections.First().Value; + var inter = ctx.Client.GetInteractivity(); + if (n.ToLower().StartsWith("http://nicovideo.jp") + || n.ToLower().StartsWith("http://sp.nicovideo.jp") + || n.ToLower().StartsWith("https://nicovideo.jp") + || n.ToLower().StartsWith("https://sp.nicovideo.jp") + || n.ToLower().StartsWith("http://www.nicovideo.jp") + || n.ToLower().StartsWith("https://www.nicovideo.jp")) + { + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("Processing NND Video...").AsEphemeral()); + var split = n.Split("/".ToCharArray()); + var nndID = split.First(x => x.StartsWith("sm") || x.StartsWith("nm")).Split("?")[0]; + FtpClient ftpClient = new(MikuBot.Config.NndConfig.FtpConfig.Hostname, new NetworkCredential(MikuBot.Config.NndConfig.FtpConfig.User, MikuBot.Config.NndConfig.FtpConfig.Password)); + ftpClient.Connect(); + if (!ftpClient.FileExists($"{nndID}.mp3")) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Preparing download...")); + var ex = await ctx.GetNNDAsync(n, nndID, msg.Id); + if (ex == null) + { + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Please try again or verify the link")); + return null; + } + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().WithContent("Uploading...")); + ftpClient.UploadStream(ex, $"{nndID}.mp3", FtpRemoteExists.Skip, true); + } + var Track = await nodeConnection.Rest.GetTracksAsync(new Uri($"https://nnd.meek.moe/new/{nndID}.mp3")); + return new TrackResult(Track.PlaylistInfo, Track.Tracks.First()); + } + else if (n.StartsWith("http://") | n.StartsWith("https://")) + { + var s = await nodeConnection.Rest.GetTracksAsync(new Uri(n)); + switch (s.LoadResultType) + { + case LavalinkLoadResultType.LoadFailed: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + + "> Playlist is set to private or unlisted\n" + + "> The song is unavailable/deleted").Build())); + return null; + }; + case LavalinkLoadResultType.NoMatches: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song/playlist was found with this URL, please try again/a different one").Build())); + return null; + }; + case LavalinkLoadResultType.PlaylistLoaded: + { + if (s.PlaylistInfo.SelectedTrack == -1) + { + List buttons = new(2) + { + new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add entire playlist"), + new DiscordButtonComponent(ButtonStyle.Primary, "no", "Don't add") + }; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Playlist link detected!") + .WithDescription("Choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + else if (resp.Result.Id == "yes") + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Adding entire playlist")); + return new TrackResult(s.PlaylistInfo, s.Tracks); + } + else + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + else + { + List buttons = new(3) + { + new DiscordButtonComponent(ButtonStyle.Primary, "yes", "Add only referred song"), + new DiscordButtonComponent(ButtonStyle.Success, "yes", "Add the entire playlist"), + new DiscordButtonComponent(ButtonStyle.Danger, "no", "Cancel") + }; + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder() + .WithTitle("Link with Playlist detected!") + .WithDescription("Please choose how to handle the playlist link") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl) + .Build()).AddComponents(buttons)); + var resp = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); + if (resp.TimedOut) + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Timed out!")); + return null; + } + else if (resp.Result.Id == "yes") + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding single song: {s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack).Title}")); + return new TrackResult(s.PlaylistInfo, s.Tracks.ElementAt(s.PlaylistInfo.SelectedTrack)); + } + else if (resp.Result.Id == "all") + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent($"Adding entire playlist: {s.PlaylistInfo.Name}")); + return new TrackResult(s.PlaylistInfo, s.Tracks); + } + else + { + buttons.ForEach(x => x.Disable()); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(buttons).WithContent("Canceled!")); + return null; + } + } + }; + default: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Playing single song: {s.Tracks.First().Title}")); + return new TrackResult(s.PlaylistInfo, s.Tracks.First()); + }; + } + } + else + { + var s = await nodeConnection.Rest.GetTracksAsync(n); + switch (s.LoadResultType) + { + case LavalinkLoadResultType.LoadFailed: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("Loading this song/playlist failed, please try again, reasons could be:\n" + + "> The song is unavailable/deleted").Build())); + return null; + }; + case LavalinkLoadResultType.NoMatches: + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithTitle("Failed to load").WithDescription("No song was found, please try again").Build())); + return null; + }; + default: + { + int leng = s.Tracks.Count; + if (leng > 5) leng = 5; + List selectOptions = new(leng) + { - }; - DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); - var em = new DiscordEmbedBuilder() - .WithTitle("Results!") - .WithDescription("Please select a track:\n") - .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); - for (int i = 0; i < leng; i++) - { - em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); - } - var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); - var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); - if (resp.TimedOut) - { - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); - return null; - } - var trackSelect = Convert.ToInt32(resp.Result.Values.First()); - var track = s.Tracks.ElementAt(trackSelect); - select.Disable(); - await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Choosed {track.Title}")); + }; + DiscordStringSelectComponent select = new("Select song to play", selectOptions, minOptions: 1, maxOptions: 1); + var em = new DiscordEmbedBuilder() + .WithTitle("Results!") + .WithDescription("Please select a track:\n") + .WithAuthor($"Requested by {ctx.Member.UsernameWithGlobalName} || Timeout 30 seconds", iconUrl: ctx.Member.AvatarUrl); + for (int i = 0; i < leng; i++) + { + em.AddField(new DiscordEmbedField($"{i + 1}.{s.Tracks.ElementAt(i).Title} [{s.Tracks.ElementAt(i).Length}]", $"by {s.Tracks.ElementAt(i).Author} [Link]({s.Tracks.ElementAt(i).Uri})")); + } + var msg = await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().AddEmbed(em.Build()).AddComponents(select)); + var resp = await inter.WaitForSelectAsync(msg, ctx.User, select.CustomId, ComponentType.StringSelect, TimeSpan.FromSeconds(30)); + if (resp.TimedOut) + { + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent("Timed out!")); + return null; + } + var trackSelect = Convert.ToInt32(resp.Result.Values.First()); + var track = s.Tracks.ElementAt(trackSelect); + select.Disable(); + await ctx.EditFollowupAsync(msg.Id, new DiscordWebhookBuilder().AddComponents(select).WithContent($"Choosed {track.Title}")); - return new TrackResult(s.PlaylistInfo, track); - }; - } - } - } + return new TrackResult(s.PlaylistInfo, track); + }; + } + } + } } */ + diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index a1329313..15a5a85d 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -1,4 +1,8 @@ -using DisCatSharp.Entities; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +using DisCatSharp.Entities; using HeyRed.Mime; @@ -6,77 +10,73 @@ using Newtonsoft.Json; -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; - using Weeb.net; namespace MikuSharp.Utilities; public static class Web { - public static async Task GetNekosLifeAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekos.life"); - dl.Embed = em.Build(); - return dl; - } + public static async Task GetNekosLifeAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) + { + Position = 0 + }; + dl.Data = str; + dl.Filetype = MimeGuesser.GuessExtension(str); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by nekos.life"); + dl.Embed = em.Build(); + return dl; + } - public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) - { - client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); - var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); - MemoryStream img = new(await client.GetByteArrayAsync(Other.ResizeLink(v.Url))); - v.Data = img; - v.Filetype = MimeGuesser.GuessExtension(img); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{v.Filetype}"); - em.WithFooter("by KSoft.si"); - v.Embed = em.Build(); - return v; - } + public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) + { + client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); + var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); + MemoryStream img = new(await client.GetByteArrayAsync(Other.ResizeLink(v.Url))); + v.Data = img; + v.Filetype = MimeGuesser.GuessExtension(img); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{v.Filetype}"); + em.WithFooter("by KSoft.si"); + v.Embed = em.Build(); + return v; + } - public static async Task GetNekobotAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekobot.xyz"); - dl.Embed = em.Build(); - return dl; - } + public static async Task GetNekobotAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) + { + Position = 0 + }; + dl.Data = str; + dl.Filetype = MimeGuesser.GuessExtension(str); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by nekobot.xyz"); + dl.Embed = em.Build(); + return dl; + } - public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) - { - var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); - MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) - { - Position = 0 - }; - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by weeb.sh"); - return new() - { - ImgData = img, - Extension = MimeGuesser.GuessExtension(img), - Embed = em - }; - } -} \ No newline at end of file + public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) + { + var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); + MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) + { + Position = 0 + }; + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by weeb.sh"); + return new() + { + ImgData = img, + Extension = MimeGuesser.GuessExtension(img), + Embed = em + }; + } +} diff --git a/NicoNicoNii b/NicoNicoNii index 4abc1356..87ee9278 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 4abc135610c3c7ab627af5d2686ecfd09880aaeb +Subproject commit 87ee9278f5f6692d84e5dbbf27383c2125c3b284 From dc69ce2c43a20d3285df4635ba906caaaaf1cf19 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 1 Feb 2025 18:48:11 +0100 Subject: [PATCH 025/113] chore(deps): update deps --- MikuSharp/Commands/Action.cs | 18 +++++++++--------- MikuSharp/MikuSharp.csproj | 34 +++++++++++++++++----------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 2fba8cee..5eed19ee 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -28,7 +28,7 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("kiss", "Kiss someone!")] @@ -43,7 +43,7 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("lick", "Lick someone!")] @@ -58,7 +58,7 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(wsh.Embed.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("pat", "Pat someone!")] @@ -77,7 +77,7 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("poke", "Poke someone!")] @@ -96,7 +96,7 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("slap", "Slap someone!")] @@ -115,7 +115,7 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("bite", "Bite someone!")] @@ -134,7 +134,7 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("nom", "Nom someone!")] @@ -153,7 +153,7 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("stare", "Stare at someone!")] @@ -172,6 +172,6 @@ public static async Task StateAsync(InteractionContext ctx, [Option("user", "The builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).AddMention(new UserMention(user))); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } } diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index b9ac36e3..302f84cf 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -45,31 +45,31 @@ - - - - - - - - + + + + + + + + - - - + + + - - + + - + - - + + - + From c4d2667e7d089a8483d58b44b33b1c609d72dfc5 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 1 Feb 2025 18:49:33 +0100 Subject: [PATCH 026/113] chore(deps): update deps --- NicoNicoNii | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NicoNicoNii b/NicoNicoNii index 87ee9278..157d9ac8 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 87ee9278f5f6692d84e5dbbf27383c2125c3b284 +Subproject commit 157d9ac86af03e34aa0c86756caf36086861d830 From 016d0f79b6fb53c0f5722b91fe278b90c30e3e4d Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 2 Feb 2025 07:33:38 +0100 Subject: [PATCH 027/113] disable all music related things --- MikuSharp/Attributes/CustomMikuAttributes.cs | 4 +- MikuSharp/Commands/{ => Old}/Music.cs | 3 +- MikuSharp/Commands/{ => Old}/Playlist.cs | 0 MikuSharp/Entities/MusicQueueEntry.cs | 37 ++ MikuSharp/Entities/{ => Old}/Entry.cs | 3 +- MikuSharp/Entities/{ => Old}/Guild.cs | 3 +- MikuSharp/Entities/{ => Old}/MusicInstance.cs | 3 +- MikuSharp/Entities/{ => Old}/Playlist.cs | 0 MikuSharp/Entities/{ => Old}/PlaylistEntry.cs | 3 +- MikuSharp/Entities/{ => Old}/QueueEntry.cs | 3 +- MikuSharp/Entities/{ => Old}/TrackResult.cs | 3 +- MikuSharp/Events/MikuGuildJoin.cs | 24 +- MikuSharp/Events/{ => Old}/Lavalink.cs | 3 +- MikuSharp/Events/{ => Old}/VoiceChat.cs | 5 +- MikuSharp/MikuBot.cs | 580 +++++++++--------- MikuSharp/Utilities/DiscordOptionProviders.cs | 6 +- MikuSharp/Utilities/{ => Old}/Database.cs | 3 +- MikuSharp/Utilities/{ => Old}/Music.cs | 3 +- MikuSharp/Utilities/{ => Old}/PlaylistDB.cs | 0 19 files changed, 370 insertions(+), 316 deletions(-) rename MikuSharp/Commands/{ => Old}/Music.cs (99%) rename MikuSharp/Commands/{ => Old}/Playlist.cs (100%) create mode 100644 MikuSharp/Entities/MusicQueueEntry.cs rename MikuSharp/Entities/{ => Old}/Entry.cs (94%) rename MikuSharp/Entities/{ => Old}/Guild.cs (98%) rename MikuSharp/Entities/{ => Old}/MusicInstance.cs (99%) rename MikuSharp/Entities/{ => Old}/Playlist.cs (100%) rename MikuSharp/Entities/{ => Old}/PlaylistEntry.cs (94%) rename MikuSharp/Entities/{ => Old}/QueueEntry.cs (94%) rename MikuSharp/Entities/{ => Old}/TrackResult.cs (92%) rename MikuSharp/Events/{ => Old}/Lavalink.cs (99%) rename MikuSharp/Events/{ => Old}/VoiceChat.cs (96%) rename MikuSharp/Utilities/{ => Old}/Database.cs (99%) rename MikuSharp/Utilities/{ => Old}/Music.cs (99%) rename MikuSharp/Utilities/{ => Old}/PlaylistDB.cs (100%) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 4996e7aa..514d027b 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -7,7 +7,7 @@ using DisCatSharp.CommandsNext.Attributes; namespace MikuSharp.Attributes; - +/* /// /// Defines that usage of this command is restricted to users in a vc. /// @@ -31,7 +31,7 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) ? await Task.FromResult(true) : await Task.FromResult(false); } -} +}*/ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class)] public sealed class NotStaffAttribute : CheckBaseAttribute diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Old/Music.cs similarity index 99% rename from MikuSharp/Commands/Music.cs rename to MikuSharp/Commands/Old/Music.cs index 05dda0e6..5f67d77a 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Old/Music.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -785,3 +785,4 @@ public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) } } } +*/ \ No newline at end of file diff --git a/MikuSharp/Commands/Playlist.cs b/MikuSharp/Commands/Old/Playlist.cs similarity index 100% rename from MikuSharp/Commands/Playlist.cs rename to MikuSharp/Commands/Old/Playlist.cs diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs new file mode 100644 index 00000000..f7ef7bba --- /dev/null +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -0,0 +1,37 @@ +using System.Threading.Tasks; + +using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; + +namespace MikuSharp.Entities; + +internal class MusicQueueEntry : IQueueEntry +{ + public ulong UserId { get; internal set; } + + public ulong GuildId { get; internal set; } + + /// + public Task BeforePlayingAsync(LavalinkGuildPlayer player) + => Task.FromResult(true); + + /// + public Task AfterPlayingAsync(LavalinkGuildPlayer player) + => Task.FromResult(true); + + /// + public LavalinkTrack Track { get; set; } + + /// + public IQueueEntry AddTrack(LavalinkTrack track) + { + this.Track = track; + dynamic? userData = track.UserData; + if (userData is null) + return this; + + this.UserId = userData.UserId; + this.GuildId = userData.GuildId; + return this; + } +} diff --git a/MikuSharp/Entities/Entry.cs b/MikuSharp/Entities/Old/Entry.cs similarity index 94% rename from MikuSharp/Entities/Entry.cs rename to MikuSharp/Entities/Old/Entry.cs index 0cae14a4..eed006cd 100644 --- a/MikuSharp/Entities/Entry.cs +++ b/MikuSharp/Entities/Old/Entry.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using DisCatSharp.Lavalink.Entities; @@ -15,3 +15,4 @@ public Entry(LavalinkTrack t, DateTimeOffset addtime) public LavalinkTrack Track { get; protected set; } public DateTimeOffset AdditionDate { get; protected set; } } +*/ \ No newline at end of file diff --git a/MikuSharp/Entities/Guild.cs b/MikuSharp/Entities/Old/Guild.cs similarity index 98% rename from MikuSharp/Entities/Guild.cs rename to MikuSharp/Entities/Old/Guild.cs index 7e6d81c5..cb5f7ec3 100644 --- a/MikuSharp/Entities/Guild.cs +++ b/MikuSharp/Entities/Old/Guild.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Threading.Tasks; namespace MikuSharp.Entities; @@ -34,3 +34,4 @@ public async Task CheckAlone() } } } +*/ \ No newline at end of file diff --git a/MikuSharp/Entities/MusicInstance.cs b/MikuSharp/Entities/Old/MusicInstance.cs similarity index 99% rename from MikuSharp/Entities/MusicInstance.cs rename to MikuSharp/Entities/Old/MusicInstance.cs index 299a7d54..dda2d108 100644 --- a/MikuSharp/Entities/MusicInstance.cs +++ b/MikuSharp/Entities/Old/MusicInstance.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Collections.Generic; using System.Linq; using System.Net; @@ -404,3 +404,4 @@ public async Task PlaySong() } // B/S(`・ω・´) ❤️ (´ω`)U/C +*/ \ No newline at end of file diff --git a/MikuSharp/Entities/Playlist.cs b/MikuSharp/Entities/Old/Playlist.cs similarity index 100% rename from MikuSharp/Entities/Playlist.cs rename to MikuSharp/Entities/Old/Playlist.cs diff --git a/MikuSharp/Entities/PlaylistEntry.cs b/MikuSharp/Entities/Old/PlaylistEntry.cs similarity index 94% rename from MikuSharp/Entities/PlaylistEntry.cs rename to MikuSharp/Entities/Old/PlaylistEntry.cs index c853214a..0bbfb54e 100644 --- a/MikuSharp/Entities/PlaylistEntry.cs +++ b/MikuSharp/Entities/Old/PlaylistEntry.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using DisCatSharp.Lavalink.Entities; @@ -15,3 +15,4 @@ public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset mod public DateTimeOffset ModifyDate { get; set; } public int Position { get; set; } } +*/ \ No newline at end of file diff --git a/MikuSharp/Entities/QueueEntry.cs b/MikuSharp/Entities/Old/QueueEntry.cs similarity index 94% rename from MikuSharp/Entities/QueueEntry.cs rename to MikuSharp/Entities/Old/QueueEntry.cs index f5fdad9f..b1e956bb 100644 --- a/MikuSharp/Entities/QueueEntry.cs +++ b/MikuSharp/Entities/Old/QueueEntry.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using DisCatSharp.Lavalink.Entities; @@ -15,3 +15,4 @@ public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : b public int Position { get; set; } public ulong AddedBy { set; get; } } +*/ \ No newline at end of file diff --git a/MikuSharp/Entities/TrackResult.cs b/MikuSharp/Entities/Old/TrackResult.cs similarity index 92% rename from MikuSharp/Entities/TrackResult.cs rename to MikuSharp/Entities/Old/TrackResult.cs index 8dd4f4ef..9263623d 100644 --- a/MikuSharp/Entities/TrackResult.cs +++ b/MikuSharp/Entities/Old/TrackResult.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +/*using System.Collections.Generic; using DisCatSharp.Lavalink.Entities; @@ -22,3 +22,4 @@ public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) public LavalinkPlaylistInfo PlaylistInfo { get; set; } public List Tracks { get; set; } } +*/ \ No newline at end of file diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index fdf2612f..d80514ce 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -16,9 +16,9 @@ public class MikuGuild /// The client. /// The event args. public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) - { - await Task.FromResult(true); - } + { + await Task.FromResult(true); + } /// /// Fired when a guild member is updated. @@ -26,14 +26,14 @@ public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventAr /// The discord client. /// The event args. public static async Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEventArgs args) - { - if (args is { PendingBefore: true, PendingAfter: false }) - { - ulong memberRoleId = 483280207927574528; - var memberRole = args.Guild.GetRole(memberRoleId); - await args.Member.GrantRoleAsync(memberRole); - } + { + if (args is { PendingBefore: true, PendingAfter: false }) + { + ulong memberRoleId = 483280207927574528; + var memberRole = args.Guild.GetRole(memberRoleId); + await args.Member.GrantRoleAsync(memberRole); + } - await Task.FromResult(true); - } + await Task.FromResult(true); + } } diff --git a/MikuSharp/Events/Lavalink.cs b/MikuSharp/Events/Old/Lavalink.cs similarity index 99% rename from MikuSharp/Events/Lavalink.cs rename to MikuSharp/Events/Old/Lavalink.cs index ac060f99..9eefc42e 100644 --- a/MikuSharp/Events/Lavalink.cs +++ b/MikuSharp/Events/Old/Lavalink.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Threading.Tasks; using DisCatSharp.Entities; @@ -80,3 +80,4 @@ await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().Wit } } } +*/ \ No newline at end of file diff --git a/MikuSharp/Events/VoiceChat.cs b/MikuSharp/Events/Old/VoiceChat.cs similarity index 96% rename from MikuSharp/Events/VoiceChat.cs rename to MikuSharp/Events/Old/VoiceChat.cs index 120a2c1e..39fcbc0d 100644 --- a/MikuSharp/Events/VoiceChat.cs +++ b/MikuSharp/Events/Old/VoiceChat.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Linq; using System.Threading.Tasks; @@ -57,7 +57,7 @@ await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder() } else if (e.After?.Channel?.Users?.Count(x => !x.IsBot) != 0 && e.After?.Channel?.Users.Contains(e.Guild.Members[client.CurrentUser.Id]) == true) if (g.MusicInstance is { AloneCts: not null }) - g.MusicInstance.AloneCts.Cancel(); + await g.MusicInstance.AloneCts.CancelAsync(); } catch (Exception ex) { @@ -66,3 +66,4 @@ await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder() } } } +*/ \ No newline at end of file diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index b74fb94b..7fc89344 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading; @@ -23,8 +22,6 @@ using MikuSharp.Attributes; using MikuSharp.Commands; using MikuSharp.Entities; -using MikuSharp.Enums; -using MikuSharp.Events; using Newtonsoft.Json; @@ -41,288 +38,297 @@ namespace MikuSharp; internal sealed class MikuBot : IDisposable { - internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "4.0.0"); - - internal static readonly Dictionary LavalinkSessions = []; - internal static readonly Dictionary Guilds = []; - - internal static Playstate Ps = Playstate.Playing; - internal static Stopwatch Psc = new(); - - internal MikuBot() - { - var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); - - Config = JsonConvert.DeserializeObject(fileData); - if (Config == null) - throw new ArgumentNullException(null, "config.json is null"); - - Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - Cts = new(); - - Log.Logger = new LoggerConfiguration() - .MinimumLevel.Debug() - .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) - .WriteTo.Console(LogEventLevel.Debug) - .CreateLogger(); - Log.Logger.Information("Starting up!"); - - ShardedClient = new(new() - { - Token = Config.DiscordToken, - TokenType = TokenType.Bot, - MinimumLogLevel = LogLevel.Debug, - AutoReconnect = true, - ApiChannel = ApiChannel.Canary, - HttpTimeout = TimeSpan.FromMinutes(1), - Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers, - MessageCacheSize = 2048, - LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger), - ShowReleaseNotesInUpdateCheck = false, - IncludePrereleaseInUpdateCheck = true, - DisableUpdateCheck = true, - EnableSentry = true, - FeedbackEmail = "aiko@aitsys.dev", - DeveloperUserId = 856780995629154305, - AttachUserInfo = true, - ReconnectIndefinitely = true - }); - - this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() - { - Timeout = TimeSpan.FromMinutes(2), - PaginationBehaviour = PaginationBehaviour.WrapAround, - PaginationDeletion = PaginationDeletion.DeleteEmojis, - PollBehaviour = PollBehaviour.DeleteEmojis, - AckPaginationButtons = true, - ButtonBehavior = ButtonPaginationBehavior.Disable, - PaginationButtons = new() - { - SkipLeft = new(ButtonStyle.Primary, "pgb-skip-left", "First", false, new("⏮️")), - Left = new(ButtonStyle.Primary, "pgb-left", "Previous", false, new("◀️")), - Stop = new(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new("⏹️")), - Right = new(ButtonStyle.Primary, "pgb-right", "Next", false, new("▶️")), - SkipRight = new(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new("⏭️")) - }, - ResponseMessage = "Something went wrong.", - ResponseBehavior = InteractionResponseBehavior.Ignore - }).Result; - - this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() - { - EnableDefaultHelp = true, - DebugStartup = true, - EnableLocalization = false, - GenerateTranslationFilesOnly = false - }).Result; - - this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() - { - CaseSensitive = false, - EnableMentionPrefix = true, - DmHelp = false, - EnableDefaultHelp = true, - IgnoreExtraArguments = true, - StringPrefixes = [], - UseDefaultCommandHandler = true, - DefaultHelpChecks = [new NotStaffAttribute()] - }).Result; - - this.LavalinkConfig = new() - { - SocketEndpoint = new() - { - Hostname = Config.LavaConfig.Hostname, - Port = Config.LavaConfig.Port - }, - RestEndpoint = new() - { - Hostname = Config.LavaConfig.Hostname, - Port = Config.LavaConfig.Port - }, - Password = Config.LavaConfig.Password - }; - - this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; - } - - internal static CancellationTokenSource Cts { get; set; } - - internal static BotConfig Config { get; set; } - internal LavalinkConfiguration LavalinkConfig { get; set; } - - internal Task GameSetThread { get; set; } - internal Task StatusThread { get; set; } - internal Task BotListThread { get; set; } - internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } - internal static DiscordShardedClient ShardedClient { get; set; } - - internal IReadOnlyDictionary InteractivityModules { get; set; } - internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } - - internal IReadOnlyDictionary CommandsNextModules { get; set; } - internal IReadOnlyDictionary LavalinkModules { get; set; } - - public void Dispose() - { - GC.SuppressFinalize(this); - } - - internal static async Task RegisterEvents() - { - ShardedClient.ClientErrored += (sender, args) => - { - sender.Logger.LogError(args.Exception.Message); - sender.Logger.LogError(args.Exception.StackTrace); - return Task.CompletedTask; - }; - - await Task.Delay(1); - - foreach (var discordClientKvp in ShardedClient.ShardClients) - { - discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; - - discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => - { - sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", args.ShardId); - args.Handled = true; - return Task.CompletedTask; - }; - - discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleReady += (sender, args) => - { - sender.Client.Logger.LogInformation("Application commands module is ready"); - args.Handled = true; - return Task.CompletedTask; - }; - - discordClientKvp.Value.GuildMemberAdded += async (sender, args) => - { - if (sender.CurrentApplication.Team.Members.Any(x => x.User.Id == args.Member.Id)) - { - var text = $"Heywo <:MikuWave:655783221942026271>!" + - $"\n\nOne of my developers joined your server!" + - $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + - $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + - $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + - $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithGlobalName}!" + - $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; - var message = await args.Guild.Owner.SendMessageAsync(text); - sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithGlobalName); - sender.Logger.LogInformation("Message content: {content}", message.Content); - } - else - await Task.FromResult(true); - }; - discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => - { - if (args.Guild.Id == 483279257431441410) - await MikuGuild.OnUpdateAsync(sender, args); - else - await Task.FromResult(true); - }; - - discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); - } - } - - internal async Task ShowConnections() - { - while (true) - { - var al = Guilds.Where(x => x.Value?.MusicInstance != null); - ShardedClient.Logger.LogInformation("Voice Connections: " + al.Count(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true)); - await Task.Delay(15000); - } - } - - internal static async Task UpdateBotList() - { - await Task.Delay(15000); - - while (true) - { - var me = await DiscordBotListApi.GetMeAsync(); - var count = Array.Empty(); - var clients = ShardedClient.ShardClients.Values; - count = clients.Aggregate(count, (current, client) => [.. current, client.Guilds.Count]); - await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); - await Task.Delay(TimeSpan.FromMinutes(15)); - } - } - - internal static async Task SetActivity() - { - while (true) - { - DiscordActivity test = new() - { - Name = "I'm using slash commands now!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test2 = new() - { - Name = "Mention me with help for nsfw commands!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test3 = new() - { - Name = "Full NND support!", - ActivityType = ActivityType.Playing - }; - await ShardedClient.UpdateStatusAsync(test3, UserStatus.Online); - await Task.Delay(TimeSpan.FromMinutes(20)); - } - } - - internal void RegisterCommands() - { - // Nsfw stuff needs to be hidden, that's why we use commands next - this.CommandsNextModules.RegisterCommands(); - - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - //ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - - // Smolcar command, only guild command - this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); - } - - internal async Task RunAsync() - { - await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); - await ShardedClient.StartAsync(); - await Task.Delay(5000); - - /*foreach (var lavalinkShard in this.LavalinkModules) - { - var connection = await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); - LavalinkSessions.Add(lavalinkShard.Key, connection); - }*/ - - //this.GameSetThread = Task.Run(SetActivity); - //StatusThread = Task.Run(ShowConnections); - //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); - //BotListThread = Task.Run(UpdateBotList); - while (!Cts.IsCancellationRequested) - await Task.Delay(25); - _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); - await ShardedClient.StopAsync(); - } - - ~MikuBot() - { - this.Dispose(); - } + internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "5.0.0"); + + //internal static readonly Dictionary LavalinkSessions = []; + //internal static readonly Dictionary Guilds = []; + + //internal static Playstate Ps = Playstate.Playing; + //internal static Stopwatch Psc = new(); + + internal MikuBot() + { + var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); + + Config = JsonConvert.DeserializeObject(fileData); + if (Config == null) + throw new ArgumentNullException(null, "config.json is null"); + + Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; + Cts = new(); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.File("miku_log.txt", fileSizeLimitBytes: null, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 2, shared: true) + .WriteTo.Console(LogEventLevel.Debug) + .CreateLogger(); + Log.Logger.Information("Starting up!"); + + ShardedClient = new(new() + { + Token = Config.DiscordToken, + TokenType = TokenType.Bot, + MinimumLogLevel = LogLevel.Debug, + AutoReconnect = true, + ApiChannel = ApiChannel.Canary, + HttpTimeout = TimeSpan.FromMinutes(1), + Intents = DiscordIntents.AllUnprivileged | DiscordIntents.GuildMembers, + MessageCacheSize = 2048, + LoggerFactory = new LoggerFactory().AddSerilog(Log.Logger), + ShowReleaseNotesInUpdateCheck = false, + IncludePrereleaseInUpdateCheck = true, + DisableUpdateCheck = true, + EnableSentry = true, + FeedbackEmail = "aiko@aitsys.dev", + DeveloperUserId = 856780995629154305, + AttachUserInfo = true, + ReconnectIndefinitely = true + }); + + this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() + { + Timeout = TimeSpan.FromMinutes(2), + PaginationBehaviour = PaginationBehaviour.WrapAround, + PaginationDeletion = PaginationDeletion.DeleteEmojis, + PollBehaviour = PollBehaviour.DeleteEmojis, + AckPaginationButtons = true, + ButtonBehavior = ButtonPaginationBehavior.Disable, + PaginationButtons = new() + { + SkipLeft = new(ButtonStyle.Primary, "pgb-skip-left", "First", false, new("⏮️")), + Left = new(ButtonStyle.Primary, "pgb-left", "Previous", false, new("◀️")), + Stop = new(ButtonStyle.Danger, "pgb-stop", "Cancel", false, new("⏹️")), + Right = new(ButtonStyle.Primary, "pgb-right", "Next", false, new("▶️")), + SkipRight = new(ButtonStyle.Primary, "pgb-skip-right", "Last", false, new("⏭️")) + }, + ResponseMessage = "Something went wrong.", + ResponseBehavior = InteractionResponseBehavior.Ignore + }).Result; + + this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() + { + EnableDefaultHelp = true, + DebugStartup = true, + EnableLocalization = false, + GenerateTranslationFilesOnly = false + }).Result; + + this.CommandsNextModules = ShardedClient.UseCommandsNextAsync(new() + { + CaseSensitive = false, + EnableMentionPrefix = true, + DmHelp = false, + EnableDefaultHelp = true, + IgnoreExtraArguments = true, + StringPrefixes = [], + UseDefaultCommandHandler = true, + DefaultHelpChecks = [new NotStaffAttribute()] + }).Result; + + this.LavalinkConfig = new() + { + SocketEndpoint = new() + { + Hostname = Config.LavaConfig.Hostname, + Port = Config.LavaConfig.Port + }, + RestEndpoint = new() + { + Hostname = Config.LavaConfig.Hostname, + Port = Config.LavaConfig.Port + }, + Password = Config.LavaConfig.Password, + EnableBuiltInQueueSystem = true, + QueueEntryFactory = () => new MusicQueueEntry() + }; + + this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; + } + + internal static CancellationTokenSource Cts { get; set; } + + internal static BotConfig Config { get; set; } + + internal LavalinkConfiguration LavalinkConfig { get; set; } + + internal Task GameSetThread { get; set; } + + internal Task StatusThread { get; set; } + + internal Task BotListThread { get; set; } + + internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } + + internal static DiscordShardedClient ShardedClient { get; set; } + + internal IReadOnlyDictionary InteractivityModules { get; set; } + + internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } + + internal IReadOnlyDictionary CommandsNextModules { get; set; } + + internal IReadOnlyDictionary LavalinkModules { get; set; } + + public void Dispose() + { + GC.SuppressFinalize(this); + } + + internal static async Task RegisterEvents() + { + ShardedClient.ClientErrored += (sender, args) => + { + sender.Logger.LogError(args.Exception.Message); + sender.Logger.LogError(args.Exception.StackTrace); + return Task.CompletedTask; + }; + + await Task.Delay(1); + + foreach (var discordClientKvp in ShardedClient.ShardClients) + { + //discordClientKvp.Value.VoiceStateUpdated += VoiceChat.LeftAlone; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleStartupFinished += (sender, args) => + { + sender.Client.Logger.LogInformation("Shard {shard} finished application command startup.", args.ShardId); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GetApplicationCommands().ApplicationCommandsModuleReady += (sender, args) => + { + sender.Client.Logger.LogInformation("Application commands module is ready"); + args.Handled = true; + return Task.CompletedTask; + }; + + discordClientKvp.Value.GuildMemberAdded += async (sender, args) => + { + if (sender.CurrentApplication.Team.Members.Any(x => x.User.Id == args.Member.Id)) + { + var text = $"Heywo <:MikuWave:655783221942026271>!" + + $"\n\nOne of my developers joined your server!" + + $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + + $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + + $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + + $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithGlobalName}!" + + $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; + var message = await args.Guild.Owner.SendMessageAsync(text); + sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithGlobalName); + sender.Logger.LogInformation("Message content: {content}", message.Content); + } + else + await Task.FromResult(true); + }; + discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => + { + if (args.Guild.Id == 483279257431441410) + await MikuGuild.OnUpdateAsync(sender, args); + else + await Task.FromResult(true); + }; + + discordClientKvp.Value.Logger.LogInformation("Registered events for shard {shard}", discordClientKvp.Value.ShardId); + } + } + + /*internal async Task ShowConnections() + { + while (true) + { + var al = Guilds.Where(x => x.Value?.MusicInstance != null); + ShardedClient.Logger.LogInformation("Voice Connections: " + al.Count(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true)); + await Task.Delay(15000); + } + }*/ + + internal static async Task UpdateBotList() + { + await Task.Delay(15000); + + while (true) + { + var me = await DiscordBotListApi.GetMeAsync(); + var count = Array.Empty(); + var clients = ShardedClient.ShardClients.Values; + count = clients.Aggregate(count, (current, client) => [.. current, client.Guilds.Count]); + await me.UpdateStatsAsync(0, ShardedClient.ShardClients.Count, count); + await Task.Delay(TimeSpan.FromMinutes(15)); + } + } + + internal static async Task SetActivity() + { + while (true) + { + DiscordActivity test = new() + { + Name = "I'm using slash commands now!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + DiscordActivity test2 = new() + { + Name = "Mention me with help for nsfw commands!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + DiscordActivity test3 = new() + { + Name = "Full NND support!", + ActivityType = ActivityType.Playing + }; + await ShardedClient.UpdateStatusAsync(test3, UserStatus.Online); + await Task.Delay(TimeSpan.FromMinutes(20)); + } + } + + internal void RegisterCommands() + { + // Nsfw stuff needs to be hidden, that's why we use commands next + this.CommandsNextModules.RegisterCommands(); + + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + //this.ApplicationCommandsModules.RegisterGlobalCommands(); + //ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + + // Smolcar command, miku discord guild command + this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); + } + + internal async Task RunAsync() + { + await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); + await ShardedClient.StartAsync(); + await Task.Delay(5000); + + /*foreach (var lavalinkShard in this.LavalinkModules) + { + var connection = await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + LavalinkSessions.Add(lavalinkShard.Key, connection); + }*/ + + //this.GameSetThread = Task.Run(SetActivity); + //StatusThread = Task.Run(ShowConnections); + //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); + //BotListThread = Task.Run(UpdateBotList); + while (!Cts.IsCancellationRequested) + await Task.Delay(25); + //_ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); + await ShardedClient.StopAsync(); + } + + ~MikuBot() + { + this.Dispose(); + } } diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 162b1460..167246c5 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -11,7 +11,7 @@ using MikuSharp.Enums; namespace MikuSharp.Utilities; - +/* internal class FixedOptionProviders { internal sealed class RepeatModeProvider : ChoiceProvider @@ -27,7 +27,7 @@ public override Task> Provide return Task.FromResult>(list); } } -} +}*/ internal class AutocompleteProviders { @@ -93,7 +93,6 @@ public async Task> Prov return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); } } - */ internal sealed class QueueProvider : IAutocompleteProvider { @@ -111,4 +110,5 @@ public async Task> Prov return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); } } + */ } diff --git a/MikuSharp/Utilities/Database.cs b/MikuSharp/Utilities/Old/Database.cs similarity index 99% rename from MikuSharp/Utilities/Database.cs rename to MikuSharp/Utilities/Old/Database.cs index f4fa2585..3103983e 100644 --- a/MikuSharp/Utilities/Database.cs +++ b/MikuSharp/Utilities/Old/Database.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -348,3 +348,4 @@ public static async Task> GetLastPlayingListAsync(DiscordGuild g) return queue; } } +*/ \ No newline at end of file diff --git a/MikuSharp/Utilities/Music.cs b/MikuSharp/Utilities/Old/Music.cs similarity index 99% rename from MikuSharp/Utilities/Music.cs rename to MikuSharp/Utilities/Old/Music.cs index f60cfe3f..b465f2a3 100644 --- a/MikuSharp/Utilities/Music.cs +++ b/MikuSharp/Utilities/Old/Music.cs @@ -1,4 +1,4 @@ -using System; +/*using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -240,3 +240,4 @@ public static async Task SendPlayingInformationAsync(this InteractionContext ctx await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); } } +*/ \ No newline at end of file diff --git a/MikuSharp/Utilities/PlaylistDB.cs b/MikuSharp/Utilities/Old/PlaylistDB.cs similarity index 100% rename from MikuSharp/Utilities/PlaylistDB.cs rename to MikuSharp/Utilities/Old/PlaylistDB.cs From 71c6094c95311b87d5a2ba195d134c252f21bd68 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 2 Feb 2025 21:30:07 +0100 Subject: [PATCH 028/113] new attributes & error handling --- MikuSharp/Attributes/CustomMikuAttributes.cs | 113 +++++- MikuSharp/Commands/Developer.cs | 354 ++++++++++--------- MikuSharp/Commands/Music.cs | 34 ++ MikuSharp/Entities/MusicQueueEntry.cs | 2 +- MikuSharp/MikuBot.cs | 40 ++- 5 files changed, 352 insertions(+), 191 deletions(-) create mode 100644 MikuSharp/Commands/Music.cs diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 514d027b..241f2c25 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -1,21 +1,34 @@ using System; +using System.Linq; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.CommandsNext; using DisCatSharp.CommandsNext.Attributes; +using DisCatSharp.Entities; +using DisCatSharp.Enums; +using DisCatSharp.Lavalink; namespace MikuSharp.Attributes; -/* + /// /// Defines that usage of this command is restricted to users in a vc. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserVoicechatConnection : ApplicationCommandCheckBaseAttribute { - public override Task ExecuteChecksAsync(BaseContext ctx) - => Task.FromResult(ctx.Member.VoiceState?.Channel != null); + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + var connected = ctx.Member.VoiceState?.Channel is not null; + + if (connected) + return true; + + await ctx.EditResponseAsync("You must be in a voice channel to use this command."); + return false; + } } /// @@ -24,18 +37,88 @@ public override Task ExecuteChecksAsync(BaseContext ctx) [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute { - public override async Task ExecuteChecksAsync(BaseContext ctx) - { - var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); - return ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null - ? await Task.FromResult(true) - : await Task.FromResult(false); - } -}*/ - -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class)] + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); + var connected = ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null; + if (connected) + return true; + + await ctx.EditResponseAsync("You and the bot must be in a voice channel to use this command."); + return false; + } +} + +/// +/// Defines that usage of this command is forbidden for discord staff. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class, Inherited = false)] public sealed class NotStaffAttribute : CheckBaseAttribute { - public override Task ExecuteCheckAsync(CommandContext ctx, bool help) - => Task.FromResult(!ctx.User.IsStaff); + /// + public override Task ExecuteCheckAsync(CommandContext ctx, bool help) + => Task.FromResult(!ctx.User.IsStaff); +} + +/// +/// Defines that the method or class will defer the response. +/// +/// Whether the response should be epehemeral. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class DeferResponseAsyncAttribute(bool ephemeral = false) : ApplicationCommandCheckBaseAttribute +{ + /// + /// Gets a value indicating whether the response should be ephemeral. + /// + public bool Ephemeral { get; } = ephemeral; + + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + if (this.Ephemeral) + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + else + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + + return true; + } } + +/// +/// Defines that the method or class needs to ensure that a lavalink session is available. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class EnsureLavalinkSession : ApplicationCommandCheckBaseAttribute +{ + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + var module = ctx.Client.GetLavalink(); + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (module is null) + return await RespondWithNoSessionAvailableAsync(ctx); + + var sessions = module.ConnectedSessions; + if (!sessions.Any()) + return await RespondWithNoSessionAvailableAsync(ctx); + + var firstSession = sessions.First().Value; + if (!firstSession.IsConnected) + return await RespondWithNoSessionAvailableAsync(ctx); + + return true; + } + + /// + /// Responds with a message indicating that no session is available. + /// + /// The context. + /// . + public static async Task RespondWithNoSessionAvailableAsync(BaseContext ctx) + { + await ctx.EditResponseAsync("No session found that can handle music at the moment."); + return false; + } +} \ No newline at end of file diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 4c16c887..b9693867 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; using DisCatSharp; @@ -11,6 +12,7 @@ using DisCatSharp.Enums; using DisCatSharp.Interactivity; using DisCatSharp.Interactivity.Extensions; +using DisCatSharp.Lavalink; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; @@ -20,163 +22,197 @@ namespace MikuSharp.Commands; /// /// The developer commands. /// +[ApplicationCommandRequireTeamMember] public class Developer : ApplicationCommandsModule { - [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - } - - [SlashCommand("guild_shard_test", "Testing")] - public static async Task GuildTestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); - } - - [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] - public static async Task DeleteMessageAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.TargetMessage.DeleteAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } + private static readonly string[] Units = ["", "ki", "Mi", "Gi"]; + + [SlashCommand("test", "Testing")] + public static async Task TestAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + } + + [SlashCommand("guild_shard_test", "Testing")] + public static async Task GuildTestAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); + } + + [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] + public static async Task DeleteMessageAsync(ContextMenuContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + await ctx.TargetMessage.DeleteAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } /// /// Gets the debug log. /// /// The interaction context. [SlashCommand("dbg", "Get the logs of today")] - public static async Task GetDebugLogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); - var now = DateTime.Now; - var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; - - if (!File.Exists(targetFile)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); - - try - { - if (!File.Exists($"temp-{targetFile}")) - File.Copy(targetFile, $"temp-{targetFile}"); - else - { - File.Delete($"temp-{targetFile}"); - File.Copy(targetFile, $"temp-{targetFile}"); - } - - FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); - log.Close(); - await log.DisposeAsync(); - File.Delete($"temp-{targetFile}"); - } - catch (Exception ex) - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } + public static async Task GetDebugLogAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); + var now = DateTime.Now; + var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; + + if (!File.Exists(targetFile)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); + + try + { + if (!File.Exists($"temp-{targetFile}")) + File.Copy(targetFile, $"temp-{targetFile}"); + else + { + File.Delete($"temp-{targetFile}"); + File.Copy(targetFile, $"temp-{targetFile}"); + } + + FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); + log.Close(); + await log.DisposeAsync(); + File.Delete($"temp-{targetFile}"); + } + catch (Exception ex) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } /// /// Evals the csharp script. /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public static async Task EvalCsAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - var msg = ctx.TargetMessage; - var code = ctx.TargetMessage.Content; - var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; - cs1 = code.IndexOf('\n', cs1) + 1; - var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); - var c = await ctx.Guild.GetActiveThreadsAsync(); - - if (cs1 == -1 || cs2 == -1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); - return; - } - - var cs = code[cs1..cs2]; - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() - .WithColor(new("#FF007F")) - .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") - .Build())).ConfigureAwait(false); - await ctx.GetOriginalResponseAsync(); - - try - { - var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); - - var sopts = ScriptOptions.Default; - sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", - "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); - sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); - - var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); - script.Compile(); - var result = await script.RunAsync(globals).ConfigureAwait(false); - - if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder - { - Title = "Evaluation Result", - Description = result.ReturnValue.ToString(), - Color = new DiscordColor("#007FFF") - }.Build())).ConfigureAwait(false); - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder - { - Title = "Evaluation Successful", - Description = "No result was returned.", - Color = new DiscordColor("#007FFF") - }.Build())).ConfigureAwait(false); - } - catch (Exception ex) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder - { - Title = "Evaluation Failure", - Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), - Color = new DiscordColor("#FF0000") - }.Build())).ConfigureAwait(false); - } - } + public static async Task EvalCsAsync(ContextMenuContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + var msg = ctx.TargetMessage; + var code = ctx.TargetMessage.Content; + var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; + cs1 = code.IndexOf('\n', cs1) + 1; + var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); + var c = await ctx.Guild.GetActiveThreadsAsync(); + + if (cs1 == -1 || cs2 == -1) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); + return; + } + + var cs = code[cs1..cs2]; + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() + .WithColor(new("#FF007F")) + .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") + .Build())).ConfigureAwait(false); + await ctx.GetOriginalResponseAsync(); + + try + { + var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); + + var sopts = ScriptOptions.Default; + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", + "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); + sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + + var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); + script.Compile(); + var result = await script.RunAsync(globals).ConfigureAwait(false); + + if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Result", + Description = result.ReturnValue.ToString(), + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Successful", + Description = "No result was returned.", + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + } + catch (Exception ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Failure", + Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), + Color = new DiscordColor("#FF0000") + }.Build())).ConfigureAwait(false); + } + } + + [SlashCommand("lstats", "Displays Lavalink statistics"), ApplicationCommandRequireTeamDeveloper] + public static async Task GetLavalinkStatsAsync(InteractionContext ctx) + { + var stats = ctx.Client.GetLavalink().ConnectedSessions.First().Value.Statistics; + var sb = new StringBuilder(); + sb.Append("Lavalink resources usage statistics: ```") + .Append("Uptime: ").Append(stats.Uptime).AppendLine() + .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() + .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() + .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() + .Append("RAM Usage: ") + .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() + .Append("Audio frames (per minute): ").Append($"{stats.Frames.Sent:#,##0} sent / {stats.Frames.Nulled:#,##0} nulled / {stats.Frames.Deficit:#,##0} deficit").AppendLine() + .Append("```"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); + } + + private static string SizeToString(long l) + { + double d = l; + var u = 0; + + while (d >= 900 && u < Units.Length - 2) + { + u++; + d /= 1024; + } + + return $"{d:#,##0.00} {Units[u]}B"; + } } /// @@ -184,7 +220,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe /// public sealed class SgTestVariables { - //public Dictionary Bot = MikuBot.Guilds; + //public Dictionary Bot = MikuBot.Guilds; /// /// Initializes a new instance of the class. @@ -193,25 +229,25 @@ public sealed class SgTestVariables /// The client. /// The context menu context. public SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) - { - this.Client = client; - this.ShardClient = shard; - - this.Message = msg; - this.Channel = ctx.Channel; - this.Guild = ctx.Guild; - this.User = ctx.User; - this.Member = ctx.Member; - this.Context = ctx; - this.Inter = this.Client.GetInteractivity(); - } + { + this.Client = client; + this.ShardClient = shard; + + this.Message = msg; + this.Channel = ctx.Channel; + this.Guild = ctx.Guild; + this.User = ctx.User; + this.Member = ctx.Member; + this.Context = ctx; + this.Inter = this.Client.GetInteractivity(); + } /// /// Gets or sets the message. /// public DiscordMessage Message { get; set; } - public InteractivityExtension Inter { get; set; } + public InteractivityExtension Inter { get; set; } /// /// Gets or sets the channel. @@ -238,7 +274,7 @@ public SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuCont /// public ContextMenuContext Context { get; set; } - public DiscordShardedClient ShardClient { get; set; } + public DiscordShardedClient ShardClient { get; set; } - public DiscordClient Client { get; set; } + public DiscordClient Client { get; set; } } diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs new file mode 100644 index 00000000..54374594 --- /dev/null +++ b/MikuSharp/Commands/Music.cs @@ -0,0 +1,34 @@ +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; +using DisCatSharp.Enums; + +using MikuSharp.Attributes; + +using System.Threading.Tasks; + +namespace MikuSharp.Commands; + +/// +/// The music commands +/// +[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync, EnsureLavalinkSession] +public class Music : ApplicationCommandsModule +{ + [SlashCommandGroup("base", "Base commands")] + public class Base : ApplicationCommandsModule + { + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] + public static async Task JoinAsync(InteractionContext ctx) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + } + + [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection] + public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + } + } +} diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index f7ef7bba..416ebbfb 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -5,7 +5,7 @@ namespace MikuSharp.Entities; -internal class MusicQueueEntry : IQueueEntry +internal sealed class MusicQueueEntry : IQueueEntry { public ulong UserId { get; internal set; } diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 7fc89344..080541f9 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -7,6 +7,8 @@ using DisCatSharp; using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Exceptions; using DisCatSharp.CommandsNext; using DisCatSharp.Entities; using DisCatSharp.Enums; @@ -203,24 +205,30 @@ internal static async Task RegisterEvents() return Task.CompletedTask; }; - discordClientKvp.Value.GuildMemberAdded += async (sender, args) => + discordClientKvp.Value.GetApplicationCommands().SlashCommandErrored += async (sender, args) => { - if (sender.CurrentApplication.Team.Members.Any(x => x.User.Id == args.Member.Id)) - { - var text = $"Heywo <:MikuWave:655783221942026271>!" + - $"\n\nOne of my developers joined your server!" + - $"\nAs you're the owner of the server ({args.Guild.Name}) I want to inform you about that. But don't worry, they won't disturb anyone!" + - $"\nThey're here to debug me on different servers to transition to slash commands because discord forces us bots to use it (Read more here: https://support-dev.discord.com/hc/en-us/articles/4404772028055)." + - $"\nThe problem is the _message content intent_ which means I can't listen to my `m%` prefix anymore :(." + - $"\n\nIf you have a problem please contact my developer {args.Member.UsernameWithGlobalName}!" + - $"\n\n\nI wish you a happy day <:mikuthumbsup:623933340520546306>"; - var message = await args.Guild.Owner.SendMessageAsync(text); - sender.Logger.LogInformation("I wrote {owner} a message", args.Guild.Owner.UsernameWithGlobalName); - sender.Logger.LogInformation("Message content: {content}", message.Content); - } - else - await Task.FromResult(true); + if (args.Exception is SlashExecutionChecksFailedException ex) + if (ex.FailedChecks.Any(x => x is ApplicationCommandRequireTeamMemberAttribute)) + { + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("This command is limit to developers")); + return; + } + + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("An error occurred while executing this command.")); + }; + + discordClientKvp.Value.GetApplicationCommands().ContextMenuErrored += async (sender, args) => + { + if (args.Exception is SlashExecutionChecksFailedException ex) + if (ex.FailedChecks.Any(x => x is ApplicationCommandRequireTeamMemberAttribute)) + { + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("This command is limit to developers")); + return; + } + + await args.Context.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("An error occurred while executing this command.")); }; + discordClientKvp.Value.GuildMemberUpdated += async (sender, args) => { if (args.Guild.Id == 483279257431441410) From 0fe41eb72e05d8c66230aab41ea74c14c5c4aabc Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 2 Feb 2025 21:32:05 +0100 Subject: [PATCH 029/113] Update Music.cs --- MikuSharp/Commands/Music.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs index 54374594..7b8ca4e6 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Music.cs @@ -1,4 +1,6 @@ -using DisCatSharp.ApplicationCommands; +using System.Linq; + +using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; @@ -8,6 +10,8 @@ using System.Threading.Tasks; +using DisCatSharp.Lavalink; + namespace MikuSharp.Commands; /// @@ -16,18 +20,20 @@ namespace MikuSharp.Commands; [SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync, EnsureLavalinkSession] public class Music : ApplicationCommandsModule { - [SlashCommandGroup("base", "Base commands")] + [SlashCommandGroup("base", "Base commands (Join & Leave)")] public class Base : ApplicationCommandsModule { [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] public static async Task JoinAsync(InteractionContext ctx) { + await ctx.Client.GetLavalink().ConnectedSessions.First().Value.ConnectAsync(ctx.Member.VoiceState.Channel); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); } [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection] public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) { + await ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild).DisconnectAsync(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); } } From 5fcf63c12181fe9f20c5d28471f08c018f6d4747 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 01:25:18 +0100 Subject: [PATCH 030/113] a --- MikuSharp.sln | 46 + MikuSharp/.editorconfig | 3957 +++++++++++++++++ MikuSharp/Attributes/CustomMikuAttributes.cs | 36 +- MikuSharp/Commands/About.cs | 275 +- MikuSharp/Commands/Action.cs | 316 +- MikuSharp/Commands/Fun.cs | 357 +- MikuSharp/Commands/MikuGuild.cs | 28 +- MikuSharp/Commands/Moderation.cs | 177 +- MikuSharp/Commands/Music.cs | 40 +- MikuSharp/Commands/NSFW.cs | 200 +- MikuSharp/Commands/Old/Music.cs | 4 +- MikuSharp/Commands/Utility.cs | 410 +- MikuSharp/Commands/Weeb.cs | 462 +- MikuSharp/Entities/BiliJson.cs | 38 +- MikuSharp/Entities/BiliPlayInfo.cs | 52 +- MikuSharp/Entities/BotConfig.cs | 76 +- MikuSharp/Entities/DogCeo.cs | 4 +- MikuSharp/Entities/Img_Data.cs | 6 +- MikuSharp/Entities/KsoftSiRanImg.cs | 8 +- MikuSharp/Entities/MeekMoe.cs | 4 +- MikuSharp/Entities/MusicSession.cs | 21 + MikuSharp/Entities/Nekobot.cs | 6 +- MikuSharp/Entities/Nekos_Life.cs | 2 +- MikuSharp/Entities/Old/Entry.cs | 4 +- MikuSharp/Entities/Old/Guild.cs | 4 +- MikuSharp/Entities/Old/MusicInstance.cs | 4 +- MikuSharp/Entities/Old/PlaylistEntry.cs | 4 +- MikuSharp/Entities/Old/QueueEntry.cs | 4 +- MikuSharp/Entities/Old/TrackResult.cs | 4 +- MikuSharp/Entities/Random_D.cs | 4 +- MikuSharp/Entities/WeebSh.cs | 6 +- MikuSharp/Enums/ExtService.cs | 6 +- MikuSharp/Enums/Playing.cs | 18 +- MikuSharp/Events/MikuGuildJoin.cs | 2 +- MikuSharp/Events/Old/Lavalink.cs | 4 +- MikuSharp/Events/Old/VoiceChat.cs | 4 +- MikuSharp/GlobalSuppressions.cs | 246 +- MikuSharp/MikuBot.cs | 34 +- MikuSharp/MikuSharp.csproj | 31 +- MikuSharp/Program.cs | 20 +- MikuSharp/Utilities/Bilibili.cs | 68 +- MikuSharp/Utilities/DiscordOptionProviders.cs | 141 +- MikuSharp/Utilities/NND.cs | 84 +- MikuSharp/Utilities/Old/Database.cs | 4 +- MikuSharp/Utilities/Old/Music.cs | 4 +- MikuSharp/Utilities/Other.cs | 32 +- MikuSharp/Utilities/Web.cs | 120 +- MikuSharp/config.example.json | 2 +- 48 files changed, 5718 insertions(+), 1661 deletions(-) create mode 100644 MikuSharp/.editorconfig create mode 100644 MikuSharp/Entities/MusicSession.cs diff --git a/MikuSharp.sln b/MikuSharp.sln index 8fa0e00d..17cf08fe 100644 --- a/MikuSharp.sln +++ b/MikuSharp.sln @@ -12,6 +12,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Lavalink", "..\DisCatSharp\DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DisCatSharp", "DisCatSharp", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp", "..\DisCatSharp\DisCatSharp\DisCatSharp.csproj", "{5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.ApplicationCommands", "..\DisCatSharp\DisCatSharp.ApplicationCommands\DisCatSharp.ApplicationCommands.csproj", "{32B1A815-0A97-D776-A8DF-974D2113DD5F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Common", "..\DisCatSharp\DisCatSharp.Common\DisCatSharp.Common.csproj", "{C58808B1-4112-1650-4864-88B8A5BCE1E9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.CommandsNext", "..\DisCatSharp\DisCatSharp.CommandsNext\DisCatSharp.CommandsNext.csproj", "{9894841A-DFE9-21E9-8FE9-E3486D6E28BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Interactivity", "..\DisCatSharp\DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,10 +40,42 @@ Global {38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}.Debug|Any CPU.Build.0 = Debug|Any CPU {38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {38EC14AC-1188-412F-A0C2-FC0BDA2C6BDD}.Release|Any CPU.Build.0 = Release|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}.Release|Any CPU.Build.0 = Release|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}.Release|Any CPU.Build.0 = Release|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {32B1A815-0A97-D776-A8DF-974D2113DD5F}.Release|Any CPU.Build.0 = Release|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C58808B1-4112-1650-4864-88B8A5BCE1E9}.Release|Any CPU.Build.0 = Release|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB}.Release|Any CPU.Build.0 = Release|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3A9FE493-FC1B-9081-1E43-A3B2F37EBC76} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {32B1A815-0A97-D776-A8DF-974D2113DD5F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {C58808B1-4112-1650-4864-88B8A5BCE1E9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {9894841A-DFE9-21E9-8FE9-E3486D6E28BB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C76757C4-CC79-4C18-83BD-40F37AC98C1A} EndGlobalSection diff --git a/MikuSharp/.editorconfig b/MikuSharp/.editorconfig new file mode 100644 index 00000000..a80a55bf --- /dev/null +++ b/MikuSharp/.editorconfig @@ -0,0 +1,3957 @@ +root = true +#### Core EditorConfig Options #### + +[*] + +charset = utf-8 +trim_trailing_whitespace = true + +indent_size = 4 +indent_style = tab +tab_width = 4 + +end_of_line = lf +insert_final_newline = true + +[*.yml] +intend_size = 2 +indent_style = space +insert_final_newline = false + +[*.sln] +indent_style = tab +tab_width = 4 + +[*.cs] +dotnet_code_quality_unused_parameters = non_public +dotnet_remove_unnecessary_suppression_exclusions = none +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_constructors = false:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = true:warning +csharp_style_expression_bodied_local_functions = when_on_single_line:warning +csharp_style_expression_bodied_methods = when_on_single_line:warning +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning +csharp_style_pattern_matching_over_as_with_null_check = true:warning +csharp_style_pattern_matching_over_is_with_cast_check = true:warning +csharp_style_prefer_not_pattern = true:warning +csharp_style_prefer_pattern_matching = false +csharp_style_prefer_switch_expression = true +csharp_style_conditional_delegate_call = false +csharp_prefer_static_local_function = true:warning +csharp_prefer_braces = false:suggestion +csharp_prefer_simple_using_statement = true +csharp_prefer_simple_default_expression = true:warning +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true:warning +csharp_style_pattern_local_over_anonymous_function = true:warning +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = false:warning +csharp_style_unused_value_assignment_preference = discard_variable:silent +csharp_style_unused_value_expression_statement_preference = discard_variable +csharp_new_line_before_members_in_anonymous_types = true +csharp_indent_block_contents = true +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_preserve_single_line_statements = true +dotnet_naming_rule.interface_should_be_begins_with_i.severity = error +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i +dotnet_naming_rule.private_static_readonly_fields_convention.severity = error +dotnet_naming_rule.private_static_readonly_fields_convention.symbols = private_static_fields_readonly +dotnet_naming_rule.private_static_readonly_fields_convention.style = private_static_camel_case +dotnet_naming_rule.private_static_fields_convention.severity = error +dotnet_naming_rule.private_static_fields_convention.symbols = private_static_field_props +dotnet_naming_rule.private_static_fields_convention.style = private_static_camel_case +dotnet_naming_rule.readonly.severity = error +dotnet_naming_rule.readonly.symbols = readonly +dotnet_naming_rule.readonly.style = underscore_prefixed_camel_case +dotnet_naming_rule.private_field_should_be_underscore_prefixed_camel_case.severity = error +dotnet_naming_rule.private_field_should_be_underscore_prefixed_camel_case.symbols = private_fields +dotnet_naming_rule.private_field_should_be_underscore_prefixed_camel_case.style = underscore_prefixed_camel_case +dotnet_naming_rule.types_should_be_pascal_case.severity = error +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case +dotnet_naming_rule.const_fields_should_be_all_upper.severity = error +dotnet_naming_rule.const_fields_should_be_all_upper.symbols = const_fields +dotnet_naming_rule.const_fields_should_be_all_upper.style = constant_style +dotnet_naming_rule.constants_should_be_upper_case.severity = error +dotnet_naming_rule.constants_should_be_upper_case.symbols = constants +dotnet_naming_rule.constants_should_be_upper_case.style = constant_style +dotnet_naming_rule.private_props_not_allowed.severity = error +dotnet_naming_rule.private_props_not_allowed.symbols = private_prop +dotnet_naming_rule.private_props_not_allowed.style = constant_style +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.private_static_fields_readonly.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_readonly.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_readonly.required_modifiers = static, readonly +dotnet_naming_symbols.private_static_field_props.applicable_kinds = field, property +dotnet_naming_symbols.private_static_field_props.applicable_accessibilities = private +dotnet_naming_symbols.private_static_field_props.required_modifiers = static +dotnet_naming_symbols.private_prop.applicable_kinds = property +dotnet_naming_symbols.private_prop.applicable_accessibilities = private +dotnet_naming_symbols.private_prop.required_modifiers = +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const +dotnet_naming_symbols.const_fields.applicable_kinds = field, property +dotnet_naming_symbols.const_fields.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.const_fields.required_modifiers = const +dotnet_naming_symbols.readonly.applicable_kinds = field, property +dotnet_naming_symbols.readonly.applicable_accessibilities = private +dotnet_naming_symbols.readonly.required_modifiers = readonly +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, private_protected +dotnet_naming_symbols.private_fields.required_modifiers = +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case +dotnet_naming_style.underscore_prefixed_camel_case.required_prefix = _ +dotnet_naming_style.underscore_prefixed_camel_case.required_suffix = +dotnet_naming_style.underscore_prefixed_camel_case.word_separator = +dotnet_naming_style.underscore_prefixed_camel_case.capitalization = camel_case +dotnet_naming_style.private_static_camel_case.required_prefix = s_ +dotnet_naming_style.private_static_camel_case.capitalization = camel_case +dotnet_naming_style.constant_style.required_prefix = +dotnet_naming_style.constant_style.required_suffix = +dotnet_naming_style.constant_style.word_separator = _ +dotnet_naming_style.constant_style.capitalization = all_upper +csharp_indent_braces = false +csharp_indent_switch_labels = true +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true +csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion +csharp_preserve_single_line_blocks = true +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false +csharp_style_namespace_declarations = file_scoped:error +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:warning +csharp_using_directive_placement = outside_namespace:error +dotnet_diagnostic.ide0161.severity = warning +dotnet_diagnostic.bc40000.severity = warning +dotnet_diagnostic.bc400005.severity = warning +dotnet_diagnostic.bc40008.severity = warning +dotnet_diagnostic.bc40056.severity = warning +dotnet_diagnostic.bc42016.severity = warning +dotnet_diagnostic.bc42024.severity = warning +dotnet_diagnostic.bc42025.severity = warning +dotnet_diagnostic.bc42104.severity = warning +dotnet_diagnostic.bc42105.severity = warning +dotnet_diagnostic.bc42106.severity = warning +dotnet_diagnostic.bc42107.severity = warning +dotnet_diagnostic.bc42304.severity = warning +dotnet_diagnostic.bc42309.severity = warning +dotnet_diagnostic.bc42322.severity = warning +dotnet_diagnostic.bc42349.severity = warning +dotnet_diagnostic.bc42353.severity = warning +dotnet_diagnostic.bc42354.severity = warning +dotnet_diagnostic.bc42355.severity = warning +dotnet_diagnostic.bc42356.severity = warning +dotnet_diagnostic.bc42358.severity = warning +dotnet_diagnostic.bc42504.severity = warning +dotnet_diagnostic.bc42505.severity = warning +dotnet_diagnostic.ca2252.severity = error +dotnet_diagnostic.cs0067.severity = warning +dotnet_diagnostic.cs0078.severity = warning +dotnet_diagnostic.cs0108.severity = warning +dotnet_diagnostic.cs0109.severity = warning +dotnet_diagnostic.cs0114.severity = warning +dotnet_diagnostic.cs0162.severity = warning +dotnet_diagnostic.cs0164.severity = warning +dotnet_diagnostic.cs0168.severity = warning +dotnet_diagnostic.cs0169.severity = warning +dotnet_diagnostic.cs0183.severity = warning +dotnet_diagnostic.cs0184.severity = warning +dotnet_diagnostic.cs0197.severity = warning +dotnet_diagnostic.cs0219.severity = warning +dotnet_diagnostic.cs0252.severity = warning +dotnet_diagnostic.cs0253.severity = warning +dotnet_diagnostic.cs0282.severity = warning +dotnet_diagnostic.cs0414.severity = warning +dotnet_diagnostic.cs0420.severity = warning +dotnet_diagnostic.cs0458.severity = warning +dotnet_diagnostic.cs0464.severity = warning +dotnet_diagnostic.cs0465.severity = warning +dotnet_diagnostic.cs0469.severity = warning +dotnet_diagnostic.cs0472.severity = warning +dotnet_diagnostic.cs0612.severity = warning +dotnet_diagnostic.cs0618.severity = warning +dotnet_diagnostic.cs0628.severity = warning +dotnet_diagnostic.cs0642.severity = warning +dotnet_diagnostic.cs0649.severity = warning +dotnet_diagnostic.cs0652.severity = warning +dotnet_diagnostic.cs0657.severity = warning +dotnet_diagnostic.cs0658.severity = warning +dotnet_diagnostic.cs0659.severity = warning +dotnet_diagnostic.cs0660.severity = warning +dotnet_diagnostic.cs0661.severity = warning +dotnet_diagnostic.cs0665.severity = warning +dotnet_diagnostic.cs0672.severity = warning +dotnet_diagnostic.cs0675.severity = warning +dotnet_diagnostic.cs0693.severity = warning +dotnet_diagnostic.cs1030.severity = warning +dotnet_diagnostic.cs1058.severity = warning +dotnet_diagnostic.cs1066.severity = warning +dotnet_diagnostic.cs1522.severity = warning +dotnet_diagnostic.cs1570.severity = warning +dotnet_diagnostic.cs1571.severity = warning +dotnet_diagnostic.cs1572.severity = warning +dotnet_diagnostic.cs1573.severity = warning +dotnet_diagnostic.cs1574.severity = warning +dotnet_diagnostic.cs1580.severity = warning +dotnet_diagnostic.cs1581.severity = warning +dotnet_diagnostic.cs1584.severity = warning +dotnet_diagnostic.cs1587.severity = warning +dotnet_diagnostic.cs1589.severity = warning +dotnet_diagnostic.cs1590.severity = warning +dotnet_diagnostic.cs1591.severity = warning +dotnet_diagnostic.cs1592.severity = warning +dotnet_diagnostic.cs1710.severity = warning +dotnet_diagnostic.cs1711.severity = warning +dotnet_diagnostic.cs1712.severity = warning +dotnet_diagnostic.cs1717.severity = warning +dotnet_diagnostic.cs1723.severity = warning +dotnet_diagnostic.cs1911.severity = warning +dotnet_diagnostic.cs1957.severity = warning +dotnet_diagnostic.cs1981.severity = warning +dotnet_diagnostic.cs1998.severity = warning +dotnet_diagnostic.cs4014.severity = warning +dotnet_diagnostic.cs7022.severity = warning +dotnet_diagnostic.cs7023.severity = warning +dotnet_diagnostic.cs7095.severity = warning +dotnet_diagnostic.cs8073.severity = warning +dotnet_diagnostic.cs8094.severity = warning +dotnet_diagnostic.cs8123.severity = warning +dotnet_diagnostic.cs8321.severity = warning +dotnet_diagnostic.cs8383.severity = warning +dotnet_diagnostic.cs8424.severity = warning +dotnet_diagnostic.cs8425.severity = warning +dotnet_diagnostic.cs8500.severity = warning +dotnet_diagnostic.cs8509.severity = warning +dotnet_diagnostic.cs8519.severity = warning +dotnet_diagnostic.cs8520.severity = warning +dotnet_diagnostic.cs8524.severity = warning +dotnet_diagnostic.cs8597.severity = warning +dotnet_diagnostic.cs8600.severity = warning +dotnet_diagnostic.cs8601.severity = warning +dotnet_diagnostic.cs8602.severity = warning +dotnet_diagnostic.cs8603.severity = warning +dotnet_diagnostic.cs8604.severity = warning +dotnet_diagnostic.cs8605.severity = warning +dotnet_diagnostic.cs8607.severity = warning +dotnet_diagnostic.cs8608.severity = warning +dotnet_diagnostic.cs8609.severity = warning +dotnet_diagnostic.cs8610.severity = warning +dotnet_diagnostic.cs8611.severity = warning +dotnet_diagnostic.cs8612.severity = warning +dotnet_diagnostic.cs8613.severity = warning +dotnet_diagnostic.cs8614.severity = warning +dotnet_diagnostic.cs8615.severity = warning +dotnet_diagnostic.cs8616.severity = warning +dotnet_diagnostic.cs8617.severity = warning +dotnet_diagnostic.cs8618.severity = warning +dotnet_diagnostic.cs8619.severity = warning +dotnet_diagnostic.cs8620.severity = warning +dotnet_diagnostic.cs8621.severity = warning +dotnet_diagnostic.cs8622.severity = warning +dotnet_diagnostic.cs8624.severity = warning +dotnet_diagnostic.cs8625.severity = warning +dotnet_diagnostic.cs8629.severity = warning +dotnet_diagnostic.cs8631.severity = warning +dotnet_diagnostic.cs8632.severity = warning +dotnet_diagnostic.cs8633.severity = warning +dotnet_diagnostic.cs8634.severity = warning +dotnet_diagnostic.cs8643.severity = warning +dotnet_diagnostic.cs8644.severity = warning +dotnet_diagnostic.cs8645.severity = warning +dotnet_diagnostic.cs8655.severity = warning +dotnet_diagnostic.cs8656.severity = warning +dotnet_diagnostic.cs8667.severity = warning +dotnet_diagnostic.cs8669.severity = warning +dotnet_diagnostic.cs8670.severity = warning +dotnet_diagnostic.cs8714.severity = warning +dotnet_diagnostic.cs8762.severity = warning +dotnet_diagnostic.cs8763.severity = warning +dotnet_diagnostic.cs8764.severity = warning +dotnet_diagnostic.cs8765.severity = warning +dotnet_diagnostic.cs8766.severity = warning +dotnet_diagnostic.cs8767.severity = warning +dotnet_diagnostic.cs8768.severity = warning +dotnet_diagnostic.cs8769.severity = warning +dotnet_diagnostic.cs8770.severity = warning +dotnet_diagnostic.cs8774.severity = warning +dotnet_diagnostic.cs8775.severity = warning +dotnet_diagnostic.cs8776.severity = warning +dotnet_diagnostic.cs8777.severity = warning +dotnet_diagnostic.cs8794.severity = warning +dotnet_diagnostic.cs8819.severity = warning +dotnet_diagnostic.cs8824.severity = warning +dotnet_diagnostic.cs8825.severity = warning +dotnet_diagnostic.cs8846.severity = warning +dotnet_diagnostic.cs8847.severity = warning +dotnet_diagnostic.cs8851.severity = warning +dotnet_diagnostic.cs8860.severity = warning +dotnet_diagnostic.cs8892.severity = warning +dotnet_diagnostic.cs8907.severity = warning +dotnet_diagnostic.cs8947.severity = warning +dotnet_diagnostic.cs8960.severity = warning +dotnet_diagnostic.cs8961.severity = warning +dotnet_diagnostic.cs8962.severity = warning +dotnet_diagnostic.cs8963.severity = warning +dotnet_diagnostic.cs8965.severity = warning +dotnet_diagnostic.cs8966.severity = warning +dotnet_diagnostic.cs8971.severity = warning +dotnet_diagnostic.cs8974.severity = warning +dotnet_diagnostic.cs8981.severity = warning +dotnet_diagnostic.cs9042.severity = warning +dotnet_diagnostic.cs9073.severity = warning +dotnet_diagnostic.cs9074.severity = warning +dotnet_diagnostic.cs9080.severity = warning +dotnet_diagnostic.cs9081.severity = warning +dotnet_diagnostic.cs9082.severity = warning +dotnet_diagnostic.cs9083.severity = warning +dotnet_diagnostic.cs9084.severity = warning +dotnet_diagnostic.cs9085.severity = warning +dotnet_diagnostic.cs9086.severity = warning +dotnet_diagnostic.cs9087.severity = warning +dotnet_diagnostic.cs9088.severity = warning +dotnet_diagnostic.cs9089.severity = warning +dotnet_diagnostic.cs9090.severity = warning +dotnet_diagnostic.cs9091.severity = warning +dotnet_diagnostic.cs9092.severity = warning +dotnet_diagnostic.cs9093.severity = warning +dotnet_diagnostic.cs9094.severity = warning +dotnet_diagnostic.cs9095.severity = warning +dotnet_diagnostic.cs9097.severity = warning +dotnet_diagnostic.cs9099.severity = warning +dotnet_diagnostic.cs9100.severity = warning +dotnet_diagnostic.cs9113.severity = warning +dotnet_diagnostic.wme006.severity = warning +dotnet_naming_rule.constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.constants_rule.severity = warning +dotnet_naming_rule.constants_rule.style = upper_camel_case_style +dotnet_naming_rule.constants_rule.symbols = constants_symbols +dotnet_naming_rule.event_rule.import_to_resharper = as_predefined +dotnet_naming_rule.event_rule.severity = warning +dotnet_naming_rule.event_rule.style = upper_camel_case_style +dotnet_naming_rule.event_rule.symbols = event_symbols +dotnet_naming_rule.interfaces_rule.import_to_resharper = as_predefined +dotnet_naming_rule.interfaces_rule.severity = warning +dotnet_naming_rule.interfaces_rule.style = i_upper_camel_case_style +dotnet_naming_rule.interfaces_rule.symbols = interfaces_symbols +dotnet_naming_rule.locals_rule.import_to_resharper = as_predefined +dotnet_naming_rule.locals_rule.severity = warning +dotnet_naming_rule.locals_rule.style = lower_camel_case_style_1 +dotnet_naming_rule.locals_rule.symbols = locals_symbols +dotnet_naming_rule.local_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.local_constants_rule.severity = warning +dotnet_naming_rule.local_constants_rule.style = lower_camel_case_style_1 +dotnet_naming_rule.local_constants_rule.symbols = local_constants_symbols +dotnet_naming_rule.local_functions_rule.import_to_resharper = as_predefined +dotnet_naming_rule.local_functions_rule.severity = warning +dotnet_naming_rule.local_functions_rule.style = upper_camel_case_style +dotnet_naming_rule.local_functions_rule.symbols = local_functions_symbols +dotnet_naming_rule.method_rule.import_to_resharper = as_predefined +dotnet_naming_rule.method_rule.severity = warning +dotnet_naming_rule.method_rule.style = upper_camel_case_style +dotnet_naming_rule.method_rule.symbols = method_symbols +dotnet_naming_rule.parameters_rule.import_to_resharper = as_predefined +dotnet_naming_rule.parameters_rule.severity = warning +dotnet_naming_rule.parameters_rule.style = lower_camel_case_style_1 +dotnet_naming_rule.parameters_rule.symbols = parameters_symbols +dotnet_naming_rule.private_constants_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_constants_rule.severity = warning +dotnet_naming_rule.private_constants_rule.style = upper_camel_case_style +dotnet_naming_rule.private_constants_rule.symbols = private_constants_symbols +dotnet_naming_rule.private_instance_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_instance_fields_rule.severity = warning +dotnet_naming_rule.private_instance_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_instance_fields_rule.symbols = private_instance_fields_symbols +dotnet_naming_rule.private_props_not_allowed_rule.import_to_resharper = True +dotnet_naming_rule.private_props_not_allowed_rule.resharper_description = private_props_not_allowed +dotnet_naming_rule.private_props_not_allowed_rule.resharper_guid = 5463b36e-97e1-4c30-9cce-30a590ec863e +dotnet_naming_rule.private_props_not_allowed_rule.resharper_style = s_ + aaBb_AaBb, AA_BB +dotnet_naming_rule.private_props_not_allowed_rule.severity = warning +dotnet_naming_rule.private_props_not_allowed_rule.style = s_lower_camel_case_underscore_tolerant_style +dotnet_naming_rule.private_props_not_allowed_rule.symbols = private_props_not_allowed_symbols +dotnet_naming_rule.private_static_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_fields_rule.severity = warning +dotnet_naming_rule.private_static_fields_rule.style = lower_camel_case_style +dotnet_naming_rule.private_static_fields_rule.symbols = private_static_fields_symbols +dotnet_naming_rule.private_static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.private_static_readonly_rule.severity = warning +dotnet_naming_rule.private_static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.private_static_readonly_rule.symbols = private_static_readonly_symbols +dotnet_naming_rule.property_rule.import_to_resharper = as_predefined +dotnet_naming_rule.property_rule.severity = warning +dotnet_naming_rule.property_rule.style = upper_camel_case_style +dotnet_naming_rule.property_rule.symbols = property_symbols +dotnet_naming_rule.public_fields_rule.import_to_resharper = as_predefined +dotnet_naming_rule.public_fields_rule.severity = warning +dotnet_naming_rule.public_fields_rule.style = upper_camel_case_style +dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols +dotnet_naming_rule.static_readonly_rule.import_to_resharper = as_predefined +dotnet_naming_rule.static_readonly_rule.severity = warning +dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style +dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols +dotnet_naming_rule.types_and_namespaces_rule.import_to_resharper = as_predefined +dotnet_naming_rule.types_and_namespaces_rule.severity = warning +dotnet_naming_rule.types_and_namespaces_rule.style = upper_camel_case_style +dotnet_naming_rule.types_and_namespaces_rule.symbols = types_and_namespaces_symbols +dotnet_naming_rule.type_parameters_rule.import_to_resharper = as_predefined +dotnet_naming_rule.type_parameters_rule.severity = warning +dotnet_naming_rule.type_parameters_rule.style = t_upper_camel_case_style +dotnet_naming_rule.type_parameters_rule.symbols = type_parameters_symbols +dotnet_naming_style.i_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.i_upper_camel_case_style.required_prefix = I +dotnet_naming_style.lower_camel_case_style.capitalization = camel_case +dotnet_naming_style.lower_camel_case_style.required_prefix = _ +dotnet_naming_style.lower_camel_case_style_1.capitalization = camel_case +dotnet_naming_style.s_lower_camel_case_underscore_tolerant_style.capitalization = camel_case +dotnet_naming_style.s_lower_camel_case_underscore_tolerant_style.required_prefix = s_ +dotnet_naming_style.s_lower_camel_case_underscore_tolerant_style.word_separator = _ +dotnet_naming_style.t_upper_camel_case_style.capitalization = pascal_case +dotnet_naming_style.t_upper_camel_case_style.required_prefix = T +dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case +dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.constants_symbols.applicable_kinds = field +dotnet_naming_symbols.constants_symbols.required_modifiers = const +dotnet_naming_symbols.event_symbols.applicable_accessibilities = * +dotnet_naming_symbols.event_symbols.applicable_kinds = event +dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.interfaces_symbols.applicable_kinds = interface +dotnet_naming_symbols.locals_symbols.applicable_accessibilities = * +dotnet_naming_symbols.locals_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_constants_symbols.applicable_kinds = local +dotnet_naming_symbols.local_constants_symbols.required_modifiers = const +dotnet_naming_symbols.local_functions_symbols.applicable_accessibilities = * +dotnet_naming_symbols.local_functions_symbols.applicable_kinds = local_function +dotnet_naming_symbols.method_symbols.applicable_accessibilities = * +dotnet_naming_symbols.method_symbols.applicable_kinds = method +dotnet_naming_symbols.parameters_symbols.applicable_accessibilities = * +dotnet_naming_symbols.parameters_symbols.applicable_kinds = parameter +dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_constants_symbols.applicable_kinds = field +dotnet_naming_symbols.private_constants_symbols.required_modifiers = const +dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_props_not_allowed_symbols.applicable_accessibilities = local,private +dotnet_naming_symbols.private_props_not_allowed_symbols.applicable_kinds = property +dotnet_naming_symbols.private_props_not_allowed_symbols.resharper_applicable_kinds = property +dotnet_naming_symbols.private_props_not_allowed_symbols.resharper_required_modifiers = any +dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_fields_symbols.required_modifiers = static +dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities = private +dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers = static,readonly +dotnet_naming_symbols.property_symbols.applicable_accessibilities = * +dotnet_naming_symbols.property_symbols.applicable_kinds = property +dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected +dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field +dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly +dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities = * +dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds = namespace,class,struct,enum,delegate +dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters_symbols.applicable_kinds = type_parameter +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = true +dotnet_style_readonly_field = true:warning +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:error +dotnet_style_predefined_type_for_member_access = true:warning +dotnet_style_qualification_for_event = true:warning +dotnet_style_qualification_for_field = true:warning +dotnet_style_qualification_for_method = true:warning +dotnet_style_qualification_for_property = true:warning +dotnet_style_require_accessibility_modifiers = for_non_interface_members +dotnet_style_coalesce_expression = true:warning +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_compound_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:warning +dotnet_style_prefer_conditional_expression_over_return = true:warning +dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true +file_header_template = + +# ReSharper properties +resharper_accessor_owner_body = expression_body +resharper_alignment_tab_fill_style = use_spaces +resharper_align_linq_query = true +resharper_align_multiline_argument = false +resharper_align_multiline_array_and_object_initializer = false +resharper_align_multiline_array_initializer = false +resharper_align_multiline_binary_patterns = false +resharper_align_multiline_comments = true +resharper_align_multiline_ctor_init = true +resharper_align_multiline_expression = true +resharper_align_multiline_expression_braces = false +resharper_align_multiline_extends_list = false +resharper_align_multiline_implements_list = true +resharper_align_multiline_list_pattern = false +resharper_align_multiline_property_pattern = false +resharper_align_multiline_statement_conditions = false +resharper_align_multiline_switch_expression = false +resharper_align_multiline_type_argument = false +resharper_align_multiline_type_parameter = false +resharper_align_multline_type_parameter_constrains = false +resharper_align_multline_type_parameter_list = false +resharper_align_tuple_components = false +resharper_align_union_type_usage = true +resharper_allow_alias = true +resharper_allow_comment_after_lbrace = false +resharper_allow_far_alignment = false +resharper_always_use_end_of_line_brace_style = false +resharper_apply_auto_detected_rules = true +resharper_apply_on_completion = true +resharper_arguments_anonymous_function = positional +resharper_arguments_literal = positional +resharper_arguments_named = positional +resharper_arguments_other = positional +resharper_arguments_skip_single = false +resharper_arguments_string_literal = positional +resharper_attribute_style = do_not_touch +resharper_autodetect_indent_settings = true +resharper_blank_lines_after_access_specifier = 0 +resharper_blank_lines_after_block_statements = 1 +resharper_blank_lines_after_case = 0 +resharper_blank_lines_after_control_transfer_statements = 0 +resharper_blank_lines_after_file_scoped_namespace_directive = 1 +resharper_blank_lines_after_imports = 1 +resharper_blank_lines_after_multiline_statements = 0 +resharper_blank_lines_after_options = 1 +resharper_blank_lines_after_start_comment = 1 +resharper_blank_lines_after_using_list = 1 +resharper_blank_lines_around_accessor = 0 +resharper_blank_lines_around_auto_property = 1 +resharper_blank_lines_around_block_case_section = 0 +resharper_blank_lines_around_class_definition = 1 +resharper_blank_lines_around_field = 1 +resharper_blank_lines_around_function_declaration = 0 +resharper_blank_lines_around_function_definition = 1 +resharper_blank_lines_around_global_attribute = 1 +resharper_blank_lines_around_invocable = 1 +resharper_blank_lines_around_local_method = 1 +resharper_blank_lines_around_multiline_case_section = 0 +resharper_blank_lines_around_namespace = 1 +resharper_blank_lines_around_other_declaration = 0 +resharper_blank_lines_around_property = 1 +resharper_blank_lines_around_razor_functions = 1 +resharper_blank_lines_around_razor_helpers = 1 +resharper_blank_lines_around_razor_sections = 1 +resharper_blank_lines_around_region = 1 +resharper_blank_lines_around_single_line_accessor = 0 +resharper_blank_lines_around_single_line_auto_property = 0 +resharper_blank_lines_around_single_line_field = 0 +resharper_blank_lines_around_single_line_function_definition = 0 +resharper_blank_lines_around_single_line_invocable = 0 +resharper_blank_lines_around_single_line_local_method = 1 +resharper_blank_lines_around_single_line_property = 0 +resharper_blank_lines_around_single_line_type = 1 +resharper_blank_lines_around_type = 1 +resharper_blank_lines_before_access_specifier = 1 +resharper_blank_lines_before_block_statements = 0 +resharper_blank_lines_before_case = 0 +resharper_blank_lines_before_control_transfer_statements = 0 +resharper_blank_lines_before_multiline_statements = 0 +resharper_blank_lines_before_single_line_comment = 0 +resharper_blank_lines_inside_namespace = 0 +resharper_blank_lines_inside_region = 1 +resharper_blank_lines_inside_type = 0 +resharper_blank_line_after_pi = true +resharper_braces_for_dowhile = required +resharper_braces_for_fixed = required +resharper_braces_for_for = not_required +resharper_braces_for_foreach = not_required +resharper_braces_for_ifelse = not_required +resharper_braces_for_lock = required +resharper_braces_for_using = required +resharper_braces_for_while = not_required +resharper_braces_redundant = true +resharper_break_template_declaration = line_break +resharper_builtin_type_apply_to_native_integer = false +resharper_can_use_global_alias = false +resharper_configure_await_analysis_mode = disabled +resharper_constructor_or_destructor_body = block_body +resharper_continuous_indent_multiplier = 1 +resharper_continuous_line_indent = single +resharper_cpp_align_first_arg_by_paren = false +resharper_cpp_align_multiline_binary_expressions_chain = false +resharper_cpp_align_multiline_calls_chain = true +resharper_cpp_align_multiline_for_stmt = true +resharper_cpp_align_multiline_parameter = true +resharper_cpp_align_multiple_declaration = true +resharper_cpp_align_ternary = align_not_nested +resharper_cpp_anonymous_method_declaration_braces = next_line +resharper_cpp_case_block_braces = next_line_shifted_2 +resharper_cpp_empty_block_style = multiline +resharper_cpp_indent_switch_labels = false +resharper_cpp_invocable_declaration_braces = next_line +resharper_cpp_keep_blank_lines_in_declarations = 2 +resharper_cpp_max_line_length = 120 +resharper_cpp_new_line_before_catch = true +resharper_cpp_new_line_before_else = true +resharper_cpp_new_line_before_while = true +resharper_cpp_other_braces = next_line +resharper_cpp_space_after_unary_operator = false +resharper_cpp_space_around_binary_operator = true +resharper_cpp_type_declaration_braces = next_line +resharper_cpp_wrap_arguments_style = wrap_if_long +resharper_cpp_wrap_lines = true +resharper_cpp_wrap_parameters_style = wrap_if_long +resharper_csharp_align_first_arg_by_paren = false +resharper_csharp_align_multiline_binary_expressions_chain = true +resharper_csharp_align_multiline_calls_chain = false +resharper_csharp_align_multiline_for_stmt = false +resharper_csharp_align_multiline_parameter = false +resharper_csharp_align_multiple_declaration = false +resharper_csharp_empty_block_style = together +resharper_csharp_keep_blank_lines_in_declarations = 1 +resharper_csharp_max_line_length = 1000 +resharper_csharp_naming_rule.enum_member = AaBb +resharper_csharp_naming_rule.method_property_event = AaBb +resharper_csharp_naming_rule.other = AaBb +resharper_csharp_new_line_before_while = true +resharper_csharp_prefer_qualified_reference = false +resharper_csharp_space_after_unary_operator = false +resharper_csharp_stick_comment = false +resharper_csharp_wrap_arguments_style = wrap_if_long +resharper_csharp_wrap_chained_binary_expressions = wrap_if_long +resharper_csharp_wrap_lines = false +resharper_csharp_wrap_parameters_style = wrap_if_long +resharper_css_brace_style = end_of_line +resharper_css_keep_blank_lines_between_declarations = 1 +resharper_css_max_line_length = 120 +resharper_css_wrap_lines = true +resharper_cxxcli_property_declaration_braces = next_line +resharper_declarations_style = separate_lines +resharper_default_exception_variable_name = e +resharper_default_value_when_type_evident = default_literal +resharper_default_value_when_type_not_evident = default_literal +resharper_delete_quotes_from_solid_values = false +resharper_disable_blank_line_changes = false +resharper_disable_formatter = false +resharper_disable_indenter = false +resharper_disable_int_align = false +resharper_disable_line_break_changes = false +resharper_disable_line_break_removal = false +resharper_disable_space_changes = false +resharper_disable_space_changes_before_trailing_comment = false +resharper_dont_remove_extra_blank_lines = false +resharper_enable_wrapping = false +resharper_enforce_line_ending_style = true +resharper_event_handler_pattern_long = $object$On$event$ +resharper_event_handler_pattern_short = On$event$ +resharper_export_declaration_braces = next_line +resharper_expression_braces = inside +resharper_expression_pars = inside +resharper_extra_spaces = remove_all +resharper_force_attribute_style = join +resharper_force_chop_compound_do_expression = false +resharper_force_chop_compound_if_expression = false +resharper_force_chop_compound_while_expression = false +resharper_force_control_statements_braces = do_not_change +resharper_force_linebreaks_inside_complex_literals = true +resharper_force_variable_declarations_on_new_line = false +resharper_formatter_off_tag = +resharper_formatter_on_tag = +resharper_formatter_tags_accept_regexp = false +resharper_formatter_tags_enabled = false +resharper_format_leading_spaces_decl = false +resharper_free_block_braces = next_line +resharper_function_declaration_return_type_style = do_not_change +resharper_function_definition_return_type_style = do_not_change +resharper_generator_mode = false +resharper_html_attribute_indent = align_by_first_attribute +resharper_html_linebreak_before_elements = body,div,p,form,h1,h2,h3 +resharper_html_max_blank_lines_between_tags = 2 +resharper_html_max_line_length = 120 +resharper_html_pi_attribute_style = on_single_line +resharper_html_space_before_self_closing = false +resharper_html_wrap_lines = true +resharper_ignore_space_preservation = false +resharper_include_prefix_comment_in_indent = false +resharper_indent_access_specifiers_from_class = false +resharper_indent_aligned_ternary = true +resharper_indent_anonymous_method_block = false +resharper_indent_braces_inside_statement_conditions = true +resharper_indent_case_from_select = true +resharper_indent_child_elements = OneIndent +resharper_indent_class_members_from_access_specifiers = false +resharper_indent_comment = true +resharper_indent_export_declaration_members = true +resharper_indent_inside_namespace = true +resharper_indent_invocation_pars = inside +resharper_indent_left_par_inside_expression = false +resharper_indent_member_initializer_list = true +resharper_indent_method_decl_pars = inside +resharper_indent_nested_fixed_stmt = false +resharper_indent_nested_foreach_stmt = false +resharper_indent_nested_for_stmt = false +resharper_indent_nested_lock_stmt = false +resharper_indent_nested_usings_stmt = false +resharper_indent_nested_while_stmt = false +resharper_indent_pars = inside +resharper_align_first_arg_by_paren = false +resharper_csharp_align_multiline_argument = false +resharper_csharp_align_multiline_expression = false +resharper_csharp_align_multiline_extends_list = false +resharper_csharp_allow_far_alignment = true +resharper_csharp_keep_blank_lines_in_code = 1 +resharper_csharp_wrap_after_declaration_lpar = true +resharper_csharp_wrap_before_declaration_rpar = true +resharper_indent_preprocessor_directives = none +resharper_indent_preprocessor_if = no_indent +resharper_indent_preprocessor_other = no_indent +resharper_indent_preprocessor_region = no_indent +resharper_indent_raw_literal_string = indent +resharper_indent_statement_pars = inside +resharper_indent_text = OneIndent +resharper_indent_typearg_angles = inside +resharper_indent_typeparam_angles = inside +resharper_indent_type_constraints = true +resharper_indent_wrapped_function_names = false +resharper_instance_members_qualify_declared_in = this_class, base_class +resharper_int_align = false +resharper_int_align_bitfield_sizes = false +resharper_int_align_comments = false +resharper_int_align_declaration_names = false +resharper_int_align_enum_initializers = false +resharper_int_align_eq = false +resharper_int_align_fix_in_adjacent = true +resharper_js_align_multiline_parameter = false +resharper_js_align_multiple_declaration = false +resharper_js_align_ternary = none +resharper_js_brace_style = end_of_line +resharper_js_empty_block_style = multiline +resharper_js_indent_switch_labels = false +resharper_js_keep_blank_lines_between_declarations = 2 +resharper_js_max_line_length = 120 +resharper_js_new_line_before_catch = false +resharper_js_new_line_before_else = false +resharper_js_new_line_before_finally = false +resharper_js_new_line_before_while = false +resharper_js_space_around_binary_operator = true +resharper_js_stick_comment = true +resharper_js_wrap_arguments_style = chop_if_long +resharper_js_wrap_chained_binary_expressions = chop_if_long +resharper_js_wrap_lines = true +resharper_js_wrap_parameters_style = chop_if_long +resharper_keep_blank_lines_in_code = 2 +resharper_keep_existing_attribute_arrangement = false +resharper_keep_existing_declaration_block_arrangement = false +resharper_keep_existing_declaration_parens_arrangement = true +resharper_keep_existing_embedded_arrangement = true +resharper_keep_existing_embedded_block_arrangement = false +resharper_keep_existing_enum_arrangement = false +resharper_keep_existing_expr_member_arrangement = true +resharper_keep_existing_invocation_parens_arrangement = true +resharper_keep_existing_list_patterns_arrangement = false +resharper_keep_existing_property_patterns_arrangement = false +resharper_keep_existing_switch_expression_arrangement = true +resharper_keep_nontrivial_alias = true +resharper_keep_user_linebreaks = true +resharper_keep_user_wrapping = true +resharper_linebreaks_around_razor_statements = true +resharper_linebreaks_inside_tags_for_elements_longer_than = 2147483647 +resharper_linebreaks_inside_tags_for_elements_with_child_elements = true +resharper_linebreaks_inside_tags_for_multiline_elements = true +resharper_linebreak_before_all_elements = false +resharper_linebreak_before_multiline_elements = true +resharper_linebreak_before_singleline_elements = false +resharper_line_break_after_colon_in_member_initializer_lists = do_not_change +resharper_line_break_after_comma_in_member_initializer_lists = false +resharper_line_break_after_init_statement = do_not_change +resharper_line_break_before_comma_in_member_initializer_lists = false +resharper_line_break_before_requires_clause = do_not_change +resharper_linkage_specification_braces = end_of_line +resharper_linkage_specification_indentation = none +resharper_local_function_body = expression_body +resharper_macro_block_begin = +resharper_macro_block_end = +resharper_max_array_initializer_elements_on_line = 10000 +resharper_max_attribute_length_for_same_line = 50 +resharper_max_enum_members_on_line = 3 +resharper_max_formal_parameters_on_line = 10000 +resharper_max_initializer_elements_on_line = 5 +resharper_max_invocation_arguments_on_line = 10000 +resharper_media_query_style = same_line +resharper_member_initializer_list_style = do_not_change +resharper_method_or_operator_body = expression_body +resharper_min_blank_lines_after_imports = 0 +resharper_min_blank_lines_around_fields = 0 +resharper_min_blank_lines_around_functions = 1 +resharper_min_blank_lines_around_types = 1 +resharper_min_blank_lines_between_declarations = 1 +resharper_namespace_declaration_braces = next_line +resharper_namespace_indentation = all +resharper_nested_ternary_style = expanded +resharper_new_line_before_enumerators = true +resharper_normalize_tag_names = false +resharper_no_indent_inside_elements = html,body,thead,tbody,tfoot +resharper_no_indent_inside_if_element_longer_than = 200 +resharper_null_checking_pattern_style = not_null_pattern +resharper_object_creation_when_type_evident = target_typed +resharper_object_creation_when_type_not_evident = target_typed +resharper_old_engine = false +resharper_options_braces_pointy = false +resharper_outdent_binary_ops = false +resharper_outdent_binary_pattern_ops = false +resharper_outdent_commas = false +resharper_outdent_dots = false +resharper_outdent_namespace_member = false +resharper_outdent_statement_labels = false +resharper_outdent_ternary_ops = false +resharper_parentheses_non_obvious_operations = none, shift, bitwise_and, bitwise_exclusive_or, bitwise_inclusive_or, bitwise +resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence +resharper_parentheses_same_type_operations = false +resharper_pi_attributes_indent = align_by_first_attribute +resharper_place_accessorholder_attribute_on_same_line = false +resharper_place_accessor_attribute_on_same_line = false +resharper_place_class_decorator_on_the_same_line = false +resharper_place_comments_at_first_column = false +resharper_place_constructor_initializer_on_same_line = false +resharper_place_each_decorator_on_new_line = false +resharper_place_event_attribute_on_same_line = false +resharper_place_expr_accessor_on_single_line = true +resharper_place_expr_method_on_single_line = false +resharper_place_expr_property_on_single_line = false +resharper_place_field_attribute_on_same_line = false +resharper_place_field_decorator_on_the_same_line = false +resharper_place_linq_into_on_new_line = true +resharper_place_method_attribute_on_same_line = false +resharper_place_method_decorator_on_the_same_line = false +resharper_place_namespace_definitions_on_same_line = false +resharper_place_property_attribute_on_same_line = false +resharper_place_property_decorator_on_the_same_line = false +resharper_place_record_field_attribute_on_same_line = true +resharper_place_simple_case_statement_on_same_line = false +resharper_place_simple_embedded_statement_on_same_line = false +resharper_place_simple_initializer_on_single_line = true +resharper_place_simple_list_pattern_on_single_line = true +resharper_place_simple_property_pattern_on_single_line = true +resharper_place_simple_switch_expression_on_single_line = false +resharper_place_template_args_on_new_line = false +resharper_place_type_attribute_on_same_line = false +resharper_place_type_constraints_on_same_line = true +resharper_prefer_explicit_discard_declaration = false +resharper_prefer_separate_deconstructed_variables_declaration = false +resharper_preserve_spaces_inside_tags = pre,textarea +resharper_properties_style = separate_lines_for_nonsingle +resharper_protobuf_brace_style = end_of_line +resharper_protobuf_empty_block_style = together_same_line +resharper_protobuf_max_line_length = 120 +resharper_protobuf_wrap_lines = true +resharper_qualified_using_at_nested_scope = false +resharper_quote_style = doublequoted +resharper_razor_prefer_qualified_reference = true +resharper_remove_blank_lines_near_braces = false +resharper_remove_blank_lines_near_braces_in_code = true +resharper_remove_blank_lines_near_braces_in_declarations = true +resharper_remove_this_qualifier = true +resharper_requires_expression_braces = next_line +resharper_resx_attribute_indent = single_indent +resharper_resx_linebreak_before_elements = +resharper_resx_max_blank_lines_between_tags = 0 +resharper_resx_max_line_length = 2147483647 +resharper_resx_pi_attribute_style = do_not_touch +resharper_resx_space_before_self_closing = false +resharper_resx_wrap_lines = false +resharper_resx_wrap_tags_and_pi = false +resharper_resx_wrap_text = false +resharper_selector_style = same_line +resharper_show_autodetect_configure_formatting_tip = false +resharper_simple_blocks = do_not_change +resharper_simple_block_style = do_not_change +resharper_simple_case_statement_style = do_not_change +resharper_simple_embedded_statement_style = do_not_change +resharper_single_statement_function_style = do_not_change +resharper_sort_attributes = false +resharper_sort_class_selectors = false +resharper_sort_usings = true +resharper_sort_usings_lowercase_first = false +resharper_spaces_around_eq_in_attribute = false +resharper_spaces_around_eq_in_pi_attribute = false +resharper_spaces_inside_tags = false +resharper_space_after_arrow = true +resharper_space_after_attributes = true +resharper_space_after_attribute_target_colon = true +resharper_space_after_cast = false +resharper_space_after_colon = true +resharper_space_after_colon_in_bitfield_declarator = true +resharper_space_after_colon_in_case = true +resharper_space_after_colon_in_inheritance_clause = true +resharper_space_after_colon_in_type_annotation = true +resharper_space_after_comma = true +resharper_space_after_ellipsis_in_parameter_pack = true +resharper_space_after_for_colon = true +resharper_space_after_function_comma = true +resharper_space_after_keywords_in_control_flow_statements = true +resharper_space_after_last_attribute = false +resharper_space_after_last_pi_attribute = false +resharper_space_after_media_colon = true +resharper_space_after_media_comma = true +resharper_space_after_operator_keyword = true +resharper_space_after_operator_not = false +resharper_space_after_property_colon = true +resharper_space_after_property_semicolon = true +resharper_space_after_ptr_in_data_member = true +resharper_space_after_ptr_in_data_members = false +resharper_space_after_ptr_in_method = true +resharper_space_after_ptr_in_nested_declarator = false +resharper_space_after_ref_in_data_member = true +resharper_space_after_ref_in_data_members = false +resharper_space_after_ref_in_method = true +resharper_space_after_selector_comma = true +resharper_space_after_semicolon_in_for_statement = true +resharper_space_after_separator = false +resharper_space_after_ternary_colon = true +resharper_space_after_ternary_quest = true +resharper_space_after_triple_slash = true +resharper_space_after_type_parameter_constraint_colon = true +resharper_space_around_additive_op = true +resharper_space_around_alias_eq = true +resharper_space_around_assignment_op = true +resharper_space_around_assignment_operator = true +resharper_space_around_attribute_match_operator = false +resharper_space_around_deref_in_trailing_return_type = true +resharper_space_around_lambda_arrow = true +resharper_space_around_member_access_operator = false +resharper_space_around_operator = true +resharper_space_around_pipe_or_amper_in_type_usage = true +resharper_space_around_relational_op = true +resharper_space_around_selector_operator = true +resharper_space_around_shift_op = true +resharper_space_around_stmt_colon = true +resharper_space_around_ternary_operator = true +resharper_space_before_array_rank_parentheses = false +resharper_space_before_arrow = true +resharper_space_before_attribute_target_colon = false +resharper_space_before_checked_parentheses = false +resharper_space_before_colon = false +resharper_space_before_colon_in_bitfield_declarator = true +resharper_space_before_colon_in_case = false +resharper_space_before_colon_in_inheritance_clause = true +resharper_space_before_colon_in_type_annotation = false +resharper_space_before_comma = false +resharper_space_before_default_parentheses = false +resharper_space_before_ellipsis_in_parameter_pack = false +resharper_space_before_empty_invocation_parentheses = false +resharper_space_before_empty_method_parentheses = false +resharper_space_before_for_colon = true +resharper_space_before_function_comma = false +resharper_space_before_initializer_braces = false +resharper_space_before_invocation_parentheses = false +resharper_space_before_label_colon = false +resharper_space_before_lambda_parentheses = false +resharper_space_before_media_colon = false +resharper_space_before_media_comma = false +resharper_space_before_method_parentheses = false +resharper_space_before_nameof_parentheses = false +resharper_space_before_new_parentheses = false +resharper_space_before_nullable_mark = false +resharper_space_before_open_square_brackets = false +resharper_space_before_pointer_asterik_declaration = false +resharper_space_before_postfix_operator = false +resharper_space_before_property_colon = false +resharper_space_before_property_semicolon = false +resharper_space_before_ptr_in_abstract_decl = false +resharper_space_before_ptr_in_data_member = false +resharper_space_before_ptr_in_data_members = true +resharper_space_before_ptr_in_method = false +resharper_space_before_ref_in_abstract_decl = false +resharper_space_before_ref_in_data_member = false +resharper_space_before_ref_in_data_members = true +resharper_space_before_ref_in_method = false +resharper_space_before_selector_comma = false +resharper_space_before_semicolon = false +resharper_space_before_semicolon_in_for_statement = false +resharper_space_before_separator = false +resharper_space_before_singleline_accessorholder = true +resharper_space_before_sizeof_parentheses = false +resharper_space_before_template_args = false +resharper_space_before_template_params = true +resharper_space_before_ternary_colon = true +resharper_space_before_ternary_quest = true +resharper_space_before_trailing_comment = true +resharper_space_before_typeof_parentheses = false +resharper_space_before_type_argument_angle = false +resharper_space_before_type_parameters_brackets = false +resharper_space_before_type_parameter_angle = false +resharper_space_before_type_parameter_constraint_colon = true +resharper_space_before_type_parameter_parentheses = true +resharper_space_between_accessors_in_singleline_property = true +resharper_space_between_attribute_sections = true +resharper_space_between_closing_angle_brackets_in_template_args = false +resharper_space_between_empty_square_brackets = false +resharper_space_between_keyword_and_expression = true +resharper_space_between_keyword_and_type = true +resharper_space_between_method_call_empty_parameter_list_parentheses = false +resharper_space_between_method_call_name_and_opening_parenthesis = false +resharper_space_between_method_call_parameter_list_parentheses = false +resharper_space_between_method_declaration_empty_parameter_list_parentheses = false +resharper_space_between_method_declaration_name_and_open_parenthesis = false +resharper_space_between_method_declaration_parameter_list_parentheses = false +resharper_space_between_parentheses_of_control_flow_statements = false +resharper_space_between_square_brackets = false +resharper_space_between_typecast_parentheses = false +resharper_space_colon_after = true +resharper_space_colon_before = false +resharper_space_comma = true +resharper_space_equals = true +resharper_space_inside_braces = true +resharper_space_in_singleline_accessorholder = true +resharper_space_in_singleline_anonymous_method = true +resharper_space_in_singleline_method = true +resharper_space_near_postfix_and_prefix_op = false +resharper_space_within_array_initialization_braces = false +resharper_space_within_array_rank_empty_parentheses = false +resharper_space_within_array_rank_parentheses = false +resharper_space_within_attribute_angles = false +resharper_space_within_attribute_match_brackets = false +resharper_space_within_checked_parentheses = false +resharper_space_within_declaration_parentheses = false +resharper_space_within_default_parentheses = false +resharper_space_within_empty_braces = true +resharper_space_within_empty_initializer_braces = false +resharper_space_within_empty_invocation_parentheses = false +resharper_space_within_empty_method_parentheses = false +resharper_space_within_empty_object_literal_braces = false +resharper_space_within_empty_template_params = false +resharper_space_within_expression_parentheses = false +resharper_space_within_function_parentheses = false +resharper_space_within_import_braces = true +resharper_space_within_initializer_braces = false +resharper_space_within_invocation_parentheses = false +resharper_space_within_media_block = true +resharper_space_within_media_parentheses = false +resharper_space_within_method_parentheses = false +resharper_space_within_nameof_parentheses = false +resharper_space_within_new_parentheses = false +resharper_space_within_object_literal_braces = true +resharper_space_within_parentheses = false +resharper_space_within_property_block = true +resharper_space_within_single_line_array_initializer_braces = true +resharper_space_within_sizeof_parentheses = false +resharper_space_within_slice_pattern = true +resharper_space_within_template_args = false +resharper_space_within_template_argument = false +resharper_space_within_template_params = false +resharper_space_within_tuple_parentheses = false +resharper_space_within_typeof_parentheses = false +resharper_space_within_type_argument_angles = false +resharper_space_within_type_parameters_brackets = false +resharper_space_within_type_parameter_angles = false +resharper_space_within_type_parameter_parentheses = false +resharper_special_else_if_treatment = true +resharper_static_members_qualify_members = none +resharper_static_members_qualify_with = declared_type +resharper_support_vs_event_naming_pattern = true +resharper_termination_style = ensure_semicolon +resharper_toplevel_function_declaration_return_type_style = do_not_change +resharper_toplevel_function_definition_return_type_style = do_not_change +resharper_trailing_comma_in_multiline_lists = false +resharper_trailing_comma_in_singleline_lists = false +resharper_types_braces = end_of_line +resharper_use_continuous_indent_inside_initializer_braces = true +resharper_use_continuous_indent_inside_parens = true +resharper_use_continuous_line_indent_in_expression_braces = false +resharper_use_continuous_line_indent_in_method_pars = false +resharper_use_heuristics_for_body_style = true +resharper_use_indents_from_main_language_in_file = true +resharper_use_indent_from_previous_element = true +resharper_use_indent_from_vs = false +resharper_use_roslyn_logic_for_evident_types = true +resharper_vb_align_multiline_parameter = true +resharper_vb_align_multiple_declaration = true +resharper_vb_keep_blank_lines_in_declarations = 2 +resharper_vb_max_line_length = 120 +resharper_vb_place_field_attribute_on_same_line = true +resharper_vb_place_method_attribute_on_same_line = false +resharper_vb_place_type_attribute_on_same_line = false +resharper_vb_prefer_qualified_reference = false +resharper_vb_space_after_unary_operator = true +resharper_vb_space_around_multiplicative_op = false +resharper_vb_stick_comment = true +resharper_vb_wrap_arguments_style = wrap_if_long +resharper_vb_wrap_lines = true +resharper_vb_wrap_parameters_style = wrap_if_long +resharper_wrap_after_binary_opsign = true +resharper_wrap_after_declaration_lpar = false +resharper_wrap_after_dot = false +resharper_wrap_after_dot_in_method_calls = false +resharper_wrap_after_expression_lbrace = true +resharper_wrap_after_invocation_lpar = false +resharper_wrap_after_property_in_chained_method_calls = false +resharper_wrap_around_elements = true +resharper_wrap_array_initializer_style = chop_if_long +resharper_wrap_array_literals = chop_if_long +resharper_wrap_base_clause_style = wrap_if_long +resharper_wrap_before_arrow_with_expressions = true +resharper_wrap_before_binary_opsign = false +resharper_wrap_before_binary_pattern_op = true +resharper_wrap_before_colon = false +resharper_wrap_before_comma = false +resharper_wrap_before_comma_in_base_clause = false +resharper_wrap_before_declaration_lpar = false +resharper_wrap_before_declaration_rpar = false +resharper_wrap_before_dot = true +resharper_wrap_before_eq = false +resharper_wrap_before_expression_rbrace = true +resharper_wrap_before_extends_colon = false +resharper_wrap_before_first_method_call = false +resharper_wrap_before_first_type_parameter_constraint = false +resharper_wrap_before_invocation_lpar = false +resharper_wrap_before_invocation_rpar = false +resharper_wrap_before_linq_expression = false +resharper_wrap_before_ternary_opsigns = true +resharper_wrap_before_type_parameter_langle = false +resharper_wrap_braced_init_list_style = wrap_if_long +resharper_wrap_chained_binary_patterns = wrap_if_long +resharper_wrap_chained_method_calls = wrap_if_long +resharper_wrap_ctor_initializer_style = wrap_if_long +resharper_wrap_enumeration_style = chop_if_long +resharper_wrap_enum_declaration = chop_always +resharper_wrap_enum_style = do_not_change +resharper_wrap_extends_list_style = wrap_if_long +resharper_wrap_for_stmt_header_style = chop_if_long +resharper_wrap_imports = chop_if_long +resharper_wrap_list_pattern = wrap_if_long +resharper_wrap_multiple_declaration_style = chop_if_long +resharper_wrap_multiple_type_parameter_constraints_style = chop_if_long +resharper_wrap_object_and_collection_initializer_style = chop_always +resharper_wrap_object_literals = chop_if_long +resharper_wrap_property_pattern = chop_if_long +resharper_wrap_switch_expression = chop_always +resharper_wrap_ternary_expr_style = chop_if_long +resharper_wrap_union_type_usage = chop_if_long +resharper_wrap_verbatim_interpolated_strings = no_wrap +resharper_xmldoc_attribute_indent = single_indent +resharper_xmldoc_linebreak_before_elements = summary,remarks,example,returns,param,typeparam,value,para +resharper_xmldoc_max_blank_lines_between_tags = 0 +resharper_xmldoc_max_line_length = 120 +resharper_xmldoc_pi_attribute_style = do_not_touch +resharper_xmldoc_space_before_self_closing = true +resharper_xmldoc_wrap_lines = true +resharper_xmldoc_wrap_tags_and_pi = true +resharper_xmldoc_wrap_text = true +resharper_xml_attribute_indent = align_by_first_attribute +resharper_xml_linebreak_before_elements = +resharper_xml_max_blank_lines_between_tags = 2 +resharper_xml_max_line_length = 120 +resharper_xml_pi_attribute_style = do_not_touch +resharper_xml_space_before_self_closing = true +resharper_xml_wrap_lines = true +resharper_xml_wrap_tags_and_pi = true +resharper_xml_wrap_text = false + +# ReSharper inspection severities +resharper_abstract_class_constructor_can_be_made_protected_highlighting = hint +resharper_access_rights_in_text_highlighting = warning +resharper_access_to_disposed_closure_highlighting = warning +resharper_access_to_for_each_variable_in_closure_highlighting = warning +resharper_access_to_modified_closure_highlighting = warning +resharper_access_to_static_member_via_derived_type_highlighting = warning +resharper_address_of_marshal_by_ref_object_highlighting = warning +resharper_all_underscore_local_parameter_name_highlighting = warning +resharper_amd_dependency_path_problem_highlighting = none +resharper_amd_external_module_highlighting = suggestion +resharper_angular_html_banana_highlighting = warning +resharper_annotate_can_be_null_parameter_highlighting = none +resharper_annotate_can_be_null_type_member_highlighting = none +resharper_annotate_not_null_parameter_highlighting = none +resharper_annotate_not_null_type_member_highlighting = none +resharper_annotation_conflict_in_hierarchy_highlighting = warning +resharper_annotation_redundancy_at_value_type_highlighting = warning +resharper_annotation_redundancy_in_hierarchy_highlighting = warning +resharper_anonymous_object_destructuring_problem_highlighting = warning +resharper_arguments_style_anonymous_function_highlighting = none +resharper_arguments_style_literal_highlighting = none +resharper_arguments_style_named_expression_highlighting = none +resharper_arguments_style_other_highlighting = none +resharper_arguments_style_string_literal_highlighting = none +resharper_arrange_accessor_owner_body_highlighting = suggestion +resharper_arrange_attributes_highlighting = warning +resharper_arrange_constructor_or_destructor_body_highlighting = none +resharper_arrange_default_value_when_type_evident_highlighting = suggestion +resharper_arrange_default_value_when_type_not_evident_highlighting = hint +resharper_arrange_local_function_body_highlighting = none +resharper_arrange_method_or_operator_body_highlighting = none +resharper_arrange_null_checking_pattern_highlighting = hint +resharper_arrange_object_creation_when_type_evident_highlighting = suggestion +resharper_arrange_object_creation_when_type_not_evident_highlighting = hint +resharper_arrange_redundant_parentheses_highlighting = hint +resharper_arrange_static_member_qualifier_highlighting = hint +resharper_arrange_this_qualifier_highlighting = hint +resharper_arrange_trailing_comma_in_multiline_lists_highlighting = hint +resharper_arrange_trailing_comma_in_singleline_lists_highlighting = hint +resharper_arrange_type_member_modifiers_highlighting = hint +resharper_arrange_type_modifiers_highlighting = hint +resharper_arrange_var_keywords_in_deconstructing_declaration_highlighting = suggestion +resharper_asp_content_placeholder_not_resolved_highlighting = error +resharper_asp_custom_page_parser_filter_type_highlighting = warning +resharper_asp_dead_code_highlighting = warning +resharper_asp_entity_highlighting = warning +resharper_asp_image_highlighting = warning +resharper_asp_invalid_control_type_highlighting = error +resharper_asp_not_resolved_highlighting = error +resharper_asp_ods_method_reference_resolve_error_highlighting = error +resharper_asp_resolve_warning_highlighting = warning +resharper_asp_skin_not_resolved_highlighting = error +resharper_asp_tag_attribute_with_optional_value_highlighting = warning +resharper_asp_theme_not_resolved_highlighting = error +resharper_asp_unused_register_directive_highlighting_highlighting = warning +resharper_asp_warning_highlighting = warning +resharper_assigned_value_is_never_used_highlighting = warning +resharper_assigned_value_wont_be_assigned_to_corresponding_field_highlighting = warning +resharper_assignment_instead_of_discard_highlighting = warning +resharper_assignment_in_conditional_expression_highlighting = warning +resharper_assignment_in_condition_expression_highlighting = warning +resharper_assignment_is_fully_discarded_highlighting = warning +resharper_assign_null_to_not_null_attribute_highlighting = warning +resharper_assign_to_constant_highlighting = error +resharper_assign_to_implicit_global_in_function_scope_highlighting = warning +resharper_asxx_path_error_highlighting = warning +resharper_async_converter_async_await_may_be_elided_highlighting_highlighting = none +resharper_async_converter_async_method_naming_highlighting_highlighting = warning +resharper_async_converter_async_wait_highlighting = hint +resharper_async_converter_configure_await_highlighting_highlighting = none +resharper_async_converter_null_return_as_task_highlighting = warning +resharper_async_iterator_invocation_without_await_foreach_highlighting = warning +resharper_async_void_lambda_highlighting = warning +resharper_async_void_method_highlighting = none +resharper_auto_property_can_be_made_get_only_global_highlighting = suggestion +resharper_auto_property_can_be_made_get_only_local_highlighting = suggestion +resharper_bad_attribute_brackets_spaces_highlighting = warning +resharper_bad_braces_spaces_highlighting = warning +resharper_bad_child_statement_indent_highlighting = warning +resharper_bad_colon_spaces_highlighting = warning +resharper_bad_comma_spaces_highlighting = warning +resharper_bad_control_braces_indent_highlighting = warning +resharper_bad_control_braces_line_breaks_highlighting = warning +resharper_bad_declaration_braces_indent_highlighting = warning +resharper_bad_declaration_braces_line_breaks_highlighting = warning +resharper_bad_empty_braces_line_breaks_highlighting = none +resharper_bad_expression_braces_indent_highlighting = warning +resharper_bad_expression_braces_line_breaks_highlighting = warning +resharper_bad_generic_brackets_spaces_highlighting = warning +resharper_bad_indent_highlighting = warning +resharper_bad_linq_line_breaks_highlighting = warning +resharper_bad_list_line_breaks_highlighting = warning +resharper_bad_member_access_spaces_highlighting = warning +resharper_bad_namespace_braces_indent_highlighting = warning +resharper_bad_parens_line_breaks_highlighting = warning +resharper_bad_parens_spaces_highlighting = warning +resharper_bad_preprocessor_indent_highlighting = warning +resharper_bad_semicolon_spaces_highlighting = warning +resharper_bad_spaces_after_keyword_highlighting = warning +resharper_bad_square_brackets_spaces_highlighting = warning +resharper_bad_switch_braces_indent_highlighting = warning +resharper_bad_symbol_spaces_highlighting = warning +resharper_base_member_has_params_highlighting = warning +resharper_base_method_call_with_default_parameter_highlighting = warning +resharper_base_object_equals_is_object_equals_highlighting = warning +resharper_base_object_get_hash_code_call_in_get_hash_code_highlighting = warning +resharper_bitwise_operator_on_enum_without_flags_highlighting = warning +resharper_blazor_editor_required_highlighting = warning +resharper_block_scope_redeclaration_highlighting = error +resharper_built_in_type_reference_style_for_member_access_highlighting = hint +resharper_built_in_type_reference_style_highlighting = hint +resharper_by_ref_argument_is_volatile_field_highlighting = warning +resharper_caller_callee_using_error_highlighting = error +resharper_caller_callee_using_highlighting = warning +resharper_cannot_apply_equality_operator_to_type_highlighting = warning +resharper_can_simplify_dictionary_lookup_with_try_add_highlighting = warning +resharper_can_simplify_dictionary_lookup_with_try_get_value_highlighting = warning +resharper_catch_all_clause_highlighting = none +resharper_center_tag_is_obsolete_highlighting = warning +resharper_check_for_reference_equality_instead_1_highlighting = suggestion +resharper_check_for_reference_equality_instead_2_highlighting = suggestion +resharper_check_for_reference_equality_instead_3_highlighting = suggestion +resharper_check_for_reference_equality_instead_4_highlighting = suggestion +resharper_check_namespace_highlighting = warning +resharper_class_cannot_be_instantiated_highlighting = warning +resharper_class_can_be_sealed_global_highlighting = hint +resharper_class_can_be_sealed_local_highlighting = hint +resharper_class_highlighting = suggestion +resharper_class_never_instantiated_global_highlighting = none +resharper_class_never_instantiated_local_highlighting = suggestion +resharper_class_with_virtual_members_never_inherited_global_highlighting = suggestion +resharper_class_with_virtual_members_never_inherited_local_highlighting = suggestion +resharper_clear_attribute_is_obsolete_all_highlighting = warning +resharper_clear_attribute_is_obsolete_highlighting = warning +resharper_closure_on_modified_variable_highlighting = warning +resharper_coerced_equals_using_highlighting = warning +resharper_coerced_equals_using_with_null_undefined_highlighting = none +resharper_collection_never_queried_global_highlighting = warning +resharper_collection_never_queried_local_highlighting = warning +resharper_collection_never_updated_global_highlighting = warning +resharper_collection_never_updated_local_highlighting = warning +resharper_comma_not_valid_here_highlighting = error +resharper_comment_typo_highlighting = none +resharper_common_js_external_module_highlighting = suggestion +resharper_compare_non_constrained_generic_with_null_highlighting = none +resharper_compare_of_floats_by_equality_operator_highlighting = warning +resharper_complex_object_destructuring_problem_highlighting = warning +resharper_complex_object_in_context_destructuring_problem_highlighting = warning +resharper_conditional_access_qualifier_is_non_nullable_according_to_api_contract_highlighting = warning +resharper_conditional_ternary_equal_branch_highlighting = warning +resharper_condition_is_always_const_highlighting = warning +resharper_condition_is_always_true_or_false_according_to_nullable_api_contract_highlighting = warning +resharper_condition_is_always_true_or_false_highlighting = hint +resharper_confusing_char_as_integer_in_constructor_highlighting = warning +resharper_constant_conditional_access_qualifier_highlighting = warning +resharper_constant_null_coalescing_condition_highlighting = warning +resharper_consteval_if_is_always_constant_highlighting = warning +resharper_constructor_call_not_used_highlighting = warning +resharper_constructor_initializer_loop_highlighting = warning +resharper_container_annotation_redundancy_highlighting = warning +resharper_contextual_logger_problem_highlighting = warning +resharper_context_value_is_provided_highlighting = none +resharper_contract_annotation_not_parsed_highlighting = warning +resharper_convert_closure_to_method_group_highlighting = suggestion +resharper_convert_conditional_ternary_expression_to_switch_expression_highlighting = hint +resharper_convert_constructor_to_member_initializers_highlighting = suggestion +resharper_convert_if_do_to_while_highlighting = suggestion +resharper_convert_if_statement_to_conditional_ternary_expression_highlighting = suggestion +resharper_convert_if_statement_to_null_coalescing_assignment_highlighting = suggestion +resharper_convert_if_statement_to_null_coalescing_expression_highlighting = suggestion +resharper_convert_if_statement_to_return_statement_highlighting = hint +resharper_convert_if_statement_to_switch_statement_highlighting = hint +resharper_convert_if_to_or_expression_highlighting = suggestion +resharper_convert_nullable_to_short_form_highlighting = suggestion +resharper_convert_switch_statement_to_switch_expression_highlighting = hint +resharper_convert_to_auto_property_highlighting = suggestion +resharper_convert_to_auto_property_when_possible_highlighting = hint +resharper_convert_to_auto_property_with_private_setter_highlighting = hint +resharper_convert_to_compound_assignment_highlighting = hint +resharper_convert_to_constant_global_highlighting = hint +resharper_convert_to_constant_local_highlighting = none +resharper_convert_to_lambda_expression_highlighting = suggestion +resharper_convert_to_local_function_highlighting = suggestion +resharper_convert_to_null_coalescing_compound_assignment_highlighting = suggestion +resharper_convert_to_primary_constructor_highlighting = suggestion +resharper_convert_to_static_class_highlighting = suggestion +resharper_convert_to_using_declaration_highlighting = suggestion +resharper_convert_to_vb_auto_property_highlighting = suggestion +resharper_convert_to_vb_auto_property_when_possible_highlighting = hint +resharper_convert_to_vb_auto_property_with_private_setter_highlighting = hint +resharper_convert_type_check_pattern_to_null_check_highlighting = warning +resharper_convert_type_check_to_null_check_highlighting = warning +resharper_co_variant_array_conversion_highlighting = warning +resharper_cpp_abstract_class_without_specifier_highlighting = warning +resharper_cpp_abstract_final_class_highlighting = warning +resharper_cpp_abstract_virtual_function_call_in_ctor_highlighting = error +resharper_cpp_access_specifier_with_no_declarations_highlighting = suggestion +resharper_cpp_assigned_value_is_never_used_highlighting = warning +resharper_cpp_awaiter_type_is_not_class_highlighting = warning +resharper_cpp_bad_angle_brackets_spaces_highlighting = none +resharper_cpp_bad_braces_spaces_highlighting = none +resharper_cpp_bad_child_statement_indent_highlighting = none +resharper_cpp_bad_colon_spaces_highlighting = none +resharper_cpp_bad_comma_spaces_highlighting = none +resharper_cpp_bad_control_braces_indent_highlighting = none +resharper_cpp_bad_control_braces_line_breaks_highlighting = none +resharper_cpp_bad_declaration_braces_indent_highlighting = none +resharper_cpp_bad_declaration_braces_line_breaks_highlighting = none +resharper_cpp_bad_empty_braces_line_breaks_highlighting = none +resharper_cpp_bad_expression_braces_indent_highlighting = none +resharper_cpp_bad_expression_braces_line_breaks_highlighting = none +resharper_cpp_bad_indent_highlighting = none +resharper_cpp_bad_list_line_breaks_highlighting = none +resharper_cpp_bad_member_access_spaces_highlighting = none +resharper_cpp_bad_namespace_braces_indent_highlighting = none +resharper_cpp_bad_parens_line_breaks_highlighting = none +resharper_cpp_bad_parens_spaces_highlighting = none +resharper_cpp_bad_semicolon_spaces_highlighting = none +resharper_cpp_bad_spaces_after_keyword_highlighting = none +resharper_cpp_bad_square_brackets_spaces_highlighting = none +resharper_cpp_bad_switch_braces_indent_highlighting = none +resharper_cpp_bad_symbol_spaces_highlighting = none +resharper_cpp_boolean_increment_expression_highlighting = warning +resharper_cpp_boost_format_bad_code_highlighting = warning +resharper_cpp_boost_format_legacy_code_highlighting = suggestion +resharper_cpp_boost_format_mixed_args_highlighting = error +resharper_cpp_boost_format_too_few_args_highlighting = error +resharper_cpp_boost_format_too_many_args_highlighting = warning +resharper_cpp_clang_tidy_abseil_cleanup_ctad_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_addition_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_comparison_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_conversion_cast_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_division_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_factory_float_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_factory_scale_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_subtraction_highlighting = none +resharper_cpp_clang_tidy_abseil_duration_unnecessary_conversion_highlighting = none +resharper_cpp_clang_tidy_abseil_faster_strsplit_delimiter_highlighting = none +resharper_cpp_clang_tidy_abseil_no_internal_dependencies_highlighting = none +resharper_cpp_clang_tidy_abseil_no_namespace_highlighting = none +resharper_cpp_clang_tidy_abseil_redundant_strcat_calls_highlighting = none +resharper_cpp_clang_tidy_abseil_string_find_startswith_highlighting = none +resharper_cpp_clang_tidy_abseil_string_find_str_contains_highlighting = none +resharper_cpp_clang_tidy_abseil_str_cat_append_highlighting = none +resharper_cpp_clang_tidy_abseil_time_comparison_highlighting = none +resharper_cpp_clang_tidy_abseil_time_subtraction_highlighting = none +resharper_cpp_clang_tidy_abseil_upgrade_duration_conversions_highlighting = none +resharper_cpp_clang_tidy_altera_id_dependent_backward_branch_highlighting = none +resharper_cpp_clang_tidy_altera_kernel_name_restriction_highlighting = none +resharper_cpp_clang_tidy_altera_single_work_item_barrier_highlighting = none +resharper_cpp_clang_tidy_altera_struct_pack_align_highlighting = none +resharper_cpp_clang_tidy_altera_unroll_loops_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_accept4_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_accept_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_creat_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_dup_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_epoll_create1_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_epoll_create_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_fopen_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_inotify_init1_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_inotify_init_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_memfd_create_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_open_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_pipe2_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_pipe_highlighting = none +resharper_cpp_clang_tidy_android_cloexec_socket_highlighting = none +resharper_cpp_clang_tidy_android_comparison_in_temp_failure_retry_highlighting = none +resharper_cpp_clang_tidy_boost_use_to_string_highlighting = suggestion +resharper_cpp_clang_tidy_bugprone_argument_comment_highlighting = suggestion +resharper_cpp_clang_tidy_bugprone_assert_side_effect_highlighting = warning +resharper_cpp_clang_tidy_bugprone_assignment_in_if_condition_highlighting = none +resharper_cpp_clang_tidy_bugprone_bad_signal_to_kill_thread_highlighting = warning +resharper_cpp_clang_tidy_bugprone_bool_pointer_implicit_conversion_highlighting = none +resharper_cpp_clang_tidy_bugprone_branch_clone_highlighting = warning +resharper_cpp_clang_tidy_bugprone_copy_constructor_init_highlighting = warning +resharper_cpp_clang_tidy_bugprone_dangling_handle_highlighting = warning +resharper_cpp_clang_tidy_bugprone_dynamic_static_initializers_highlighting = warning +resharper_cpp_clang_tidy_bugprone_easily_swappable_parameters_highlighting = none +resharper_cpp_clang_tidy_bugprone_exception_escape_highlighting = none +resharper_cpp_clang_tidy_bugprone_fold_init_type_highlighting = warning +resharper_cpp_clang_tidy_bugprone_forwarding_reference_overload_highlighting = warning +resharper_cpp_clang_tidy_bugprone_forward_declaration_namespace_highlighting = warning +resharper_cpp_clang_tidy_bugprone_implicit_widening_of_multiplication_result_highlighting = warning +resharper_cpp_clang_tidy_bugprone_inaccurate_erase_highlighting = warning +resharper_cpp_clang_tidy_bugprone_incorrect_roundings_highlighting = warning +resharper_cpp_clang_tidy_bugprone_infinite_loop_highlighting = warning +resharper_cpp_clang_tidy_bugprone_integer_division_highlighting = warning +resharper_cpp_clang_tidy_bugprone_lambda_function_name_highlighting = warning +resharper_cpp_clang_tidy_bugprone_macro_parentheses_highlighting = warning +resharper_cpp_clang_tidy_bugprone_macro_repeated_side_effects_highlighting = warning +resharper_cpp_clang_tidy_bugprone_misplaced_operator_in_strlen_in_alloc_highlighting = warning +resharper_cpp_clang_tidy_bugprone_misplaced_pointer_arithmetic_in_alloc_highlighting = warning +resharper_cpp_clang_tidy_bugprone_misplaced_widening_cast_highlighting = warning +resharper_cpp_clang_tidy_bugprone_move_forwarding_reference_highlighting = warning +resharper_cpp_clang_tidy_bugprone_multiple_statement_macro_highlighting = warning +resharper_cpp_clang_tidy_bugprone_narrowing_conversions_highlighting = warning +resharper_cpp_clang_tidy_bugprone_not_null_terminated_result_highlighting = warning +resharper_cpp_clang_tidy_bugprone_no_escape_highlighting = warning +resharper_cpp_clang_tidy_bugprone_parent_virtual_call_highlighting = warning +resharper_cpp_clang_tidy_bugprone_posix_return_highlighting = warning +resharper_cpp_clang_tidy_bugprone_redundant_branch_condition_highlighting = warning +resharper_cpp_clang_tidy_bugprone_reserved_identifier_highlighting = warning +resharper_cpp_clang_tidy_bugprone_shared_ptr_array_mismatch_highlighting = warning +resharper_cpp_clang_tidy_bugprone_signal_handler_highlighting = warning +resharper_cpp_clang_tidy_bugprone_signed_char_misuse_highlighting = warning +resharper_cpp_clang_tidy_bugprone_sizeof_container_highlighting = warning +resharper_cpp_clang_tidy_bugprone_sizeof_expression_highlighting = warning +resharper_cpp_clang_tidy_bugprone_spuriously_wake_up_functions_highlighting = warning +resharper_cpp_clang_tidy_bugprone_standalone_empty_highlighting = warning +resharper_cpp_clang_tidy_bugprone_stringview_nullptr_highlighting = warning +resharper_cpp_clang_tidy_bugprone_string_constructor_highlighting = warning +resharper_cpp_clang_tidy_bugprone_string_integer_assignment_highlighting = warning +resharper_cpp_clang_tidy_bugprone_string_literal_with_embedded_nul_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_enum_usage_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_include_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_memory_comparison_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_memset_usage_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_missing_comma_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_realloc_usage_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_semicolon_highlighting = warning +resharper_cpp_clang_tidy_bugprone_suspicious_string_compare_highlighting = warning +resharper_cpp_clang_tidy_bugprone_swapped_arguments_highlighting = warning +resharper_cpp_clang_tidy_bugprone_terminating_continue_highlighting = warning +resharper_cpp_clang_tidy_bugprone_throw_keyword_missing_highlighting = warning +resharper_cpp_clang_tidy_bugprone_too_small_loop_variable_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unchecked_optional_access_highlighting = warning +resharper_cpp_clang_tidy_bugprone_undefined_memory_manipulation_highlighting = warning +resharper_cpp_clang_tidy_bugprone_undelegated_constructor_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unhandled_exception_at_new_highlighting = none +resharper_cpp_clang_tidy_bugprone_unhandled_self_assignment_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unused_raii_highlighting = warning +resharper_cpp_clang_tidy_bugprone_unused_return_value_highlighting = warning +resharper_cpp_clang_tidy_bugprone_use_after_move_highlighting = warning +resharper_cpp_clang_tidy_bugprone_virtual_near_miss_highlighting = suggestion +resharper_cpp_clang_tidy_cert_con36_c_highlighting = none +resharper_cpp_clang_tidy_cert_con54_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl03_c_highlighting = none +resharper_cpp_clang_tidy_cert_dcl16_c_highlighting = none +resharper_cpp_clang_tidy_cert_dcl21_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl37_c_highlighting = none +resharper_cpp_clang_tidy_cert_dcl50_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl51_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl54_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_dcl58_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_dcl59_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_env33_c_highlighting = none +resharper_cpp_clang_tidy_cert_err09_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_err33_c_highlighting = warning +resharper_cpp_clang_tidy_cert_err34_c_highlighting = suggestion +resharper_cpp_clang_tidy_cert_err52_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_err58_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_err60_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_err61_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_exp42_c_highlighting = none +resharper_cpp_clang_tidy_cert_fio38_c_highlighting = none +resharper_cpp_clang_tidy_cert_flp30_c_highlighting = warning +resharper_cpp_clang_tidy_cert_flp37_c_highlighting = none +resharper_cpp_clang_tidy_cert_mem57_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_msc30_c_highlighting = none +resharper_cpp_clang_tidy_cert_msc32_c_highlighting = none +resharper_cpp_clang_tidy_cert_msc50_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_msc51_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_msc54_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_oop11_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_oop54_cpp_highlighting = none +resharper_cpp_clang_tidy_cert_oop57_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_oop58_cpp_highlighting = warning +resharper_cpp_clang_tidy_cert_pos44_c_highlighting = none +resharper_cpp_clang_tidy_cert_pos47_c_highlighting = none +resharper_cpp_clang_tidy_cert_sig30_c_highlighting = none +resharper_cpp_clang_tidy_cert_str34_c_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_errno_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_google_g_test_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_llvm_cast_value_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_llvm_return_value_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_std_c_library_functions_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_trust_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_api_modeling_trust_returns_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_builtin_builtin_functions_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_builtin_no_return_functions_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_call_and_message_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_call_and_message_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_divide_zero_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_dynamic_type_propagation_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_nonnil_string_constants_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_non_null_param_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_null_dereference_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_stack_address_escape_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_stack_addr_escape_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_undefined_binary_operator_result_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_array_subscript_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_assign_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_branch_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_captured_block_variable_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_new_array_size_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_uninitialized_undef_return_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_core_vla_size_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_inner_pointer_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_move_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_new_delete_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_new_delete_leaks_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_placement_new_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_pure_virtual_call_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_self_assignment_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_smart_ptr_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_string_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_cplusplus_virtual_call_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_deadcode_dead_stores_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_fuchsia_handle_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullability_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_dereferenced_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_passed_to_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_nullable_returned_from_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_null_passed_to_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_nullability_null_returned_from_nonnull_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_cplusplus_uninitialized_object_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_cplusplus_virtual_call_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_mpi_mpi_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_osx_cocoa_localizability_empty_localization_context_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_osx_cocoa_localizability_non_localized_string_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_osx_os_object_c_style_cast_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_performance_gcd_antipattern_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_performance_padding_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_optin_portability_unix_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_at_sync_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_autorelease_write_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_class_release_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_dealloc_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_incompatible_method_types_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_loops_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_missing_super_call_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_nil_arg_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_non_nil_return_value_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_ns_autorelease_pool_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_ns_error_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_obj_c_generics_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_retain_count_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_retain_count_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_run_loop_autorelease_leak_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_self_init_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_super_dealloc_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_unused_ivars_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_cocoa_variadic_method_types_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_error_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_number_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_cf_retain_release_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_containers_out_of_bounds_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_core_foundation_containers_pointer_sized_values_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_mig_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_ns_or_cf_error_deref_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_number_object_conversion_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_obj_c_property_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_os_object_retain_count_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_osx_sec_keychain_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_float_loop_counter_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bcmp_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bcopy_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_bzero_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_decode_value_of_obj_c_type_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_deprecated_or_unsafe_buffer_handling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_getpw_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_gets_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_mkstemp_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_mktemp_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_rand_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_security_syntax_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_strcpy_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_unchecked_return_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_security_insecure_api_vfork_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_api_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_cstring_bad_size_arg_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_cstring_c_string_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_cstring_null_arg_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_dynamic_memory_modeling_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_malloc_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_malloc_sizeof_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_mismatched_deallocator_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_unix_vfork_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_copy_to_self_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_uninitialized_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_unterminated_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_valist_valist_base_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_webkit_no_uncounted_member_checker_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_webkit_ref_cntbl_base_virtual_dtor_highlighting = none +resharper_cpp_clang_tidy_clang_analyzer_webkit_uncounted_lambda_captures_checker_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_absolute_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_abstract_final_class_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_abstract_vbase_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_address_of_packed_member_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_address_of_temporary_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_aix_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_align_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_alloca_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_alloca_with_align_alignof_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_always_inline_coroutine_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_delete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_ellipsis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_member_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ambiguous_reversed_operator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_analyzer_incompatible_plugin_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_anonymous_pack_parens_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_anon_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_bridge_casts_disallowed_in_nonarc_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_maybe_repeated_use_of_weak_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_non_pod_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_perform_selector_leaks_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_repeated_use_of_weak_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_retain_cycles_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_arc_unsafe_retained_assign_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_argument_outside_range_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_argument_undefined_behaviour_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_array_bounds_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_array_bounds_pointer_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_array_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_asm_operand_widths_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_assign_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_assume_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atimport_in_framework_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_access_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_alignment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_implicit_seq_cst_highlighting = suggestion +resharper_cpp_clang_tidy_clang_diagnostic_atomic_memory_ordering_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_atomic_property_with_user_defined_accessor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_attribute_packed_for_bitfield_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_attribute_warning_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_at_protocol_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_disable_vptr_sanitizer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_import_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_storage_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_auto_var_id_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_availability_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_avr_rtlib_linking_quirks_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_backslash_newline_escape_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bad_function_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_binding_in_condition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bind_to_temporary_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitfield_constant_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitfield_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitfield_width_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitwise_conditional_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitwise_instead_of_logical_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bitwise_op_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bit_int_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_block_capture_autoreleasing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bool_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bool_operation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_braced_scalar_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_branch_protection_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_bridge_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_builtin_assume_aligned_alignment_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_builtin_macro_redefined_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_builtin_memcpy_chk_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_builtin_requires_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c11_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c2x_compat_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c2x_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c99_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_c99_designator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_c99_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_called_once_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_call_to_pure_virtual_from_ctor_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_align_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_calling_convention_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_function_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_function_type_strict_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_of_sel_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_qual_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cast_qual_unrelated_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cf_string_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_char_subscripts_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_clang_cl_pch_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_class_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_class_varargs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cmse_union_leak_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_comma_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_comment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compare_distinct_pointer_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_completion_handler_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_complex_component_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_by_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_by_space_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_compound_token_split_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_conditional_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_conditional_uninitialized_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_config_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constant_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constant_evaluated_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constant_logical_operand_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_constexpr_not_const_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_consumed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_coroutine_missing_unhandled_exception_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_coro_non_aligned_allocation_funciton_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_covered_switch_default_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_deprecated_writable_strings_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_compat_reserved_user_defined_literal_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_extra_semi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_inline_namespace_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_long_long_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp11_narrowing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_binary_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp14_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_mangling_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp17_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_designator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp20_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp2a_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp2a_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp2b_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_bind_to_temporary_copy_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_extra_semi_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_local_type_template_args_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_compat_unnamed_type_template_args_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_binary_literal_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_cpp17_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp98_cpp11_cpp14_cpp17_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cpp_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cstring_format_directive_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ctad_maybe_unsupported_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ctu_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_cuda_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_custom_atomic_properties_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_cxx_attribute_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_else_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_gsl_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dangling_initializer_list_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_darwin_sdk_settings_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_date_time_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dealloc_in_category_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_debug_compression_unavailable_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_declaration_after_statement_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_defaulted_function_deleted_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_delegating_ctor_cycles_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_abstract_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_incomplete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_non_abstract_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delete_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_delimited_escape_sequence_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_altivec_src_compat_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_anon_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_array_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_attributes_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_builtins_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_comma_subscript_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_with_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_with_user_provided_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_copy_with_user_provided_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_coroutine_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_declarations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_dynamic_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_compare_conditional_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_enum_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_experimental_coroutine_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_implementations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_increment_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_module_ts_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_non_prototype_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_isa_usage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_pointer_introspection_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_objc_pointer_introspection_perform_selector_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_pragma_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_register_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_static_analyzer_flag_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_this_capture_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecated_volatile_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_deprecate_lax_vec_conv_all_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_direct_ivar_access_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_disabled_macro_expansion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_distributed_object_modifiers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_division_by_zero_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dllexport_explicit_instantiation_decl_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dllimport_static_field_def_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dll_attribute_on_redeclaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_documentation_deprecated_sync_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_html_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_documentation_unknown_command_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_dollar_in_identifier_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_double_promotion_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_dtor_name_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dtor_typedef_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_decl_specifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_method_arg_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_method_match_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_duplicate_protocol_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dynamic_class_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_dynamic_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_embedded_directive_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_body_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_decomposition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_init_stmt_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_empty_translation_unit_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_encode_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_compare_conditional_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_compare_switch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_constexpr_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_enum_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_enum_too_large_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_error_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_exceptions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_excess_initializers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_exit_time_destructors_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_expansion_to_defined_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_explicit_initialize_call_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_explicit_ownership_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_export_unnamed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_export_using_directive_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extern_c_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_extern_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_qualification_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_semi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_semi_stmt_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_extra_tokens_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_final_dtor_non_final_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_final_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_fixed_enum_extension_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_fixed_point_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_flag_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_flexible_array_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_equal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_overflow_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_float_zero_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_extra_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_insufficient_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_invalid_specifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_nonliteral_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_non_iso_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_pedantic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_security_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_type_confusion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_format_zero_length_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_fortify_source_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_for_loop_analysis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_four_char_constants_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_framework_include_private_from_public_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_frame_address_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_frame_larger_than_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_free_nonheap_object_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_function_def_in_objc_container_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_function_multiversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_future_attribute_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gcc_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_global_constructors_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_global_isel_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_alignof_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_anonymous_struct_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_gnu_array_member_paren_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_auto_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_binary_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_case_range_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_complex_integer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_compound_literal_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_conditional_omitted_operand_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_designator_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_empty_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_empty_struct_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_flexible_array_initializer_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_flexible_array_union_member_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_folding_constant_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_imaginary_constant_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_include_next_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_inline_cpp_without_extern_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_label_as_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_line_marker_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_null_pointer_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_offsetof_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_pointer_arith_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_redeclared_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_statement_expression_from_macro_expansion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_statement_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_static_float_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_string_literal_operator_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_union_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_variable_sized_type_not_at_end_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gnu_zero_variadic_macro_arguments_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_gpu_maybe_wrong_side_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_header_guard_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_header_hygiene_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_hip_only_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_hlsl_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_idiomatic_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_attributes_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ignored_availability_without_sdk_settings_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ignored_optimization_argument_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_pragmas_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_pragma_intrinsic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ignored_pragma_optimize_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_ignored_qualifiers_highlighting = suggestion +resharper_cpp_clang_tidy_clang_diagnostic_ignored_reference_qualifiers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicitly_unsigned_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_atomic_properties_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_const_int_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_conversion_floating_point_to_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_exception_spec_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_fallthrough_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_fallthrough_per_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_fixed_point_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_function_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_int_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_int_float_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_int_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_implicit_retain_self_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_import_preprocessor_directive_pedantic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inaccessible_base_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_include_next_absolute_path_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_include_next_outside_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_function_pointer_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_function_pointer_types_strict_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_library_redeclaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_ms_struct_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_pointer_types_discards_qualifiers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_pointer_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_property_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incompatible_sysroot_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_framework_module_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_implementation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_setjmp_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_incomplete_umbrella_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inconsistent_dllimport_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inconsistent_missing_destructor_override_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inconsistent_missing_override_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_increment_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_independent_class_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_infinite_recursion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_initializer_overrides_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_injected_class_name_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inline_asm_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inline_namespace_reopened_noninline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_inline_new_delete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_instantiation_after_specialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_integer_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_interrupt_service_routine_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_in_bool_context_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_to_pointer_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_int_to_void_pointer_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_constexpr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_iboutlet_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_initializer_from_system_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_ios_deployment_target_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_noreturn_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_no_builtin_names_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_offsetof_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_or_nonexistent_directory_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_partial_specialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_pp_token_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_source_encoding_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_token_paste_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_invalid_utf8_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_jump_seh_finally_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_keyword_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_keyword_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_knr_promoted_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_language_extension_token_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_large_by_value_copy_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_linker_warnings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_literal_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_literal_range_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_local_type_template_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_logical_not_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_logical_op_parentheses_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_long_long_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_macro_redefined_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_main_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_main_return_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_malformed_warning_check_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_many_braces_around_scalar_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mathematical_notation_identifier_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_max_tokens_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_max_unsigned_zero_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_memset_transposed_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_memsize_comparison_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_method_signatures_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_abstract_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_anon_tag_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_charize_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_comment_paste_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_const_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_cpp_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_default_arg_redefinition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_drectve_section_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_end_of_file_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_enum_forward_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_enum_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_exists_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_explicit_constructor_call_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_extra_qualification_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_fixed_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_flexible_array_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_goto_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_inaccessible_base_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_include_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_mutable_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_pure_definition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_redeclare_static_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_sealed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_static_assert_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_template_shadow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_union_member_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_unqualified_friend_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_using_decl_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_microsoft_void_pseudo_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_misexpect_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_misleading_indentation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_new_delete_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_parameter_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_return_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_mismatched_tags_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_braces_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_constinit_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_declarations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_field_initializers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_method_return_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_noescape_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_noreturn_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_prototypes_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_missing_prototype_for_cc_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_selector_name_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_sysroot_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_missing_variable_declarations_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_misspelled_assumption_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_modules_ambiguous_internal_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_modules_import_nested_redundant_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_conflict_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_file_config_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_file_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_module_import_in_extern_c_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_msvc_not_found_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_multichar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_multiple_move_vbase_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nested_anon_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_newline_eof_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_new_returns_null_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_noderef_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nonnull_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nonportable_include_path_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_nonportable_system_include_path_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_nonportable_vector_initialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nontrivial_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_c_typedef_for_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_literal_null_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_modular_include_in_framework_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_modular_include_in_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_pod_varargs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_power_of_two_alignment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_non_virtual_dtor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nsconsumed_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nsreturns_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ns_object_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_completeness_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_completeness_on_arrays_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_declspec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullability_inferred_on_nested_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_nullable_to_nonnull_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_character_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_dereference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_pointer_arithmetic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_null_pointer_subtraction_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_odr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_old_style_cast_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_opencl_unsupported_rgba_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp51_extensions_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_clauses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_loop_form_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_mapping_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_openmp_target_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_option_ignored_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_ordered_compare_function_pointers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_out_of_line_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_out_of_scope_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overlength_strings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overloaded_shift_op_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overloaded_virtual_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_override_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_override_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overriding_method_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_overriding_t_option_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_over_aligned_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_packed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_packed_non_pod_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_padded_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_parentheses_equality_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pass_failed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pch_date_time_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pedantic_core_features_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pedantic_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pedantic_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pessimizing_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_arith_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_bool_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_integer_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_sign_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_to_enum_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_to_int_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pointer_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_poison_system_directories_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_potentially_evaluated_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragmas_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_clang_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_messages_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_once_outside_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_pack_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_pack_suspicious_include_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pragma_system_header_outside_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_predefined_identifier_outside_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_pre_c2x_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_c2x_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp14_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp14_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp17_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp17_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp20_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp20_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp2b_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_cpp2b_compat_pedantic_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_pre_openmp51_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_private_extern_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_private_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_private_module_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_profile_instr_missing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_profile_instr_out_of_date_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_profile_instr_unprofiled_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_property_access_dot_syntax_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_property_attribute_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_protocol_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_protocol_property_synthesis_ambiguity_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_psabi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_qualified_void_return_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_quoted_include_in_framework_header_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_range_loop_analysis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_range_loop_bind_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_range_loop_construct_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_readonly_iboutlet_property_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_read_only_types_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_receiver_expr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_receiver_forward_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redeclared_class_member_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redundant_consteval_if_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redundant_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_redundant_parens_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_register_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reinterpret_base_class_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reorder_ctor_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reorder_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reorder_init_list_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_requires_super_attribute_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_identifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_id_macro_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_macro_identifier_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_reserved_user_defined_literal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_restrict_expansion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_retained_language_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_local_addr_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_stack_address_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_std_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_type_c_linkage_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_return_type_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_rewrite_not_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sarif_format_unstable_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_section_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_selector_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_selector_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_assign_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_assign_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_assign_overloaded_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_self_move_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_semicolon_before_method_body_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sentinel_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_serialized_diagnostics_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_field_in_constructor_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_shadow_field_in_constructor_modified_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_shadow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_ivar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shadow_uncaptured_local_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_shift_count_negative_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_count_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_negative_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_op_parentheses_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shift_sign_overflow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_shorten64_to32_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_signed_enum_bitfield_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_signed_unsigned_wchar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sign_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sign_conversion_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_single_bit_bitfield_constant_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_argument_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_decay_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_array_div_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_pointer_div_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sizeof_pointer_memaccess_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_slash_u_filename_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_slh_asm_goto_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_sometimes_uninitialized_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_source_uses_openmp_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_spirv_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_spir_compat_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_static_float_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_inline_explicit_instantiation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_in_inline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_local_in_inline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_static_self_init_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_stdlibcxx_not_found_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_strict_prototypes_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_strict_selector_match_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_concatenation_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_plus_char_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_string_plus_int_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_strlcpy_strlcat_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_strncat_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_suggest_destructor_override_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_suggest_override_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_super_class_method_mismatch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_suspicious_bzero_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_switch_bool_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_switch_enum_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_switch_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_sync_fetch_and_nand_semantics_changed_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_target_clones_mixed_specifiers_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_tautological_bitwise_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_in_range_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_constant_out_of_range_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_objc_bool_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_overlap_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_pointer_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_type_limit_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_undefined_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_char_zero_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_enum_zero_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tautological_unsigned_zero_compare_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_tautological_value_range_compare_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_tentative_definition_incomplete_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_analysis_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_attributes_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_beta_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_negative_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_precise_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_thread_safety_verbose_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_trigraphs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_typedef_redefinition_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_typename_missing_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_type_safety_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unable_to_open_stats_file_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unaligned_access_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unaligned_qualifier_implicit_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unavailable_declarations_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undeclared_selector_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_bool_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_func_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_inline_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_internal_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_internal_type_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_reinterpret_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undefined_var_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undef_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_undef_prefix_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_underaligned_exception_object_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unevaluated_expression_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unguarded_availability_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unguarded_availability_new_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_homoglyph_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_whitespace_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unicode_zero_width_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_uninitialized_const_reference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_uninitialized_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_argument_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_attributes_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unknown_cuda_version_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unknown_directives_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_escape_sequence_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_pragmas_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unknown_sanitizers_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unknown_warning_option_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unnamed_type_template_args_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unneeded_internal_declaration_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unneeded_member_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unqualified_std_cast_call_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_break_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_fallthrough_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_generic_assoc_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_loop_increment_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unreachable_code_return_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsafe_buffer_usage_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unsequenced_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_abi_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_abs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_availability_guard_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_cb_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_dll_base_class_template_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_friend_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_gpopt_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_nan_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_target_opt_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unsupported_visibility_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unusable_partial_specialization_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_but_set_parameter_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_but_set_variable_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_comparison_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_const_variable_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_exception_parameter_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_getter_return_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_label_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_lambda_capture_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_local_typedef_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_member_function_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_parameter_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_private_field_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_property_ivar_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_result_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_template_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_value_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_unused_variable_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_unused_volatile_lvalue_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_used_but_marked_unused_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_user_defined_literals_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_user_defined_warnings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_varargs_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_variadic_macros_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vector_conversion_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vec_elem_size_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vexing_parse_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_visibility_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vla_extension_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_vla_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_void_pointer_to_enum_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_void_pointer_to_int_cast_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_void_ptr_dereference_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_warnings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_wasm_exception_spec_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_weak_template_vtables_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_weak_vtables_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_writable_strings_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_xor_used_as_pow_highlighting = warning +resharper_cpp_clang_tidy_clang_diagnostic_zero_as_null_pointer_constant_highlighting = none +resharper_cpp_clang_tidy_clang_diagnostic_zero_length_array_highlighting = warning +resharper_cpp_clang_tidy_concurrency_mt_unsafe_highlighting = warning +resharper_cpp_clang_tidy_concurrency_thread_canceltype_asynchronous_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_const_or_ref_data_members_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_c_arrays_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_do_while_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_goto_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_magic_numbers_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_non_const_global_variables_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_avoid_reference_coroutine_parameters_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_c_copy_assignment_signature_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_explicit_virtual_functions_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_init_variables_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_interfaces_global_init_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_macro_usage_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_narrowing_conversions_highlighting = warning +resharper_cpp_clang_tidy_cppcoreguidelines_non_private_member_variables_in_classes_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_no_malloc_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_owning_memory_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_prefer_member_initializer_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_array_to_pointer_decay_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_constant_array_index_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_bounds_pointer_arithmetic_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_const_cast_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_cstyle_cast_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_member_init_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_reinterpret_cast_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_static_cast_downcast_highlighting = suggestion +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_union_access_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_pro_type_vararg_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_slicing_highlighting = none +resharper_cpp_clang_tidy_cppcoreguidelines_special_member_functions_highlighting = suggestion +resharper_cpp_clang_tidy_cppcoreguidelines_virtual_class_destructor_highlighting = none +resharper_cpp_clang_tidy_darwin_avoid_spinlock_highlighting = none +resharper_cpp_clang_tidy_darwin_dispatch_once_nonstatic_highlighting = none +resharper_cpp_clang_tidy_fuchsia_default_arguments_calls_highlighting = none +resharper_cpp_clang_tidy_fuchsia_default_arguments_declarations_highlighting = none +resharper_cpp_clang_tidy_fuchsia_header_anon_namespaces_highlighting = none +resharper_cpp_clang_tidy_fuchsia_multiple_inheritance_highlighting = none +resharper_cpp_clang_tidy_fuchsia_overloaded_operator_highlighting = none +resharper_cpp_clang_tidy_fuchsia_statically_constructed_objects_highlighting = none +resharper_cpp_clang_tidy_fuchsia_trailing_return_highlighting = none +resharper_cpp_clang_tidy_fuchsia_virtual_inheritance_highlighting = none +resharper_cpp_clang_tidy_google_build_explicit_make_pair_highlighting = none +resharper_cpp_clang_tidy_google_build_namespaces_highlighting = none +resharper_cpp_clang_tidy_google_build_using_namespace_highlighting = none +resharper_cpp_clang_tidy_google_default_arguments_highlighting = none +resharper_cpp_clang_tidy_google_explicit_constructor_highlighting = none +resharper_cpp_clang_tidy_google_global_names_in_headers_highlighting = none +resharper_cpp_clang_tidy_google_objc_avoid_nsobject_new_highlighting = none +resharper_cpp_clang_tidy_google_objc_avoid_throwing_exception_highlighting = none +resharper_cpp_clang_tidy_google_objc_function_naming_highlighting = none +resharper_cpp_clang_tidy_google_objc_global_variable_declaration_highlighting = none +resharper_cpp_clang_tidy_google_readability_avoid_underscore_in_googletest_name_highlighting = none +resharper_cpp_clang_tidy_google_readability_braces_around_statements_highlighting = none +resharper_cpp_clang_tidy_google_readability_casting_highlighting = none +resharper_cpp_clang_tidy_google_readability_function_size_highlighting = none +resharper_cpp_clang_tidy_google_readability_namespace_comments_highlighting = none +resharper_cpp_clang_tidy_google_readability_todo_highlighting = none +resharper_cpp_clang_tidy_google_runtime_int_highlighting = none +resharper_cpp_clang_tidy_google_runtime_operator_highlighting = warning +resharper_cpp_clang_tidy_google_upgrade_googletest_case_highlighting = suggestion +resharper_cpp_clang_tidy_hicpp_avoid_c_arrays_highlighting = none +resharper_cpp_clang_tidy_hicpp_avoid_goto_highlighting = warning +resharper_cpp_clang_tidy_hicpp_braces_around_statements_highlighting = none +resharper_cpp_clang_tidy_hicpp_deprecated_headers_highlighting = none +resharper_cpp_clang_tidy_hicpp_exception_baseclass_highlighting = suggestion +resharper_cpp_clang_tidy_hicpp_explicit_conversions_highlighting = none +resharper_cpp_clang_tidy_hicpp_function_size_highlighting = none +resharper_cpp_clang_tidy_hicpp_invalid_access_moved_highlighting = none +resharper_cpp_clang_tidy_hicpp_member_init_highlighting = none +resharper_cpp_clang_tidy_hicpp_move_const_arg_highlighting = none +resharper_cpp_clang_tidy_hicpp_multiway_paths_covered_highlighting = warning +resharper_cpp_clang_tidy_hicpp_named_parameter_highlighting = none +resharper_cpp_clang_tidy_hicpp_new_delete_operators_highlighting = none +resharper_cpp_clang_tidy_hicpp_noexcept_move_highlighting = none +resharper_cpp_clang_tidy_hicpp_no_array_decay_highlighting = none +resharper_cpp_clang_tidy_hicpp_no_assembler_highlighting = none +resharper_cpp_clang_tidy_hicpp_no_malloc_highlighting = none +resharper_cpp_clang_tidy_hicpp_signed_bitwise_highlighting = none +resharper_cpp_clang_tidy_hicpp_special_member_functions_highlighting = none +resharper_cpp_clang_tidy_hicpp_static_assert_highlighting = none +resharper_cpp_clang_tidy_hicpp_undelegated_constructor_highlighting = none +resharper_cpp_clang_tidy_hicpp_uppercase_literal_suffix_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_auto_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_emplace_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_equals_default_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_equals_delete_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_noexcept_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_nullptr_highlighting = none +resharper_cpp_clang_tidy_hicpp_use_override_highlighting = none +resharper_cpp_clang_tidy_hicpp_vararg_highlighting = none +resharper_cpp_clang_tidy_highlighting_highlighting = suggestion +resharper_cpp_clang_tidy_linuxkernel_must_check_errs_highlighting = warning +resharper_cpp_clang_tidy_llvmlibc_callee_namespace_highlighting = none +resharper_cpp_clang_tidy_llvmlibc_implementation_in_namespace_highlighting = none +resharper_cpp_clang_tidy_llvmlibc_restrict_system_libc_headers_highlighting = none +resharper_cpp_clang_tidy_llvm_else_after_return_highlighting = none +resharper_cpp_clang_tidy_llvm_header_guard_highlighting = none +resharper_cpp_clang_tidy_llvm_include_order_highlighting = none +resharper_cpp_clang_tidy_llvm_namespace_comment_highlighting = none +resharper_cpp_clang_tidy_llvm_prefer_isa_or_dyn_cast_in_conditionals_highlighting = none +resharper_cpp_clang_tidy_llvm_prefer_register_over_unsigned_highlighting = suggestion +resharper_cpp_clang_tidy_llvm_qualified_auto_highlighting = none +resharper_cpp_clang_tidy_llvm_twine_local_highlighting = none +resharper_cpp_clang_tidy_misc_confusable_identifiers_highlighting = warning +resharper_cpp_clang_tidy_misc_const_correctness_highlighting = none +resharper_cpp_clang_tidy_misc_definitions_in_headers_highlighting = none +resharper_cpp_clang_tidy_misc_misleading_bidirectional_highlighting = warning +resharper_cpp_clang_tidy_misc_misleading_identifier_highlighting = warning +resharper_cpp_clang_tidy_misc_misplaced_const_highlighting = none +resharper_cpp_clang_tidy_misc_new_delete_overloads_highlighting = warning +resharper_cpp_clang_tidy_misc_non_copyable_objects_highlighting = warning +resharper_cpp_clang_tidy_misc_non_private_member_variables_in_classes_highlighting = none +resharper_cpp_clang_tidy_misc_no_recursion_highlighting = none +resharper_cpp_clang_tidy_misc_redundant_expression_highlighting = warning +resharper_cpp_clang_tidy_misc_static_assert_highlighting = suggestion +resharper_cpp_clang_tidy_misc_throw_by_value_catch_by_reference_highlighting = warning +resharper_cpp_clang_tidy_misc_unconventional_assign_operator_highlighting = warning +resharper_cpp_clang_tidy_misc_uniqueptr_reset_release_highlighting = suggestion +resharper_cpp_clang_tidy_misc_unused_alias_decls_highlighting = suggestion +resharper_cpp_clang_tidy_misc_unused_parameters_highlighting = none +resharper_cpp_clang_tidy_misc_unused_using_decls_highlighting = suggestion +resharper_cpp_clang_tidy_misc_use_anonymous_namespace_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_avoid_bind_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_avoid_c_arrays_highlighting = none +resharper_cpp_clang_tidy_modernize_concat_nested_namespaces_highlighting = none +resharper_cpp_clang_tidy_modernize_deprecated_headers_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_deprecated_ios_base_aliases_highlighting = warning +resharper_cpp_clang_tidy_modernize_loop_convert_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_macro_to_enum_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_make_shared_highlighting = none +resharper_cpp_clang_tidy_modernize_make_unique_highlighting = none +resharper_cpp_clang_tidy_modernize_pass_by_value_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_raw_string_literal_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_redundant_void_arg_highlighting = none +resharper_cpp_clang_tidy_modernize_replace_auto_ptr_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_replace_disallow_copy_and_assign_macro_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_replace_random_shuffle_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_return_braced_init_list_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_shrink_to_fit_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_unary_static_assert_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_auto_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_bool_literals_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_default_member_init_highlighting = none +resharper_cpp_clang_tidy_modernize_use_emplace_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_equals_default_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_equals_delete_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_nodiscard_highlighting = hint +resharper_cpp_clang_tidy_modernize_use_noexcept_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_nullptr_highlighting = none +resharper_cpp_clang_tidy_modernize_use_override_highlighting = none +resharper_cpp_clang_tidy_modernize_use_trailing_return_type_highlighting = none +resharper_cpp_clang_tidy_modernize_use_transparent_functors_highlighting = suggestion +resharper_cpp_clang_tidy_modernize_use_uncaught_exceptions_highlighting = warning +resharper_cpp_clang_tidy_modernize_use_using_highlighting = none +resharper_cpp_clang_tidy_mpi_buffer_deref_highlighting = warning +resharper_cpp_clang_tidy_mpi_type_mismatch_highlighting = warning +resharper_cpp_clang_tidy_objc_assert_equals_highlighting = warning +resharper_cpp_clang_tidy_objc_avoid_nserror_init_highlighting = warning +resharper_cpp_clang_tidy_objc_dealloc_in_category_highlighting = warning +resharper_cpp_clang_tidy_objc_forbidden_subclassing_highlighting = warning +resharper_cpp_clang_tidy_objc_missing_hash_highlighting = warning +resharper_cpp_clang_tidy_objc_nsdate_formatter_highlighting = none +resharper_cpp_clang_tidy_objc_nsinvocation_argument_lifetime_highlighting = warning +resharper_cpp_clang_tidy_objc_property_declaration_highlighting = warning +resharper_cpp_clang_tidy_objc_super_self_highlighting = warning +resharper_cpp_clang_tidy_openmp_exception_escape_highlighting = warning +resharper_cpp_clang_tidy_openmp_use_default_none_highlighting = warning +resharper_cpp_clang_tidy_performance_faster_string_find_highlighting = suggestion +resharper_cpp_clang_tidy_performance_for_range_copy_highlighting = suggestion +resharper_cpp_clang_tidy_performance_implicit_conversion_in_loop_highlighting = suggestion +resharper_cpp_clang_tidy_performance_inefficient_algorithm_highlighting = suggestion +resharper_cpp_clang_tidy_performance_inefficient_string_concatenation_highlighting = suggestion +resharper_cpp_clang_tidy_performance_inefficient_vector_operation_highlighting = suggestion +resharper_cpp_clang_tidy_performance_move_constructor_init_highlighting = warning +resharper_cpp_clang_tidy_performance_move_const_arg_highlighting = suggestion +resharper_cpp_clang_tidy_performance_noexcept_move_constructor_highlighting = none +resharper_cpp_clang_tidy_performance_no_automatic_move_highlighting = warning +resharper_cpp_clang_tidy_performance_no_int_to_ptr_highlighting = warning +resharper_cpp_clang_tidy_performance_trivially_destructible_highlighting = suggestion +resharper_cpp_clang_tidy_performance_type_promotion_in_math_fn_highlighting = suggestion +resharper_cpp_clang_tidy_performance_unnecessary_copy_initialization_highlighting = suggestion +resharper_cpp_clang_tidy_performance_unnecessary_value_param_highlighting = suggestion +resharper_cpp_clang_tidy_portability_restrict_system_includes_highlighting = none +resharper_cpp_clang_tidy_portability_simd_intrinsics_highlighting = none +resharper_cpp_clang_tidy_portability_std_allocator_const_highlighting = warning +resharper_cpp_clang_tidy_readability_avoid_const_params_in_decls_highlighting = none +resharper_cpp_clang_tidy_readability_braces_around_statements_highlighting = none +resharper_cpp_clang_tidy_readability_const_return_type_highlighting = none +resharper_cpp_clang_tidy_readability_container_contains_highlighting = none +resharper_cpp_clang_tidy_readability_container_data_pointer_highlighting = suggestion +resharper_cpp_clang_tidy_readability_container_size_empty_highlighting = suggestion +resharper_cpp_clang_tidy_readability_convert_member_functions_to_static_highlighting = none +resharper_cpp_clang_tidy_readability_delete_null_pointer_highlighting = suggestion +resharper_cpp_clang_tidy_readability_duplicate_include_highlighting = none +resharper_cpp_clang_tidy_readability_else_after_return_highlighting = none +resharper_cpp_clang_tidy_readability_function_cognitive_complexity_highlighting = none +resharper_cpp_clang_tidy_readability_function_size_highlighting = none +resharper_cpp_clang_tidy_readability_identifier_length_highlighting = none +resharper_cpp_clang_tidy_readability_identifier_naming_highlighting = none +resharper_cpp_clang_tidy_readability_implicit_bool_conversion_highlighting = none +resharper_cpp_clang_tidy_readability_inconsistent_declaration_parameter_name_highlighting = suggestion +resharper_cpp_clang_tidy_readability_isolate_declaration_highlighting = none +resharper_cpp_clang_tidy_readability_magic_numbers_highlighting = none +resharper_cpp_clang_tidy_readability_make_member_function_const_highlighting = none +resharper_cpp_clang_tidy_readability_misleading_indentation_highlighting = none +resharper_cpp_clang_tidy_readability_misplaced_array_index_highlighting = suggestion +resharper_cpp_clang_tidy_readability_named_parameter_highlighting = none +resharper_cpp_clang_tidy_readability_non_const_parameter_highlighting = none +resharper_cpp_clang_tidy_readability_qualified_auto_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_access_specifiers_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_control_flow_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_declaration_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_function_ptr_dereference_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_member_init_highlighting = none +resharper_cpp_clang_tidy_readability_redundant_preprocessor_highlighting = warning +resharper_cpp_clang_tidy_readability_redundant_smartptr_get_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_string_cstr_highlighting = suggestion +resharper_cpp_clang_tidy_readability_redundant_string_init_highlighting = suggestion +resharper_cpp_clang_tidy_readability_simplify_boolean_expr_highlighting = none +resharper_cpp_clang_tidy_readability_simplify_subscript_expr_highlighting = warning +resharper_cpp_clang_tidy_readability_static_accessed_through_instance_highlighting = suggestion +resharper_cpp_clang_tidy_readability_static_definition_in_anonymous_namespace_highlighting = none +resharper_cpp_clang_tidy_readability_string_compare_highlighting = warning +resharper_cpp_clang_tidy_readability_suspicious_call_argument_highlighting = warning +resharper_cpp_clang_tidy_readability_uniqueptr_delete_release_highlighting = suggestion +resharper_cpp_clang_tidy_readability_uppercase_literal_suffix_highlighting = none +resharper_cpp_clang_tidy_readability_use_anyofallof_highlighting = suggestion +resharper_cpp_clang_tidy_zircon_temporary_objects_highlighting = none +resharper_cpp_class_can_be_final_highlighting = hint +resharper_cpp_class_is_incomplete_highlighting = warning +resharper_cpp_class_needs_constructor_because_of_uninitialized_member_highlighting = warning +resharper_cpp_class_never_used_highlighting = warning +resharper_cpp_compile_time_constant_can_be_replaced_with_boolean_constant_highlighting = suggestion +resharper_cpp_concept_never_used_highlighting = warning +resharper_cpp_const_parameter_in_declaration_highlighting = suggestion +resharper_cpp_const_value_function_return_type_highlighting = suggestion +resharper_cpp_coroutine_call_resolve_error_highlighting = warning +resharper_cpp_cv_qualifier_can_not_be_applied_to_reference_highlighting = warning +resharper_cpp_c_style_cast_highlighting = suggestion +resharper_cpp_declaration_hides_local_highlighting = warning +resharper_cpp_declaration_hides_uncaptured_local_highlighting = hint +resharper_cpp_declaration_specifier_without_declarators_highlighting = warning +resharper_cpp_declarator_disambiguated_as_function_highlighting = warning +resharper_cpp_declarator_never_used_highlighting = warning +resharper_cpp_declarator_used_before_initialization_highlighting = error +resharper_cpp_defaulted_special_member_function_is_implicitly_deleted_highlighting = warning +resharper_cpp_default_case_not_handled_in_switch_statement_highlighting = warning +resharper_cpp_default_initialization_with_no_user_constructor_highlighting = warning +resharper_cpp_default_is_used_as_identifier_highlighting = warning +resharper_cpp_deleting_void_pointer_highlighting = warning +resharper_cpp_dependent_template_without_template_keyword_highlighting = warning +resharper_cpp_dependent_type_without_typename_keyword_highlighting = warning +resharper_cpp_deprecated_entity_highlighting = warning +resharper_cpp_deprecated_register_storage_class_specifier_highlighting = warning +resharper_cpp_dereference_operator_limit_exceeded_highlighting = warning +resharper_cpp_discarded_postfix_operator_result_highlighting = suggestion +resharper_cpp_doxygen_syntax_error_highlighting = warning +resharper_cpp_doxygen_undocumented_parameter_highlighting = suggestion +resharper_cpp_doxygen_unresolved_reference_highlighting = warning +resharper_cpp_empty_declaration_highlighting = warning +resharper_cpp_enforce_cv_qualifiers_order_highlighting = none +resharper_cpp_enforce_cv_qualifiers_placement_highlighting = none +resharper_cpp_enforce_do_statement_braces_highlighting = none +resharper_cpp_enforce_for_statement_braces_highlighting = none +resharper_cpp_enforce_function_declaration_style_highlighting = none +resharper_cpp_enforce_if_statement_braces_highlighting = none +resharper_cpp_enforce_nested_namespaces_style_highlighting = hint +resharper_cpp_enforce_overriding_destructor_style_highlighting = suggestion +resharper_cpp_enforce_overriding_function_style_highlighting = suggestion +resharper_cpp_enforce_type_alias_code_style_highlighting = none +resharper_cpp_enforce_while_statement_braces_highlighting = none +resharper_cpp_entity_assigned_but_no_read_highlighting = warning +resharper_cpp_entity_used_only_in_unevaluated_context_highlighting = warning +resharper_cpp_enumerator_never_used_highlighting = warning +resharper_cpp_equal_operands_in_binary_expression_highlighting = warning +resharper_cpp_explicit_specialization_in_non_namespace_scope_highlighting = warning +resharper_cpp_expression_without_side_effects_highlighting = warning +resharper_cpp_final_function_in_final_class_highlighting = suggestion +resharper_cpp_final_non_overriding_virtual_function_highlighting = suggestion +resharper_cpp_forward_enum_declaration_without_underlying_type_highlighting = warning +resharper_cpp_for_loop_can_be_replaced_with_while_highlighting = suggestion +resharper_cpp_functional_style_cast_highlighting = suggestion +resharper_cpp_function_doesnt_return_value_highlighting = warning +resharper_cpp_function_is_not_implemented_highlighting = warning +resharper_cpp_function_result_should_be_used_highlighting = hint +resharper_cpp_header_has_been_already_included_highlighting = hint +resharper_cpp_hidden_function_highlighting = warning +resharper_cpp_hiding_function_highlighting = warning +resharper_cpp_identical_operands_in_binary_expression_highlighting = warning +resharper_cpp_if_can_be_replaced_by_constexpr_if_highlighting = suggestion +resharper_cpp_implicit_default_constructor_not_available_highlighting = warning +resharper_cpp_incompatible_pointer_conversion_highlighting = warning +resharper_cpp_incomplete_switch_statement_highlighting = warning +resharper_cpp_inconsistent_naming_highlighting = hint +resharper_cpp_incorrect_blank_lines_near_braces_highlighting = none +resharper_cpp_initialized_value_is_always_rewritten_highlighting = warning +resharper_cpp_integral_to_pointer_conversion_highlighting = warning +resharper_cpp_invalid_line_continuation_highlighting = warning +resharper_cpp_join_declaration_and_assignment_highlighting = suggestion +resharper_cpp_lambda_capture_never_used_highlighting = warning +resharper_cpp_local_variable_may_be_const_highlighting = suggestion +resharper_cpp_local_variable_might_not_be_initialized_highlighting = warning +resharper_cpp_local_variable_with_non_trivial_dtor_is_never_used_highlighting = none +resharper_cpp_long_float_highlighting = warning +resharper_cpp_member_function_may_be_const_highlighting = suggestion +resharper_cpp_member_function_may_be_static_highlighting = suggestion +resharper_cpp_member_initializers_order_highlighting = suggestion +resharper_cpp_mismatched_class_tags_highlighting = warning +resharper_cpp_missing_blank_lines_highlighting = none +resharper_cpp_missing_include_guard_highlighting = warning +resharper_cpp_missing_indent_highlighting = none +resharper_cpp_missing_keyword_throw_highlighting = warning +resharper_cpp_missing_linebreak_highlighting = none +resharper_cpp_missing_space_highlighting = warning +resharper_cpp_ms_ext_address_of_class_r_value_highlighting = warning +resharper_cpp_ms_ext_binding_r_value_to_lvalue_reference_highlighting = warning +resharper_cpp_ms_ext_copy_elision_in_copy_init_declarator_highlighting = warning +resharper_cpp_ms_ext_double_user_conversion_in_copy_init_highlighting = warning +resharper_cpp_ms_ext_not_initialized_static_const_local_var_highlighting = warning +resharper_cpp_ms_ext_reinterpret_cast_from_nullptr_highlighting = warning +resharper_cpp_multiple_spaces_highlighting = warning +resharper_cpp_must_be_public_virtual_to_implement_interface_highlighting = warning +resharper_cpp_mutable_specifier_on_reference_member_highlighting = warning +resharper_cpp_nodiscard_function_without_return_value_highlighting = warning +resharper_cpp_non_exception_safe_resource_acquisition_highlighting = hint +resharper_cpp_non_explicit_conversion_operator_highlighting = hint +resharper_cpp_non_explicit_converting_constructor_highlighting = hint +resharper_cpp_non_inline_function_definition_in_header_file_highlighting = warning +resharper_cpp_non_inline_variable_definition_in_header_file_highlighting = warning +resharper_cpp_not_all_paths_return_value_highlighting = warning +resharper_cpp_no_discard_expression_highlighting = warning +resharper_cpp_object_member_might_not_be_initialized_highlighting = warning +resharper_cpp_outdent_is_off_prev_level_highlighting = none +resharper_cpp_out_parameter_must_be_written_highlighting = warning +resharper_cpp_parameter_may_be_const_highlighting = hint +resharper_cpp_parameter_may_be_const_ptr_or_ref_highlighting = suggestion +resharper_cpp_parameter_names_mismatch_highlighting = hint +resharper_cpp_parameter_never_used_highlighting = hint +resharper_cpp_parameter_value_is_reassigned_highlighting = warning +resharper_cpp_pass_value_parameter_by_const_reference_highlighting = suggestion +resharper_cpp_pointer_conversion_drops_qualifiers_highlighting = warning +resharper_cpp_pointer_to_integral_conversion_highlighting = warning +resharper_cpp_polymorphic_class_with_non_virtual_public_destructor_highlighting = warning +resharper_cpp_possibly_erroneous_empty_statements_highlighting = warning +resharper_cpp_possibly_uninitialized_member_highlighting = warning +resharper_cpp_possibly_unintended_object_slicing_highlighting = warning +resharper_cpp_precompiled_header_is_not_included_highlighting = error +resharper_cpp_precompiled_header_not_found_highlighting = error +resharper_cpp_printf_bad_format_highlighting = warning +resharper_cpp_printf_extra_arg_highlighting = warning +resharper_cpp_printf_missed_arg_highlighting = error +resharper_cpp_printf_risky_format_highlighting = warning +resharper_cpp_private_special_member_function_is_not_implemented_highlighting = warning +resharper_cpp_range_based_for_incompatible_reference_highlighting = warning +resharper_cpp_redefinition_of_default_argument_in_override_function_highlighting = warning +resharper_cpp_redundant_access_specifier_highlighting = hint +resharper_cpp_redundant_base_class_access_specifier_highlighting = hint +resharper_cpp_redundant_base_class_initializer_highlighting = suggestion +resharper_cpp_redundant_blank_lines_highlighting = none +resharper_cpp_redundant_boolean_expression_argument_highlighting = warning +resharper_cpp_redundant_cast_expression_highlighting = hint +resharper_cpp_redundant_complexity_in_comparison_highlighting = suggestion +resharper_cpp_redundant_const_specifier_highlighting = hint +resharper_cpp_redundant_control_flow_jump_highlighting = hint +resharper_cpp_redundant_dereferencing_and_taking_address_highlighting = suggestion +resharper_cpp_redundant_elaborated_type_specifier_highlighting = hint +resharper_cpp_redundant_else_keyword_highlighting = hint +resharper_cpp_redundant_else_keyword_inside_compound_statement_highlighting = hint +resharper_cpp_redundant_empty_declaration_highlighting = hint +resharper_cpp_redundant_empty_statement_highlighting = hint +resharper_cpp_redundant_inline_specifier_highlighting = hint +resharper_cpp_redundant_lambda_parameter_list_highlighting = hint +resharper_cpp_redundant_linebreak_highlighting = none +resharper_cpp_redundant_member_initializer_highlighting = suggestion +resharper_cpp_redundant_namespace_definition_highlighting = suggestion +resharper_cpp_redundant_parentheses_highlighting = hint +resharper_cpp_redundant_qualifier_highlighting = hint +resharper_cpp_redundant_space_highlighting = warning +resharper_cpp_redundant_static_specifier_on_member_allocation_function_highlighting = hint +resharper_cpp_redundant_static_specifier_on_thread_local_local_variable_highlighting = hint +resharper_cpp_redundant_template_arguments_highlighting = hint +resharper_cpp_redundant_template_keyword_highlighting = warning +resharper_cpp_redundant_typename_keyword_highlighting = warning +resharper_cpp_redundant_void_argument_list_highlighting = suggestion +resharper_cpp_reinterpret_cast_from_void_ptr_highlighting = suggestion +resharper_cpp_remove_redundant_braces_highlighting = none +resharper_cpp_replace_memset_with_zero_initialization_highlighting = suggestion +resharper_cpp_replace_tie_with_structured_binding_highlighting = suggestion +resharper_cpp_return_no_value_in_non_void_function_highlighting = warning +resharper_cpp_smart_pointer_vs_make_function_highlighting = suggestion +resharper_cpp_some_object_members_might_not_be_initialized_highlighting = warning +resharper_cpp_special_function_without_noexcept_specification_highlighting = warning +resharper_cpp_static_assert_failure_highlighting = error +resharper_cpp_static_data_member_in_unnamed_struct_highlighting = warning +resharper_cpp_static_specifier_on_anonymous_namespace_member_highlighting = suggestion +resharper_cpp_string_literal_to_char_pointer_conversion_highlighting = warning +resharper_cpp_tabs_and_spaces_mismatch_highlighting = none +resharper_cpp_tabs_are_disallowed_highlighting = none +resharper_cpp_tabs_outside_indent_highlighting = none +resharper_cpp_template_arguments_can_be_deduced_highlighting = hint +resharper_cpp_template_parameter_never_used_highlighting = hint +resharper_cpp_template_parameter_shadowing_highlighting = warning +resharper_cpp_this_arg_member_func_delegate_ctor_is_unsuported_by_dot_net_core_highlighting = none +resharper_cpp_throw_expression_can_be_replaced_with_rethrow_highlighting = warning +resharper_cpp_too_wide_scope_highlighting = suggestion +resharper_cpp_too_wide_scope_init_statement_highlighting = hint +resharper_cpp_type_alias_never_used_highlighting = warning +resharper_cpp_ue4_blueprint_callable_function_may_be_const_highlighting = hint +resharper_cpp_ue4_blueprint_callable_function_may_be_static_highlighting = hint +resharper_cpp_ue4_coding_standard_naming_violation_warning_highlighting = hint +resharper_cpp_ue4_coding_standard_u_class_naming_violation_error_highlighting = error +resharper_cpp_ue4_probable_memory_issues_with_u_objects_in_container_highlighting = warning +resharper_cpp_ue4_probable_memory_issues_with_u_object_highlighting = warning +resharper_cpp_ue_blueprint_callable_function_unused_highlighting = warning +resharper_cpp_ue_blueprint_implementable_event_not_implemented_highlighting = warning +resharper_cpp_ue_incorrect_engine_directory_highlighting = error +resharper_cpp_ue_non_existent_input_action_highlighting = warning +resharper_cpp_ue_non_existent_input_axis_highlighting = warning +resharper_cpp_ue_source_file_without_predefined_macros_highlighting = warning +resharper_cpp_ue_source_file_without_standard_library_highlighting = error +resharper_cpp_ue_version_file_doesnt_exist_highlighting = error +resharper_cpp_uninitialized_dependent_base_class_highlighting = warning +resharper_cpp_uninitialized_non_static_data_member_highlighting = warning +resharper_cpp_union_member_of_reference_type_highlighting = warning +resharper_cpp_unnamed_namespace_in_header_file_highlighting = warning +resharper_cpp_unnecessary_whitespace_highlighting = warning +resharper_cpp_unreachable_code_highlighting = warning +resharper_cpp_unsigned_zero_comparison_highlighting = warning +resharper_cpp_unused_include_directive_highlighting = warning +resharper_cpp_user_defined_literal_suffix_does_not_start_with_underscore_highlighting = warning +resharper_cpp_use_algorithm_with_count_highlighting = suggestion +resharper_cpp_use_associative_contains_highlighting = suggestion +resharper_cpp_use_auto_for_numeric_highlighting = hint +resharper_cpp_use_auto_highlighting = hint +resharper_cpp_use_elements_view_highlighting = suggestion +resharper_cpp_use_erase_algorithm_highlighting = suggestion +resharper_cpp_use_familiar_template_syntax_for_generic_lambdas_highlighting = suggestion +resharper_cpp_use_of_undeclared_class_highlighting = hint +resharper_cpp_use_range_algorithm_highlighting = suggestion +resharper_cpp_use_std_size_highlighting = suggestion +resharper_cpp_use_structured_binding_highlighting = hint +resharper_cpp_use_type_trait_alias_highlighting = suggestion +resharper_cpp_using_result_of_assignment_as_condition_highlighting = warning +resharper_cpp_u_function_macro_call_has_no_effect_highlighting = warning +resharper_cpp_u_property_macro_call_has_no_effect_highlighting = warning +resharper_cpp_variable_can_be_made_constexpr_highlighting = suggestion +resharper_cpp_virtual_function_call_inside_ctor_highlighting = warning +resharper_cpp_virtual_function_in_final_class_highlighting = warning +resharper_cpp_volatile_parameter_in_declaration_highlighting = suggestion +resharper_cpp_warning_directive_highlighting = warning +resharper_cpp_wrong_includes_order_highlighting = hint +resharper_cpp_wrong_indent_size_highlighting = none +resharper_cpp_wrong_slashes_in_include_directive_highlighting = hint +resharper_cpp_zero_constant_can_be_replaced_with_nullptr_highlighting = suggestion +resharper_cpp_zero_valued_expression_used_as_null_pointer_highlighting = warning +resharper_create_specialized_overload_highlighting = hint +resharper_css_browser_compatibility_highlighting = warning +resharper_css_caniuse_feature_requires_prefix_highlighting = hint +resharper_css_caniuse_unsupported_feature_highlighting = hint +resharper_css_not_resolved_highlighting = error +resharper_css_obsolete_highlighting = hint +resharper_css_property_does_not_override_vendor_property_highlighting = warning +resharper_cyclic_reference_comment_highlighting = none +resharper_c_declaration_with_implicit_int_type_highlighting = warning +resharper_c_sharp_build_cs_invalid_module_name_highlighting = warning +resharper_c_sharp_missing_plugin_dependency_highlighting = warning +resharper_declaration_hides_highlighting = hint +resharper_declaration_is_empty_highlighting = warning +resharper_declaration_visibility_error_highlighting = error +resharper_default_value_attribute_for_optional_parameter_highlighting = warning +resharper_deleting_non_qualified_reference_highlighting = error +resharper_dl_tag_contains_non_dt_or_dd_elements_highlighting = hint +resharper_double_colons_expected_highlighting = error +resharper_double_colons_preferred_highlighting = suggestion +resharper_double_negation_in_pattern_highlighting = suggestion +resharper_double_negation_of_boolean_highlighting = warning +resharper_double_negation_operator_highlighting = suggestion +resharper_duplicate_identifier_error_highlighting = error +resharper_duplicate_reference_comment_highlighting = warning +resharper_duplicate_resource_highlighting = warning +resharper_duplicating_local_declaration_highlighting = warning +resharper_duplicating_parameter_declaration_error_highlighting = error +resharper_duplicating_property_declaration_error_highlighting = error +resharper_duplicating_property_declaration_highlighting = warning +resharper_duplicating_switch_label_highlighting = warning +resharper_elided_trailing_element_highlighting = warning +resharper_empty_constructor_highlighting = warning +resharper_empty_destructor_highlighting = warning +resharper_empty_embedded_statement_highlighting = warning +resharper_empty_for_statement_highlighting = warning +resharper_empty_general_catch_clause_highlighting = warning +resharper_empty_namespace_highlighting = warning +resharper_empty_object_property_declaration_highlighting = error +resharper_empty_region_highlighting = suggestion +resharper_empty_return_value_for_type_annotated_function_highlighting = warning +resharper_empty_statement_highlighting = warning +resharper_empty_title_tag_highlighting = hint +resharper_enforce_do_while_statement_braces_highlighting = none +resharper_enforce_fixed_statement_braces_highlighting = none +resharper_enforce_foreach_statement_braces_highlighting = none +resharper_enforce_for_statement_braces_highlighting = none +resharper_enforce_if_statement_braces_highlighting = warning +resharper_enforce_lock_statement_braces_highlighting = none +resharper_enforce_using_statement_braces_highlighting = none +resharper_enforce_while_statement_braces_highlighting = none +resharper_entity_framework_n_plus_one_incomplete_data_query_highlighting = suggestion +resharper_entity_framework_n_plus_one_incomplete_data_usage_highlighting = warning +resharper_entity_framework_n_plus_one_query_highlighting = suggestion +resharper_entity_framework_n_plus_one_usage_highlighting = warning +resharper_entity_name_captured_only_global_highlighting = warning +resharper_entity_name_captured_only_local_highlighting = warning +resharper_enumerable_sum_in_explicit_unchecked_context_highlighting = warning +resharper_enum_underlying_type_is_int_highlighting = warning +resharper_equal_expression_comparison_highlighting = warning +resharper_error_in_xml_doc_reference_highlighting = error +resharper_es6_feature_highlighting = error +resharper_es7_feature_highlighting = error +resharper_eval_arguments_name_error_highlighting = error +resharper_event_exception_not_documented_highlighting = suggestion +resharper_event_never_invoked_global_highlighting = suggestion +resharper_event_never_subscribed_to_global_highlighting = suggestion +resharper_event_never_subscribed_to_local_highlighting = suggestion +resharper_event_unsubscription_via_anonymous_delegate_highlighting = warning +resharper_exception_not_documented_highlighting = hint +resharper_exception_not_documented_optional_highlighting = none +resharper_exception_not_thrown_highlighting = hint +resharper_exception_not_thrown_optional_highlighting = hint +resharper_exception_passed_as_template_argument_problem_highlighting = warning +resharper_experimental_feature_highlighting = error +resharper_explicit_caller_info_argument_highlighting = warning +resharper_expression_is_always_const_highlighting = warning +resharper_expression_is_always_null_highlighting = warning +resharper_extract_common_property_pattern_highlighting = hint +resharper_field_can_be_made_read_only_global_highlighting = suggestion +resharper_field_can_be_made_read_only_local_highlighting = suggestion +resharper_field_hides_interface_property_with_default_implementation_highlighting = warning +resharper_foreach_can_be_converted_to_query_using_another_get_enumerator_highlighting = hint +resharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting = hint +resharper_format_string_placeholders_mismatch_highlighting = warning +resharper_format_string_problem_highlighting = warning +resharper_for_can_be_converted_to_foreach_highlighting = suggestion +resharper_for_statement_condition_is_true_highlighting = warning +resharper_functions_used_before_declared_highlighting = none +resharper_function_complexity_overflow_highlighting = none +resharper_function_never_returns_highlighting = warning +resharper_function_parameter_named_arguments_highlighting = warning +resharper_function_recursive_on_all_paths_highlighting = warning +resharper_function_used_out_of_scope_highlighting = warning +resharper_gc_suppress_finalize_for_type_without_destructor_highlighting = warning +resharper_generic_enumerator_not_disposed_highlighting = warning +resharper_heap_view_boxing_allocation_highlighting = hint +resharper_heap_view_can_avoid_closure_highlighting = suggestion +resharper_heap_view_closure_allocation_highlighting = hint +resharper_heap_view_delegate_allocation_highlighting = hint +resharper_heap_view_implicit_capture_highlighting = none +resharper_heap_view_object_allocation_evident_highlighting = hint +resharper_heap_view_object_allocation_highlighting = hint +resharper_heap_view_object_allocation_possible_highlighting = hint +resharper_heap_view_possible_boxing_allocation_highlighting = hint +resharper_heuristically_unreachable_code_highlighting = warning +resharper_heuristic_unreachable_code_highlighting = warning +resharper_hex_color_value_with_alpha_highlighting = error +resharper_html_attributes_quotes_highlighting = hint +resharper_html_attribute_not_resolved_highlighting = warning +resharper_html_attribute_value_not_resolved_highlighting = warning +resharper_html_dead_code_highlighting = warning +resharper_html_event_not_resolved_highlighting = warning +resharper_html_id_duplication_highlighting = warning +resharper_html_id_not_resolved_highlighting = warning +resharper_html_obsolete_highlighting = warning +resharper_html_path_error_highlighting = warning +resharper_html_tag_not_closed_highlighting = error +resharper_html_tag_not_resolved_highlighting = warning +resharper_html_tag_should_be_self_closed_highlighting = warning +resharper_html_tag_should_not_be_self_closed_highlighting = warning +resharper_html_warning_highlighting = warning +resharper_identifier_typo_highlighting = none +resharper_if_std_is_constant_evaluated_can_be_replaced_highlighting = suggestion +resharper_implicit_any_error_highlighting = error +resharper_implicit_any_type_warning_highlighting = warning +resharper_import_keyword_not_with_invocation_highlighting = error +resharper_inactive_preprocessor_branch_highlighting = warning +resharper_inconsistently_synchronized_field_highlighting = warning +resharper_inconsistent_context_log_property_naming_highlighting = warning +resharper_inconsistent_function_returns_highlighting = warning +resharper_inconsistent_log_property_naming_highlighting = none +resharper_inconsistent_naming_highlighting = warning +resharper_inconsistent_order_of_locks_highlighting = warning +resharper_incorrect_blank_lines_near_braces_highlighting = warning +resharper_incorrect_operand_in_type_of_comparison_highlighting = warning +resharper_incorrect_triple_slash_location_highlighting = warning +resharper_indexing_by_invalid_range_highlighting = warning +resharper_inheritdoc_consider_usage_highlighting = none +resharper_inheritdoc_invalid_usage_highlighting = warning +resharper_inline_out_variable_declaration_highlighting = suggestion +resharper_inline_temporary_variable_highlighting = hint +resharper_internal_module_highlighting = suggestion +resharper_internal_or_private_member_not_documented_highlighting = warning +resharper_interpolated_string_expression_is_not_i_formattable_highlighting = warning +resharper_introduce_optional_parameters_global_highlighting = suggestion +resharper_introduce_optional_parameters_local_highlighting = suggestion +resharper_introduce_variable_to_apply_guard_highlighting = hint +resharper_int_division_by_zero_highlighting = warning +resharper_int_variable_overflow_highlighting = warning +resharper_int_variable_overflow_in_checked_context_highlighting = warning +resharper_int_variable_overflow_in_unchecked_context_highlighting = warning +resharper_invalid_attribute_value_highlighting = warning +resharper_invalid_json_syntax_highlighting = error +resharper_invalid_task_element_highlighting = none +resharper_invalid_value_highlighting = error +resharper_invalid_value_type_highlighting = warning +resharper_invalid_xml_doc_comment_highlighting = warning +resharper_invert_condition_1_highlighting = hint +resharper_invert_if_highlighting = hint +resharper_invocation_is_skipped_highlighting = hint +resharper_invocation_of_non_function_highlighting = warning +resharper_invoked_expression_maybe_non_function_highlighting = warning +resharper_invoke_as_extension_method_highlighting = suggestion +resharper_is_expression_always_false_highlighting = warning +resharper_is_expression_always_true_highlighting = warning +resharper_iterator_method_result_is_ignored_highlighting = warning +resharper_iterator_never_returns_highlighting = warning +resharper_join_declaration_and_initializer_highlighting = suggestion +resharper_join_declaration_and_initializer_js_highlighting = suggestion +resharper_join_null_check_with_usage_highlighting = suggestion +resharper_json_validation_failed_highlighting = error +resharper_js_path_not_found_highlighting = error +resharper_js_unreachable_code_highlighting = warning +resharper_jump_must_be_in_loop_highlighting = warning +resharper_label_or_semicolon_expected_highlighting = error +resharper_lambda_expression_can_be_made_static_highlighting = none +resharper_lambda_expression_must_be_static_highlighting = suggestion +resharper_lambda_highlighting = suggestion +resharper_lambda_should_not_capture_context_highlighting = warning +resharper_less_specific_overload_than_main_signature_highlighting = warning +resharper_lexical_declaration_needs_block_highlighting = error +resharper_localizable_element_highlighting = warning +resharper_local_function_can_be_made_static_highlighting = none +resharper_local_function_hides_method_highlighting = warning +resharper_local_function_redefined_later_highlighting = warning +resharper_local_variable_hides_member_highlighting = warning +resharper_log_message_is_sentence_problem_highlighting = warning +resharper_long_literal_ending_lower_l_highlighting = warning +resharper_loop_can_be_converted_to_query_highlighting = hint +resharper_loop_can_be_partly_converted_to_query_highlighting = none +resharper_loop_variable_is_never_changed_inside_loop_highlighting = warning +resharper_l_value_is_expected_highlighting = error +resharper_markup_attribute_typo_highlighting = suggestion +resharper_markup_text_typo_highlighting = suggestion +resharper_math_abs_method_is_redundant_highlighting = warning +resharper_math_clamp_min_greater_than_max_highlighting = warning +resharper_meaningless_default_parameter_value_highlighting = warning +resharper_member_can_be_file_local_highlighting = none +resharper_member_can_be_internal_highlighting = none +resharper_member_can_be_made_static_global_highlighting = hint +resharper_member_can_be_made_static_local_highlighting = hint +resharper_member_can_be_private_global_highlighting = none +resharper_member_can_be_private_local_highlighting = suggestion +resharper_member_can_be_protected_global_highlighting = none +resharper_member_can_be_protected_local_highlighting = suggestion +resharper_member_hides_interface_member_with_default_implementation_highlighting = warning +resharper_member_hides_static_from_outer_class_highlighting = warning +resharper_member_initializer_value_ignored_highlighting = warning +resharper_merge_and_pattern_highlighting = suggestion +resharper_merge_cast_with_type_check_highlighting = suggestion +resharper_merge_conditional_expression_highlighting = suggestion +resharper_merge_into_logical_pattern_highlighting = hint +resharper_merge_into_negated_pattern_highlighting = hint +resharper_merge_into_pattern_highlighting = suggestion +resharper_merge_nested_property_patterns_highlighting = suggestion +resharper_merge_sequential_checks_highlighting = hint +resharper_method_has_async_overload_highlighting = suggestion +resharper_method_has_async_overload_with_cancellation_highlighting = suggestion +resharper_method_overload_with_optional_parameter_highlighting = warning +resharper_method_safe_this_highlighting = suggestion +resharper_method_supports_cancellation_highlighting = suggestion +resharper_missing_alt_attribute_in_img_tag_highlighting = hint +resharper_missing_attribute_highlighting = warning +resharper_missing_blank_lines_highlighting = warning +resharper_missing_body_tag_highlighting = warning +resharper_missing_has_own_property_in_foreach_highlighting = warning +resharper_missing_head_and_body_tags_highlighting = warning +resharper_missing_head_tag_highlighting = warning +resharper_missing_indent_highlighting = warning +resharper_missing_linebreak_highlighting = warning +resharper_missing_space_highlighting = warning +resharper_missing_title_tag_highlighting = hint +resharper_missing_xml_doc_highlighting = none +resharper_misuse_of_owner_function_this_highlighting = warning +resharper_more_specific_foreach_variable_type_available_highlighting = suggestion +resharper_more_specific_signature_after_less_specific_highlighting = warning +resharper_move_local_function_after_jump_statement_highlighting = hint +resharper_move_to_existing_positional_deconstruction_pattern_highlighting = hint +resharper_move_variable_declaration_inside_loop_condition_highlighting = suggestion +resharper_multiple_declarations_in_foreach_highlighting = error +resharper_multiple_nullable_attributes_usage_highlighting = warning +resharper_multiple_order_by_highlighting = warning +resharper_multiple_output_tags_highlighting = warning +resharper_multiple_resolve_candidates_in_text_highlighting = warning +resharper_multiple_spaces_highlighting = warning +resharper_multiple_statements_on_one_line_highlighting = warning +resharper_multiple_type_members_on_one_line_highlighting = warning +resharper_must_use_return_value_highlighting = warning +resharper_mvc_action_not_resolved_highlighting = error +resharper_mvc_area_not_resolved_highlighting = error +resharper_mvc_controller_not_resolved_highlighting = error +resharper_mvc_invalid_model_type_highlighting = error +resharper_mvc_masterpage_not_resolved_highlighting = error +resharper_mvc_partial_view_not_resolved_highlighting = error +resharper_mvc_template_not_resolved_highlighting = error +resharper_mvc_view_component_not_resolved_highlighting = error +resharper_mvc_view_component_view_not_resolved_highlighting = error +resharper_mvc_view_not_resolved_highlighting = error +resharper_native_type_prototype_extending_highlighting = warning +resharper_native_type_prototype_overwriting_highlighting = warning +resharper_negation_of_relational_pattern_highlighting = suggestion +resharper_negative_equality_expression_highlighting = suggestion +resharper_negative_index_highlighting = warning +resharper_nested_string_interpolation_highlighting = suggestion +resharper_non_assigned_constant_highlighting = error +resharper_non_atomic_compound_operator_highlighting = warning +resharper_non_constant_equality_expression_has_constant_result_highlighting = warning +resharper_non_parsable_element_highlighting = warning +resharper_non_readonly_member_in_get_hash_code_highlighting = none +resharper_non_volatile_field_in_double_check_locking_highlighting = warning +resharper_not_accessed_field_global_highlighting = suggestion +resharper_not_accessed_field_local_highlighting = warning +resharper_not_accessed_out_parameter_variable_highlighting = warning +resharper_not_accessed_positional_property_global_highlighting = warning +resharper_not_accessed_positional_property_local_highlighting = warning +resharper_not_accessed_variable_highlighting = warning +resharper_not_all_paths_return_value_highlighting = warning +resharper_not_assigned_out_parameter_highlighting = warning +resharper_not_declared_in_parent_culture_highlighting = warning +resharper_not_null_or_required_member_is_not_initialized_highlighting = warning +resharper_not_observable_annotation_redundancy_highlighting = warning +resharper_not_overridden_in_specific_culture_highlighting = warning +resharper_not_resolved_highlighting = warning +resharper_not_resolved_in_text_highlighting = warning +resharper_nullable_warning_suppression_is_used_highlighting = none +resharper_nullness_annotation_conflict_with_jet_brains_annotations_highlighting = warning +resharper_null_coalescing_condition_is_always_not_null_according_to_api_contract_highlighting = warning +resharper_n_unit_async_method_must_be_task_highlighting = warning +resharper_n_unit_attribute_produces_too_many_tests_highlighting = none +resharper_n_unit_auto_fixture_incorrect_argument_type_highlighting = warning +resharper_n_unit_auto_fixture_missed_test_attribute_highlighting = warning +resharper_n_unit_auto_fixture_missed_test_or_test_fixture_attribute_highlighting = warning +resharper_n_unit_auto_fixture_redundant_argument_in_inline_auto_data_attribute_highlighting = warning +resharper_n_unit_duplicate_values_highlighting = warning +resharper_n_unit_ignored_parameter_attribute_highlighting = warning +resharper_n_unit_implicit_unspecified_null_values_highlighting = warning +resharper_n_unit_incorrect_argument_type_highlighting = warning +resharper_n_unit_incorrect_expected_result_type_highlighting = warning +resharper_n_unit_incorrect_range_bounds_highlighting = warning +resharper_n_unit_method_with_parameters_and_test_attribute_highlighting = warning +resharper_n_unit_missing_arguments_in_test_case_attribute_highlighting = warning +resharper_n_unit_non_public_method_with_test_attribute_highlighting = warning +resharper_n_unit_no_values_provided_highlighting = warning +resharper_n_unit_parameter_type_is_not_compatible_with_attribute_highlighting = warning +resharper_n_unit_range_attribute_bounds_are_out_of_range_highlighting = warning +resharper_n_unit_range_step_sign_mismatch_highlighting = warning +resharper_n_unit_range_step_value_must_not_be_zero_highlighting = warning +resharper_n_unit_range_to_value_is_not_reachable_highlighting = warning +resharper_n_unit_redundant_argument_instead_of_expected_result_highlighting = warning +resharper_n_unit_redundant_argument_in_test_case_attribute_highlighting = warning +resharper_n_unit_redundant_expected_result_in_test_case_attribute_highlighting = warning +resharper_n_unit_test_case_attribute_requires_expected_result_highlighting = warning +resharper_n_unit_test_case_result_property_duplicates_expected_result_highlighting = warning +resharper_n_unit_test_case_result_property_is_obsolete_highlighting = warning +resharper_n_unit_test_case_source_cannot_be_resolved_highlighting = warning +resharper_n_unit_test_case_source_must_be_field_property_method_highlighting = warning +resharper_n_unit_test_case_source_must_be_static_highlighting = warning +resharper_n_unit_test_case_source_should_implement_i_enumerable_highlighting = warning +resharper_object_creation_as_statement_highlighting = warning +resharper_object_destructuring_without_parentheses_highlighting = error +resharper_object_literals_are_not_comma_free_highlighting = error +resharper_obsolete_element_error_highlighting = error +resharper_obsolete_element_highlighting = warning +resharper_octal_literals_not_allowed_error_highlighting = error +resharper_ol_tag_contains_non_li_elements_highlighting = hint +resharper_one_way_operation_contract_with_return_type_highlighting = warning +resharper_operation_contract_without_service_contract_highlighting = warning +resharper_operator_is_can_be_used_highlighting = warning +resharper_operator_without_matched_checked_operator_highlighting = warning +resharper_optional_parameter_hierarchy_mismatch_highlighting = warning +resharper_optional_parameter_ref_out_highlighting = warning +resharper_other_tags_inside_script1_highlighting = error +resharper_other_tags_inside_script2_highlighting = error +resharper_other_tags_inside_unclosed_script_highlighting = error +resharper_outdent_is_off_prev_level_highlighting = warning +resharper_output_tag_required_highlighting = warning +resharper_out_parameter_value_is_always_discarded_global_highlighting = suggestion +resharper_out_parameter_value_is_always_discarded_local_highlighting = warning +resharper_overload_signature_inferring_highlighting = hint +resharper_overridden_with_empty_value_highlighting = warning +resharper_overridden_with_same_value_highlighting = suggestion +resharper_parameter_doesnt_make_any_sense_highlighting = warning +resharper_parameter_hides_member_highlighting = warning +resharper_parameter_only_used_for_precondition_check_global_highlighting = suggestion +resharper_parameter_only_used_for_precondition_check_local_highlighting = warning +resharper_parameter_type_can_be_enumerable_global_highlighting = hint +resharper_parameter_type_can_be_enumerable_local_highlighting = hint +resharper_parameter_value_is_not_used_highlighting = warning +resharper_partial_method_parameter_name_mismatch_highlighting = warning +resharper_partial_method_with_single_part_highlighting = warning +resharper_partial_type_with_single_part_highlighting = warning +resharper_pass_string_interpolation_highlighting = hint +resharper_path_not_resolved_highlighting = error +resharper_pattern_always_matches_highlighting = warning +resharper_pattern_is_always_true_or_false_highlighting = warning +resharper_pattern_is_redundant_highlighting = warning +resharper_pattern_never_matches_highlighting = warning +resharper_place_assignment_expression_into_block_highlighting = none +resharper_polymorphic_field_like_event_invocation_highlighting = warning +resharper_positional_property_used_problem_highlighting = warning +resharper_possible_infinite_inheritance_highlighting = warning +resharper_possible_intended_rethrow_highlighting = warning +resharper_possible_interface_member_ambiguity_highlighting = warning +resharper_possible_invalid_cast_exception_highlighting = warning +resharper_possible_invalid_cast_exception_in_foreach_loop_highlighting = warning +resharper_possible_invalid_operation_exception_highlighting = warning +resharper_possible_loss_of_fraction_highlighting = warning +resharper_possible_mistaken_argument_highlighting = warning +resharper_possible_mistaken_call_to_get_type_1_highlighting = warning +resharper_possible_mistaken_call_to_get_type_2_highlighting = warning +resharper_possible_multiple_enumeration_highlighting = warning +resharper_possible_multiple_write_access_in_double_check_locking_highlighting = warning +resharper_possible_null_reference_exception_highlighting = warning +resharper_possible_struct_member_modification_of_non_variable_struct_highlighting = warning +resharper_possible_unintended_linear_search_in_set_highlighting = warning +resharper_possible_unintended_queryable_as_enumerable_highlighting = suggestion +resharper_possible_unintended_reference_comparison_highlighting = warning +resharper_possible_write_to_me_highlighting = warning +resharper_possibly_impure_method_call_on_readonly_variable_highlighting = warning +resharper_possibly_incorrectly_broken_statement_highlighting = warning +resharper_possibly_missing_indexer_initializer_comma_highlighting = warning +resharper_possibly_mistaken_use_of_interpolated_string_insert_highlighting = warning +resharper_possibly_unassigned_property_highlighting = hint +resharper_possibly_unintended_usage_parameterless_get_expression_type_highlighting = error +resharper_private_field_can_be_converted_to_local_variable_highlighting = warning +resharper_private_variable_can_be_made_readonly_highlighting = hint +resharper_property_can_be_made_init_only_global_highlighting = suggestion +resharper_property_can_be_made_init_only_local_highlighting = suggestion +resharper_property_field_keyword_is_never_assigned_highlighting = warning +resharper_property_field_keyword_is_never_used_highlighting = warning +resharper_property_getter_cannot_have_parameters_highlighting = error +resharper_property_not_resolved_highlighting = error +resharper_property_setter_must_have_single_parameter_highlighting = error +resharper_public_constructor_in_abstract_class_highlighting = suggestion +resharper_pure_attribute_on_void_method_highlighting = warning +resharper_qualified_expression_is_null_highlighting = warning +resharper_qualified_expression_maybe_null_highlighting = warning +resharper_raw_string_can_be_simplified_highlighting = hint +resharper_razor_layout_not_resolved_highlighting = error +resharper_razor_section_not_resolved_highlighting = error +resharper_read_access_in_double_check_locking_highlighting = warning +resharper_redundant_abstract_modifier_highlighting = warning +resharper_redundant_accessor_body_highlighting = suggestion +resharper_redundant_always_match_subpattern_highlighting = suggestion +resharper_redundant_anonymous_type_property_name_highlighting = warning +resharper_redundant_argument_default_value_highlighting = warning +resharper_redundant_array_creation_expression_highlighting = hint +resharper_redundant_array_lower_bound_specification_highlighting = warning +resharper_redundant_assignment_highlighting = warning +resharper_redundant_attribute_parentheses_highlighting = hint +resharper_redundant_attribute_suffix_highlighting = warning +resharper_redundant_attribute_usage_property_highlighting = suggestion +resharper_redundant_base_constructor_call_highlighting = warning +resharper_redundant_base_qualifier_highlighting = warning +resharper_redundant_blank_lines_highlighting = warning +resharper_redundant_block_highlighting = warning +resharper_redundant_bool_compare_highlighting = warning +resharper_redundant_caller_argument_expression_default_value_highlighting = warning +resharper_redundant_case_label_highlighting = warning +resharper_redundant_cast_highlighting = warning +resharper_redundant_catch_clause_highlighting = warning +resharper_redundant_check_before_assignment_highlighting = warning +resharper_redundant_collection_initializer_element_braces_highlighting = hint +resharper_redundant_comparison_with_boolean_highlighting = warning +resharper_redundant_configure_await_highlighting = suggestion +resharper_redundant_css_hack_highlighting = warning +resharper_redundant_declaration_semicolon_highlighting = hint +resharper_redundant_default_member_initializer_highlighting = none +resharper_redundant_delegate_creation_highlighting = warning +resharper_redundant_dictionary_contains_key_before_adding_highlighting = warning +resharper_redundant_disable_warning_comment_highlighting = warning +resharper_redundant_discard_designation_highlighting = suggestion +resharper_redundant_else_block_highlighting = warning +resharper_redundant_empty_case_else_highlighting = warning +resharper_redundant_empty_constructor_highlighting = warning +resharper_redundant_empty_finally_block_highlighting = warning +resharper_redundant_empty_object_creation_argument_list_highlighting = hint +resharper_redundant_empty_object_or_collection_initializer_highlighting = warning +resharper_redundant_empty_switch_section_highlighting = warning +resharper_redundant_enumerable_cast_call_highlighting = warning +resharper_redundant_enum_case_label_for_default_section_highlighting = none +resharper_redundant_explicit_array_creation_highlighting = warning +resharper_redundant_explicit_array_size_highlighting = warning +resharper_redundant_explicit_nullable_creation_highlighting = warning +resharper_redundant_explicit_params_array_creation_highlighting = suggestion +resharper_redundant_explicit_positional_property_declaration_highlighting = warning +resharper_redundant_explicit_tuple_component_name_highlighting = warning +resharper_redundant_extends_list_entry_highlighting = warning +resharper_redundant_fixed_pointer_declaration_highlighting = suggestion +resharper_redundant_highlighting = warning +resharper_redundant_if_else_block_highlighting = hint +resharper_redundant_if_statement_then_keyword_highlighting = none +resharper_redundant_immediate_delegate_invocation_highlighting = suggestion +resharper_redundant_intermediate_variable_highlighting = hint +resharper_redundant_is_before_relational_pattern_highlighting = suggestion +resharper_redundant_iterator_keyword_highlighting = warning +resharper_redundant_jump_statement_highlighting = warning +resharper_redundant_lambda_parameter_type_highlighting = warning +resharper_redundant_lambda_signature_parentheses_highlighting = hint +resharper_redundant_linebreak_highlighting = none +resharper_redundant_local_class_name_highlighting = hint +resharper_redundant_local_function_name_highlighting = hint +resharper_redundant_logical_conditional_expression_operand_highlighting = warning +resharper_redundant_me_qualifier_highlighting = warning +resharper_redundant_my_base_qualifier_highlighting = warning +resharper_redundant_my_class_qualifier_highlighting = warning +resharper_redundant_name_qualifier_highlighting = warning +resharper_redundant_not_null_constraint_highlighting = warning +resharper_redundant_nullable_annotation_on_reference_type_constraint_highlighting = warning +resharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_base_type_highlighting = warning +resharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_type_kind_highlighting = warning +resharper_redundant_nullable_directive_highlighting = warning +resharper_redundant_nullable_flow_attribute_highlighting = warning +resharper_redundant_nullable_type_mark_highlighting = warning +resharper_redundant_nullness_attribute_with_nullable_reference_types_highlighting = warning +resharper_redundant_overflow_checking_context_highlighting = warning +resharper_redundant_overload_global_highlighting = suggestion +resharper_redundant_overload_local_highlighting = suggestion +resharper_redundant_overridden_member_highlighting = warning +resharper_redundant_params_highlighting = warning +resharper_redundant_parentheses_highlighting = none +resharper_redundant_parent_type_declaration_highlighting = warning +resharper_redundant_pattern_parentheses_highlighting = hint +resharper_redundant_property_parentheses_highlighting = hint +resharper_redundant_property_pattern_clause_highlighting = suggestion +resharper_redundant_qualifier_highlighting = warning +resharper_redundant_query_order_by_ascending_keyword_highlighting = hint +resharper_redundant_range_bound_highlighting = suggestion +resharper_redundant_readonly_modifier_highlighting = suggestion +resharper_redundant_record_class_keyword_highlighting = warning +resharper_redundant_scoped_parameter_modifier_highlighting = warning +resharper_redundant_setter_value_parameter_declaration_highlighting = hint +resharper_redundant_set_contains_before_adding_highlighting = warning +resharper_redundant_space_highlighting = warning +resharper_redundant_string_format_call_highlighting = warning +resharper_redundant_string_interpolation_highlighting = suggestion +resharper_redundant_string_to_char_array_call_highlighting = warning +resharper_redundant_string_type_highlighting = suggestion +resharper_redundant_suppress_nullable_warning_expression_highlighting = warning +resharper_redundant_ternary_expression_highlighting = warning +resharper_redundant_to_string_call_for_value_type_highlighting = hint +resharper_redundant_to_string_call_highlighting = warning +resharper_redundant_type_arguments_of_method_highlighting = warning +resharper_redundant_type_cast_highlighting = warning +resharper_redundant_type_cast_structural_highlighting = warning +resharper_redundant_type_check_in_pattern_highlighting = warning +resharper_redundant_type_declaration_body_highlighting = warning +resharper_redundant_units_highlighting = warning +resharper_redundant_unsafe_context_highlighting = warning +resharper_redundant_using_directive_global_highlighting = warning +resharper_redundant_using_directive_highlighting = warning +resharper_redundant_variable_type_specification_highlighting = hint +resharper_redundant_verbatim_prefix_highlighting = suggestion +resharper_redundant_verbatim_string_prefix_highlighting = suggestion +resharper_redundant_virtual_modifier_highlighting = warning +resharper_redundant_with_cancellation_highlighting = warning +resharper_redundant_with_expression_highlighting = suggestion +resharper_reference_equals_with_value_type_highlighting = warning +resharper_reg_exp_inspections_highlighting = warning +resharper_remove_constructor_invocation_highlighting = none +resharper_remove_redundant_braces_highlighting = suggestion +resharper_remove_redundant_or_statement_false_highlighting = suggestion +resharper_remove_redundant_or_statement_true_highlighting = suggestion +resharper_remove_to_list_1_highlighting = suggestion +resharper_remove_to_list_2_highlighting = suggestion +resharper_replace_auto_property_with_computed_property_highlighting = hint +resharper_replace_conditional_expression_with_null_coalescing_highlighting = suggestion +resharper_replace_indicing_with_array_destructuring_highlighting = hint +resharper_replace_indicing_with_short_hand_properties_after_destructuring_highlighting = hint +resharper_replace_object_pattern_with_var_pattern_highlighting = suggestion +resharper_replace_sequence_equal_with_constant_pattern_highlighting = suggestion +resharper_replace_slice_with_range_indexer_highlighting = hint +resharper_replace_substring_with_range_indexer_highlighting = hint +resharper_replace_undefined_checking_series_with_object_destructuring_highlighting = hint +resharper_replace_with_destructuring_swap_highlighting = hint +resharper_replace_with_field_keyword_highlighting = suggestion +resharper_replace_with_first_or_default_1_highlighting = suggestion +resharper_replace_with_first_or_default_2_highlighting = suggestion +resharper_replace_with_first_or_default_3_highlighting = suggestion +resharper_replace_with_first_or_default_4_highlighting = suggestion +resharper_replace_with_last_or_default_1_highlighting = suggestion +resharper_replace_with_last_or_default_2_highlighting = suggestion +resharper_replace_with_last_or_default_3_highlighting = suggestion +resharper_replace_with_last_or_default_4_highlighting = suggestion +resharper_replace_with_of_type_1_highlighting = suggestion +resharper_replace_with_of_type_2_highlighting = suggestion +resharper_replace_with_of_type_3_highlighting = suggestion +resharper_replace_with_of_type_any_1_highlighting = suggestion +resharper_replace_with_of_type_any_2_highlighting = suggestion +resharper_replace_with_of_type_count_1_highlighting = suggestion +resharper_replace_with_of_type_count_2_highlighting = suggestion +resharper_replace_with_of_type_first_1_highlighting = suggestion +resharper_replace_with_of_type_first_2_highlighting = suggestion +resharper_replace_with_of_type_first_or_default_1_highlighting = suggestion +resharper_replace_with_of_type_first_or_default_2_highlighting = suggestion +resharper_replace_with_of_type_last_1_highlighting = suggestion +resharper_replace_with_of_type_last_2_highlighting = suggestion +resharper_replace_with_of_type_last_or_default_1_highlighting = suggestion +resharper_replace_with_of_type_last_or_default_2_highlighting = suggestion +resharper_replace_with_of_type_long_count_highlighting = suggestion +resharper_replace_with_of_type_single_1_highlighting = suggestion +resharper_replace_with_of_type_single_2_highlighting = suggestion +resharper_replace_with_of_type_single_or_default_1_highlighting = suggestion +resharper_replace_with_of_type_single_or_default_2_highlighting = suggestion +resharper_replace_with_of_type_where_highlighting = suggestion +resharper_replace_with_primary_constructor_parameter_highlighting = suggestion +resharper_replace_with_simple_assignment_false_highlighting = suggestion +resharper_replace_with_simple_assignment_true_highlighting = suggestion +resharper_replace_with_single_assignment_false_highlighting = suggestion +resharper_replace_with_single_assignment_true_highlighting = suggestion +resharper_replace_with_single_call_to_any_highlighting = suggestion +resharper_replace_with_single_call_to_count_highlighting = suggestion +resharper_replace_with_single_call_to_first_highlighting = suggestion +resharper_replace_with_single_call_to_first_or_default_highlighting = suggestion +resharper_replace_with_single_call_to_last_highlighting = suggestion +resharper_replace_with_single_call_to_last_or_default_highlighting = suggestion +resharper_replace_with_single_call_to_single_highlighting = suggestion +resharper_replace_with_single_call_to_single_or_default_highlighting = suggestion +resharper_replace_with_single_or_default_1_highlighting = suggestion +resharper_replace_with_single_or_default_2_highlighting = suggestion +resharper_replace_with_single_or_default_3_highlighting = suggestion +resharper_replace_with_single_or_default_4_highlighting = suggestion +resharper_replace_with_string_is_null_or_empty_highlighting = suggestion +resharper_required_base_types_conflict_highlighting = warning +resharper_required_base_types_direct_conflict_highlighting = warning +resharper_required_base_types_is_not_inherited_highlighting = warning +resharper_requires_fallback_color_highlighting = warning +resharper_resource_item_not_resolved_highlighting = error +resharper_resource_not_resolved_highlighting = error +resharper_resx_not_resolved_highlighting = warning +resharper_return_from_global_scopet_with_value_highlighting = warning +resharper_return_of_task_produced_by_using_variable_highlighting = warning +resharper_return_of_using_variable_highlighting = warning +resharper_return_type_can_be_enumerable_global_highlighting = hint +resharper_return_type_can_be_enumerable_local_highlighting = hint +resharper_return_type_can_be_not_nullable_highlighting = warning +resharper_return_value_of_pure_method_is_not_used_highlighting = warning +resharper_route_templates_action_route_prefix_can_be_extracted_to_controller_route_highlighting = hint +resharper_route_templates_ambiguous_matching_constraint_constructor_highlighting = warning +resharper_route_templates_constraint_argument_cannot_be_converted_highlighting = warning +resharper_route_templates_controller_route_parameter_is_not_passed_to_methods_highlighting = hint +resharper_route_templates_duplicated_parameter_highlighting = warning +resharper_route_templates_matching_constraint_constructor_not_resolved_highlighting = warning +resharper_route_templates_method_missing_route_parameters_highlighting = hint +resharper_route_templates_optional_parameter_can_be_preceded_only_by_single_period_highlighting = warning +resharper_route_templates_optional_parameter_must_be_at_the_end_of_segment_highlighting = warning +resharper_route_templates_parameter_constraint_can_be_specified_highlighting = hint +resharper_route_templates_parameter_type_and_constraints_mismatch_highlighting = warning +resharper_route_templates_parameter_type_can_be_made_stricter_highlighting = suggestion +resharper_route_templates_route_parameter_constraint_not_resolved_highlighting = warning +resharper_route_templates_route_parameter_is_not_passed_to_method_highlighting = hint +resharper_route_templates_route_token_not_resolved_highlighting = warning +resharper_route_templates_symbol_not_resolved_highlighting = warning +resharper_route_templates_syntax_error_highlighting = warning +resharper_safe_cast_is_used_as_type_check_highlighting = suggestion +resharper_same_imports_with_different_name_highlighting = warning +resharper_same_variable_assignment_highlighting = warning +resharper_script_tag_has_both_src_and_content_attributes_highlighting = error +resharper_script_tag_with_content_before_includes_highlighting = hint +resharper_sealed_member_in_sealed_class_highlighting = warning +resharper_separate_control_transfer_statement_highlighting = suggestion +resharper_separate_local_functions_with_jump_statement_highlighting = hint +resharper_service_contract_without_operations_highlighting = warning +resharper_shift_expression_real_shift_count_is_zero_highlighting = warning +resharper_shift_expression_result_equals_zero_highlighting = warning +resharper_shift_expression_right_operand_not_equal_real_count_highlighting = warning +resharper_shift_expression_zero_left_operand_highlighting = warning +resharper_similar_anonymous_type_nearby_highlighting = hint +resharper_similar_expressions_comparison_highlighting = warning +resharper_simplify_conditional_operator_highlighting = suggestion +resharper_simplify_conditional_ternary_expression_highlighting = suggestion +resharper_simplify_i_if_highlighting = suggestion +resharper_simplify_linq_expression_use_all_highlighting = suggestion +resharper_simplify_linq_expression_use_any_highlighting = suggestion +resharper_simplify_linq_expression_use_min_by_and_max_by_highlighting = suggestion +resharper_simplify_string_interpolation_highlighting = suggestion +resharper_specify_a_culture_in_string_conversion_explicitly_highlighting = warning +resharper_specify_string_comparison_highlighting = hint +resharper_specify_variable_type_explicitly_highlighting = hint +resharper_spin_lock_in_readonly_field_highlighting = warning +resharper_stack_alloc_inside_loop_highlighting = warning +resharper_statement_termination_highlighting = warning +resharper_static_member_initializer_referes_to_member_below_highlighting = warning +resharper_static_member_in_generic_type_highlighting = warning +resharper_static_problem_in_text_highlighting = warning +resharper_std_is_constant_evaluated_will_always_evaluate_to_constant_highlighting = warning +resharper_string_compare_is_culture_specific_1_highlighting = warning +resharper_string_compare_is_culture_specific_2_highlighting = warning +resharper_string_compare_is_culture_specific_3_highlighting = warning +resharper_string_compare_is_culture_specific_4_highlighting = warning +resharper_string_compare_is_culture_specific_5_highlighting = warning +resharper_string_compare_is_culture_specific_6_highlighting = warning +resharper_string_compare_to_is_culture_specific_highlighting = warning +resharper_string_concatenation_to_template_string_highlighting = hint +resharper_string_ends_with_is_culture_specific_highlighting = warning +resharper_string_index_of_is_culture_specific_1_highlighting = warning +resharper_string_index_of_is_culture_specific_2_highlighting = warning +resharper_string_index_of_is_culture_specific_3_highlighting = warning +resharper_string_last_index_of_is_culture_specific_1_highlighting = warning +resharper_string_last_index_of_is_culture_specific_2_highlighting = warning +resharper_string_last_index_of_is_culture_specific_3_highlighting = warning +resharper_string_literal_as_interpolation_argument_highlighting = suggestion +resharper_string_literal_typo_highlighting = none +resharper_string_literal_wrong_quotes_highlighting = hint +resharper_string_starts_with_is_culture_specific_highlighting = warning +resharper_structured_message_template_problem_highlighting = warning +resharper_struct_can_be_made_read_only_highlighting = suggestion +resharper_struct_member_can_be_made_read_only_highlighting = none +resharper_suggest_base_type_for_parameter_highlighting = hint +resharper_suggest_base_type_for_parameter_in_constructor_highlighting = hint +resharper_suggest_discard_declaration_var_style_highlighting = hint +resharper_suggest_var_or_type_built_in_types_highlighting = hint +resharper_suggest_var_or_type_deconstruction_declarations_highlighting = hint +resharper_suggest_var_or_type_elsewhere_highlighting = hint +resharper_suggest_var_or_type_simple_types_highlighting = hint +resharper_super_call_highlighting = suggestion +resharper_super_call_prohibits_this_highlighting = error +resharper_suppress_nullable_warning_expression_as_inverted_is_expression_highlighting = warning +resharper_suspicious_instanceof_check_highlighting = warning +resharper_suspicious_lambda_block_highlighting = warning +resharper_suspicious_lock_over_synchronization_primitive_highlighting = warning +resharper_suspicious_math_sign_method_highlighting = warning +resharper_suspicious_parameter_name_in_argument_null_exception_highlighting = warning +resharper_suspicious_this_usage_highlighting = warning +resharper_suspicious_typeof_check_highlighting = warning +resharper_suspicious_type_conversion_global_highlighting = warning +resharper_swap_via_deconstruction_highlighting = suggestion +resharper_switch_expression_handles_some_known_enum_values_with_exception_in_default_highlighting = hint +resharper_switch_statement_for_enum_misses_default_section_highlighting = hint +resharper_switch_statement_handles_some_known_enum_values_with_default_highlighting = hint +resharper_switch_statement_missing_some_enum_cases_no_default_highlighting = hint +resharper_symbol_from_not_copied_locally_reference_used_warning_highlighting = warning +resharper_syntax_is_not_allowed_highlighting = warning +resharper_tabs_and_spaces_mismatch_highlighting = warning +resharper_tabs_are_disallowed_highlighting = none +resharper_tabs_outside_indent_highlighting = warning +resharper_tail_recursive_call_highlighting = hint +resharper_tasks_not_loaded_highlighting = warning +resharper_template_duplicate_property_problem_highlighting = warning +resharper_template_format_string_problem_highlighting = warning +resharper_template_is_not_compile_time_constant_problem_highlighting = warning +resharper_ternary_can_be_replaced_by_its_condition_highlighting = warning +resharper_this_in_global_context_highlighting = warning +resharper_thread_static_at_instance_field_highlighting = warning +resharper_thread_static_field_has_initializer_highlighting = warning +resharper_throwing_system_exception_highlighting = suggestion +resharper_throw_from_catch_with_no_inner_exception_highlighting = warning +resharper_throw_must_be_followed_by_expression_highlighting = error +resharper_too_wide_local_variable_scope_highlighting = suggestion +resharper_try_cast_always_succeeds_highlighting = suggestion +resharper_try_statements_can_be_merged_highlighting = hint +resharper_ts_not_resolved_highlighting = error +resharper_ts_resolved_from_inaccessible_module_highlighting = error +resharper_type_guard_doesnt_affect_anything_highlighting = warning +resharper_type_guard_produces_never_type_highlighting = warning +resharper_type_parameter_can_be_variant_highlighting = suggestion +resharper_type_parameter_hides_type_param_from_outer_scope_highlighting = warning +resharper_ul_tag_contains_non_li_elements_highlighting = hint +resharper_unassigned_field_global_highlighting = suggestion +resharper_unassigned_field_local_highlighting = warning +resharper_unassigned_get_only_auto_property_highlighting = warning +resharper_unassigned_readonly_field_highlighting = warning +resharper_unclosed_script_highlighting = error +resharper_undeclared_global_variable_using_highlighting = warning +resharper_unexpected_value_highlighting = error +resharper_unknown_css_class_highlighting = warning +resharper_unknown_css_variable_highlighting = warning +resharper_unknown_css_vendor_extension_highlighting = hint +resharper_unknown_item_group_highlighting = suggestion +resharper_unknown_metadata_highlighting = suggestion +resharper_unknown_output_parameter_highlighting = warning +resharper_unknown_property_highlighting = none +resharper_unknown_target_highlighting = suggestion +resharper_unknown_task_attribute_highlighting = suggestion +resharper_unknown_task_highlighting = warning +resharper_unnecessary_whitespace_highlighting = suggestion +resharper_unreachable_switch_arm_due_to_integer_analysis_highlighting = warning +resharper_unreachable_switch_case_due_to_integer_analysis_highlighting = warning +resharper_unreal_header_tool_error_highlighting = error +resharper_unreal_header_tool_warning_highlighting = warning +resharper_unsafe_comma_in_object_properties_list_highlighting = warning +resharper_unsupported_required_base_type_highlighting = warning +resharper_unused_anonymous_method_signature_highlighting = warning +resharper_unused_auto_property_accessor_global_highlighting = none +resharper_unused_auto_property_accessor_local_highlighting = warning +resharper_unused_import_clause_highlighting = warning +resharper_unused_inherited_parameter_highlighting = hint +resharper_unused_locals_highlighting = warning +resharper_unused_local_function_highlighting = warning +resharper_unused_local_function_parameter_highlighting = warning +resharper_unused_local_function_return_value_highlighting = warning +resharper_unused_local_import_highlighting = warning +resharper_unused_member_global_highlighting = none +resharper_unused_member_hierarchy_global_highlighting = suggestion +resharper_unused_member_hierarchy_local_highlighting = warning +resharper_unused_member_in_super_global_highlighting = suggestion +resharper_unused_member_in_super_local_highlighting = warning +resharper_unused_member_local_highlighting = warning +resharper_unused_method_return_value_global_highlighting = suggestion +resharper_unused_method_return_value_local_highlighting = warning +resharper_unused_nullable_directive_highlighting = warning +resharper_unused_parameter_global_highlighting = suggestion +resharper_unused_parameter_highlighting = warning +resharper_unused_parameter_in_partial_method_highlighting = warning +resharper_unused_parameter_local_highlighting = warning +resharper_unused_property_highlighting = warning +resharper_unused_tuple_component_in_return_value_highlighting = warning +resharper_unused_type_global_highlighting = none +resharper_unused_type_local_highlighting = warning +resharper_unused_type_parameter_highlighting = warning +resharper_unused_variable_highlighting = warning +resharper_usage_of_definitely_unassigned_value_highlighting = warning +resharper_usage_of_possibly_unassigned_value_highlighting = warning +resharper_useless_binary_operation_highlighting = warning +resharper_useless_comparison_to_integral_constant_highlighting = warning +resharper_use_array_creation_expression_1_highlighting = suggestion +resharper_use_array_creation_expression_2_highlighting = suggestion +resharper_use_array_empty_method_highlighting = suggestion +resharper_use_as_instead_of_type_cast_highlighting = hint +resharper_use_await_using_highlighting = suggestion +resharper_use_cancellation_token_for_i_async_enumerable_highlighting = suggestion +resharper_use_collection_count_property_highlighting = suggestion +resharper_use_configure_await_false_for_async_disposable_highlighting = none +resharper_use_configure_await_false_highlighting = suggestion +resharper_use_deconstruction_highlighting = hint +resharper_use_discard_assignment_highlighting = suggestion +resharper_use_empty_types_field_highlighting = suggestion +resharper_use_event_args_empty_field_highlighting = suggestion +resharper_use_format_specifier_in_format_string_highlighting = suggestion +resharper_use_implicitly_typed_variable_evident_highlighting = hint +resharper_use_implicitly_typed_variable_highlighting = none +resharper_use_implicit_by_val_modifier_highlighting = hint +resharper_use_indexed_property_highlighting = suggestion +resharper_use_index_from_end_expression_highlighting = suggestion +resharper_use_is_operator_1_highlighting = suggestion +resharper_use_is_operator_2_highlighting = suggestion +resharper_use_method_any_0_highlighting = suggestion +resharper_use_method_any_1_highlighting = suggestion +resharper_use_method_any_2_highlighting = suggestion +resharper_use_method_any_3_highlighting = suggestion +resharper_use_method_any_4_highlighting = suggestion +resharper_use_method_is_instance_of_type_highlighting = suggestion +resharper_use_nameof_expression_for_part_of_the_string_highlighting = none +resharper_use_nameof_expression_highlighting = suggestion +resharper_use_nameof_for_dependency_property_highlighting = suggestion +resharper_use_name_of_instead_of_type_of_highlighting = suggestion +resharper_use_negated_pattern_in_is_expression_highlighting = hint +resharper_use_negated_pattern_matching_highlighting = hint +resharper_use_nullable_annotation_instead_of_attribute_highlighting = suggestion +resharper_use_nullable_attributes_supported_by_compiler_highlighting = suggestion +resharper_use_nullable_reference_types_annotation_syntax_highlighting = warning +resharper_use_null_propagation_highlighting = hint +resharper_use_object_or_collection_initializer_highlighting = suggestion +resharper_use_of_implicit_global_in_function_scope_highlighting = warning +resharper_use_of_possibly_unassigned_property_highlighting = warning +resharper_use_pattern_matching_highlighting = suggestion +resharper_use_positional_deconstruction_pattern_highlighting = none +resharper_use_raw_string_highlighting = hint +resharper_use_string_interpolation_highlighting = suggestion +resharper_use_string_interpolation_when_possible_highlighting = hint +resharper_use_switch_case_pattern_variable_highlighting = suggestion +resharper_use_throw_if_null_method_highlighting = none +resharper_use_unsigned_right_shift_operator_highlighting = suggestion +resharper_use_verbatim_string_highlighting = hint +resharper_use_with_expression_to_copy_anonymous_object_highlighting = suggestion +resharper_use_with_expression_to_copy_record_highlighting = suggestion +resharper_use_with_expression_to_copy_struct_highlighting = suggestion +resharper_use_with_expression_to_copy_tuple_highlighting = suggestion +resharper_using_of_reserved_word_error_highlighting = error +resharper_using_of_reserved_word_highlighting = warning +resharper_using_statement_resource_initialization_expression_highlighting = hint +resharper_using_statement_resource_initialization_highlighting = warning +resharper_value_parameter_not_used_highlighting = warning +resharper_value_range_attribute_violation_highlighting = warning +resharper_value_should_have_units_highlighting = error +resharper_variable_can_be_made_const_highlighting = hint +resharper_variable_can_be_made_let_highlighting = hint +resharper_variable_can_be_moved_to_inner_block_highlighting = hint +resharper_variable_can_be_not_nullable_highlighting = warning +resharper_variable_hides_outer_variable_highlighting = warning +resharper_variable_used_before_declared_highlighting = warning +resharper_variable_used_in_inner_scope_before_declared_highlighting = warning +resharper_variable_used_out_of_scope_highlighting = warning +resharper_vb_check_for_reference_equality_instead_1_highlighting = suggestion +resharper_vb_check_for_reference_equality_instead_2_highlighting = suggestion +resharper_vb_possible_mistaken_argument_highlighting = warning +resharper_vb_possible_mistaken_call_to_get_type_1_highlighting = warning +resharper_vb_possible_mistaken_call_to_get_type_2_highlighting = warning +resharper_vb_remove_to_list_1_highlighting = suggestion +resharper_vb_remove_to_list_2_highlighting = suggestion +resharper_vb_replace_with_first_or_default_highlighting = suggestion +resharper_vb_replace_with_last_or_default_highlighting = suggestion +resharper_vb_replace_with_of_type_1_highlighting = suggestion +resharper_vb_replace_with_of_type_2_highlighting = suggestion +resharper_vb_replace_with_of_type_any_1_highlighting = suggestion +resharper_vb_replace_with_of_type_any_2_highlighting = suggestion +resharper_vb_replace_with_of_type_count_1_highlighting = suggestion +resharper_vb_replace_with_of_type_count_2_highlighting = suggestion +resharper_vb_replace_with_of_type_first_1_highlighting = suggestion +resharper_vb_replace_with_of_type_first_2_highlighting = suggestion +resharper_vb_replace_with_of_type_first_or_default_1_highlighting = suggestion +resharper_vb_replace_with_of_type_first_or_default_2_highlighting = suggestion +resharper_vb_replace_with_of_type_last_1_highlighting = suggestion +resharper_vb_replace_with_of_type_last_2_highlighting = suggestion +resharper_vb_replace_with_of_type_last_or_default_1_highlighting = suggestion +resharper_vb_replace_with_of_type_last_or_default_2_highlighting = suggestion +resharper_vb_replace_with_of_type_single_1_highlighting = suggestion +resharper_vb_replace_with_of_type_single_2_highlighting = suggestion +resharper_vb_replace_with_of_type_single_or_default_1_highlighting = suggestion +resharper_vb_replace_with_of_type_single_or_default_2_highlighting = suggestion +resharper_vb_replace_with_of_type_where_highlighting = suggestion +resharper_vb_replace_with_single_assignment_1_highlighting = suggestion +resharper_vb_replace_with_single_assignment_2_highlighting = suggestion +resharper_vb_replace_with_single_call_to_any_highlighting = suggestion +resharper_vb_replace_with_single_call_to_count_highlighting = suggestion +resharper_vb_replace_with_single_call_to_first_highlighting = suggestion +resharper_vb_replace_with_single_call_to_first_or_default_highlighting = suggestion +resharper_vb_replace_with_single_call_to_last_highlighting = suggestion +resharper_vb_replace_with_single_call_to_last_or_default_highlighting = suggestion +resharper_vb_replace_with_single_call_to_single_highlighting = suggestion +resharper_vb_replace_with_single_call_to_single_or_default_highlighting = suggestion +resharper_vb_replace_with_single_or_default_highlighting = suggestion +resharper_vb_simplify_linq_expression_10_highlighting = hint +resharper_vb_simplify_linq_expression_1_highlighting = suggestion +resharper_vb_simplify_linq_expression_2_highlighting = suggestion +resharper_vb_simplify_linq_expression_3_highlighting = suggestion +resharper_vb_simplify_linq_expression_4_highlighting = suggestion +resharper_vb_simplify_linq_expression_5_highlighting = suggestion +resharper_vb_simplify_linq_expression_6_highlighting = suggestion +resharper_vb_simplify_linq_expression_7_highlighting = hint +resharper_vb_simplify_linq_expression_8_highlighting = hint +resharper_vb_simplify_linq_expression_9_highlighting = hint +resharper_vb_string_compare_is_culture_specific_1_highlighting = warning +resharper_vb_string_compare_is_culture_specific_2_highlighting = warning +resharper_vb_string_compare_is_culture_specific_3_highlighting = warning +resharper_vb_string_compare_is_culture_specific_4_highlighting = warning +resharper_vb_string_compare_is_culture_specific_5_highlighting = warning +resharper_vb_string_compare_is_culture_specific_6_highlighting = warning +resharper_vb_string_compare_to_is_culture_specific_highlighting = warning +resharper_vb_string_ends_with_is_culture_specific_highlighting = none +resharper_vb_string_index_of_is_culture_specific_1_highlighting = warning +resharper_vb_string_index_of_is_culture_specific_2_highlighting = warning +resharper_vb_string_index_of_is_culture_specific_3_highlighting = warning +resharper_vb_string_last_index_of_is_culture_specific_1_highlighting = warning +resharper_vb_string_last_index_of_is_culture_specific_2_highlighting = warning +resharper_vb_string_last_index_of_is_culture_specific_3_highlighting = warning +resharper_vb_string_starts_with_is_culture_specific_highlighting = none +resharper_vb_unreachable_code_highlighting = warning +resharper_vb_use_array_creation_expression_1_highlighting = suggestion +resharper_vb_use_array_creation_expression_2_highlighting = suggestion +resharper_vb_use_first_instead_highlighting = warning +resharper_vb_use_method_any_1_highlighting = suggestion +resharper_vb_use_method_any_2_highlighting = suggestion +resharper_vb_use_method_any_3_highlighting = suggestion +resharper_vb_use_method_any_4_highlighting = suggestion +resharper_vb_use_method_any_5_highlighting = suggestion +resharper_vb_use_method_is_instance_of_type_highlighting = suggestion +resharper_vb_use_type_of_is_operator_1_highlighting = suggestion +resharper_vb_use_type_of_is_operator_2_highlighting = suggestion +resharper_virtual_member_call_in_constructor_highlighting = none +resharper_virtual_member_never_overridden_global_highlighting = suggestion +resharper_virtual_member_never_overridden_local_highlighting = suggestion +resharper_void_method_with_must_use_return_value_attribute_highlighting = warning +resharper_vulnerable_package_highlighting = warning +resharper_web_config_module_not_resolved_highlighting = error +resharper_web_config_module_qualification_resolve_highlighting = warning +resharper_web_config_redundant_add_namespace_tag_highlighting = warning +resharper_web_config_redundant_location_tag_highlighting = warning +resharper_web_config_tag_prefix_redundand_highlighting = warning +resharper_web_config_type_not_resolved_highlighting = error +resharper_web_config_unused_add_tag_highlighting = warning +resharper_web_config_unused_element_due_to_config_source_attribute_highlighting = warning +resharper_web_config_unused_remove_or_clear_tag_highlighting = warning +resharper_web_config_web_config_path_warning_highlighting = warning +resharper_web_config_wrong_module_highlighting = error +resharper_web_ignored_path_highlighting = none +resharper_web_mapped_path_highlighting = hint +resharper_with_expression_instead_of_initializer_highlighting = suggestion +resharper_with_expression_modifies_all_members_highlighting = warning +resharper_with_statement_using_error_highlighting = error +resharper_wrong_expression_statement_highlighting = warning +resharper_wrong_indent_size_highlighting = warning +resharper_wrong_metadata_use_highlighting = none +resharper_wrong_public_modifier_specification_highlighting = hint +resharper_wrong_require_relative_path_highlighting = hint +resharper_xaml_assign_null_to_not_null_attribute_highlighting = warning +resharper_xaml_avalonia_wrong_binding_mode_for_stream_binding_operator_highlighting = warning +resharper_xaml_binding_without_context_not_resolved_highlighting = hint +resharper_xaml_binding_with_context_not_resolved_highlighting = warning +resharper_xaml_compiled_binding_missing_data_type_error_highlighting_highlighting = error +resharper_xaml_constructor_warning_highlighting = warning +resharper_xaml_decimal_parsing_is_culture_dependent_highlighting = warning +resharper_xaml_dependency_property_resolve_error_highlighting = warning +resharper_xaml_duplicate_style_setter_highlighting = warning +resharper_xaml_dynamic_resource_error_highlighting = error +resharper_xaml_element_name_reference_not_resolved_highlighting = error +resharper_xaml_empty_grid_length_definition_highlighting = error +resharper_xaml_field_modifier_requires_name_attribute_highlighting = warning +resharper_xaml_grid_definitions_can_be_converted_to_attribute_highlighting = hint +resharper_xaml_ignored_path_highlighting_highlighting = none +resharper_xaml_index_out_of_grid_definition_highlighting = warning +resharper_xaml_invalid_member_type_highlighting = error +resharper_xaml_invalid_resource_target_type_highlighting = error +resharper_xaml_invalid_resource_type_highlighting = error +resharper_xaml_invalid_type_highlighting = error +resharper_xaml_language_level_highlighting = error +resharper_xaml_mapped_path_highlighting_highlighting = hint +resharper_xaml_method_arguments_will_be_ignored_highlighting = warning +resharper_xaml_missing_grid_index_highlighting = warning +resharper_xaml_overloads_collision_highlighting = warning +resharper_xaml_parent_is_out_of_current_component_tree_highlighting = warning +resharper_xaml_path_error_highlighting = warning +resharper_xaml_possible_null_reference_exception_highlighting = suggestion +resharper_xaml_redundant_attached_property_highlighting = warning +resharper_xaml_redundant_binding_mode_attribute_highlighting = warning +resharper_xaml_redundant_collection_property_highlighting = warning +resharper_xaml_redundant_freeze_attribute_highlighting = warning +resharper_xaml_redundant_grid_definitions_highlighting = warning +resharper_xaml_redundant_grid_span_highlighting = warning +resharper_xaml_redundant_modifiers_attribute_highlighting = warning +resharper_xaml_redundant_namespace_alias_highlighting = warning +resharper_xaml_redundant_name_attribute_highlighting = warning +resharper_xaml_redundant_property_type_qualifier_highlighting = warning +resharper_xaml_redundant_resource_highlighting = warning +resharper_xaml_redundant_styled_value_highlighting = warning +resharper_xaml_redundant_update_source_trigger_attribute_highlighting = warning +resharper_xaml_redundant_xamarin_forms_class_declaration_highlighting = warning +resharper_xaml_resource_file_path_case_mismatch_highlighting = warning +resharper_xaml_routed_event_resolve_error_highlighting = warning +resharper_xaml_static_resource_not_resolved_highlighting = warning +resharper_xaml_style_class_not_found_highlighting = warning +resharper_xaml_style_invalid_target_type_highlighting = error +resharper_xaml_unexpected_element_highlighting = error +resharper_xaml_unexpected_text_token_highlighting = error +resharper_xaml_xaml_duplicate_device_family_type_view_highlighting_highlighting = error +resharper_xaml_xaml_mismatched_device_family_view_clr_name_highlighting_highlighting = warning +resharper_xaml_xaml_relative_source_default_mode_warning_highlighting_highlighting = warning +resharper_xaml_xaml_unknown_device_family_type_highlighting_highlighting = warning +resharper_xaml_xaml_xamarin_forms_data_type_and_binding_context_type_mismatched_highlighting_highlighting = warning +resharper_xaml_x_key_attribute_disallowed_highlighting = error +resharper_xml_doc_comment_syntax_problem_highlighting = warning +resharper_xunit_xunit_test_with_console_output_highlighting = warning +resharper_zero_index_from_end_highlighting = warning + +[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,c,c++,cc,cginc,compute,config,cp,cpp,cppm,cs,cshtml,csproj,css,cu,cuh,cxx,dbml,discomap,dtd,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,htm,html,hxx,inc,inl,ino,ipp,ixx,js,json,jsproj,jsx,lsproj,master,mpp,mq4,mq5,mqh,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,tpp,ts,tsx,usf,ush,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] +indent_style = tab +indent_size = tab +tab_width = 4 diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 241f2c25..dd7ff1d9 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands.Attributes; @@ -10,6 +9,8 @@ using DisCatSharp.Enums; using DisCatSharp.Lavalink; +using MikuSharp.Utilities; + namespace MikuSharp.Attributes; /// @@ -32,7 +33,7 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) } /// -/// Defines that usage of this command is restricted to users & the bot in a vc. +/// Defines that usage of this command is restricted to users in a vc and the bot is in a vc. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandCheckBaseAttribute @@ -54,7 +55,7 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) /// Defines that usage of this command is forbidden for discord staff. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Class, Inherited = false)] -public sealed class NotStaffAttribute : CheckBaseAttribute +public sealed class NotDiscordStaffAttribute : CheckBaseAttribute { /// public override Task ExecuteCheckAsync(CommandContext ctx, bool help) @@ -100,12 +101,8 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) if (module is null) return await RespondWithNoSessionAvailableAsync(ctx); - var sessions = module.ConnectedSessions; - if (!sessions.Any()) - return await RespondWithNoSessionAvailableAsync(ctx); - - var firstSession = sessions.First().Value; - if (!firstSession.IsConnected) + var session = module.DefaultSession(); + if (session is null || !session.IsConnected) return await RespondWithNoSessionAvailableAsync(ctx); return true; @@ -121,4 +118,23 @@ public static async Task RespondWithNoSessionAvailableAsync(BaseContext ct await ctx.EditResponseAsync("No session found that can handle music at the moment."); return false; } -} \ No newline at end of file +} + +/// +/// Defines that the method or class will automatically disconnect an existing session. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class AutomaticallyDisconnectExistingSessionAttribute : ApplicationCommandCheckBaseAttribute +{ + /// + public override async Task ExecuteChecksAsync(BaseContext ctx) + { + if (!MikuBot.MusicSessions.ContainsKey(ctx.GuildId!.Value)) + return true; + + await ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild).DisconnectAsync(); + MikuBot.MusicSessions.Remove(ctx.GuildId.Value); + + return true; + } +} diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 74663d31..0561ea37 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -16,141 +16,142 @@ namespace MikuSharp.Commands; [SlashCommandGroup("about", "About")] internal class About : ApplicationCommandsModule { - [SlashCommand("donate", "Financial support information")] - public static async Task DonateAsync(InteractionContext ctx) - { - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) - .WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) - .AddField(new("PayPal", "[Link](https://paypal.me/speyd3r)", true)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); - } - - [SlashCommand("bot", "About the bot")] - public static async Task BotAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) - .WithDescription(ctx.Client.CurrentApplication.Description); - foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id - ? "Owner" - : "Developer", member.User.UsernameWithGlobalName)); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] - public static async Task FollowNewsAsync( - InteractionContext ctx, - [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] DiscordChannel channel, - [Option("name", "Name of webhook")] string name = "Miku Bot Announcements" - ) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); - var f = await announcementChannel.FollowAsync(channel); - await Task.Delay(5000); - var msgs = await channel.GetMessagesAsync(); - var target = msgs.First(x => x.MessageType == MessageType.ChannelFollowAdd); - await target.DeleteAsync("Message cleanup"); - var webhooks = await channel.GetWebhooksAsync(); - var webhook = webhooks.First(x => x.Id == f.WebhookId); - var selfAvatarUrl = ctx.Client.CurrentUser.AvatarUrl; - var stream = await ctx.Client.RestClient.GetStreamAsync(selfAvatarUrl); - var memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream); - memoryStream.Position = 0; - await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( - $"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); - } - - [SlashCommand("feedback", "Send feedback!")] - public static async Task FeedbackAsync(InteractionContext ctx) - { - DiscordInteractionModalBuilder modalBuilder = new(); - modalBuilder.WithTitle("Feedback modal"); - modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); - modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20)); - await ctx.CreateModalResponseAsync(modalBuilder); - - var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); - - if (!res.TimedOut) - { - await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; - var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); - var emb = new DiscordEmbedBuilder(); - emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); - if (ctx.Guild != null) - emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); - var forum = guild.GetChannel(1020433162662322257); - List tags = - [ - ctx.Guild != null - ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) - : forum.AvailableTags.First(x => x.Id == 1020434935502360576) - ]; - var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); - var msg = await thread.GetMessageAsync(thread.Id); - await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); - await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); - await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); - } - else - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is one minute.").AsEphemeral()); - } - - [SlashCommand("ping", "Current ping to discord's services")] - public static async Task PingAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); - - [SlashCommand("which_shard", "What shard am I on?")] - public static async Task GetExecutingShardAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); - - [SlashCommand("stats", "Some stats of the MikuBot!")] - public static async Task StatsAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var guildCount = 0; - var userCount = 0; - var channelCount = 0; - - foreach (var client in MikuBot.ShardedClient.ShardClients) - { - guildCount += client.Value.Guilds.Count; - - foreach (var guild in client.Value.Guilds) - { - userCount += guild.Value.MemberCount!.Value; - channelCount += guild.Value.Channels.Count; - } - } - - var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", guildCount.ToString(), true)).AddField(new("Users", userCount.ToString(), true)) - .AddField(new("Channels", channelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) - .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("support", "Link to my support server")] - public static async Task SupportAsybc(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); - var widget = await guild.GetWidgetAsync(); - var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); - } + [SlashCommand("donate", "Financial support information")] + public static async Task DonateAsync(InteractionContext ctx) + { + var emb = new DiscordEmbedBuilder(); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) + .WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) + .AddField(new("PayPal", "[Link](https://paypal.me/speyd3r)", true)); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); + } + + [SlashCommand("bot", "About the bot")] + public static async Task BotAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var emb = new DiscordEmbedBuilder(); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) + .WithDescription(ctx.Client.CurrentApplication.Description); + foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) + emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id + ? "Owner" + : "Developer", member.User.UsernameWithGlobalName)); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] + public static async Task FollowNewsAsync( + InteractionContext ctx, + [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] + DiscordChannel channel, + [Option("name", "Name of webhook")] string name = "Miku Bot Announcements" + ) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + + if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); + return; + } + + var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); + var f = await announcementChannel.FollowAsync(channel); + await Task.Delay(5000); + var msgs = await channel.GetMessagesAsync(); + var target = msgs.First(x => x.MessageType == MessageType.ChannelFollowAdd); + await target.DeleteAsync("Message cleanup"); + var webhooks = await channel.GetWebhooksAsync(); + var webhook = webhooks.First(x => x.Id == f.WebhookId); + var selfAvatarUrl = ctx.Client.CurrentUser.AvatarUrl; + var stream = await ctx.Client.RestClient.GetStreamAsync(selfAvatarUrl); + var memoryStream = new MemoryStream(); + await stream.CopyToAsync(memoryStream); + memoryStream.Position = 0; + await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( + $"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); + } + + [SlashCommand("feedback", "Send feedback!")] + public static async Task FeedbackAsync(InteractionContext ctx) + { + DiscordInteractionModalBuilder modalBuilder = new(); + modalBuilder.WithTitle("Feedback modal"); + modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); + modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20)); + await ctx.CreateModalResponseAsync(modalBuilder); + + var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); + + if (!res.TimedOut) + { + await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; + var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; + var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var emb = new DiscordEmbedBuilder(); + emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); + if (ctx.Guild != null) + emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); + var forum = guild.GetChannel(1020433162662322257); + List tags = + [ + ctx.Guild != null + ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) + : forum.AvailableTags.First(x => x.Id == 1020434935502360576) + ]; + var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); + var msg = await thread.GetMessageAsync(thread.Id); + await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); + await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); + await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); + } + else + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is one minute.").AsEphemeral()); + } + + [SlashCommand("ping", "Current ping to discord's services")] + public static async Task PingAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); + + [SlashCommand("which_shard", "What shard am I on?")] + public static async Task GetExecutingShardAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); + + [SlashCommand("stats", "Some stats of the MikuBot!")] + public static async Task StatsAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var guildCount = 0; + var userCount = 0; + var channelCount = 0; + + foreach (var client in MikuBot.ShardedClient.ShardClients) + { + guildCount += client.Value.Guilds.Count; + + foreach (var guild in client.Value.Guilds) + { + userCount += guild.Value.MemberCount!.Value; + channelCount += guild.Value.Channels.Count; + } + } + + var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", guildCount.ToString(), true)).AddField(new("Users", userCount.ToString(), true)) + .AddField(new("Channels", channelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) + .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("support", "Link to my support server")] + public static async Task SupportAsybc(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var widget = await guild.GetWidgetAsync(); + var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); + } } diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 5eed19ee..d59bd179 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -16,162 +16,162 @@ namespace MikuSharp.Commands; [SlashCommandGroup("action", "Actions", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] internal class Action : ApplicationCommandsModule { - [SlashCommand("hug", "Hug someone!")] - public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", [""]); - wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("kiss", "Kiss someone!")] - public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", [""]); - wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("lick", "Lick someone!")] - public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", [""]); - wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("pat", "Pat someone!")] - public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("poke", "Poke someone!")] - public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("slap", "Slap someone!")] - public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("bite", "Bite someone!")] - public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} bites {user.Mention} x~x"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("nom", "Nom someone!")] - public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} noms {user.Mention} >:3c"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } - - [SlashCommand("stare", "Stare at someone!")] - public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} stares {user.Mention} O.o")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} stares at {user.Mention} O.o"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); - } + [SlashCommand("hug", "Hug someone!")] + public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", [""]); + wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("kiss", "Kiss someone!")] + public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", [""]); + wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("lick", "Lick someone!")] + public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", [""]); + wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddEmbed(wsh.Embed.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("pat", "Pat someone!")] + public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("poke", "Poke someone!")] + public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("slap", "Slap someone!")] + public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("bite", "Bite someone!")] + public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} bites {user.Mention} x~x"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("nom", "Nom someone!")] + public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} noms {user.Mention} >:3c"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } + + [SlashCommand("stare", "Stare at someone!")] + public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} stares {user.Mention} O.o")); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", [""]); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithDescription($"{ctx.User.Mention} stares at {user.Mention} O.o"); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + if (ctx.Interaction.Context is InteractionContextType.Guild) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); + } } diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 6b18a383..8c718a8f 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -17,201 +17,168 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("fun", "Fun commands", false, [ - InteractionContextType.Guild, InteractionContextType.PrivateChannel -], [ - ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall -])] +[SlashCommandGroup("fun", "Fun commands", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] internal class Fun : ApplicationCommandsModule { - [SlashCommand("8ball", "Yes? No? Maybe?")] - public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var responses = new[] - { - "It is certain.", - "It is decidedly so.", - "Without a doubt.", - "Yes - definitely.", - "You may rely on it.", - "As I see it, yes.", - "Most likely.", - "Outlook good.", - "Yes.", - "Signs point to yes.", - "Reply hazy, try again", - "Ask again later.", - "Better not tell you now.", - "Cannot predict now.", - "Concentrate and ask again.", - "Don't count on it.", - "My reply is no.", - "My sources say no.", - "Outlook not so good.", - "Very doubtful.", - "No." - }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); - } - - [SlashCommand("cat", "Get a random cat image!")] - public static async Task CatAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); - builder.AddEmbed(imgUrl.Embed); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("clyde", "Say something as clyde bot")] - public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); - - DiscordWebhookBuilder builder = new(); - builder.AddFile("clyde.png", img); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("coinflip", "Flip a coin lol")] - public static async Task CoinflipAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var flip = new[] - { - $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" - }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); - } - - [SlashCommand("dog", "Random Dog Image")] - public static async Task DogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); - em.WithDescription($"[Full Image]({dc.Message})"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - /* - [SlashCommand("duck", "Random duck image")] - public static async Task DuckAsync(InteractionContext ctx) - { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); - em.WithDescription($"[Full Image]({dc.message})"); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.WithEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - }*/ - /* - [SlashCommand("lion", "Get a random lion image")] - public static async Task Lion(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/lion")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - [SlashCommand("lizard", "Get a random lizard image")] - public static async Task LizardAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - } - /* - [SlashCommand("panda", "Random panda image")] - public static async Task PandaAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/panda")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("penguin", "Radnom penguin image")] - public static async Task PenguinAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/penguin")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - /* - [SlashCommand("redpanda", "Random red panda image")] - public static async Task RedPandaAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/red_panda")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - var rock = new[] - { - $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" - }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); - } - /* - [SlashCommand("tiger", "Random tiger image")] - public static async Task TigerAsync(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/tiger")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ - - /* - [SlashCommand("trumptweet", "generate a tweet by Trump")] - public static async Task TrumpTweetAsync(InteractionContext ctx, [RemainingText]string text) - { - //https://nekobot.xyz/api/imagegen?type=trumptweet&text= - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=trumptweet&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"trump.png", img); - await ctx.EditResponseAsync(builder); - }*/ + [SlashCommand("8ball", "Yes? No? Maybe?")] + public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var responses = new[] { "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No." }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); + } + + [SlashCommand("cat", "Get a random cat image!")] + public static async Task CatAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); + builder.AddEmbed(imgUrl.Embed); + await ctx.EditResponseAsync(builder); + } + + [SlashCommand("clyde", "Say something as clyde bot")] + public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); + + DiscordWebhookBuilder builder = new(); + builder.AddFile("clyde.png", img); + await ctx.EditResponseAsync(builder); + } + + [SlashCommand("coinflip", "Flip a coin lol")] + public static async Task CoinflipAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); + } + + [SlashCommand("dog", "Random Dog Image")] + public static async Task DogAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); + em.WithDescription($"[Full Image]({dc.Message})"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + } + /* + [SlashCommand("duck", "Random duck image")] + public static async Task DuckAsync(InteractionContext ctx) + { + var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); + em.WithDescription($"[Full Image]({dc.message})"); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.WithEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + }*/ + /* + [SlashCommand("lion", "Get a random lion image")] + public static async Task Lion(InteractionContext ctx) + { + var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/lion")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + }*/ + + [SlashCommand("lizard", "Get a random lizard image")] + public static async Task LizardAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + } + /* + [SlashCommand("panda", "Random panda image")] + public static async Task PandaAsync(InteractionContext ctx) + { + var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/panda")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + } + + [SlashCommand("penguin", "Radnom penguin image")] + public static async Task PenguinAsync(InteractionContext ctx) + { + var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/penguin")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + }*/ + + /* + [SlashCommand("redpanda", "Random red panda image")] + public static async Task RedPandaAsync(InteractionContext ctx) + { + var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/red_panda")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + }*/ + + [SlashCommand("rps", "Play rock paper scissors!")] + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); + var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); + } + /* + [SlashCommand("tiger", "Random tiger image")] + public static async Task TigerAsync(InteractionContext ctx) + { + var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/tiger")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + }*/ + + /* + [SlashCommand("trumptweet", "generate a tweet by Trump")] + public static async Task TrumpTweetAsync(InteractionContext ctx, [RemainingText]string text) + { + //https://nekobot.xyz/api/imagegen?type=trumptweet&text= + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=trumptweet&text={text}")); + Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); + + DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); + builder.AddFile($"trump.png", img); + await ctx.EditResponseAsync(builder); + }*/ } diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuild.cs index f9765386..d6a73b0e 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuild.cs @@ -11,18 +11,18 @@ namespace MikuSharp.Commands; public class MikuGuild : ApplicationCommandsModule { - [SlashCommand("smolcar", "#SmolArmy")] - public static async Task SmolCarAsync(InteractionContext ctx) - { - if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) - { - await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":(")); - } - else - { - await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); - } - } + [SlashCommand("smolcar", "#SmolArmy")] + public static async Task SmolCarAsync(InteractionContext ctx) + { + if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) + { + await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":(")); + } + else + { + await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); + } + } } diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 43256cc8..156b107c 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -17,102 +17,103 @@ namespace MikuSharp.Commands; [SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, dmPermission: false)] internal class Moderation : ApplicationCommandsModule { - [SlashCommand("disable_invites", "Disable invites usage for guild")] - public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); + [SlashCommand("disable_invites", "Disable invites usage for guild")] + public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); - try - { - await ctx.Guild.DisableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Disabled invites")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not disable invites")); - } - } + try + { + await ctx.Guild.DisableInvitesAsync(reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Disabled invites")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not disable invites")); + } + } - [SlashCommand("enable_invites", "Enable invites usage for guild")] - public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); + [SlashCommand("enable_invites", "Enable invites usage for guild")] + public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); - try - { - await ctx.Guild.EnableInvitesAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Enabled invites")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not enable invites")); - } - } + try + { + await ctx.Guild.EnableInvitesAsync(reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Enabled invites")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Could not enable invites")); + } + } - [SlashCommand("ban", "Ban someone")] - public static async Task BanAsync( - InteractionContext ctx, - [Option("user", "User to ban")] DiscordUser user, - [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] int deletionDays = 0, - [Option("reason", "Auditlog reason")] string? reason = null - ) - { - await ctx.DeferAsync(false); + [SlashCommand("ban", "Ban someone")] + public static async Task BanAsync( + InteractionContext ctx, + [Option("user", "User to ban")] DiscordUser user, + [Option("deletion_days", "Delete messages of x days"), MaximumValue(7)] + int deletionDays = 0, + [Option("reason", "Auditlog reason")] string? reason = null + ) + { + await ctx.DeferAsync(false); - try - { - await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithGlobalName}")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithGlobalName}")); - } - } + try + { + await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Banned {user.UsernameWithGlobalName}")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not ban {user.UsernameWithGlobalName}")); + } + } - [SlashCommand("unban", "Unban someone")] - public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); - var userId = Convert.ToUInt64(id); - var user = await ctx.Client.GetUserAsync(userId, true); - await ctx.Guild.UnbanMemberAsync(user, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithGlobalName}")); - } + [SlashCommand("unban", "Unban someone")] + public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); + var userId = Convert.ToUInt64(id); + var user = await ctx.Client.GetUserAsync(userId, true); + await ctx.Guild.UnbanMemberAsync(user, reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Unbanned {user.UsernameWithGlobalName}")); + } - [SlashCommand("kick", "Kick someone")] - public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(false); + [SlashCommand("kick", "Kick someone")] + public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(false); - try - { - var member = await user.ConvertToMember(ctx.Guild); - await member.RemoveAsync(reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithGlobalName}")); - } - catch (Exception) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithGlobalName}")); - } - } + try + { + var member = await user.ConvertToMember(ctx.Guild); + await member.RemoveAsync(reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Kicked {user.UsernameWithGlobalName}")); + } + catch (Exception) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Could not kick {user.UsernameWithGlobalName}")); + } + } - [SlashCommand("purge", "Delete a large amount of messages fast")] - public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) - { - await ctx.DeferAsync(); + [SlashCommand("purge", "Delete a large amount of messages fast")] + public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) + { + await ctx.DeferAsync(); - try - { - var msgs = await ctx.Channel.GetMessagesAsync(amount); - var under14DaysOld = msgs.Where(x => (DateTime.Now - x.CreationTimestamp.DateTime).TotalDays < 14).ToList().AsReadOnly(); - if (under14DaysOld.Any()) - await ctx.Channel.DeleteMessagesAsync(under14DaysOld, reason); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Purged {under14DaysOld.Count} messages")); - } - catch (BadRequestException ex) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.JsonMessage.BlockCode("json"))); - } - } + try + { + var msgs = await ctx.Channel.GetMessagesAsync(amount); + var under14DaysOld = msgs.Where(x => (DateTime.Now - x.CreationTimestamp.DateTime).TotalDays < 14).ToList().AsReadOnly(); + if (under14DaysOld.Any()) + await ctx.Channel.DeleteMessagesAsync(under14DaysOld, reason); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Purged {under14DaysOld.Count} messages")); + } + catch (BadRequestException ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.JsonMessage.BlockCode("json"))); + } + } } diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs index 7b8ca4e6..f66c7251 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Music.cs @@ -1,16 +1,14 @@ -using System.Linq; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; +using DisCatSharp.Lavalink; using MikuSharp.Attributes; - -using System.Threading.Tasks; - -using DisCatSharp.Lavalink; +using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -20,21 +18,25 @@ namespace MikuSharp.Commands; [SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync, EnsureLavalinkSession] public class Music : ApplicationCommandsModule { - [SlashCommandGroup("base", "Base commands (Join & Leave)")] - public class Base : ApplicationCommandsModule + /// + /// Joins a voice channel the user is in. + /// + /// The interaction context. + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] + public static async Task JoinAsync(InteractionContext ctx) { - [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] - public static async Task JoinAsync(InteractionContext ctx) - { - await ctx.Client.GetLavalink().ConnectedSessions.First().Value.ConnectAsync(ctx.Member.VoiceState.Channel); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - } + await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); + MikuBot.MusicSessions.Add(ctx.GuildId.Value, new()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + } - [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection] - public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) - { - await ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild).DisconnectAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } + /// + /// Leaves a voice channel. + /// + /// The interaction context. + [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection, AutomaticallyDisconnectExistingSession] + public static async Task LeaveAsync(InteractionContext ctx) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); } } diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index 249290f5..d74ab6be 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp.CommandsNext; using DisCatSharp.CommandsNext.Attributes; @@ -9,105 +9,105 @@ namespace MikuSharp.Commands; -[RequireNsfw, NotStaff] +[RequireNsfw, NotDiscordStaff] public class Nsfw : BaseCommandModule { - [Command("4k"), Description("lewd")] - public async Task FourK(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("anal"), Description("lewd")] - public async Task Anal(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("ass"), Description("lewd")] - public async Task Ass(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("gonewild"), Description("lewd")] - public async Task Gonewild(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdkitsune"), Description("lewd")] - public async Task LewdKitsune(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdneko"), Description("lewd")] - public async Task LewdNeko(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("porngif"), Description("lewd")] - public async Task PornGif(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("pussy"), Description("lewd")] - public async Task Pussy(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("thighs"), Aliases("thigh"), Description("lewd")] - public async Task Thighs(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); - - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); - builder.WithEmbed(d.Embed); - await ctx.RespondAsync(builder); - } + [Command("4k"), Description("lewd")] + public async Task FourK(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("anal"), Description("lewd")] + public async Task Anal(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("ass"), Description("lewd")] + public async Task Ass(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("gonewild"), Description("lewd")] + public async Task Gonewild(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdkitsune"), Description("lewd")] + public async Task LewdKitsune(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdneko"), Description("lewd")] + public async Task LewdNeko(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("porngif"), Description("lewd")] + public async Task PornGif(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("pussy"), Description("lewd")] + public async Task Pussy(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } + + [Command("thighs"), Aliases("thigh"), Description("lewd")] + public async Task Thighs(CommandContext ctx) + { + var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); + + DiscordMessageBuilder builder = new(); + builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddEmbed(d.Embed); + await ctx.RespondAsync(builder); + } } diff --git a/MikuSharp/Commands/Old/Music.cs b/MikuSharp/Commands/Old/Music.cs index 5f67d77a..ede34e0f 100644 --- a/MikuSharp/Commands/Old/Music.cs +++ b/MikuSharp/Commands/Old/Music.cs @@ -785,4 +785,6 @@ public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) } } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index f7a05b1f..56a594d1 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -24,209 +24,209 @@ namespace MikuSharp.Commands; [SlashCommandGroup("utility", "Utilities")] internal class Utility : ApplicationCommandsModule { - [SlashCommandGroup("am", "Anime & Mange")] - internal class AnimeMangaUtility : ApplicationCommandsModule - { - [SlashCommand("anime_search", "Search for an anime")] - public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - try - { - var ine = ctx.Client.GetInteractivity(); - var a = await Anime.GetAnimeAsync(searchQuery); - var emb = new DiscordEmbedBuilder(); - List res = []; - List ress = []; - - foreach (var aa in a.Data) - { - emb.WithColor(new(0212255)); - emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis.Length != 0) - emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype.Length != 0) - emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.EpisodeCount != null) - emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); - if (aa.Attributes.EpisodeLength != null) - emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); - if (aa.Attributes.StartDate != null) - emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) - emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) - emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) - emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); - emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); - if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); - res.Add(emb); - emb = new(); - } - - res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); - var i = 1; - - foreach (var aa in res) - { - aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new(embed: aa)); - i++; - } - - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Anime found!")); - } - } - - [SlashCommand("manga_search", "Search for an manga")] - public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - try - { - var ine = ctx.Client.GetInteractivity(); - var a = await Manga.GetMangaAsync(searchQuery); - var emb = new DiscordEmbedBuilder(); - List res = []; - List ress = []; - - foreach (var aa in a.Data) - { - emb.WithColor(new(0212255)); - emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis != null) - emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype != null) - emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.StartDate != null) - emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) - emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) - emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) - emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); - if (aa.Attributes.CoverImage?.Small != null) - emb.WithThumbnail(aa.Attributes.CoverImage.Small); - emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - res.Add(emb); - emb = new(); - } - - res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); - var i = 1; - - foreach (var aa in res) - { - aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); - ress.Add(new(embed: aa)); - i++; - } - - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Manga found!")); - } - } - } - - [SlashCommandGroup("discord", "Discord Utilities")] - internal class DiscordUtility : ApplicationCommandsModule - { - [SlashCommand("avatar", "Get the avatar of someone or yourself")] - public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null - ? user.AvatarUrl - : ctx.User.AvatarUrl).Build())); - - [SlashCommand("server_info", "Get information about the server")] - public static async Task GuildInfoAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (ctx.Guild == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); - return; - } - - var members = await ctx.Guild.GetAllMembersAsync(); - var bots = members.Count(x => x.IsBot); - - var emb = new DiscordEmbedBuilder(); - emb.WithTitle(ctx.Guild.Name); - emb.WithColor(new(0212255)); - emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); - emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); - emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); - emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); - emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("user_info", "Get information about a user")] - public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (user == null) - user = ctx.User; - - DiscordMember? member = null; - - if (ctx.Guild != null) - try - { - member = await user.ConvertToMember(ctx.Guild); - } - catch (NotFoundException) - { } - - var emb = new DiscordEmbedBuilder(); - emb.WithColor(new(0212255)); - emb.WithTitle("User Info"); - emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); - if (member != null) - if (member.DisplayName != user.Username) - emb.AddField(new("Nickname", $"{member.DisplayName}", true)); - emb.AddField(new("ID", $"{user.Id}", true)); - emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); - if (member != null) - emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); - emb.WithThumbnail(user.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public static async Task EmojiListAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var wat = "You have to execute this command in a server!"; - - if (ctx.Guild != null && ctx.Guild.Emojis.Any()) - { - wat = "**Emojies:** "; - foreach (var em in ctx.Guild.Emojis.Values) - wat += em + " "; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); - } - } + [SlashCommandGroup("am", "Anime & Mange")] + internal class AnimeMangaUtility : ApplicationCommandsModule + { + [SlashCommand("anime_search", "Search for an anime")] + public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + try + { + var ine = ctx.Client.GetInteractivity(); + var a = await Anime.GetAnimeAsync(searchQuery); + var emb = new DiscordEmbedBuilder(); + List res = []; + List ress = []; + + foreach (var aa in a.Data) + { + emb.WithColor(new(0212255)); + emb.WithTitle(aa.Attributes.Titles.EnJp); + if (aa.Attributes.Synopsis.Length != 0) + emb.WithDescription(aa.Attributes.Synopsis); + if (aa.Attributes.Subtype.Length != 0) + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); + if (aa.Attributes.EpisodeCount != null) + emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); + if (aa.Attributes.EpisodeLength != null) + emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); + if (aa.Attributes.StartDate != null) + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); + if (aa.Attributes.EndDate != null) + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); + if (aa.Attributes.AgeRating != null) + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); + if (aa.Attributes.AverageRating != null) + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); + if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); + res.Add(emb); + emb = new(); + } + + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); + var i = 1; + + foreach (var aa in res) + { + aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + ress.Add(new(embed: aa)); + i++; + } + + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Anime found!")); + } + } + + [SlashCommand("manga_search", "Search for an manga")] + public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + try + { + var ine = ctx.Client.GetInteractivity(); + var a = await Manga.GetMangaAsync(searchQuery); + var emb = new DiscordEmbedBuilder(); + List res = []; + List ress = []; + + foreach (var aa in a.Data) + { + emb.WithColor(new(0212255)); + emb.WithTitle(aa.Attributes.Titles.EnJp); + if (aa.Attributes.Synopsis != null) + emb.WithDescription(aa.Attributes.Synopsis); + if (aa.Attributes.Subtype != null) + emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); + if (aa.Attributes.StartDate != null) + emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); + if (aa.Attributes.EndDate != null) + emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); + if (aa.Attributes.AgeRating != null) + emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); + if (aa.Attributes.AverageRating != null) + emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); + if (aa.Attributes.CoverImage?.Small != null) + emb.WithThumbnail(aa.Attributes.CoverImage.Small); + emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + res.Add(emb); + emb = new(); + } + + res.Sort((x, y) => string.Compare(x.Title, y.Title, StringComparison.Ordinal)); + var i = 1; + + foreach (var aa in res) + { + aa.WithFooter($"via Kitsu.io -- Page {i}/{a.Data.Count}", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); + ress.Add(new(embed: aa)); + i++; + } + + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + } + catch (Exception ex) + { + ctx.Client.Logger.LogError("{ex}", ex.Message); + ctx.Client.Logger.LogError("{ex}", ex.StackTrace); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("No Manga found!")); + } + } + } + + [SlashCommandGroup("discord", "Discord Utilities")] + internal class DiscordUtility : ApplicationCommandsModule + { + [SlashCommand("avatar", "Get the avatar of someone or yourself")] + public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null + ? user.AvatarUrl + : ctx.User.AvatarUrl).Build())); + + [SlashCommand("server_info", "Get information about the server")] + public static async Task GuildInfoAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + if (ctx.Guild == null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + var members = await ctx.Guild.GetAllMembersAsync(); + var bots = members.Count(x => x.IsBot); + + var emb = new DiscordEmbedBuilder(); + emb.WithTitle(ctx.Guild.Name); + emb.WithColor(new(0212255)); + emb.WithThumbnail(ctx.Guild.IconUrl); + emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); + emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); + emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); + emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); + emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); + emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("user_info", "Get information about a user")] + public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + + if (user == null) + user = ctx.User; + + DiscordMember? member = null; + + if (ctx.Guild != null) + try + { + member = await user.ConvertToMember(ctx.Guild); + } + catch (NotFoundException) + { } + + var emb = new DiscordEmbedBuilder(); + emb.WithColor(new(0212255)); + emb.WithTitle("User Info"); + emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); + if (member != null) + if (member.DisplayName != user.Username) + emb.AddField(new("Nickname", $"{member.DisplayName}", true)); + emb.AddField(new("ID", $"{user.Id}", true)); + emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); + if (member != null) + emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); + emb.WithThumbnail(user.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("emojilist", "Lists all custom emoji on this server")] + public static async Task EmojiListAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); + var wat = "You have to execute this command in a server!"; + + if (ctx.Guild != null && ctx.Guild.Emojis.Any()) + { + wat = "**Emojies:** "; + foreach (var em in ctx.Guild.Emojis.Values) + wat += em + " "; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); + } + } } diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 94c5a153..b6893a99 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -16,259 +16,255 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("weeb", "Weeb Stuff!", false, [ - InteractionContextType.Guild, InteractionContextType.PrivateChannel -], [ - ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall -])] +[SlashCommandGroup("weeb", "Weeb Stuff!", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] internal class Weeb : ApplicationCommandsModule { - [SlashCommand("awooify", "Awooify your or someones avatar!")] - public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) - { - await ctx.DeferAsync(false); - var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); - } + [SlashCommand("awooify", "Awooify your or someones avatar!")] + public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) + { + await ctx.DeferAsync(false); + var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); + } - [SlashCommand("diva", "Radnom PJD Loading image")] - public static async Task DivaPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); + [SlashCommand("diva", "Radnom PJD Loading image")] + public static async Task DivaPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("gumi", "Random Gumi image")] - public static async Task GumiPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("gumi", "Random Gumi image")] + public static async Task GumiPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("kaito", "Random Kaito image")] - public static async Task KaitoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("kaito", "Random Kaito image")] + public static async Task KaitoPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("len", "Random Len image")] - public static async Task KLenPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("len", "Random Len image")] + public static async Task KLenPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("luka", "Random Luka image")] - public static async Task LukaPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("luka", "Random Luka image")] + public static async Task LukaPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("meiko", "Random Meiko image")] - public static async Task MeikoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("meiko", "Random Meiko image")] + public static async Task MeikoPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("miku", "Random Miku image")] - public static async Task HMikuPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("miku", "Random Miku image")] + public static async Task HMikuPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("neko", "Get a random neko image")] - public static async Task Cat(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); + [SlashCommand("neko", "Get a random neko image")] + public static async Task Cat(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by nekos.life"); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("rin", "Random Rin image")] - public static async Task KRinPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("rin", "Random Rin image")] + public static async Task KRinPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("teto", "Random Teto image")] - public static async Task KTetoPic(InteractionContext ctx) - { - await ctx.DeferAsync(false); - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); + [SlashCommand("teto", "Random Teto image")] + public static async Task KTetoPic(InteractionContext ctx) + { + await ctx.DeferAsync(false); + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + { + Position = 0 + }; + var emim = new DiscordEmbedBuilder + { + Description = $"[Full Source Image Link]({res.Url})", + ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" + }; + if (res.Creator.Length != 0) + emim.AddField(new("Creator", res.Creator)); + emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); + emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(emim.Build()); + await ctx.EditResponseAsync(builder); + } } diff --git a/MikuSharp/Entities/BiliJson.cs b/MikuSharp/Entities/BiliJson.cs index ed822829..bb93dc96 100644 --- a/MikuSharp/Entities/BiliJson.cs +++ b/MikuSharp/Entities/BiliJson.cs @@ -4,27 +4,27 @@ namespace MikuSharp.Entities; public class Durl2 { - public int Order { get; set; } - public int Length { get; set; } - public int Size { get; set; } - public string Ahead { get; set; } - public string Vhead { get; set; } - public string Url { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } } public class BiliJson { - public string From { get; set; } - public string Result { get; set; } - public int Quality { get; set; } - public string Format { get; set; } - public int Timelength { get; set; } - public string AcceptFormat { get; set; } - public List AcceptDescription { get; set; } - public List AcceptQuality { get; set; } - public int VideoCodecid { get; set; } - public bool VideoProject { get; set; } - public string SeekParam { get; set; } - public string SeekType { get; set; } - public List Durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public bool VideoProject { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } diff --git a/MikuSharp/Entities/BiliPlayInfo.cs b/MikuSharp/Entities/BiliPlayInfo.cs index 85b99891..7b22744b 100644 --- a/MikuSharp/Entities/BiliPlayInfo.cs +++ b/MikuSharp/Entities/BiliPlayInfo.cs @@ -4,40 +4,40 @@ namespace MikuSharp.Entities; public class BiliPlayinfo { - public int Code { get; set; } - public string Message { get; set; } - public int Ttl { get; set; } - public Data Data { get; set; } - public string Session { get; set; } - public VideoFrame VideoFrame { get; set; } + public int Code { get; set; } + public string Message { get; set; } + public int Ttl { get; set; } + public Data Data { get; set; } + public string Session { get; set; } + public VideoFrame VideoFrame { get; set; } } public class Durl { - public int Order { get; set; } - public int Length { get; set; } - public int Size { get; set; } - public string Ahead { get; set; } - public string Vhead { get; set; } - public string Url { get; set; } - public object BackupUrl { get; set; } + public int Order { get; set; } + public int Length { get; set; } + public int Size { get; set; } + public string Ahead { get; set; } + public string Vhead { get; set; } + public string Url { get; set; } + public object BackupUrl { get; set; } } public class Data { - public string From { get; set; } - public string Result { get; set; } - public string Message { get; set; } - public int Quality { get; set; } - public string Format { get; set; } - public int Timelength { get; set; } - public string AcceptFormat { get; set; } - public List AcceptDescription { get; set; } - public List AcceptQuality { get; set; } - public int VideoCodecid { get; set; } - public string SeekParam { get; set; } - public string SeekType { get; set; } - public List Durl { get; set; } + public string From { get; set; } + public string Result { get; set; } + public string Message { get; set; } + public int Quality { get; set; } + public string Format { get; set; } + public int Timelength { get; set; } + public string AcceptFormat { get; set; } + public List AcceptDescription { get; set; } + public List AcceptQuality { get; set; } + public int VideoCodecid { get; set; } + public string SeekParam { get; set; } + public string SeekType { get; set; } + public List Durl { get; set; } } public class VideoFrame diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index c5508df3..766cb7e1 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -5,72 +5,72 @@ namespace MikuSharp.Entities; public sealed class BotConfig { #if DEBUG - [JsonProperty("discordTokenDev")] + [JsonProperty("discordTokenDev")] #else [JsonProperty("discordToken")] #endif - public string DiscordToken { get; set; } + public string DiscordToken { get; set; } - [JsonProperty("discordBotListToken")] - public string DiscordBotListToken { get; set; } + [JsonProperty("discordBotListToken")] + public string DiscordBotListToken { get; set; } - [JsonProperty("weebShToken")] - public string WeebShToken { get; set; } + [JsonProperty("weebShToken")] + public string WeebShToken { get; set; } - [JsonProperty("youtubeApiToken")] - public string YoutubeApiToken { get; set; } + [JsonProperty("youtubeApiToken")] + public string YoutubeApiToken { get; set; } - [JsonProperty("ksoftSiToken")] - public string KsoftSiToken { get; set; } + [JsonProperty("ksoftSiToken")] + public string KsoftSiToken { get; set; } - [JsonIgnore] - public string DbConnectString { get; set; } + [JsonIgnore] + public string DbConnectString { get; set; } - [JsonProperty("dbConfig")] - public DatabaseConfig DbConfig { get; set; } + [JsonProperty("dbConfig")] + public DatabaseConfig DbConfig { get; set; } - [JsonProperty("lavaConfig")] - public LavalinkConfig LavaConfig { get; set; } + [JsonProperty("lavaConfig")] + public LavalinkConfig LavaConfig { get; set; } - [JsonProperty("nndConfig")] - public NndConfig NndConfig { get; set; } + [JsonProperty("nndConfig")] + public NndConfig NndConfig { get; set; } } public sealed class DatabaseConfig { - [JsonProperty("hostname")] - public string Hostname { get; set; } + [JsonProperty("hostname")] + public string Hostname { get; set; } - [JsonProperty("user")] - public string User { get; set; } + [JsonProperty("user")] + public string User { get; set; } - [JsonProperty("password")] - public string Password { get; set; } + [JsonProperty("password")] + public string Password { get; set; } - [JsonProperty("database")] - public string Database { get; set; } + [JsonProperty("database")] + public string Database { get; set; } } public sealed class LavalinkConfig { - [JsonProperty("hostname")] - public string Hostname { get; set; } + [JsonProperty("hostname")] + public string Hostname { get; set; } - [JsonProperty("password")] - public string Password { get; set; } + [JsonProperty("password")] + public string Password { get; set; } - [JsonProperty("port")] - public int Port { get; set; } + [JsonProperty("port")] + public int Port { get; set; } } public sealed class NndConfig { - [JsonProperty("mail")] - public string Mail { get; set; } + [JsonProperty("mail")] + public string Mail { get; set; } - [JsonProperty("password")] - public string Password { get; set; } + [JsonProperty("password")] + public string Password { get; set; } - [JsonProperty("ftpConfig")] - public DatabaseConfig FtpConfig { get; set; } + [JsonProperty("ftpConfig")] + public DatabaseConfig FtpConfig { get; set; } } diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index a72a3216..4cfeb3fe 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -2,6 +2,6 @@ public sealed class DogCeo { - public string Status { get; set; } - public string Message { get; set; } + public string Status { get; set; } + public string Message { get; set; } } diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs index b6f4322a..3f2bfdf6 100644 --- a/MikuSharp/Entities/Img_Data.cs +++ b/MikuSharp/Entities/Img_Data.cs @@ -6,8 +6,8 @@ namespace MikuSharp.Entities; public class ImgData { - public Stream Data { get; set; } - public string Filetype { get; set; } + public Stream Data { get; set; } + public string Filetype { get; set; } - public DiscordEmbed Embed { get; set; } + public DiscordEmbed Embed { get; set; } } diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs index 46b85a28..ad0c2a52 100644 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ b/MikuSharp/Entities/KsoftSiRanImg.cs @@ -2,8 +2,8 @@ public sealed class KsoftSiRanImg : ImgData { - public string Url { get; set; } - public string Snowflake { get; set; } - public bool Nsfw { get; set; } - public string Tag { get; set; } + public string Url { get; set; } + public string Snowflake { get; set; } + public bool Nsfw { get; set; } + public string Tag { get; set; } } diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs index 5a1ddf86..a50daed3 100644 --- a/MikuSharp/Entities/MeekMoe.cs +++ b/MikuSharp/Entities/MeekMoe.cs @@ -2,6 +2,6 @@ public sealed class MeekMoe { - public string Url { get; set; } - public string Creator { get; set; } + public string Url { get; set; } + public string Creator { get; set; } } diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs new file mode 100644 index 00000000..ccca1ae1 --- /dev/null +++ b/MikuSharp/Entities/MusicSession.cs @@ -0,0 +1,21 @@ +using DisCatSharp.Entities; +using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Enums; + +namespace MikuSharp.Entities; + +public sealed class MusicSession +{ + public DiscordChannel CurrentChannel { get; set; } + + public DiscordGuild CurrentGuild { get; set; } + + public LavalinkSession LavalinkSession { get; set; } + + public LavalinkGuildPlayer LavalinkGuildPlayer + => this.LavalinkSession.GetGuildPlayer(this.CurrentGuild); + + public RepeatMode RepeatMode { get; set; } + + public DiscordMember CurrentDj { get; internal set; } +} diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs index 4c64301c..af79d18a 100644 --- a/MikuSharp/Entities/Nekobot.cs +++ b/MikuSharp/Entities/Nekobot.cs @@ -2,7 +2,7 @@ public sealed class NekoBot : ImgData { - public string Message { get; set; } - public int Status { get; set; } - public bool Success { get; set; } + public string Message { get; set; } + public int Status { get; set; } + public bool Success { get; set; } } diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs index 83cecb3f..235044e5 100644 --- a/MikuSharp/Entities/Nekos_Life.cs +++ b/MikuSharp/Entities/Nekos_Life.cs @@ -2,5 +2,5 @@ public sealed class NekosLife : ImgData { - public string Url { get; set; } + public string Url { get; set; } } diff --git a/MikuSharp/Entities/Old/Entry.cs b/MikuSharp/Entities/Old/Entry.cs index eed006cd..f40e341b 100644 --- a/MikuSharp/Entities/Old/Entry.cs +++ b/MikuSharp/Entities/Old/Entry.cs @@ -15,4 +15,6 @@ public Entry(LavalinkTrack t, DateTimeOffset addtime) public LavalinkTrack Track { get; protected set; } public DateTimeOffset AdditionDate { get; protected set; } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Entities/Old/Guild.cs b/MikuSharp/Entities/Old/Guild.cs index cb5f7ec3..af21d977 100644 --- a/MikuSharp/Entities/Old/Guild.cs +++ b/MikuSharp/Entities/Old/Guild.cs @@ -34,4 +34,6 @@ public async Task CheckAlone() } } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Entities/Old/MusicInstance.cs b/MikuSharp/Entities/Old/MusicInstance.cs index dda2d108..8dd2e781 100644 --- a/MikuSharp/Entities/Old/MusicInstance.cs +++ b/MikuSharp/Entities/Old/MusicInstance.cs @@ -404,4 +404,6 @@ public async Task PlaySong() } // B/S(`・ω・´) ❤️ (´ω`)U/C -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Entities/Old/PlaylistEntry.cs b/MikuSharp/Entities/Old/PlaylistEntry.cs index 0bbfb54e..d1c9fde1 100644 --- a/MikuSharp/Entities/Old/PlaylistEntry.cs +++ b/MikuSharp/Entities/Old/PlaylistEntry.cs @@ -15,4 +15,6 @@ public PlaylistEntry(LavalinkTrack t, DateTimeOffset addDate, DateTimeOffset mod public DateTimeOffset ModifyDate { get; set; } public int Position { get; set; } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Entities/Old/QueueEntry.cs b/MikuSharp/Entities/Old/QueueEntry.cs index b1e956bb..382774ac 100644 --- a/MikuSharp/Entities/Old/QueueEntry.cs +++ b/MikuSharp/Entities/Old/QueueEntry.cs @@ -15,4 +15,6 @@ public QueueEntry(LavalinkTrack t, ulong m, DateTimeOffset adddate, int pos) : b public int Position { get; set; } public ulong AddedBy { set; get; } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Entities/Old/TrackResult.cs b/MikuSharp/Entities/Old/TrackResult.cs index 9263623d..5c1399b1 100644 --- a/MikuSharp/Entities/Old/TrackResult.cs +++ b/MikuSharp/Entities/Old/TrackResult.cs @@ -22,4 +22,6 @@ public TrackResult(LavalinkPlaylistInfo pl, LavalinkTrack tr) public LavalinkPlaylistInfo PlaylistInfo { get; set; } public List Tracks { get; set; } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Entities/Random_D.cs b/MikuSharp/Entities/Random_D.cs index 349fdfce..c5214332 100644 --- a/MikuSharp/Entities/Random_D.cs +++ b/MikuSharp/Entities/Random_D.cs @@ -2,6 +2,6 @@ public class RandomD { - public string Url { get; set; } - public string Message { get; set; } + public string Url { get; set; } + public string Message { get; set; } } diff --git a/MikuSharp/Entities/WeebSh.cs b/MikuSharp/Entities/WeebSh.cs index f56a651e..f778bd92 100644 --- a/MikuSharp/Entities/WeebSh.cs +++ b/MikuSharp/Entities/WeebSh.cs @@ -6,7 +6,7 @@ namespace MikuSharp.Entities; public sealed class WeebSh { - public MemoryStream ImgData { get; set; } - public string Extension { get; set; } - public DiscordEmbedBuilder Embed { get; set; } + public MemoryStream ImgData { get; set; } + public string Extension { get; set; } + public DiscordEmbedBuilder Embed { get; set; } } diff --git a/MikuSharp/Enums/ExtService.cs b/MikuSharp/Enums/ExtService.cs index 835ca48b..b3819ef4 100644 --- a/MikuSharp/Enums/ExtService.cs +++ b/MikuSharp/Enums/ExtService.cs @@ -2,7 +2,7 @@ public enum ExtService { - None = 0, - Youtube = 1, - Soundcloud = 2 + None = 0, + Youtube = 1, + Soundcloud = 2 } diff --git a/MikuSharp/Enums/Playing.cs b/MikuSharp/Enums/Playing.cs index ccfbb806..25e791e4 100644 --- a/MikuSharp/Enums/Playing.cs +++ b/MikuSharp/Enums/Playing.cs @@ -2,21 +2,21 @@ public enum Playstate { - NotPlaying = 0, - Playing = 1, - Paused = 2, - Stopped = 3 + NotPlaying = 0, + Playing = 1, + Paused = 2, + Stopped = 3 } public enum RepeatMode { - Off = 0, - On = 1, - All = 2 + Off = 0, + On = 1, + All = 2 } public enum ShuffleMode { - Off = 0, - On = 1 + Off = 0, + On = 1 } diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index d80514ce..0e54d4e3 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp; using DisCatSharp.EventArgs; diff --git a/MikuSharp/Events/Old/Lavalink.cs b/MikuSharp/Events/Old/Lavalink.cs index 9eefc42e..6ca14388 100644 --- a/MikuSharp/Events/Old/Lavalink.cs +++ b/MikuSharp/Events/Old/Lavalink.cs @@ -80,4 +80,6 @@ await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().Wit } } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Events/Old/VoiceChat.cs b/MikuSharp/Events/Old/VoiceChat.cs index 39fcbc0d..7fc59b75 100644 --- a/MikuSharp/Events/Old/VoiceChat.cs +++ b/MikuSharp/Events/Old/VoiceChat.cs @@ -66,4 +66,6 @@ await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder() } } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/GlobalSuppressions.cs b/MikuSharp/GlobalSuppressions.cs index 145c54cf..197c10f2 100644 --- a/MikuSharp/GlobalSuppressions.cs +++ b/MikuSharp/GlobalSuppressions.cs @@ -87,160 +87,160 @@ [assembly: SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.#ctor")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.Dispose")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.FourK(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.FourK(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.Gonewild(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Gonewild(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.LewdKitsune(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.LewdKitsune(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.LewdNeko(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.LewdNeko(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.PornGif(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.Entry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.DateTimeOffset)")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Entry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.DateTimeOffset)")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.Guild.#ctor(System.Int32,MikuSharp.Entities.MusicInstance)")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Guild.#ctor(System.Int32,MikuSharp.Entities.MusicInstance)")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.CheckAlone~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.Guild.CheckAlone~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.MusicInstance.#ctor(DisCatSharp.Lavalink.LavalinkNodeConnection,System.Int32)")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.#ctor(DisCatSharp.Lavalink.LavalinkNodeConnection,System.Int32)")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.MusicInstance.ConnectToChannel(DisCatSharp.Entities.DiscordChannel)~System.Threading.Tasks.Task{DisCatSharp.Lavalink.LavalinkGuildConnection}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.ConnectToChannel(DisCatSharp.Entities.DiscordChannel)~System.Threading.Tasks.Task{DisCatSharp.Lavalink.LavalinkGuildConnection}")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.Playlist.#ctor(MikuSharp.Enums.ExtService,System.String,System.String,System.UInt64,System.Int32,System.DateTimeOffset,System.DateTimeOffset)")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Playlist.#ctor(MikuSharp.Enums.ExtService,System.String,System.String,System.UInt64,System.Int32,System.DateTimeOffset,System.DateTimeOffset)")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.Playlist.GetEntries~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry}}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.Playlist.GetEntries~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry}}")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.QueueEntry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.UInt64,System.DateTimeOffset,System.Int32)")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.QueueEntry.#ctor(DisCatSharp.Lavalink.LavalinkTrack,System.UInt64,System.DateTimeOffset,System.Int32)")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,DisCatSharp.Lavalink.LavalinkTrack)")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,DisCatSharp.Lavalink.LavalinkTrack)")] [assembly: - SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,System.Collections.Generic.IEnumerable{DisCatSharp.Lavalink.LavalinkTrack})")] + SuppressMessage("DocumentationHeader", "ConstructorDocumentationHeader:The constructor must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Entities.TrackResult.#ctor(DisCatSharp.Lavalink.LavalinkPlaylistInfo,System.Collections.Generic.IEnumerable{DisCatSharp.Lavalink.LavalinkTrack})")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.AddToLastPlayingListAsync(System.UInt64,System.String)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToLastPlayingListAsync(System.UInt64,System.String)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.AddToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.ClearQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.ClearQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.GetLastPlayingListAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.Entry}}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.GetLastPlayingListAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.Entry}}")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.GetQueueAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.QueueEntry}}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.GetQueueAsync(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task{System.Collections.Generic.List{MikuSharp.Entities.QueueEntry}}")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.InsertToQueue(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.MoveQueueItems(DisCatSharp.Entities.DiscordGuild,System.Int32,System.Int32)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.MoveQueueItems(DisCatSharp.Entities.DiscordGuild,System.Int32,System.Int32)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.RebuildQueue(DisCatSharp.Entities.DiscordGuild,System.Collections.Generic.List{MikuSharp.Entities.QueueEntry})~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.RebuildQueue(DisCatSharp.Entities.DiscordGuild,System.Collections.Generic.List{MikuSharp.Entities.QueueEntry})~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.RemoveFromQueueAsync(System.Int32,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.RemoveFromQueueAsync(System.Int32,DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Database.ReorderQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Database.ReorderQueue(DisCatSharp.Entities.DiscordGuild)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Other.resizeLink(System.String)~System.String")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Other.resizeLink(System.String)~System.String")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack})~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddEntry(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.AddPlaylist(System.String,System.UInt64,MikuSharp.Enums.ExtService,System.String)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.AddPlaylist(System.String,System.UInt64,MikuSharp.Enums.ExtService,System.String)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.ClearList(System.String,System.UInt64)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.ClearList(System.String,System.UInt64)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylists(DisCatSharp.Entities.DiscordGuild,System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.Dictionary{System.String,MikuSharp.Entities.Playlist}}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylists(DisCatSharp.Entities.DiscordGuild,System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.Dictionary{System.String,MikuSharp.Entities.Playlist}}")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylistsSimple(System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.List{System.String}}")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylistsSimple(System.UInt64)~System.Threading.Tasks.Task{System.Collections.Generic.List{System.String}}")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Collections.Generic.List{DisCatSharp.Lavalink.LavalinkTrack},System.Int32)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.InsertEntry(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.String,System.Int32)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.MoveListItems(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Int32,System.Int32)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.MoveListItems(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64,System.Int32,System.Int32)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.RebuildList(System.UInt64,System.String,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RebuildList(System.UInt64,System.String,System.Collections.Generic.List{MikuSharp.Entities.PlaylistEntry})~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.RemoveFromList(DisCatSharp.Entities.DiscordGuild,System.Int32,System.String,System.UInt64)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RemoveFromList(DisCatSharp.Entities.DiscordGuild,System.Int32,System.String,System.UInt64)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.RemovePlaylist(System.String,System.UInt64)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RemovePlaylist(System.String,System.UInt64)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.RenameList(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.RenameList(System.String,System.UInt64,System.String)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.ReorderList(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64)~System.Threading.Tasks.Task")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.ReorderList(DisCatSharp.Entities.DiscordGuild,System.String,System.UInt64)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot._cts")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot.GameSetThread")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.MikuBot.StatusThread")] @@ -401,25 +401,25 @@ [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.PlaylistDB")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Web")] [assembly: - SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] + SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuild")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.Random_D")] [assembly: - SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.PlaylistDB.GetPlaylist(DisCatSharp.Entities.DiscordGuild,System.UInt64,System.String)~System.Threading.Tasks.Task{MikuSharp.Entities.Playlist}")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.#ctor")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.Lavalink.LavalinkTrackFinish(DisCatSharp.Lavalink.LavalinkGuildConnection,DisCatSharp.Lavalink.EventArgs.TrackFinishEventArgs)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.VoiceChat.LeftAlone(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.VoiceStateUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.ShowConnections~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Anal(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Ass(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] @@ -431,23 +431,23 @@ [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Pussy(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.NSFW.Thighs(DisCatSharp.CommandsNext.CommandContext)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Events.MikuGuild.OnJoinAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberAddEventArgs)~System.Threading.Tasks.Task")] + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.MikuGuild.OnJoinAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberAddEventArgs)~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Events.MikuGuild.OnUpdateAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberUpdateEventArgs)~System.Threading.Tasks.Task")] + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Events.MikuGuild.OnUpdateAsync(DisCatSharp.DiscordClient,DisCatSharp.EventArgs.GuildMemberUpdateEventArgs)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", Target = "~M:MikuSharp.Program.Main(System.String[])")] [assembly: SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.MikuBot.RegisterEvents~System.Threading.Tasks.Task")] [assembly: - SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", Target = "~M:MikuSharp.Entities.MusicInstance.PlaySong~System.Threading.Tasks.Task{MikuSharp.Entities.QueueEntry}")] [assembly: - SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", - Target = - "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] + SuppressMessage("Usage", "CA2254:Template should be a static expression", Justification = "", Scope = "member", + Target = + "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] [assembly: - SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] + SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", + Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] [assembly: - SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", - Target = - "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] + SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", + Target = + "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 080541f9..b07a964c 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -48,6 +48,11 @@ internal sealed class MikuBot : IDisposable //internal static Playstate Ps = Playstate.Playing; //internal static Stopwatch Psc = new(); + /// + /// Gets the music sessions. + /// + internal static readonly Dictionary MusicSessions = []; + internal MikuBot() { var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); @@ -124,7 +129,7 @@ internal MikuBot() IgnoreExtraArguments = true, StringPrefixes = [], UseDefaultCommandHandler = true, - DefaultHelpChecks = [new NotStaffAttribute()] + DefaultHelpChecks = [new NotDiscordStaffAttribute()] }).Result; this.LavalinkConfig = new() @@ -243,12 +248,12 @@ internal static async Task RegisterEvents() /*internal async Task ShowConnections() { - while (true) - { - var al = Guilds.Where(x => x.Value?.MusicInstance != null); - ShardedClient.Logger.LogInformation("Voice Connections: " + al.Count(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true)); - await Task.Delay(15000); - } + while (true) + { + var al = Guilds.Where(x => x.Value?.MusicInstance != null); + ShardedClient.Logger.LogInformation("Voice Connections: " + al.Count(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true)); + await Task.Delay(15000); + } }*/ internal static async Task UpdateBotList() @@ -304,7 +309,7 @@ internal void RegisterCommands() this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); - //this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); //ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); @@ -319,19 +324,16 @@ internal async Task RunAsync() await ShardedClient.StartAsync(); await Task.Delay(5000); - /*foreach (var lavalinkShard in this.LavalinkModules) - { - var connection = await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); - LavalinkSessions.Add(lavalinkShard.Key, connection); - }*/ + foreach (var lavalinkShard in this.LavalinkModules) + await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); - //this.GameSetThread = Task.Run(SetActivity); + this.GameSetThread = Task.Run(SetActivity); //StatusThread = Task.Run(ShowConnections); //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); //BotListThread = Task.Run(UpdateBotList); while (!Cts.IsCancellationRequested) await Task.Delay(25); - //_ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); + _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); await ShardedClient.StopAsync(); } diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 302f84cf..b649df2d 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -27,10 +27,10 @@ - + 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 - + 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 @@ -43,16 +43,33 @@ - - - - - + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index 20a26501..14ec2fd5 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -4,15 +4,15 @@ namespace MikuSharp; internal class Program { - private static void Main(string[] args) - { - using (var bot = new MikuBot()) - { - MikuBot.RegisterEvents().Wait(); - bot.RegisterCommands(); - bot.RunAsync().Wait(); - } + private static void Main(string[] args) + { + using (var bot = new MikuBot()) + { + MikuBot.RegisterEvents().Wait(); + bot.RegisterCommands(); + bot.RunAsync().Wait(); + } - Log.Logger.Information("Shutdown!"); - } + Log.Logger.Information("Shutdown!"); + } } diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index 18163377..93b4eb33 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -13,40 +13,40 @@ namespace MikuSharp.Utilities; public static class Bilibili { - public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) - { - try - { - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); - var youtubeDl = new YoutubeDL(@"youtube-dl.exe"); - youtubeDl.Options.FilesystemOptions.Output = $@"{s}.mp4"; - youtubeDl.Options.PostProcessingOptions.ExtractAudio = true; - youtubeDl.Options.PostProcessingOptions.FfmpegLocation = @"ffmpeg.exe"; - youtubeDl.Options.PostProcessingOptions.AudioFormat = NYoutubeDL.Helpers.Enums.AudioFormat.mp3; - youtubeDl.Options.PostProcessingOptions.AddMetadata = true; - youtubeDl.Options.PostProcessingOptions.KeepVideo = false; - youtubeDl.StandardOutputEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; - youtubeDl.StandardErrorEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; - youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; - await youtubeDl.DownloadAsync(); - var ms = new MemoryStream(); + public static async Task GetBilibiliAsync(this InteractionContext ctx, string s, ulong msgId) + { + try + { + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video(this may take up to 5 min)")); + var youtubeDl = new YoutubeDL(@"youtube-dl.exe"); + youtubeDl.Options.FilesystemOptions.Output = $@"{s}.mp4"; + youtubeDl.Options.PostProcessingOptions.ExtractAudio = true; + youtubeDl.Options.PostProcessingOptions.FfmpegLocation = @"ffmpeg.exe"; + youtubeDl.Options.PostProcessingOptions.AudioFormat = NYoutubeDL.Helpers.Enums.AudioFormat.mp3; + youtubeDl.Options.PostProcessingOptions.AddMetadata = true; + youtubeDl.Options.PostProcessingOptions.KeepVideo = false; + youtubeDl.StandardOutputEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; + youtubeDl.StandardErrorEvent += (e, f) => { ctx.Client.Logger.LogDebug("{data}", f); }; + youtubeDl.VideoUrl = "https://www.bilibili.com/video/" + s; + await youtubeDl.DownloadAsync(); + var ms = new MemoryStream(); - if (File.Exists($@"{s}.mp3")) - { - var song = File.Open($@"{s}.mp3", FileMode.Open); - await song.CopyToAsync(ms); - ms.Position = 0; - song.Close(); - File.Delete($@"{s}.mp3"); - } + if (File.Exists($@"{s}.mp3")) + { + var song = File.Open($@"{s}.mp3", FileMode.Open); + await song.CopyToAsync(ms); + ms.Position = 0; + song.Close(); + File.Delete($@"{s}.mp3"); + } - return ms; - } - catch (Exception ex) - { - ctx.Client.Logger.LogDebug("{ex}", ex.Message); - ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - return null; - } - } + return ms; + } + catch (Exception ex) + { + ctx.Client.Logger.LogDebug("{ex}", ex.Message); + ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); + return null; + } + } } diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 167246c5..f0ee2a98 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -7,9 +7,6 @@ using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; -using MikuSharp.Entities; -using MikuSharp.Enums; - namespace MikuSharp.Utilities; /* internal class FixedOptionProviders @@ -31,84 +28,84 @@ public override Task> Provide internal class AutocompleteProviders { - internal sealed class BanProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var bans = await ctx.Guild.GetBansAsync(); - List bannedUsers = new(25); - bannedUsers.AddRange(ctx.FocusedOption.Value is null - ? bans.Take(25) - : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); + internal sealed class BanProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var bans = await ctx.Guild.GetBansAsync(); + List bannedUsers = new(25); + bannedUsers.AddRange(ctx.FocusedOption.Value is null + ? bans.Take(25) + : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); - return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); - } - } + return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); + } + } - /* - internal class PlaylistProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (plls.Count == 0) - return new List() { new("You have no songs", "error") }; + /* + internal class PlaylistProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); + if (plls.Count == 0) + return new List() { new("You have no songs", "error") }; - var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); + var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - List> playlists = new(25); - if (ctx.FocusedOption.Value == null) - playlists.AddRange(DbPlaylists.Take(25)); - else - playlists.AddRange(DbPlaylists.Where(x => x.Value.Name.Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + List> playlists = new(25); + if (ctx.FocusedOption.Value == null) + playlists.AddRange(DbPlaylists.Take(25)); + else + playlists.AddRange(DbPlaylists.Where(x => x.Value.Name.Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); - } - } + return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); + } + } - internal class SongProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); + internal class SongProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); - switch (playlist) - { - case null: - return new List() { new("You have no playlist selected", "error") }; - case "error": - return new List() { new("You have no valid playlist selected", "error") }; - } + switch (playlist) + { + case null: + return new List() { new("You have no playlist selected", "error") }; + case "error": + return new List() { new("You have no valid playlist selected", "error") }; + } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var tracks = await pls.GetEntries(); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(tracks.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(tracks.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); + var tracks = await pls.GetEntries(); + List songs = new(25); + if (ctx.FocusedOption.Value == null) + songs.AddRange(tracks.Take(25)); + else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) + songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); + else + songs.AddRange(tracks.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); - } - } + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); + } + } - internal sealed class QueueProvider : IAutocompleteProvider - { - public async Task> Provider(AutocompleteContext ctx) - { - var queue = await Database.GetQueueAsync(ctx.Guild); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(queue.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(queue.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(queue.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + internal sealed class QueueProvider : IAutocompleteProvider + { + public async Task> Provider(AutocompleteContext ctx) + { + var queue = await Database.GetQueueAsync(ctx.Guild); + List songs = new(25); + if (ctx.FocusedOption.Value == null) + songs.AddRange(queue.Take(25)); + else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) + songs.AddRange(queue.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); + else + songs.AddRange(queue.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); - } - } - */ + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); + } + } + */ } diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs index a16d145a..30ec10af 100644 --- a/MikuSharp/Utilities/NND.cs +++ b/MikuSharp/Utilities/NND.cs @@ -14,46 +14,46 @@ namespace MikuSharp.Utilities; public static class Nnd { - public static async Task GetNndAsync(this InteractionContext ctx, string n, string s, ulong msgId) - { - try - { - NndClient nndClient = new(); - NicoVideoClient videoClient = new(nndClient); - var videoPage = await videoClient.GetWatchPageInfoAsync(s); - var downloadExe = "nnd.exe"; - var linuxExe = "nndownload.py"; - var cmd = downloadExe; - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); - if (OperatingSystem.IsLinux()) - cmd = linuxExe; - Process downloadProcess = new(); - downloadProcess.StartInfo.FileName = cmd; - downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; - downloadProcess.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); }; - downloadProcess.Start(); - await downloadProcess.WaitForExitAsync(); - var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; - var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); - Process convertProgress = new(); - convertProgress.StartInfo.FileName = "ffmpeg"; - convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; - convertProgress.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", f.Data); }; - convertProgress.Start(); - await convertProgress.WaitForExitAsync(); - File.Delete($@"{s}.mp4"); - MemoryStream ms = new(await File.ReadAllBytesAsync($@"{s}.mp3")); - File.Delete($@"{s}.mp3"); - ms.Position = 0; - return ms; - } - catch (Exception ex) - { - ctx.Client.Logger.LogDebug("{ex}", ex.Message); - ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); - return null; - } - } + public static async Task GetNndAsync(this InteractionContext ctx, string n, string s, ulong msgId) + { + try + { + NndClient nndClient = new(); + NicoVideoClient videoClient = new(nndClient); + var videoPage = await videoClient.GetWatchPageInfoAsync(s); + var downloadExe = "nnd.exe"; + var linuxExe = "nndownload.py"; + var cmd = downloadExe; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); + if (OperatingSystem.IsLinux()) + cmd = linuxExe; + Process downloadProcess = new(); + downloadProcess.StartInfo.FileName = cmd; + downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; + downloadProcess.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); }; + downloadProcess.Start(); + await downloadProcess.WaitForExitAsync(); + var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; + var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); + Process convertProgress = new(); + convertProgress.StartInfo.FileName = "ffmpeg"; + convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; + convertProgress.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", f.Data); }; + convertProgress.Start(); + await convertProgress.WaitForExitAsync(); + File.Delete($@"{s}.mp4"); + MemoryStream ms = new(await File.ReadAllBytesAsync($@"{s}.mp3")); + File.Delete($@"{s}.mp3"); + ms.Position = 0; + return ms; + } + catch (Exception ex) + { + ctx.Client.Logger.LogDebug("{ex}", ex.Message); + ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); + return null; + } + } } diff --git a/MikuSharp/Utilities/Old/Database.cs b/MikuSharp/Utilities/Old/Database.cs index 3103983e..478a322a 100644 --- a/MikuSharp/Utilities/Old/Database.cs +++ b/MikuSharp/Utilities/Old/Database.cs @@ -348,4 +348,6 @@ public static async Task> GetLastPlayingListAsync(DiscordGuild g) return queue; } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Utilities/Old/Music.cs b/MikuSharp/Utilities/Old/Music.cs index b465f2a3..c890095c 100644 --- a/MikuSharp/Utilities/Old/Music.cs +++ b/MikuSharp/Utilities/Old/Music.cs @@ -240,4 +240,6 @@ public static async Task SendPlayingInformationAsync(this InteractionContext ctx await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); } } -*/ \ No newline at end of file +*/ + + diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index e96d24b3..ca753169 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -1,21 +1,33 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; +using DisCatSharp.Lavalink; namespace MikuSharp.Utilities; public static class Other { - public static string ResizeLink(string url) - => $"https://api.meek.moe/im/?image={url}&resize=500"; + public static string ResizeLink(string url) + => $"https://api.meek.moe/im/?image={url}&resize=500"; - public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) - { - var builder = new DiscordInteractionResponseBuilder(); - if (ephemeral) - builder.AsEphemeral(); - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); - } + public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) + { + var builder = new DiscordInteractionResponseBuilder(); + if (ephemeral) + builder.AsEphemeral(); + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); + } + + /// + /// Gets the default session. + /// + /// The lavalink extension. + /// The first session or . + public static LavalinkSession? DefaultSession(this LavalinkExtension lavalink) + => lavalink.ConnectedSessions.Any() + ? lavalink.ConnectedSessions.First().Value + : null; } diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index 15a5a85d..f3d72f78 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -16,67 +16,67 @@ namespace MikuSharp.Utilities; public static class Web { - public static async Task GetNekosLifeAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekos.life"); - dl.Embed = em.Build(); - return dl; - } + public static async Task GetNekosLifeAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) + { + Position = 0 + }; + dl.Data = str; + dl.Filetype = MimeGuesser.GuessExtension(str); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by nekos.life"); + dl.Embed = em.Build(); + return dl; + } - public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) - { - client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); - var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); - MemoryStream img = new(await client.GetByteArrayAsync(Other.ResizeLink(v.Url))); - v.Data = img; - v.Filetype = MimeGuesser.GuessExtension(img); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{v.Filetype}"); - em.WithFooter("by KSoft.si"); - v.Embed = em.Build(); - return v; - } + public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) + { + client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); + var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); + MemoryStream img = new(await client.GetByteArrayAsync(Other.ResizeLink(v.Url))); + v.Data = img; + v.Filetype = MimeGuesser.GuessExtension(img); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{v.Filetype}"); + em.WithFooter("by KSoft.si"); + v.Embed = em.Build(); + return v; + } - public static async Task GetNekobotAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekobot.xyz"); - dl.Embed = em.Build(); - return dl; - } + public static async Task GetNekobotAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) + { + Position = 0 + }; + dl.Data = str; + dl.Filetype = MimeGuesser.GuessExtension(str); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by nekobot.xyz"); + dl.Embed = em.Build(); + return dl; + } - public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) - { - var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); - MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) - { - Position = 0 - }; - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by weeb.sh"); - return new() - { - ImgData = img, - Extension = MimeGuesser.GuessExtension(img), - Embed = em - }; - } + public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) + { + var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); + MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) + { + Position = 0 + }; + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by weeb.sh"); + return new() + { + ImgData = img, + Extension = MimeGuesser.GuessExtension(img), + Embed = em + }; + } } diff --git a/MikuSharp/config.example.json b/MikuSharp/config.example.json index e8af1858..4deb8114 100644 --- a/MikuSharp/config.example.json +++ b/MikuSharp/config.example.json @@ -24,4 +24,4 @@ "password": "" } } -} \ No newline at end of file +} From f1e255556dfbc653faae29859d71942596aeeffd Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 01:59:11 +0100 Subject: [PATCH 031/113] nom --- MikuSharp/Commands/Developer.cs | 6 +++--- MikuSharp/Events/MikuGuildJoin.cs | 12 +----------- MikuSharp/Utilities/Other.cs | 8 +++----- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index b9693867..18985a5e 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -25,7 +25,7 @@ namespace MikuSharp.Commands; [ApplicationCommandRequireTeamMember] public class Developer : ApplicationCommandsModule { - private static readonly string[] Units = ["", "ki", "Mi", "Gi"]; + private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; [SlashCommand("test", "Testing")] public static async Task TestAsync(InteractionContext ctx) @@ -205,13 +205,13 @@ private static string SizeToString(long l) double d = l; var u = 0; - while (d >= 900 && u < Units.Length - 2) + while (d >= 900 && u < s_units.Length - 2) { u++; d /= 1024; } - return $"{d:#,##0.00} {Units[u]}B"; + return $"{d:#,##0.00} {s_units[u]}B"; } } diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index 0e54d4e3..dd789001 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -10,16 +10,6 @@ namespace MikuSharp.Events; /// public class MikuGuild { - /// - /// Fired when a new guild member joins. - /// - /// The client. - /// The event args. - public static async Task OnJoinAsync(DiscordClient sender, GuildMemberAddEventArgs args) - { - await Task.FromResult(true); - } - /// /// Fired when a guild member is updated. /// @@ -30,7 +20,7 @@ public static async Task OnUpdateAsync(DiscordClient sender, GuildMemberUpdateEv if (args is { PendingBefore: true, PendingAfter: false }) { ulong memberRoleId = 483280207927574528; - var memberRole = args.Guild.GetRole(memberRoleId); + var memberRole = await args.Guild.GetRoleAsync(memberRoleId); await args.Member.GrantRoleAsync(memberRole); } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index ca753169..71f93156 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands.Context; @@ -26,8 +26,6 @@ public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral /// /// The lavalink extension. /// The first session or . - public static LavalinkSession? DefaultSession(this LavalinkExtension lavalink) - => lavalink.ConnectedSessions.Any() - ? lavalink.ConnectedSessions.First().Value - : null; + public static LavalinkSession DefaultSession(this LavalinkExtension lavalink) + => lavalink.ConnectedSessions.First().Value; } From f866524648568c2349033325b5d39d371962f3ab Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 01:59:20 +0100 Subject: [PATCH 032/113] meow --- MikuSharp/Commands/Music.cs | 27 ++++++++++-- MikuSharp/Entities/MusicSession.cs | 71 ++++++++++++++++++++++++++---- 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs index f66c7251..f6a66a1f 100644 --- a/MikuSharp/Commands/Music.cs +++ b/MikuSharp/Commands/Music.cs @@ -5,9 +5,11 @@ using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; +using DisCatSharp.Exceptions; using DisCatSharp.Lavalink; using MikuSharp.Attributes; +using MikuSharp.Entities; using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -15,7 +17,7 @@ namespace MikuSharp.Commands; /// /// The music commands /// -[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync, EnsureLavalinkSession] +[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] public class Music : ApplicationCommandsModule { /// @@ -25,18 +27,37 @@ public class Music : ApplicationCommandsModule [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] public static async Task JoinAsync(InteractionContext ctx) { + var session = ctx.Client.GetLavalink().DefaultSession(); await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); - MikuBot.MusicSessions.Add(ctx.GuildId.Value, new()); + MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); + MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); + var embed = new DiscordEmbedBuilder() + .WithAuthor(ctx.Client.CurrentUser.UsernameWithGlobalName, null, ctx.Client.CurrentUser.AvatarUrl) + .WithColor(DiscordColor.Black) + .WithTitle("Miku Music Status") + .WithDescription("Nothing playing yet") + .AddField(new("State", "Stopped")) + .AddField(new("Repeat Mode", musicSession.RepeatMode.ToString())) + .Build(); + musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(embed)); } /// /// Leaves a voice channel. /// /// The interaction context. - [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection, AutomaticallyDisconnectExistingSession] + [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection] public static async Task LeaveAsync(InteractionContext ctx) { + if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) + { + await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + await musicSession.LavalinkGuildPlayer.DisconnectAsync(); + await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); + } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); } } diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index ccca1ae1..9e99493e 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -4,18 +4,73 @@ namespace MikuSharp.Entities; -public sealed class MusicSession +/// +/// Represents a music session. +/// +/// The channel the music session is for. +/// The guild the music session is for. +/// The Lavalink session. +public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, LavalinkSession lavalinkSession) { - public DiscordChannel CurrentChannel { get; set; } + /// + /// Gets the current channel. + /// + public DiscordChannel CurrentChannel { get; } = channel; - public DiscordGuild CurrentGuild { get; set; } + /// + /// Gets the current guild. + /// + public DiscordGuild CurrentGuild { get; } = guild; - public LavalinkSession LavalinkSession { get; set; } + /// + /// Gets the Lavalink session. + /// + public LavalinkSession LavalinkSession { get; } = lavalinkSession; - public LavalinkGuildPlayer LavalinkGuildPlayer - => this.LavalinkSession.GetGuildPlayer(this.CurrentGuild); + /// + /// Gets the Lavalink guild player. + /// + public LavalinkGuildPlayer LavalinkGuildPlayer { get; internal set; } - public RepeatMode RepeatMode { get; set; } + /// + /// Gets the repeat mode. + /// + public RepeatMode RepeatMode { get; internal set; } - public DiscordMember CurrentDj { get; internal set; } + /// + /// Gets the status message. + /// + public DiscordMessage StatusMessage { get; internal set; } + + /// + /// Injects the player. + /// + /// The current music session. + public MusicSession InjectPlayer() + { + this.LavalinkGuildPlayer = this.LavalinkSession.GetGuildPlayer(this.CurrentGuild)!; + return this; + } + + /// + /// Updates the repeat mode. + /// + /// The new repeat mode. + /// The current music session. + public MusicSession UpdateRepeatMode(RepeatMode mode) + { + this.RepeatMode = mode; + return this; + } + + /// + /// Updates the status message. + /// + /// The new status message. + /// The current music session. + public MusicSession UpdateStatusMessage(DiscordMessage message) + { + this.StatusMessage = message; + return this; + } } From e3e2fef1467ce2f8b949a9b63589a2bfd190cbe7 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 02:28:17 +0100 Subject: [PATCH 033/113] nom --- MikuSharp/Attributes/CustomMikuAttributes.cs | 5 +- MikuSharp/Commands/Developer.cs | 6 +- MikuSharp/Commands/Music.cs | 63 -------- MikuSharp/Commands/MusicCommands.cs | 138 ++++++++++++++++++ MikuSharp/Entities/MusicSession.cs | 20 ++- MikuSharp/Enums/Playing.cs | 17 +-- MikuSharp/MikuBot.cs | 14 +- MikuSharp/Utilities/DiscordOptionProviders.cs | 33 +++-- MikuSharp/Utilities/Other.cs | 24 +++ 9 files changed, 211 insertions(+), 109 deletions(-) delete mode 100644 MikuSharp/Commands/Music.cs create mode 100644 MikuSharp/Commands/MusicCommands.cs diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index dd7ff1d9..57aaecef 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -102,10 +102,7 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) return await RespondWithNoSessionAvailableAsync(ctx); var session = module.DefaultSession(); - if (session is null || !session.IsConnected) - return await RespondWithNoSessionAvailableAsync(ctx); - - return true; + return session is null || !session.IsConnected ? await RespondWithNoSessionAvailableAsync(ctx) : true; } /// diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 18985a5e..14f86816 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -28,10 +28,8 @@ public class Developer : ApplicationCommandsModule private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - } + public static async Task TestAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); [SlashCommand("guild_shard_test", "Testing")] public static async Task GuildTestAsync(InteractionContext ctx) diff --git a/MikuSharp/Commands/Music.cs b/MikuSharp/Commands/Music.cs deleted file mode 100644 index f6a66a1f..00000000 --- a/MikuSharp/Commands/Music.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Exceptions; -using DisCatSharp.Lavalink; - -using MikuSharp.Attributes; -using MikuSharp.Entities; -using MikuSharp.Utilities; - -namespace MikuSharp.Commands; - -/// -/// The music commands -/// -[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] -public class Music : ApplicationCommandsModule -{ - /// - /// Joins a voice channel the user is in. - /// - /// The interaction context. - [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] - public static async Task JoinAsync(InteractionContext ctx) - { - var session = ctx.Client.GetLavalink().DefaultSession(); - await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); - MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); - MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); - var embed = new DiscordEmbedBuilder() - .WithAuthor(ctx.Client.CurrentUser.UsernameWithGlobalName, null, ctx.Client.CurrentUser.AvatarUrl) - .WithColor(DiscordColor.Black) - .WithTitle("Miku Music Status") - .WithDescription("Nothing playing yet") - .AddField(new("State", "Stopped")) - .AddField(new("Repeat Mode", musicSession.RepeatMode.ToString())) - .Build(); - musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(embed)); - } - - /// - /// Leaves a voice channel. - /// - /// The interaction context. - [SlashCommand("leave", "Leaves the channel"), RequireUserAndBotVoicechatConnection] - public static async Task LeaveAsync(InteractionContext ctx) - { - if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) - { - await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); - await musicSession.LavalinkGuildPlayer.DisconnectAsync(); - await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } -} diff --git a/MikuSharp/Commands/MusicCommands.cs b/MikuSharp/Commands/MusicCommands.cs new file mode 100644 index 00000000..27729262 --- /dev/null +++ b/MikuSharp/Commands/MusicCommands.cs @@ -0,0 +1,138 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; +using DisCatSharp.Enums; +using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Enums; + +using MikuSharp.Attributes; +using MikuSharp.Entities; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +/// +/// The music commands +/// +[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] +public class MusicCommands : ApplicationCommandsModule +{ + /// + /// Joins a voice channel the user is in. + /// + /// The interaction context. + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] + public static async Task JoinAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); + ArgumentNullException.ThrowIfNull(ctx.Guild); + ArgumentNullException.ThrowIfNull(ctx.GuildId); + + var session = ctx.Client.GetLavalink().DefaultSession(); + await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); + MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); + MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); + musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, "Nothing playing yet"))); + } + + /// + /// Leaves a voice channel. + /// + /// The interaction context. + [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection] + public static async Task LeaveAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + + if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) + { + await musicSession.LavalinkGuildPlayer.DisconnectAsync(); + await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); + await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + } + + /// + /// The playback commands. + /// + [SlashCommandGroup("playback", "Music playback commands"), RequireUserAndBotVoicechatConnection] + public class PlaybackCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } + + /// + /// The options commands. + /// + [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection] + public class OptionsCommands : ApplicationCommandsModule + { + [SlashCommand("repeat", "Repeat the current song or the entire queue")] + public static async Task RepeatAsync( + InteractionContext ctx, + [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] + RepeatMode mode + ) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + musicSession.UpdateRepeatMode(mode); + musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, musicSession.StatusMessage.Embeds.First().Description))); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); + } + + [SlashCommand("shuffle", "Shuffle the queue")] + public static async Task ShuffleAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + musicSession.LavalinkGuildPlayer.ShuffleQueue(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); + } + } + + /// + /// The queue commands. + /// + [SlashCommandGroup("queue", "Music queue commands"), RequireUserAndBotVoicechatConnection] + public class QueueCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } + + /// + /// The info commands. + /// + [SlashCommandGroup("info", "Music info commands"), RequireUserAndBotVoicechatConnection] + public class InfoCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } +} diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index 9e99493e..3c2154b7 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -2,6 +2,8 @@ using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Enums; +using MikuSharp.Enums; + namespace MikuSharp.Entities; /// @@ -35,13 +37,18 @@ public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, Lav /// /// Gets the repeat mode. /// - public RepeatMode RepeatMode { get; internal set; } + public RepeatMode RepeatMode { get; internal set; } = RepeatMode.None; /// /// Gets the status message. /// public DiscordMessage StatusMessage { get; internal set; } + /// + /// Gets the play state. + /// + public PlayState PlayState { get; internal set; } = PlayState.Stopped; + /// /// Injects the player. /// @@ -73,4 +80,15 @@ public MusicSession UpdateStatusMessage(DiscordMessage message) this.StatusMessage = message; return this; } + + /// + /// Updates the play state. + /// + /// The new play state. + /// The current music session. + public MusicSession UpdatePlayState(PlayState state) + { + this.PlayState = state; + return this; + } } diff --git a/MikuSharp/Enums/Playing.cs b/MikuSharp/Enums/Playing.cs index 25e791e4..fe2f7caf 100644 --- a/MikuSharp/Enums/Playing.cs +++ b/MikuSharp/Enums/Playing.cs @@ -1,22 +1,9 @@ -namespace MikuSharp.Enums; +namespace MikuSharp.Enums; -public enum Playstate +public enum PlayState { NotPlaying = 0, Playing = 1, Paused = 2, Stopped = 3 } - -public enum RepeatMode -{ - Off = 0, - On = 1, - All = 2 -} - -public enum ShuffleMode -{ - Off = 0, - On = 1 -} diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index b07a964c..498a095e 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -57,9 +57,9 @@ internal MikuBot() { var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); - Config = JsonConvert.DeserializeObject(fileData); - if (Config == null) - throw new ArgumentNullException(null, "config.json is null"); + var config = JsonConvert.DeserializeObject(fileData); + ArgumentNullException.ThrowIfNull(config); + Config = config; Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; Cts = new(); @@ -178,7 +178,9 @@ internal MikuBot() public void Dispose() { +#pragma warning disable IDE0022 // Use expression body for method GC.SuppressFinalize(this); +#pragma warning restore IDE0022 // Use expression body for method } internal static async Task RegisterEvents() @@ -277,14 +279,14 @@ internal static async Task SetActivity() { DiscordActivity test = new() { - Name = "I'm using slash commands now!", + Name = "New music system coming up soon!", ActivityType = ActivityType.Playing }; await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); DiscordActivity test2 = new() { - Name = "Mention me with help for nsfw commands!", + Name = "Mention me with help for other commands!", ActivityType = ActivityType.Playing }; await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); @@ -309,7 +311,7 @@ internal void RegisterCommands() this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); //ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index f0ee2a98..8a1aeefd 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -6,25 +6,26 @@ using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; +using DisCatSharp.Lavalink.Enums; namespace MikuSharp.Utilities; -/* + internal class FixedOptionProviders { - internal sealed class RepeatModeProvider : ChoiceProvider - { - public override Task> Provider() - { - var list = new List(3) - { - new("Off", $"{(int)RepeatMode.Off}"), - new("On", $"{(int)RepeatMode.On}"), - new("All", $"{(int)RepeatMode.All}") - }; - return Task.FromResult>(list); - } - } -}*/ + internal sealed class RepeatModeProvider : ChoiceProvider + { + public override Task> Provider() + { + var list = new List(3) + { + new("None", $"{(int)RepeatMode.None}"), + new("All", $"{(int)RepeatMode.All}"), + new("Current", $"{(int)RepeatMode.Current}") + }; + return Task.FromResult>(list); + } + } +} internal class AutocompleteProviders { diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 71f93156..05d7a474 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -6,6 +8,8 @@ using DisCatSharp.Enums; using DisCatSharp.Lavalink; +using MikuSharp.Entities; + namespace MikuSharp.Utilities; public static class Other @@ -28,4 +32,24 @@ public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral /// The first session or . public static LavalinkSession DefaultSession(this LavalinkExtension lavalink) => lavalink.ConnectedSessions.First().Value; + + public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, MusicSession session, string description, List? additionalEmbedFields = null) + { + var builder = new DiscordEmbedBuilder() + .WithAuthor(ctx.Client.CurrentUser.UsernameWithGlobalName, iconUrl: ctx.Client.CurrentUser.AvatarUrl) + .WithColor(DiscordColor.Black) + .WithTitle("Miku Music Status") + .WithDescription(description); + + builder.AddField(new("State", session.PlayState.ToString())); + builder.AddField(new("Repeat Mode", session.RepeatMode.ToString())); + + if (additionalEmbedFields is null) + return builder.Build(); + + ArgumentOutOfRangeException.ThrowIfGreaterThan(additionalEmbedFields.Count, 23, nameof(additionalEmbedFields)); + builder.AddFields(additionalEmbedFields); + + return builder.Build(); + } } From 19b3f6ffaa418af1eef120158e63aa2161298aec Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 02:29:59 +0100 Subject: [PATCH 034/113] nom --- MikuSharp/Commands/MusicCommands.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MikuSharp/Commands/MusicCommands.cs b/MikuSharp/Commands/MusicCommands.cs index 27729262..d58a6302 100644 --- a/MikuSharp/Commands/MusicCommands.cs +++ b/MikuSharp/Commands/MusicCommands.cs @@ -92,6 +92,7 @@ RepeatMode mode ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; musicSession.UpdateRepeatMode(mode); + await musicSession.StatusMessage.DeleteAsync("Updating miku status"); musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, musicSession.StatusMessage.Embeds.First().Description))); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); } From 24c29622ce60a55ff83ba46ede2c17c7de87effd Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 07:37:07 +0100 Subject: [PATCH 035/113] nom --- .../Music/MusicCommands.InfoCommands.cs | 27 ++++ .../Music/MusicCommands.OptionsCommands.cs | 48 ++++++ .../Music/MusicCommands.PlaybackCommands.cs | 27 ++++ .../Music/MusicCommands.QueueCommands.cs | 27 ++++ MikuSharp/Commands/Music/MusicCommands.cs | 61 ++++++++ MikuSharp/Commands/MusicCommands.cs | 139 ------------------ .../PlaylistCommands.ManageCommands.cs | 22 +++ .../Playlist/PlaylistCommands.SongCommands.cs | 22 +++ .../Commands/Playlist/PlaylistCommands.cs | 25 ++++ MikuSharp/MikuBot.cs | 4 +- 10 files changed, 262 insertions(+), 140 deletions(-) create mode 100644 MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs create mode 100644 MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs create mode 100644 MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs create mode 100644 MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs create mode 100644 MikuSharp/Commands/Music/MusicCommands.cs delete mode 100644 MikuSharp/Commands/MusicCommands.cs create mode 100644 MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs create mode 100644 MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs create mode 100644 MikuSharp/Commands/Playlist/PlaylistCommands.cs diff --git a/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs new file mode 100644 index 00000000..1923b58f --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; + +using MikuSharp.Attributes; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The info commands. + /// + [SlashCommandGroup("info", "Music info commands"), RequireUserAndBotVoicechatConnection] + public class InfoCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs new file mode 100644 index 00000000..15a5c0cf --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; +using DisCatSharp.Lavalink.Enums; + +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The options commands. + /// + [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection] + public class OptionsCommands : ApplicationCommandsModule + { + [SlashCommand("repeat", "Repeat the current song or the entire queue")] + public static async Task RepeatAsync( + InteractionContext ctx, + [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] + RepeatMode mode + ) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + musicSession.UpdateRepeatMode(mode); + await musicSession.StatusMessage.DeleteAsync("Updating miku status"); + musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, musicSession.StatusMessage.Embeds.First().Description))); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); + } + + [SlashCommand("shuffle", "Shuffle the queue")] + public static async Task ShuffleAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + musicSession.LavalinkGuildPlayer.ShuffleQueue(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); + } + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs new file mode 100644 index 00000000..6c0d9e6c --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; + +using MikuSharp.Attributes; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The playback commands. + /// + [SlashCommandGroup("playback", "Music playback commands"), RequireUserAndBotVoicechatConnection] + public class PlaybackCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs new file mode 100644 index 00000000..3fbd0839 --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; + +using MikuSharp.Attributes; + +namespace MikuSharp.Commands.Music; + +public partial class MusicCommands +{ + /// + /// The queue commands. + /// + [SlashCommandGroup("queue", "Music queue commands"), RequireUserAndBotVoicechatConnection] + public class QueueCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs new file mode 100644 index 00000000..2589009a --- /dev/null +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; +using DisCatSharp.Enums; +using DisCatSharp.Lavalink; + +using MikuSharp.Attributes; +using MikuSharp.Entities; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Music; + +/// +/// The music commands +/// +[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] +public partial class MusicCommands : ApplicationCommandsModule +{ + /// + /// Joins a voice channel the user is in. + /// + /// The interaction context. + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] + public static async Task JoinAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); + ArgumentNullException.ThrowIfNull(ctx.Guild); + ArgumentNullException.ThrowIfNull(ctx.GuildId); + + var session = ctx.Client.GetLavalink().DefaultSession(); + await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); + MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); + MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); + musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, "Nothing playing yet"))); + } + + /// + /// Leaves a voice channel. + /// + /// The interaction context. + [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection] + public static async Task LeaveAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + + if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) + { + await musicSession.LavalinkGuildPlayer.DisconnectAsync(); + await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); + await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + } +} diff --git a/MikuSharp/Commands/MusicCommands.cs b/MikuSharp/Commands/MusicCommands.cs deleted file mode 100644 index d58a6302..00000000 --- a/MikuSharp/Commands/MusicCommands.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Lavalink; -using DisCatSharp.Lavalink.Enums; - -using MikuSharp.Attributes; -using MikuSharp.Entities; -using MikuSharp.Utilities; - -namespace MikuSharp.Commands; - -/// -/// The music commands -/// -[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] -public class MusicCommands : ApplicationCommandsModule -{ - /// - /// Joins a voice channel the user is in. - /// - /// The interaction context. - [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] - public static async Task JoinAsync(InteractionContext ctx) - { - ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); - ArgumentNullException.ThrowIfNull(ctx.Guild); - ArgumentNullException.ThrowIfNull(ctx.GuildId); - - var session = ctx.Client.GetLavalink().DefaultSession(); - await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); - MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); - MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); - musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, "Nothing playing yet"))); - } - - /// - /// Leaves a voice channel. - /// - /// The interaction context. - [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection] - public static async Task LeaveAsync(InteractionContext ctx) - { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - - if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) - { - await musicSession.LavalinkGuildPlayer.DisconnectAsync(); - await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); - await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } - - /// - /// The playback commands. - /// - [SlashCommandGroup("playback", "Music playback commands"), RequireUserAndBotVoicechatConnection] - public class PlaybackCommands : ApplicationCommandsModule - { - /// - /// A dummy command. - /// - /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); - } - - /// - /// The options commands. - /// - [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection] - public class OptionsCommands : ApplicationCommandsModule - { - [SlashCommand("repeat", "Repeat the current song or the entire queue")] - public static async Task RepeatAsync( - InteractionContext ctx, - [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] - RepeatMode mode - ) - { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession.UpdateRepeatMode(mode); - await musicSession.StatusMessage.DeleteAsync("Updating miku status"); - musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, musicSession.StatusMessage.Embeds.First().Description))); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); - } - - [SlashCommand("shuffle", "Shuffle the queue")] - public static async Task ShuffleAsync(InteractionContext ctx) - { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession.LavalinkGuildPlayer.ShuffleQueue(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); - } - } - - /// - /// The queue commands. - /// - [SlashCommandGroup("queue", "Music queue commands"), RequireUserAndBotVoicechatConnection] - public class QueueCommands : ApplicationCommandsModule - { - /// - /// A dummy command. - /// - /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); - } - - /// - /// The info commands. - /// - [SlashCommandGroup("info", "Music info commands"), RequireUserAndBotVoicechatConnection] - public class InfoCommands : ApplicationCommandsModule - { - /// - /// A dummy command. - /// - /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); - } -} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs new file mode 100644 index 00000000..c20c380f --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; + +namespace MikuSharp.Commands.Playlist; + +public partial class PlaylistCommands +{ + [SlashCommandGroup("manage", "Playlist management")] + public class ManageCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } +} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs new file mode 100644 index 00000000..cc8282c1 --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; + +namespace MikuSharp.Commands.Playlist; + +public partial class PlaylistCommands +{ + [SlashCommandGroup("song", "Song management")] + public class SongCommands : ApplicationCommandsModule + { + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + } +} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.cs new file mode 100644 index 00000000..e3babaa6 --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.cs @@ -0,0 +1,25 @@ +using System.Threading.Tasks; + +using DisCatSharp.ApplicationCommands; +using DisCatSharp.ApplicationCommands.Attributes; +using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Enums; + +using MikuSharp.Attributes; + +namespace MikuSharp.Commands.Playlist; + +/// +/// The playlist commands +/// +[SlashCommandGroup("playlist", "Playlist commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] +public partial class PlaylistCommands : ApplicationCommandsModule +{ + /// + /// A dummy command. + /// + /// The interaction context. + [SlashCommand("dummy", "Dummy command")] + public async Task DummyCommand(InteractionContext ctx) + => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); +} diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 498a095e..472a7d39 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -23,6 +23,7 @@ using MikuSharp.Attributes; using MikuSharp.Commands; +using MikuSharp.Commands.Music; using MikuSharp.Entities; using Newtonsoft.Json; @@ -34,6 +35,7 @@ using Action = MikuSharp.Commands.Action; using MikuGuild = MikuSharp.Events.MikuGuild; +using PlaylistCommands = MikuSharp.Commands.Playlist.PlaylistCommands; using TokenType = DisCatSharp.Enums.TokenType; namespace MikuSharp; @@ -312,7 +314,7 @@ internal void RegisterCommands() this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); - //ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); From 2f9f945e97b335131885cb4630b240e36ec381c1 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 08:36:47 +0100 Subject: [PATCH 036/113] aaaaaaaaaaaaaaa --- .../Music/MusicCommands.OptionsCommands.cs | 5 +- .../Music/MusicCommands.PlaybackCommands.cs | 73 +++++++++++++++++-- MikuSharp/Commands/Music/MusicCommands.cs | 2 +- MikuSharp/Entities/MusicSession.cs | 9 +++ MikuSharp/Utilities/Other.cs | 30 ++++++++ 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 15a5c0cf..96d44d42 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; @@ -31,8 +31,7 @@ RepeatMode mode ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; musicSession.UpdateRepeatMode(mode); - await musicSession.StatusMessage.DeleteAsync("Updating miku status"); - musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, musicSession.StatusMessage.Embeds.First().Description))); + await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); } diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 6c0d9e6c..cc8d8c16 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -1,10 +1,14 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; +using DisCatSharp.Entities; using MikuSharp.Attributes; +using MikuSharp.Enums; +using MikuSharp.Utilities; namespace MikuSharp.Commands.Music; @@ -17,11 +21,70 @@ public partial class MusicCommands public class PlaybackCommands : ApplicationCommandsModule { /// - /// A dummy command. + /// Pauses the playback. /// /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + [SlashCommand("pause", "Pauses the playback")] + public async Task PauseAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + await musicSession.LavalinkGuildPlayer.PauseAsync(); + musicSession.UpdatePlayState(PlayState.Paused); + await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + await ctx.EditResponseAsync("Paused the playback! "); + } + + /// + /// Resumes the playback. + /// + /// The interaction context. + [SlashCommand("resume", "Resumes the playback")] + public async Task ResumeAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession.UpdatePlayState(PlayState.Playing); + await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + await ctx.EditResponseAsync("Resumed the playback!"); + } + + [SlashCommand("stop", "Stop Playback"), RequireUserAndBotVoicechatConnection] + public static async Task StopAsync(InteractionContext ctx) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + await musicSession.LavalinkGuildPlayer.StopAsync(); + musicSession.LavalinkGuildPlayer.ClearQueue(); + musicSession.UpdatePlayState(PlayState.Stopped); + await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + await ctx.EditResponseAsync("Stopped the playback!"); + } + + [SlashCommand("volume", "Change the music volume"), RequireUserAndBotVoicechatConnection] + public static async Task ModifyVolumeAsync( + InteractionContext ctx, + [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] + int vol = 100 + ) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + await musicSession.LavalinkGuildPlayer.SetVolumeAsync(vol); + await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{vol}%**!")); + } + + [SlashCommand("seek", "Seek a song"), RequireUserAndBotVoicechatConnection] + public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + var targetSeek = TimeSpan.FromSeconds(position); + await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); + } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 2589009a..da5e3f4f 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index 3c2154b7..84cf45fe 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -1,3 +1,5 @@ +using System.Threading.Tasks; + using DisCatSharp.Entities; using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Enums; @@ -91,4 +93,11 @@ public MusicSession UpdatePlayState(PlayState state) this.PlayState = state; return this; } + + public async Task UpdateStatusMessageAsync(DiscordEmbed embed) + { + await this.StatusMessage.DeleteAsync("Updating miku status"); + this.StatusMessage = await this.CurrentChannel.SendMessageAsync(embed); + return this; + } } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 05d7a474..4ef552eb 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -33,6 +33,14 @@ public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral public static LavalinkSession DefaultSession(this LavalinkExtension lavalink) => lavalink.ConnectedSessions.First().Value; + /// + /// Builds a music status embed. + /// + /// The interaction context. + /// The music session. + /// The description. + /// The additional embed fields. + /// The built embed. public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, MusicSession session, string description, List? additionalEmbedFields = null) { var builder = new DiscordEmbedBuilder() @@ -52,4 +60,26 @@ public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, Mu return builder.Build(); } + + /// + /// Builds a music status embed. + /// + /// The interaction context. + /// The music session. + /// The additional embed fields. + /// The built embed. + public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, MusicSession session, List? additionalEmbedFields = null) + => BuildMusicStatusEmbed(ctx, session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields); + + /// + /// Formats a into a human-readable string. + /// + /// The time span to format. + /// The formatted time span. + public static string FormatTimeSpan(this TimeSpan timeSpan) + => timeSpan.TotalHours >= 1 + ? $"{(int)timeSpan.TotalHours:D2}:{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}" + : timeSpan.TotalMinutes >= 1 + ? $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}" + : $"{timeSpan.Seconds:D2} sec"; } From c30525d3a1660610b7a407057d26ab91e356b625 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 09:56:59 +0100 Subject: [PATCH 037/113] nom --- MikuSharp/Attributes/CustomMikuAttributes.cs | 28 +++++++++- .../Music/MusicCommands.OptionsCommands.cs | 3 +- .../Music/MusicCommands.PlaybackCommands.cs | 56 ++++++++++++++----- .../Music/MusicCommands.QueueCommands.cs | 2 +- MikuSharp/Commands/Music/MusicCommands.cs | 2 +- .../PlaylistCommands.ManageCommands.cs | 2 +- .../Playlist/PlaylistCommands.SongCommands.cs | 2 +- .../Commands/Playlist/PlaylistCommands.cs | 4 +- MikuSharp/Entities/MusicQueueEntry.cs | 36 +++++------- MikuSharp/Entities/MusicSession.cs | 6 +- MikuSharp/Enums/Playing.cs | 7 +-- MikuSharp/Utilities/Other.cs | 12 ++-- 12 files changed, 99 insertions(+), 61 deletions(-) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 57aaecef..59899c9f 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands.Attributes; @@ -9,6 +10,7 @@ using DisCatSharp.Enums; using DisCatSharp.Lavalink; +using MikuSharp.Enums; using MikuSharp.Utilities; namespace MikuSharp.Attributes; @@ -129,9 +131,31 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) if (!MikuBot.MusicSessions.ContainsKey(ctx.GuildId!.Value)) return true; - await ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild).DisconnectAsync(); + var player = ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild); + if (player is not null) + await player.DisconnectAsync(); MikuBot.MusicSessions.Remove(ctx.GuildId.Value); return true; } } + +/// +/// Defines that the method or class requires specific playback state(s). +/// +/// The target playback states. +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] +public sealed class RequirePlaybackState(params PlaybackState[] targetStates) : ApplicationCommandCheckBaseAttribute +{ + /// + /// Gets the target playback state. + /// + public List TargetStates { get; } = [..targetStates]; + + /// + public override Task ExecuteChecksAsync(BaseContext ctx) + { + var musicSession = MikuBot.MusicSessions[ctx.GuildId!.Value]; + return Task.FromResult(this.TargetStates.Contains(musicSession.PlaybackState)); + } +} diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 96d44d42..806047f4 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; @@ -31,7 +30,7 @@ RepeatMode mode ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; musicSession.UpdateRepeatMode(mode); - await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); } diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index cc8d8c16..d4656941 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -1,10 +1,14 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; +using DisCatSharp.Lavalink.Entities; +using DisCatSharp.Lavalink.Enums; using MikuSharp.Attributes; using MikuSharp.Enums; @@ -24,14 +28,14 @@ public class PlaybackCommands : ApplicationCommandsModule /// Pauses the playback. /// /// The interaction context. - [SlashCommand("pause", "Pauses the playback")] + [SlashCommand("pause", "Pauses the playback"), RequirePlaybackState(PlaybackState.Playing)] public async Task PauseAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; await musicSession.LavalinkGuildPlayer.PauseAsync(); - musicSession.UpdatePlayState(PlayState.Paused); - await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + musicSession.UpdatePlaybackState(PlaybackState.Paused); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync("Paused the playback! "); } @@ -39,44 +43,44 @@ public async Task PauseAsync(InteractionContext ctx) /// Resumes the playback. /// /// The interaction context. - [SlashCommand("resume", "Resumes the playback")] + [SlashCommand("resume", "Resumes the playback"), RequirePlaybackState(PlaybackState.Paused)] public async Task ResumeAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; await musicSession.LavalinkGuildPlayer.ResumeAsync(); - musicSession.UpdatePlayState(PlayState.Playing); - await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync("Resumed the playback!"); } - [SlashCommand("stop", "Stop Playback"), RequireUserAndBotVoicechatConnection] + [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task StopAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; await musicSession.LavalinkGuildPlayer.StopAsync(); musicSession.LavalinkGuildPlayer.ClearQueue(); - musicSession.UpdatePlayState(PlayState.Stopped); - await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync("Stopped the playback!"); } - [SlashCommand("volume", "Change the music volume"), RequireUserAndBotVoicechatConnection] + [SlashCommand("volume", "Change the music volume")] public static async Task ModifyVolumeAsync( InteractionContext ctx, [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] - int vol = 100 + int volume = 100 ) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - await musicSession.LavalinkGuildPlayer.SetVolumeAsync(vol); - await musicSession.UpdateStatusMessageAsync(ctx.BuildMusicStatusEmbed(musicSession)); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{vol}%**!")); + await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); } - [SlashCommand("seek", "Seek a song"), RequireUserAndBotVoicechatConnection] + [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) { ArgumentNullException.ThrowIfNull(ctx.GuildId); @@ -86,5 +90,27 @@ public static async Task SeekAsync(InteractionContext ctx, [Option("position", " await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); } + + [SlashCommand("play", "Plays a url")] + public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + var result = await musicSession.LavalinkGuildPlayer.LoadTracksAsync(url); + var track = result.LoadType is LavalinkLoadResultType.Search + ? result.GetResultAs>().First() + : result.LoadType is LavalinkLoadResultType.Track + ? result.GetResultAs() + : throw new ArgumentOutOfRangeException(); + musicSession.LavalinkGuildPlayer.AddToQueue(track); + if (musicSession.PlaybackState is PlaybackState.Stopped) + { + musicSession.LavalinkGuildPlayer.PlayQueue(); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Playing **{url}**!")); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added **{url}** to the queue!")); + } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index 3fbd0839..eebe1dce 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index da5e3f4f..6b00eb26 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -37,7 +37,7 @@ public static async Task JoinAsync(InteractionContext ctx) MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); - musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(ctx.BuildMusicStatusEmbed(musicSession, "Nothing playing yet"))); + musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet"))); } /// diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs index c20c380f..8f3e2c49 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs index cc8282c1..3e688be7 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.cs index e3babaa6..63bc654d 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; @@ -12,7 +12,7 @@ namespace MikuSharp.Commands.Playlist; /// /// The playlist commands /// -[SlashCommandGroup("playlist", "Playlist commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] +[SlashCommandGroup("playlist", "Playlist commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), EnsureLavalinkSession] public partial class PlaylistCommands : ApplicationCommandsModule { /// diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index 416ebbfb..d30af986 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -1,37 +1,29 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Entities; +using MikuSharp.Utilities; + namespace MikuSharp.Entities; internal sealed class MusicQueueEntry : IQueueEntry { - public ulong UserId { get; internal set; } - - public ulong GuildId { get; internal set; } - /// - public Task BeforePlayingAsync(LavalinkGuildPlayer player) - => Task.FromResult(true); + public async Task BeforePlayingAsync(LavalinkGuildPlayer player) + { + var musicSession = MikuBot.MusicSessions[player.GuildId]; + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title} from {this.Track.Info.Author}")); + return true; + } /// - public Task AfterPlayingAsync(LavalinkGuildPlayer player) - => Task.FromResult(true); + public async Task AfterPlayingAsync(LavalinkGuildPlayer player) + { + var musicSession = MikuBot.MusicSessions[player.GuildId]; + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + } /// public LavalinkTrack Track { get; set; } - - /// - public IQueueEntry AddTrack(LavalinkTrack track) - { - this.Track = track; - dynamic? userData = track.UserData; - if (userData is null) - return this; - - this.UserId = userData.UserId; - this.GuildId = userData.GuildId; - return this; - } } diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index 84cf45fe..3b8bd12d 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -49,7 +49,7 @@ public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, Lav /// /// Gets the play state. /// - public PlayState PlayState { get; internal set; } = PlayState.Stopped; + public PlaybackState PlaybackState { get; internal set; } = PlaybackState.Stopped; /// /// Injects the player. @@ -88,9 +88,9 @@ public MusicSession UpdateStatusMessage(DiscordMessage message) /// /// The new play state. /// The current music session. - public MusicSession UpdatePlayState(PlayState state) + public MusicSession UpdatePlaybackState(PlaybackState state) { - this.PlayState = state; + this.PlaybackState = state; return this; } diff --git a/MikuSharp/Enums/Playing.cs b/MikuSharp/Enums/Playing.cs index fe2f7caf..1ca45bc3 100644 --- a/MikuSharp/Enums/Playing.cs +++ b/MikuSharp/Enums/Playing.cs @@ -1,9 +1,8 @@ namespace MikuSharp.Enums; -public enum PlayState +public enum PlaybackState { - NotPlaying = 0, + Stopped = 0, Playing = 1, - Paused = 2, - Stopped = 3 + Paused = 2 } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 4ef552eb..60787d5e 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -36,20 +36,19 @@ public static LavalinkSession DefaultSession(this LavalinkExtension lavalink) /// /// Builds a music status embed. /// - /// The interaction context. /// The music session. /// The description. /// The additional embed fields. /// The built embed. - public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, MusicSession session, string description, List? additionalEmbedFields = null) + public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, string description, List? additionalEmbedFields = null) { var builder = new DiscordEmbedBuilder() - .WithAuthor(ctx.Client.CurrentUser.UsernameWithGlobalName, iconUrl: ctx.Client.CurrentUser.AvatarUrl) + .WithAuthor(MikuBot.ShardedClient.CurrentUser.UsernameWithGlobalName, iconUrl: MikuBot.ShardedClient.CurrentUser.AvatarUrl) .WithColor(DiscordColor.Black) .WithTitle("Miku Music Status") .WithDescription(description); - builder.AddField(new("State", session.PlayState.ToString())); + builder.AddField(new("State", session.PlaybackState.ToString())); builder.AddField(new("Repeat Mode", session.RepeatMode.ToString())); if (additionalEmbedFields is null) @@ -64,12 +63,11 @@ public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, Mu /// /// Builds a music status embed. /// - /// The interaction context. /// The music session. /// The additional embed fields. /// The built embed. - public static DiscordEmbed BuildMusicStatusEmbed(this InteractionContext ctx, MusicSession session, List? additionalEmbedFields = null) - => BuildMusicStatusEmbed(ctx, session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields); + public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List? additionalEmbedFields = null) + => BuildMusicStatusEmbed(session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields); /// /// Formats a into a human-readable string. From 42d537ff5e14ce22bdcd40453bb7e5350a505bfd Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 3 Feb 2025 15:17:13 +0100 Subject: [PATCH 038/113] i'm slowly loosing my miiiiiiiiiiiiind --- .../Music/MusicCommands.OptionsCommands.cs | 4 +- .../Music/MusicCommands.PlaybackCommands.cs | 53 +++++++++------- MikuSharp/Commands/Music/MusicCommands.cs | 11 ++-- MikuSharp/Entities/MusicQueueEntry.cs | 16 +++-- MikuSharp/Entities/MusicSession.cs | 41 +++++++++--- MikuSharp/Utilities/Other.cs | 63 ++++++++++++++++++- 6 files changed, 145 insertions(+), 43 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 806047f4..b005e3ac 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -29,9 +29,10 @@ RepeatMode mode { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession.UpdateRepeatMode(mode); + musicSession = musicSession.UpdateRepeatMode(mode); await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } [SlashCommand("shuffle", "Shuffle the queue")] @@ -41,6 +42,7 @@ public static async Task ShuffleAsync(InteractionContext ctx) var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; musicSession.LavalinkGuildPlayer.ShuffleQueue(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index d4656941..0e21d250 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using DisCatSharp.ApplicationCommands; using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; -using DisCatSharp.Lavalink.Entities; using DisCatSharp.Lavalink.Enums; using MikuSharp.Attributes; @@ -34,9 +31,10 @@ public async Task PauseAsync(InteractionContext ctx) ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; await musicSession.LavalinkGuildPlayer.PauseAsync(); - musicSession.UpdatePlaybackState(PlaybackState.Paused); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + musicSession = musicSession.UpdatePlaybackState(PlaybackState.Paused); + musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync("Paused the playback! "); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } /// @@ -49,9 +47,10 @@ public async Task ResumeAsync(InteractionContext ctx) ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; await musicSession.LavalinkGuildPlayer.ResumeAsync(); - musicSession.UpdatePlaybackState(PlaybackState.Playing); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + musicSession = musicSession.UpdatePlaybackState(PlaybackState.Playing); + musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync("Resumed the playback!"); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] @@ -59,11 +58,13 @@ public static async Task StopAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - await musicSession.LavalinkGuildPlayer.StopAsync(); + musicSession = musicSession.UpdateRepeatMode(RepeatMode.None); musicSession.LavalinkGuildPlayer.ClearQueue(); - musicSession.UpdatePlaybackState(PlaybackState.Stopped); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await musicSession.LavalinkGuildPlayer.StopAsync(); + musicSession = musicSession.UpdatePlaybackState(PlaybackState.Stopped); + musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync("Stopped the playback!"); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } [SlashCommand("volume", "Change the music volume")] @@ -76,8 +77,9 @@ public static async Task ModifyVolumeAsync( ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] @@ -89,28 +91,33 @@ public static async Task SeekAsync(InteractionContext ctx, [Option("position", " var targetSeek = TimeSpan.FromSeconds(position); await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } [SlashCommand("play", "Plays a url")] public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; + musicSession = await musicSession.LoadAndPlayTrackAsync(ctx, url); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + } + + [SlashCommand("skip", "Skips to the next song")] + public async Task SkipAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - var result = await musicSession.LavalinkGuildPlayer.LoadTracksAsync(url); - var track = result.LoadType is LavalinkLoadResultType.Search - ? result.GetResultAs>().First() - : result.LoadType is LavalinkLoadResultType.Track - ? result.GetResultAs() - : throw new ArgumentOutOfRangeException(); - musicSession.LavalinkGuildPlayer.AddToQueue(track); - if (musicSession.PlaybackState is PlaybackState.Stopped) + if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) { - musicSession.LavalinkGuildPlayer.PlayQueue(); - musicSession.UpdatePlaybackState(PlaybackState.Playing); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Playing **{url}**!")); + await musicSession.LavalinkGuildPlayer.SkipAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); } else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added **{url}** to the queue!")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 6b00eb26..47508124 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -34,10 +34,11 @@ public static async Task JoinAsync(InteractionContext ctx) var session = ctx.Client.GetLavalink().DefaultSession(); await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); - MikuBot.MusicSessions.Add(ctx.GuildId.Value, musicSession.InjectPlayer()); + MikuBot.MusicSessions.Add(ctx.GuildId.Value, await musicSession.InjectPlayerAsync()); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); - musicSession.UpdateStatusMessage(await musicSession.CurrentChannel.SendMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet"))); + musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); + MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } /// @@ -51,9 +52,11 @@ public static async Task LeaveAsync(InteractionContext ctx) if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) { - await musicSession.LavalinkGuildPlayer.DisconnectAsync(); + if (musicSession.LavalinkGuildPlayer is not null) + await musicSession.LavalinkGuildPlayer.DisconnectAsync(); await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); - await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + if (musicSession.StatusMessage is not null) + await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); } await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index d30af986..9b3cff69 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -1,8 +1,10 @@ using System.Threading.Tasks; +using DisCatSharp; using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Entities; +using MikuSharp.Enums; using MikuSharp.Utilities; namespace MikuSharp.Entities; @@ -12,16 +14,22 @@ internal sealed class MusicQueueEntry : IQueueEntry /// public async Task BeforePlayingAsync(LavalinkGuildPlayer player) { - var musicSession = MikuBot.MusicSessions[player.GuildId]; - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title} from {this.Track.Info.Author}")); + if (!MikuBot.MusicSessions.TryGetValue(player.GuildId, out var musicSession)) + return false; + + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title.Bold()} from {this.Track.Info.Author.Italic()}")); return true; } /// public async Task AfterPlayingAsync(LavalinkGuildPlayer player) { - var musicSession = MikuBot.MusicSessions[player.GuildId]; - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + if (MikuBot.MusicSessions.TryGetValue(player.GuildId, out var musicSession)) + { + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + } } /// diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index 3b8bd12d..de051a91 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -1,6 +1,9 @@ +using System; +using System.Linq; using System.Threading.Tasks; using DisCatSharp.Entities; +using DisCatSharp.Exceptions; using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Enums; @@ -34,7 +37,7 @@ public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, Lav /// /// Gets the Lavalink guild player. /// - public LavalinkGuildPlayer LavalinkGuildPlayer { get; internal set; } + public LavalinkGuildPlayer? LavalinkGuildPlayer { get; internal set; } /// /// Gets the repeat mode. @@ -44,10 +47,10 @@ public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, Lav /// /// Gets the status message. /// - public DiscordMessage StatusMessage { get; internal set; } + public DiscordMessage? StatusMessage { get; internal set; } /// - /// Gets the play state. + /// Gets the play state. /// public PlaybackState PlaybackState { get; internal set; } = PlaybackState.Stopped; @@ -55,9 +58,11 @@ public sealed class MusicSession(DiscordChannel channel, DiscordGuild guild, Lav /// Injects the player. /// /// The current music session. - public MusicSession InjectPlayer() + public async Task InjectPlayerAsync() { this.LavalinkGuildPlayer = this.LavalinkSession.GetGuildPlayer(this.CurrentGuild)!; + this.LavalinkGuildPlayer.SetRepeatMode(this.RepeatMode); + await this.LavalinkGuildPlayer.SetVolumeAsync(20); return this; } @@ -69,6 +74,7 @@ public MusicSession InjectPlayer() public MusicSession UpdateRepeatMode(RepeatMode mode) { this.RepeatMode = mode; + this.LavalinkGuildPlayer?.SetRepeatMode(mode); return this; } @@ -84,7 +90,7 @@ public MusicSession UpdateStatusMessage(DiscordMessage message) } /// - /// Updates the play state. + /// Updates the play state. /// /// The new play state. /// The current music session. @@ -94,10 +100,29 @@ public MusicSession UpdatePlaybackState(PlaybackState state) return this; } + /// + /// Updates the status message. + /// + /// The new status message embed. + /// The current music session. public async Task UpdateStatusMessageAsync(DiscordEmbed embed) { - await this.StatusMessage.DeleteAsync("Updating miku status"); - this.StatusMessage = await this.CurrentChannel.SendMessageAsync(embed); - return this; + try + { + if (this.StatusMessage is not null) + await this.StatusMessage.DeleteAsync("Updating miku status"); + else + { + var messages = await this.CurrentChannel.GetMessagesAsync(50); + var mikuMessages = messages.Where(msg => msg.Author.Id == MikuBot.ShardedClient.CurrentUser.Id).OrderByDescending(msg => msg.CreationTimestamp).ToList(); + var targetMessage = mikuMessages.FirstOrDefault(msg => msg.Embeds.Count is 1); + if (targetMessage is not null) + await targetMessage.DeleteAsync("Updating miku status"); + } + } + catch (NotFoundException) + { } + + return this.UpdateStatusMessage(await this.CurrentChannel.SendMessageAsync(embed)); } } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 60787d5e..feb6d252 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -3,12 +3,18 @@ using System.Linq; using System.Threading.Tasks; +using DisCatSharp; using DisCatSharp.ApplicationCommands.Context; using DisCatSharp.Entities; using DisCatSharp.Enums; using DisCatSharp.Lavalink; +using DisCatSharp.Lavalink.Entities; +using DisCatSharp.Lavalink.Enums; + +using Microsoft.CodeAnalysis.CSharp.Syntax; using MikuSharp.Entities; +using MikuSharp.Enums; namespace MikuSharp.Utilities; @@ -34,7 +40,7 @@ public static LavalinkSession DefaultSession(this LavalinkExtension lavalink) => lavalink.ConnectedSessions.First().Value; /// - /// Builds a music status embed. + /// Builds a music status embed. /// /// The music session. /// The description. @@ -61,7 +67,7 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, stri } /// - /// Builds a music status embed. + /// Builds a music status embed. /// /// The music session. /// The additional embed fields. @@ -70,7 +76,7 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List => BuildMusicStatusEmbed(session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields); /// - /// Formats a into a human-readable string. + /// Formats a into a human-readable string. /// /// The time span to format. /// The formatted time span. @@ -80,4 +86,55 @@ public static string FormatTimeSpan(this TimeSpan timeSpan) : timeSpan.TotalMinutes >= 1 ? $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}" : $"{timeSpan.Seconds:D2} sec"; + + /// + /// Loads and plays an . + /// + /// The music session. + /// The interaction context. + /// The identifier to load. + /// The optional search type. Defaults to . + /// Whether the track was successfully loaded and added to the queue. + /// + public static async Task LoadAndPlayTrackAsync(this MusicSession musicSession, InteractionContext ctx, string identifier, LavalinkSearchType searchType = LavalinkSearchType.Plain) + { + var loadResult = await musicSession.LavalinkGuildPlayer.LoadTracksAsync(searchType, identifier); + switch (loadResult.LoadType) + { + case LavalinkLoadResultType.Track: + var track = loadResult.GetResultAs(); + musicSession.LavalinkGuildPlayer.AddToQueue(track); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added {track.Info.Title.Bold()} to the queue!")); + break; + case LavalinkLoadResultType.Playlist: + var playlist = loadResult.GetResultAs(); + musicSession.LavalinkGuildPlayer.AddToQueue(playlist); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added playlist {playlist.Info.Name.Bold()} to the queue.")); + break; + case LavalinkLoadResultType.Search: + var tracks = loadResult.GetResultAs>(); + musicSession.LavalinkGuildPlayer.AddToQueue(tracks.First()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added {tracks.First().Info.Title.Bold()} to the queue!")); + break; + case LavalinkLoadResultType.Empty: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"No results found for `{identifier.InlineCode()}`")); + throw new("No results found"); + case LavalinkLoadResultType.Error: + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Something went wrong..\nReason: {loadResult.GetResultAs().Message ?? "unknown"}")); + throw new("Lavalink error"); + default: + throw new ArgumentOutOfRangeException(); + } + + if (musicSession.PlaybackState is PlaybackState.Stopped) + musicSession.LavalinkGuildPlayer.PlayQueue(); + else if (musicSession.PlaybackState is PlaybackState.Paused) + { + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession = musicSession.UpdatePlaybackState(PlaybackState.Playing); + musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + } + + return musicSession; + } } From 90a2c5575c6564b85b507eefe05c8543c438a7ae Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 02:43:54 +0100 Subject: [PATCH 039/113] hope that works --- .../Music/MusicCommands.OptionsCommands.cs | 31 +++-- .../Music/MusicCommands.PlaybackCommands.cs | 122 ++++++++++++------ MikuSharp/Commands/Music/MusicCommands.cs | 46 ++++--- MikuSharp/Entities/MusicQueueEntry.cs | 26 ++-- MikuSharp/MikuBot.cs | 15 ++- MikuSharp/MikuSharp.csproj | 3 +- MikuSharp/Utilities/Other.cs | 22 ++-- 7 files changed, 176 insertions(+), 89 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index b005e3ac..5cd80d35 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -28,21 +28,34 @@ RepeatMode mode ) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession = musicSession.UpdateRepeatMode(mode); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + musicSession.UpdateRepeatMode(mode); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); + } + } } [SlashCommand("shuffle", "Shuffle the queue")] public static async Task ShuffleAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession.LavalinkGuildPlayer.ShuffleQueue(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + musicSession.LavalinkGuildPlayer.ShuffleQueue(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); + } + } } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 0e21d250..8e6cb4f4 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -29,12 +29,18 @@ public class PlaybackCommands : ApplicationCommandsModule public async Task PauseAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - await musicSession.LavalinkGuildPlayer.PauseAsync(); - musicSession = musicSession.UpdatePlaybackState(PlaybackState.Paused); - musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync("Paused the playback! "); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + await musicSession.LavalinkGuildPlayer.PauseAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Paused); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Paused the playback! "); + } + } } /// @@ -45,26 +51,38 @@ public async Task PauseAsync(InteractionContext ctx) public async Task ResumeAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - await musicSession.LavalinkGuildPlayer.ResumeAsync(); - musicSession = musicSession.UpdatePlaybackState(PlaybackState.Playing); - musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync("Resumed the playback!"); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Resumed the playback!"); + } + } } [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task StopAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession = musicSession.UpdateRepeatMode(RepeatMode.None); - musicSession.LavalinkGuildPlayer.ClearQueue(); - await musicSession.LavalinkGuildPlayer.StopAsync(); - musicSession = musicSession.UpdatePlaybackState(PlaybackState.Stopped); - musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync("Stopped the playback!"); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + musicSession.UpdateRepeatMode(RepeatMode.None); + musicSession.LavalinkGuildPlayer.ClearQueue(); + await musicSession.LavalinkGuildPlayer.StopAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Stopped the playback!"); + } + } } [SlashCommand("volume", "Change the music volume")] @@ -75,23 +93,34 @@ public static async Task ModifyVolumeAsync( ) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); - musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); + } + } } [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - var targetSeek = TimeSpan.FromSeconds(position); - await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + var targetSeek = TimeSpan.FromSeconds(position); + await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); + } + } } [SlashCommand("play", "Plays a url")] @@ -99,25 +128,34 @@ public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url t { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - musicSession = await musicSession.LoadAndPlayTrackAsync(ctx, url); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + await musicSession.LoadAndPlayTrackAsync(ctx, url); + } } [SlashCommand("skip", "Skips to the next song")] public async Task SkipAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var musicSession = MikuBot.MusicSessions[ctx.GuildId.Value]; - if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) { - await musicSession.LavalinkGuildPlayer.SkipAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) + { + await musicSession.LavalinkGuildPlayer.SkipAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + } } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); - - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 47508124..5a2d2777 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -31,14 +31,23 @@ public static async Task JoinAsync(InteractionContext ctx) ArgumentNullException.ThrowIfNull(ctx.Guild); ArgumentNullException.ThrowIfNull(ctx.GuildId); - var session = ctx.Client.GetLavalink().DefaultSession(); - await ctx.Client.GetLavalink().DefaultSession().ConnectAsync(ctx.Member.VoiceState.Channel); - MusicSession musicSession = new(ctx.Member.VoiceState.Channel, ctx.Guild, session); - MikuBot.MusicSessions.Add(ctx.GuildId.Value, await musicSession.InjectPlayerAsync()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); - musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); - MikuBot.MusicSessions[ctx.GuildId.Value] = musicSession; + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (!MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + var session = ctx.Client.GetLavalink().DefaultSession(); + await session.ConnectAsync(ctx.Member.VoiceState.Channel); + musicSession = await new MusicSession(ctx.Member.VoiceState.Channel, ctx.Guild, session).InjectPlayerAsync(); + MikuBot.MusicSessions[guildId] = musicSession; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); + } } /// @@ -50,15 +59,22 @@ public static async Task LeaveAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - if (MikuBot.MusicSessions.Remove(ctx.GuildId.Value, out var musicSession)) + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) { - if (musicSession.LavalinkGuildPlayer is not null) - await musicSession.LavalinkGuildPlayer.DisconnectAsync(); - await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); - if (musicSession.StatusMessage is not null) - await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + if (MikuBot.MusicSessions.TryRemove(ctx.GuildId.Value, out var musicSession)) + { + if (musicSession.LavalinkGuildPlayer is not null) + await musicSession.LavalinkGuildPlayer.DisconnectAsync(); + await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); + if (musicSession.StatusMessage is not null) + await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + MikuBot.MusicSessionLocks.TryRemove(guildId, out _); } } diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index 9b3cff69..5a5e3ac3 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -14,21 +14,31 @@ internal sealed class MusicQueueEntry : IQueueEntry /// public async Task BeforePlayingAsync(LavalinkGuildPlayer player) { - if (!MikuBot.MusicSessions.TryGetValue(player.GuildId, out var musicSession)) - return false; + var guildId = player.GuildId; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (!MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + return false; - musicSession.UpdatePlaybackState(PlaybackState.Playing); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title.Bold()} from {this.Track.Info.Author.Italic()}")); - return true; + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title.Bold()} from {this.Track.Info.Author.Italic()}")); + return true; + } } /// public async Task AfterPlayingAsync(LavalinkGuildPlayer player) { - if (MikuBot.MusicSessions.TryGetValue(player.GuildId, out var musicSession)) + var guildId = player.GuildId; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) { - musicSession.UpdatePlaybackState(PlaybackState.Stopped); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + { + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + } } } diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 472a7d39..2c4ab3e6 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -26,6 +27,8 @@ using MikuSharp.Commands.Music; using MikuSharp.Entities; +using NeoSmart.AsyncLock; + using Newtonsoft.Json; using Serilog; @@ -44,20 +47,22 @@ internal sealed class MikuBot : IDisposable { internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "5.0.0"); - //internal static readonly Dictionary LavalinkSessions = []; - //internal static readonly Dictionary Guilds = []; - //internal static Playstate Ps = Playstate.Playing; //internal static Stopwatch Psc = new(); /// /// Gets the music sessions. /// - internal static readonly Dictionary MusicSessions = []; + internal static readonly ConcurrentDictionary MusicSessions = []; + + /// + /// Gets the music session locks. + /// + internal static readonly ConcurrentDictionary MusicSessionLocks = []; internal MikuBot() { - var fileData = File.ReadAllText(@"config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); + var fileData = File.ReadAllText("config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); var config = JsonConvert.DeserializeObject(fileData); ArgumentNullException.ThrowIfNull(config); diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index b649df2d..10c859aa 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -77,6 +77,7 @@ + @@ -86,7 +87,7 @@ - + diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index feb6d252..5939b390 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -11,8 +11,6 @@ using DisCatSharp.Lavalink.Entities; using DisCatSharp.Lavalink.Enums; -using Microsoft.CodeAnalysis.CSharp.Syntax; - using MikuSharp.Entities; using MikuSharp.Enums; @@ -73,7 +71,7 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, stri /// The additional embed fields. /// The built embed. public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List? additionalEmbedFields = null) - => BuildMusicStatusEmbed(session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields); + => session.StatusMessage is not null ? BuildMusicStatusEmbed(session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields) : throw new NullReferenceException(); /// /// Formats a into a human-readable string. @@ -126,13 +124,19 @@ public static async Task LoadAndPlayTrackAsync(this MusicSession m throw new ArgumentOutOfRangeException(); } - if (musicSession.PlaybackState is PlaybackState.Stopped) - musicSession.LavalinkGuildPlayer.PlayQueue(); - else if (musicSession.PlaybackState is PlaybackState.Paused) + switch (musicSession.PlaybackState) { - await musicSession.LavalinkGuildPlayer.ResumeAsync(); - musicSession = musicSession.UpdatePlaybackState(PlaybackState.Playing); - musicSession = await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + case PlaybackState.Stopped: + musicSession.LavalinkGuildPlayer.PlayQueue(); + break; + case PlaybackState.Paused: + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + break; + case PlaybackState.Playing: + default: + break; } return musicSession; From 168b9799d90a202bb074d7d0afbfdf9902cf9f3a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 02:50:29 +0100 Subject: [PATCH 040/113] aaaaaaaa --- MikuSharp/Attributes/CustomMikuAttributes.cs | 46 +++++++++---------- MikuSharp/Commands/About.cs | 15 +----- MikuSharp/Commands/Action.cs | 11 +---- MikuSharp/Commands/Developer.cs | 16 ------- MikuSharp/Commands/Fun.cs | 14 +----- MikuSharp/Commands/MikuGuild.cs | 11 +---- MikuSharp/Commands/Moderation.cs | 12 +---- .../Music/MusicCommands.InfoCommands.cs | 8 +--- .../Music/MusicCommands.OptionsCommands.cs | 9 ---- .../Music/MusicCommands.PlaybackCommands.cs | 9 ---- .../Music/MusicCommands.QueueCommands.cs | 6 --- MikuSharp/Commands/Music/MusicCommands.cs | 10 ---- MikuSharp/Commands/NSFW.cs | 8 +--- .../PlaylistCommands.ManageCommands.cs | 6 --- .../Playlist/PlaylistCommands.SongCommands.cs | 6 --- .../Commands/Playlist/PlaylistCommands.cs | 7 --- MikuSharp/Commands/Utility.cs | 18 +------- MikuSharp/Commands/Weeb.cs | 13 +----- MikuSharp/Entities/BiliJson.cs | 4 +- MikuSharp/Entities/BiliPlayInfo.cs | 4 +- MikuSharp/Entities/BotConfig.cs | 4 +- MikuSharp/Entities/Img_Data.cs | 6 +-- MikuSharp/Entities/MusicQueueEntry.cs | 4 -- MikuSharp/Entities/MusicSession.cs | 7 --- MikuSharp/Entities/WeebSh.cs | 6 +-- MikuSharp/Events/MikuGuildJoin.cs | 3 -- MikuSharp/GlobalUsings.cs | 30 ++++++++++++ MikuSharp/MikuBot.cs | 25 ---------- MikuSharp/Program.cs | 4 +- MikuSharp/Utilities/Bilibili.cs | 11 +---- MikuSharp/Utilities/DiscordOptionProviders.cs | 10 ---- MikuSharp/Utilities/NND.cs | 10 +--- MikuSharp/Utilities/Other.cs | 11 ----- MikuSharp/Utilities/Web.cs | 8 +--- 34 files changed, 71 insertions(+), 301 deletions(-) create mode 100644 MikuSharp/GlobalUsings.cs diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index 59899c9f..d63de033 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -1,15 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.CommandsNext; -using DisCatSharp.CommandsNext.Attributes; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Lavalink; - using MikuSharp.Enums; using MikuSharp.Utilities; @@ -128,20 +116,27 @@ public sealed class AutomaticallyDisconnectExistingSessionAttribute : Applicatio /// public override async Task ExecuteChecksAsync(BaseContext ctx) { - if (!MikuBot.MusicSessions.ContainsKey(ctx.GuildId!.Value)) - return true; - - var player = ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild); - if (player is not null) - await player.DisconnectAsync(); - MikuBot.MusicSessions.Remove(ctx.GuildId.Value); + ArgumentNullException.ThrowIfNull(ctx.GuildId); + ArgumentNullException.ThrowIfNull(ctx.Guild); + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (!MikuBot.MusicSessions.TryGetValue(guildId, out _)) + return true; + + var player = ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild); + if (player is not null) + await player.DisconnectAsync(); + MikuBot.MusicSessions.TryRemove(ctx.GuildId.Value, out _); + } return true; } } /// -/// Defines that the method or class requires specific playback state(s). +/// Defines that the method or class requires specific playback state(s). /// /// The target playback states. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false)] @@ -153,9 +148,14 @@ public sealed class RequirePlaybackState(params PlaybackState[] targetStates) : public List TargetStates { get; } = [..targetStates]; /// - public override Task ExecuteChecksAsync(BaseContext ctx) + public override async Task ExecuteChecksAsync(BaseContext ctx) { - var musicSession = MikuBot.MusicSessions[ctx.GuildId!.Value]; - return Task.FromResult(this.TargetStates.Contains(musicSession.PlaybackState)); + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var guildId = ctx.GuildId.Value; + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + return MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession) && this.TargetStates.Contains(musicSession.PlaybackState); + } } } diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 0561ea37..9dbb91d3 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -1,17 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity.Extensions; - -namespace MikuSharp.Commands; +namespace MikuSharp.Commands; [SlashCommandGroup("about", "About")] internal class About : ApplicationCommandsModule diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index d59bd179..97074604 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -1,13 +1,4 @@ -using System.IO; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using HeyRed.Mime; +using HeyRed.Mime; using MikuSharp.Utilities; diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 14f86816..66031dc6 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -1,19 +1,3 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Extensions; -using DisCatSharp.Lavalink; - using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 8c718a8f..7cf4fac5 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -1,20 +1,8 @@ -using System; -using System.IO; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using HeyRed.Mime; +using HeyRed.Mime; using MikuSharp.Entities; using MikuSharp.Utilities; -using Newtonsoft.Json; - namespace MikuSharp.Commands; [SlashCommandGroup("fun", "Fun commands", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuild.cs index d6a73b0e..42b66f17 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuild.cs @@ -1,13 +1,4 @@ -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -namespace MikuSharp.Commands; +namespace MikuSharp.Commands; public class MikuGuild : ApplicationCommandsModule { diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 156b107c..249f6401 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -1,14 +1,4 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Exceptions; +using DisCatSharp.Exceptions; using MikuSharp.Utilities; diff --git a/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs index 1923b58f..b23714a0 100644 --- a/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs @@ -1,10 +1,4 @@ -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; - -using MikuSharp.Attributes; +using MikuSharp.Attributes; namespace MikuSharp.Commands.Music; diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 5cd80d35..4e0e64b9 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -1,12 +1,3 @@ -using System; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Lavalink.Enums; - using MikuSharp.Attributes; using MikuSharp.Utilities; diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 8e6cb4f4..58b5ec60 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -1,12 +1,3 @@ -using System; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Lavalink.Enums; - using MikuSharp.Attributes; using MikuSharp.Enums; using MikuSharp.Utilities; diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index eebe1dce..974a10b4 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -1,9 +1,3 @@ -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; - using MikuSharp.Attributes; namespace MikuSharp.Commands.Music; diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 5a2d2777..286e3a1f 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -1,13 +1,3 @@ -using System; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Lavalink; - using MikuSharp.Attributes; using MikuSharp.Entities; using MikuSharp.Utilities; diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index d74ab6be..c071f423 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -1,10 +1,4 @@ -using System.Threading.Tasks; - -using DisCatSharp.CommandsNext; -using DisCatSharp.CommandsNext.Attributes; -using DisCatSharp.Entities; - -using MikuSharp.Attributes; +using MikuSharp.Attributes; using MikuSharp.Utilities; namespace MikuSharp.Commands; diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs index 8f3e2c49..1fe14d7e 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs @@ -1,9 +1,3 @@ -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; - namespace MikuSharp.Commands.Playlist; public partial class PlaylistCommands diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs index 3e688be7..ee9ab7af 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs @@ -1,9 +1,3 @@ -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; - namespace MikuSharp.Commands.Playlist; public partial class PlaylistCommands diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.cs index 63bc654d..7807529c 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.cs @@ -1,10 +1,3 @@ -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Enums; - using MikuSharp.Attributes; namespace MikuSharp.Commands.Playlist; diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index 56a594d1..a70ff89f 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -1,24 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Exceptions; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Enums; -using DisCatSharp.Interactivity.Extensions; +using DisCatSharp.Exceptions; using Kitsu.Anime; using Kitsu.Manga; -using Microsoft.Extensions.Logging; - namespace MikuSharp.Commands; [SlashCommandGroup("utility", "Utilities")] diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index b6893a99..326be47e 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -1,19 +1,8 @@ -using System.IO; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; - -using HeyRed.Mime; +using HeyRed.Mime; using MikuSharp.Entities; using MikuSharp.Utilities; -using Newtonsoft.Json; - namespace MikuSharp.Commands; [SlashCommandGroup("weeb", "Weeb Stuff!", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] diff --git a/MikuSharp/Entities/BiliJson.cs b/MikuSharp/Entities/BiliJson.cs index bb93dc96..5ccd537a 100644 --- a/MikuSharp/Entities/BiliJson.cs +++ b/MikuSharp/Entities/BiliJson.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public class Durl2 { diff --git a/MikuSharp/Entities/BiliPlayInfo.cs b/MikuSharp/Entities/BiliPlayInfo.cs index 7b22744b..22d099d7 100644 --- a/MikuSharp/Entities/BiliPlayInfo.cs +++ b/MikuSharp/Entities/BiliPlayInfo.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public class BiliPlayinfo { diff --git a/MikuSharp/Entities/BotConfig.cs b/MikuSharp/Entities/BotConfig.cs index 766cb7e1..5553c499 100644 --- a/MikuSharp/Entities/BotConfig.cs +++ b/MikuSharp/Entities/BotConfig.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public sealed class BotConfig { diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs index 3f2bfdf6..5ab88651 100644 --- a/MikuSharp/Entities/Img_Data.cs +++ b/MikuSharp/Entities/Img_Data.cs @@ -1,8 +1,4 @@ -using System.IO; - -using DisCatSharp.Entities; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public class ImgData { diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index 5a5e3ac3..f97df291 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -1,7 +1,3 @@ -using System.Threading.Tasks; - -using DisCatSharp; -using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Entities; using MikuSharp.Enums; diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index de051a91..b1bb3fe5 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -1,11 +1,4 @@ -using System; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp.Entities; using DisCatSharp.Exceptions; -using DisCatSharp.Lavalink; -using DisCatSharp.Lavalink.Enums; using MikuSharp.Enums; diff --git a/MikuSharp/Entities/WeebSh.cs b/MikuSharp/Entities/WeebSh.cs index f778bd92..4f0f9081 100644 --- a/MikuSharp/Entities/WeebSh.cs +++ b/MikuSharp/Entities/WeebSh.cs @@ -1,8 +1,4 @@ -using System.IO; - -using DisCatSharp.Entities; - -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; public sealed class WeebSh { diff --git a/MikuSharp/Events/MikuGuildJoin.cs b/MikuSharp/Events/MikuGuildJoin.cs index dd789001..fbdb7220 100644 --- a/MikuSharp/Events/MikuGuildJoin.cs +++ b/MikuSharp/Events/MikuGuildJoin.cs @@ -1,6 +1,3 @@ -using System.Threading.Tasks; - -using DisCatSharp; using DisCatSharp.EventArgs; namespace MikuSharp.Events; diff --git a/MikuSharp/GlobalUsings.cs b/MikuSharp/GlobalUsings.cs new file mode 100644 index 00000000..8aae8949 --- /dev/null +++ b/MikuSharp/GlobalUsings.cs @@ -0,0 +1,30 @@ +global using System; +global using System.Collections.Concurrent; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Text; +global using System.Threading; +global using System.Threading.Tasks; + +global using DisCatSharp; +global using DisCatSharp.ApplicationCommands; +global using DisCatSharp.ApplicationCommands.Attributes; +global using DisCatSharp.ApplicationCommands.Context; +global using DisCatSharp.CommandsNext; +global using DisCatSharp.CommandsNext.Attributes; +global using DisCatSharp.Entities; +global using DisCatSharp.Enums; +global using DisCatSharp.Interactivity; +global using DisCatSharp.Interactivity.Enums; +global using DisCatSharp.Interactivity.Extensions; +global using DisCatSharp.Lavalink; +global using DisCatSharp.Lavalink.Enums; + +global using Microsoft.Extensions.Logging; + +global using NeoSmart.AsyncLock; + +global using Newtonsoft.Json; + +global using Serilog; diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 2c4ab3e6..02f81754 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -1,37 +1,12 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -using DisCatSharp; -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; using DisCatSharp.ApplicationCommands.Exceptions; -using DisCatSharp.CommandsNext; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Enums; -using DisCatSharp.Interactivity.Extensions; -using DisCatSharp.Lavalink; using DiscordBotsList.Api; -using Microsoft.Extensions.Logging; - using MikuSharp.Attributes; using MikuSharp.Commands; using MikuSharp.Commands.Music; using MikuSharp.Entities; -using NeoSmart.AsyncLock; - -using Newtonsoft.Json; - -using Serilog; using Serilog.Events; using Weeb.net; diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index 14ec2fd5..27e439bd 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -1,6 +1,4 @@ -using Serilog; - -namespace MikuSharp; +namespace MikuSharp; internal class Program { diff --git a/MikuSharp/Utilities/Bilibili.cs b/MikuSharp/Utilities/Bilibili.cs index 93b4eb33..eb177bae 100644 --- a/MikuSharp/Utilities/Bilibili.cs +++ b/MikuSharp/Utilities/Bilibili.cs @@ -1,13 +1,4 @@ -using System; -using System.IO; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using Microsoft.Extensions.Logging; - -using NYoutubeDL; +using NYoutubeDL; namespace MikuSharp.Utilities; diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 8a1aeefd..e377ba29 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,13 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Lavalink.Enums; - namespace MikuSharp.Utilities; internal class FixedOptionProviders diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs index 30ec10af..58939730 100644 --- a/MikuSharp/Utilities/NND.cs +++ b/MikuSharp/Utilities/NND.cs @@ -1,12 +1,4 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; - -using Microsoft.Extensions.Logging; +using System.Diagnostics; using NicoNicoNii; diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 5939b390..9a946806 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -1,15 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -using DisCatSharp; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Lavalink; using DisCatSharp.Lavalink.Entities; -using DisCatSharp.Lavalink.Enums; using MikuSharp.Entities; using MikuSharp.Enums; diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs index f3d72f78..c9de0e00 100644 --- a/MikuSharp/Utilities/Web.cs +++ b/MikuSharp/Utilities/Web.cs @@ -1,15 +1,9 @@ -using System.IO; -using System.Net.Http; -using System.Threading.Tasks; - -using DisCatSharp.Entities; +using System.Net.Http; using HeyRed.Mime; using MikuSharp.Entities; -using Newtonsoft.Json; - using Weeb.net; namespace MikuSharp.Utilities; From fe6396f1aac7fff4de6723e51d656704425d4dc8 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 02:53:53 +0100 Subject: [PATCH 041/113] a --- MikuSharp/Attributes/CustomMikuAttributes.cs | 7 ++++--- MikuSharp/Commands/Music/MusicCommands.cs | 1 + MikuSharp/Utilities/Other.cs | 9 +++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CustomMikuAttributes.cs index d63de033..4e7cb564 100644 --- a/MikuSharp/Attributes/CustomMikuAttributes.cs +++ b/MikuSharp/Attributes/CustomMikuAttributes.cs @@ -12,7 +12,7 @@ public sealed class RequireUserVoicechatConnection : ApplicationCommandCheckBase /// public override async Task ExecuteChecksAsync(BaseContext ctx) { - var connected = ctx.Member.VoiceState?.Channel is not null; + var connected = ctx.Member?.VoiceState?.Channel is not null; if (connected) return true; @@ -31,8 +31,9 @@ public sealed class RequireUserAndBotVoicechatConnection : ApplicationCommandChe /// public override async Task ExecuteChecksAsync(BaseContext ctx) { + ArgumentNullException.ThrowIfNull(ctx.Guild); var bot = await ctx.Guild.GetMemberAsync(ctx.Client.CurrentUser.Id); - var connected = ctx.Member.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null; + var connected = ctx.Member?.VoiceState?.Channel is not null && bot.VoiceState?.Channel is not null; if (connected) return true; @@ -92,7 +93,7 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) return await RespondWithNoSessionAvailableAsync(ctx); var session = module.DefaultSession(); - return session is null || !session.IsConnected ? await RespondWithNoSessionAvailableAsync(ctx) : true; + return (session is not null && session.IsConnected) || await RespondWithNoSessionAvailableAsync(ctx); } /// diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 286e3a1f..21583ada 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -29,6 +29,7 @@ public static async Task JoinAsync(InteractionContext ctx) if (!MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) { var session = ctx.Client.GetLavalink().DefaultSession(); + ArgumentNullException.ThrowIfNull(session); await session.ConnectAsync(ctx.Member.VoiceState.Channel); musicSession = await new MusicSession(ctx.Member.VoiceState.Channel, ctx.Guild, session).InjectPlayerAsync(); MikuBot.MusicSessions[guildId] = musicSession; diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index 9a946806..c82ceaab 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -7,6 +7,11 @@ namespace MikuSharp.Utilities; public static class Other { + /// + /// Resizes an image link. + /// + /// The url of the image to resize. + /// The resized image. public static string ResizeLink(string url) => $"https://api.meek.moe/im/?image={url}&resize=500"; @@ -23,8 +28,8 @@ public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral /// /// The lavalink extension. /// The first session or . - public static LavalinkSession DefaultSession(this LavalinkExtension lavalink) - => lavalink.ConnectedSessions.First().Value; + public static LavalinkSession? DefaultSession(this LavalinkExtension lavalink) + => lavalink.ConnectedSessions.Count > 0 ? lavalink.ConnectedSessions.First().Value : null; /// /// Builds a music status embed. From 84e313908eb00173293ae3858fac62f352b1761a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 03:07:35 +0100 Subject: [PATCH 042/113] rawr --- .../Music/MusicCommands.PlaybackCommands.cs | 2 +- MikuSharp/MikuBot.cs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 58b5ec60..6458807e 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -70,7 +70,7 @@ public static async Task StopAsync(InteractionContext ctx) musicSession.LavalinkGuildPlayer.ClearQueue(); await musicSession.LavalinkGuildPlayer.StopAsync(); musicSession.UpdatePlaybackState(PlaybackState.Stopped); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing")); await ctx.EditResponseAsync("Stopped the playback!"); } } diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 02f81754..65938a93 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -308,8 +308,20 @@ internal async Task RunAsync() await ShardedClient.StartAsync(); await Task.Delay(5000); - foreach (var lavalinkShard in this.LavalinkModules) - await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + var success = false; + while (!success) + { + try + { + foreach (var lavalinkShard in this.LavalinkModules) + await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + success = true; + } + catch + { + success = false; + } + } this.GameSetThread = Task.Run(SetActivity); //StatusThread = Task.Run(ShowConnections); From 41f08f0c7355240902c8e210ce09d0b9f43c1e41 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 04:04:31 +0100 Subject: [PATCH 043/113] meepmeow --- MikuSharp/Commands/About.cs | 23 +++++----------- MikuSharp/Commands/Developer.cs | 2 +- MikuSharp/Commands/Fun.cs | 11 ++------ MikuSharp/Commands/Moderation.cs | 16 ++--------- MikuSharp/Commands/NSFW.cs | 47 +++++++++++++------------------- MikuSharp/Commands/Utility.cs | 38 +++++++++----------------- MikuSharp/Commands/Weeb.cs | 16 ++--------- MikuSharp/Utilities/Other.cs | 4 +-- 8 files changed, 51 insertions(+), 106 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 9dbb91d3..b8a77c9a 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -1,4 +1,6 @@ -namespace MikuSharp.Commands; +using MikuSharp.Attributes; + +namespace MikuSharp.Commands; [SlashCommandGroup("about", "About")] internal class About : ApplicationCommandsModule @@ -13,10 +15,9 @@ public static async Task DonateAsync(InteractionContext ctx) await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); } - [SlashCommand("bot", "About the bot")] + [SlashCommand("bot", "About the bot"), DeferResponseAsync(true)] public static async Task BotAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var emb = new DiscordEmbedBuilder(); emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) .WithDescription(ctx.Client.CurrentApplication.Description); @@ -27,7 +28,7 @@ public static async Task BotAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } - [SlashCommand("news", "Get news about the bot in your server", dmPermission: false)] + [SlashCommand("news", "Get news about the bot in your server", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true)] public static async Task FollowNewsAsync( InteractionContext ctx, [Option("target_channel", "Target channel to post updates."), ChannelTypes(ChannelType.Text)] @@ -35,14 +36,6 @@ public static async Task FollowNewsAsync( [Option("name", "Name of webhook")] string name = "Miku Bot Announcements" ) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != ctx.Guild.OwnerId) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - var announcementChannel = await ctx.Client.GetChannelAsync(483290389047017482); var f = await announcementChannel.FollowAsync(channel); await Task.Delay(5000); @@ -107,10 +100,9 @@ public static async Task PingAsync(InteractionContext ctx) public static async Task GetExecutingShardAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); - [SlashCommand("stats", "Some stats of the MikuBot!")] + [SlashCommand("stats", "Some stats of the MikuBot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var guildCount = 0; var userCount = 0; var channelCount = 0; @@ -132,10 +124,9 @@ public static async Task StatsAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } - [SlashCommand("support", "Link to my support server")] + [SlashCommand("support", "Link to my support server"), DeferResponseAsync(true)] public static async Task SupportAsybc(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var widget = await guild.GetWidgetAsync(); var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 66031dc6..bfb01274 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -12,7 +12,7 @@ public class Developer : ApplicationCommandsModule private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) + public static async Task TestAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); [SlashCommand("guild_shard_test", "Testing")] diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 7cf4fac5..44b3547c 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -1,17 +1,17 @@ -using HeyRed.Mime; +using HeyRed.Mime; +using MikuSharp.Attributes; using MikuSharp.Entities; using MikuSharp.Utilities; namespace MikuSharp.Commands; -[SlashCommandGroup("fun", "Fun commands", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] +[SlashCommandGroup("fun", "Fun commands", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] internal class Fun : ApplicationCommandsModule { [SlashCommand("8ball", "Yes? No? Maybe?")] public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var responses = new[] { "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No." }; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); } @@ -19,7 +19,6 @@ public static async Task EightBallAsync(InteractionContext ctx, [Option("text", [SlashCommand("cat", "Get a random cat image!")] public static async Task CatAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); DiscordWebhookBuilder builder = new(); @@ -31,7 +30,6 @@ public static async Task CatAsync(InteractionContext ctx) [SlashCommand("clyde", "Say something as clyde bot")] public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); @@ -51,7 +49,6 @@ public static async Task CoinflipAsync(InteractionContext ctx) [SlashCommand("dog", "Random Dog Image")] public static async Task DogAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); var em = new DiscordEmbedBuilder(); @@ -95,7 +92,6 @@ public static async Task Lion(InteractionContext ctx) [SlashCommand("lizard", "Get a random lizard image")] public static async Task LizardAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); @@ -141,7 +137,6 @@ public static async Task RedPandaAsync(InteractionContext ctx) [SlashCommand("rps", "Play rock paper scissors!")] public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); } diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/Moderation.cs index 249f6401..a3c11728 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/Moderation.cs @@ -1,17 +1,16 @@ -using DisCatSharp.Exceptions; +using DisCatSharp.Exceptions; +using MikuSharp.Attributes; using MikuSharp.Utilities; namespace MikuSharp.Commands; -[SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, dmPermission: false)] +[SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall])] internal class Moderation : ApplicationCommandsModule { [SlashCommand("disable_invites", "Disable invites usage for guild")] public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); - try { await ctx.Guild.DisableInvitesAsync(reason); @@ -26,8 +25,6 @@ public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("re [SlashCommand("enable_invites", "Enable invites usage for guild")] public static async Task EnableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); - try { await ctx.Guild.EnableInvitesAsync(reason); @@ -48,8 +45,6 @@ public static async Task BanAsync( [Option("reason", "Auditlog reason")] string? reason = null ) { - await ctx.DeferAsync(false); - try { await ctx.Guild.BanMemberAsync(user.Id, deletionDays, reason); @@ -64,7 +59,6 @@ public static async Task BanAsync( [SlashCommand("unban", "Unban someone")] public static async Task UnbanAsync(InteractionContext ctx, [Option("username", "User to unban", true), Autocomplete(typeof(AutocompleteProviders.BanProvider))] string id, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); var userId = Convert.ToUInt64(id); var user = await ctx.Client.GetUserAsync(userId, true); await ctx.Guild.UnbanMemberAsync(user, reason); @@ -74,8 +68,6 @@ public static async Task UnbanAsync(InteractionContext ctx, [Option("username", [SlashCommand("kick", "Kick someone")] public static async Task KickAsync(InteractionContext ctx, [Option("user", "User to kick")] DiscordUser user, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(false); - try { var member = await user.ConvertToMember(ctx.Guild); @@ -91,8 +83,6 @@ public static async Task KickAsync(InteractionContext ctx, [Option("user", "User [SlashCommand("purge", "Delete a large amount of messages fast")] public static async Task PurgeAsync(InteractionContext ctx, [Option("amount", "Amount of messages to purge"), MinimumValue(1), MaximumValue(100)] int amount, [Option("reason", "Auditlog reason")] string? reason = null) { - await ctx.DeferAsync(); - try { var msgs = await ctx.Channel.GetMessagesAsync(amount); diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index c071f423..9746b5e6 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -1,4 +1,4 @@ -using MikuSharp.Attributes; +using MikuSharp.Attributes; using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -7,100 +7,91 @@ namespace MikuSharp.Commands; public class Nsfw : BaseCommandModule { [Command("4k"), Description("lewd")] - public async Task FourK(CommandContext ctx) + public static async Task FourK(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("anal"), Description("lewd")] - public async Task Anal(CommandContext ctx) + public static async Task Anal(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("ass"), Description("lewd")] - public async Task Ass(CommandContext ctx) + public static async Task Ass(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("gonewild"), Description("lewd")] - public async Task Gonewild(CommandContext ctx) + public static async Task Gonewild(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("lewdkitsune"), Description("lewd")] - public async Task LewdKitsune(CommandContext ctx) + public static async Task LewdKitsune(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("lewdneko"), Description("lewd")] - public async Task LewdNeko(CommandContext ctx) + public static async Task LewdNeko(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("porngif"), Description("lewd")] - public async Task PornGif(CommandContext ctx) + public static async Task PornGif(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("pussy"), Description("lewd")] - public async Task Pussy(CommandContext ctx) + public static async Task Pussy(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } [Command("thighs"), Aliases("thigh"), Description("lewd")] - public async Task Thighs(CommandContext ctx) + public static async Task Thighs(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); - DiscordMessageBuilder builder = new(); - builder.WithFile($"image.{d.Filetype}", d.Data); + builder.AddFile($"image.{d.Filetype}", d.Data); builder.AddEmbed(d.Embed); await ctx.RespondAsync(builder); } diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index a70ff89f..402070d7 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -1,21 +1,21 @@ -using DisCatSharp.Exceptions; +using DisCatSharp.Exceptions; using Kitsu.Anime; using Kitsu.Manga; +using MikuSharp.Attributes; + namespace MikuSharp.Commands; [SlashCommandGroup("utility", "Utilities")] internal class Utility : ApplicationCommandsModule { - [SlashCommandGroup("am", "Anime & Mange")] + [SlashCommandGroup("am", "Anime & Mange"), DeferResponseAsync] internal class AnimeMangaUtility : ApplicationCommandsModule { [SlashCommand("anime_search", "Search for an anime")] public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - try { var ine = ctx.Client.GetInteractivity(); @@ -73,8 +73,6 @@ public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("searc [SlashCommand("manga_search", "Search for an manga")] public static async Task SearchMangaAsync(InteractionContext ctx, [Option("search_query", "Search query")] string searchQuery) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - try { var ine = ctx.Client.GetInteractivity(); @@ -136,12 +134,10 @@ public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", ? user.AvatarUrl : ctx.User.AvatarUrl).Build())); - [SlashCommand("server_info", "Get information about the server")] + [SlashCommand("server_info", "Get information about the server"), DeferResponseAsync(true)] public static async Task GuildInfoAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (ctx.Guild == null) + if (ctx.Guild is null) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); return; @@ -164,17 +160,14 @@ public static async Task GuildInfoAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } - [SlashCommand("user_info", "Get information about a user")] + [SlashCommand("user_info", "Get information about a user"), DeferResponseAsync(true)] public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - - if (user == null) - user = ctx.User; + user ??= ctx.User; DiscordMember? member = null; - if (ctx.Guild != null) + if (ctx.Guild is not null) try { member = await user.ConvertToMember(ctx.Guild); @@ -186,12 +179,12 @@ public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", " emb.WithColor(new(0212255)); emb.WithTitle("User Info"); emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); - if (member != null) + if (member is not null) if (member.DisplayName != user.Username) emb.AddField(new("Nickname", $"{member.DisplayName}", true)); emb.AddField(new("ID", $"{user.Id}", true)); emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); - if (member != null) + if (member is not null) emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); emb.WithThumbnail(user.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); @@ -200,15 +193,10 @@ public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", " [SlashCommand("emojilist", "Lists all custom emoji on this server")] public static async Task EmojiListAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var wat = "You have to execute this command in a server!"; - if (ctx.Guild != null && ctx.Guild.Emojis.Any()) - { - wat = "**Emojies:** "; - foreach (var em in ctx.Guild.Emojis.Values) - wat += em + " "; - } + if (ctx.Guild is not null && ctx.Guild.Emojis.Any()) + wat = ctx.Guild.Emojis.Values.Aggregate("**Emojies:** ", (current, em) => current + (em + " ")); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); } diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 326be47e..f3491430 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -1,17 +1,17 @@ -using HeyRed.Mime; +using HeyRed.Mime; +using MikuSharp.Attributes; using MikuSharp.Entities; using MikuSharp.Utilities; namespace MikuSharp.Commands; -[SlashCommandGroup("weeb", "Weeb Stuff!", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] +[SlashCommandGroup("weeb", "Weeb Stuff!", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] internal class Weeb : ApplicationCommandsModule { [SlashCommand("awooify", "Awooify your or someones avatar!")] public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) { - await ctx.DeferAsync(false); var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); @@ -20,7 +20,6 @@ public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "U [SlashCommand("diva", "Radnom PJD Loading image")] public static async Task DivaPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -44,7 +43,6 @@ public static async Task DivaPic(InteractionContext ctx) [SlashCommand("gumi", "Random Gumi image")] public static async Task GumiPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -69,7 +67,6 @@ public static async Task GumiPic(InteractionContext ctx) [SlashCommand("kaito", "Random Kaito image")] public static async Task KaitoPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -94,7 +91,6 @@ public static async Task KaitoPic(InteractionContext ctx) [SlashCommand("len", "Random Len image")] public static async Task KLenPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -119,7 +115,6 @@ public static async Task KLenPic(InteractionContext ctx) [SlashCommand("luka", "Random Luka image")] public static async Task LukaPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -144,7 +139,6 @@ public static async Task LukaPic(InteractionContext ctx) [SlashCommand("meiko", "Random Meiko image")] public static async Task MeikoPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -169,7 +163,6 @@ public static async Task MeikoPic(InteractionContext ctx) [SlashCommand("miku", "Random Miku image")] public static async Task HMikuPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -194,7 +187,6 @@ public static async Task HMikuPic(InteractionContext ctx) [SlashCommand("neko", "Get a random neko image")] public static async Task Cat(InteractionContext ctx) { - await ctx.DeferAsync(false); var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); var em = new DiscordEmbedBuilder(); @@ -210,7 +202,6 @@ public static async Task Cat(InteractionContext ctx) [SlashCommand("rin", "Random Rin image")] public static async Task KRinPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { @@ -235,7 +226,6 @@ public static async Task KRinPic(InteractionContext ctx) [SlashCommand("teto", "Random Teto image")] public static async Task KTetoPic(InteractionContext ctx) { - await ctx.DeferAsync(false); var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) { diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index c82ceaab..cd1b56ea 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -65,7 +65,7 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, stri /// The additional embed fields. /// The built embed. public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List? additionalEmbedFields = null) - => session.StatusMessage is not null ? BuildMusicStatusEmbed(session, session.StatusMessage.Embeds.First().Description, additionalEmbedFields) : throw new NullReferenceException(); + => session.StatusMessage is not null ? BuildMusicStatusEmbed(session, session.StatusMessage.Embeds[0].Description, additionalEmbedFields) : throw new NullReferenceException(); /// /// Formats a into a human-readable string. @@ -115,7 +115,7 @@ public static async Task LoadAndPlayTrackAsync(this MusicSession m await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Something went wrong..\nReason: {loadResult.GetResultAs().Message ?? "unknown"}")); throw new("Lavalink error"); default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(null, "Could not determine the type of the lavalink search result"); } switch (musicSession.PlaybackState) From 15cb93e234d26d22f866a8efca8376cf8b15b3fa Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 04:07:03 +0100 Subject: [PATCH 044/113] fix: no static --- MikuSharp/Commands/NSFW.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs index 9746b5e6..60d6fcd8 100644 --- a/MikuSharp/Commands/NSFW.cs +++ b/MikuSharp/Commands/NSFW.cs @@ -7,7 +7,7 @@ namespace MikuSharp.Commands; public class Nsfw : BaseCommandModule { [Command("4k"), Description("lewd")] - public static async Task FourK(CommandContext ctx) + public async Task FourK(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); DiscordMessageBuilder builder = new(); @@ -17,7 +17,7 @@ public static async Task FourK(CommandContext ctx) } [Command("anal"), Description("lewd")] - public static async Task Anal(CommandContext ctx) + public async Task Anal(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); DiscordMessageBuilder builder = new(); @@ -27,7 +27,7 @@ public static async Task Anal(CommandContext ctx) } [Command("ass"), Description("lewd")] - public static async Task Ass(CommandContext ctx) + public async Task Ass(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); DiscordMessageBuilder builder = new(); @@ -37,7 +37,7 @@ public static async Task Ass(CommandContext ctx) } [Command("gonewild"), Description("lewd")] - public static async Task Gonewild(CommandContext ctx) + public async Task Gonewild(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); DiscordMessageBuilder builder = new(); @@ -47,7 +47,7 @@ public static async Task Gonewild(CommandContext ctx) } [Command("lewdkitsune"), Description("lewd")] - public static async Task LewdKitsune(CommandContext ctx) + public async Task LewdKitsune(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); DiscordMessageBuilder builder = new(); @@ -57,7 +57,7 @@ public static async Task LewdKitsune(CommandContext ctx) } [Command("lewdneko"), Description("lewd")] - public static async Task LewdNeko(CommandContext ctx) + public async Task LewdNeko(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); DiscordMessageBuilder builder = new(); @@ -67,7 +67,7 @@ public static async Task LewdNeko(CommandContext ctx) } [Command("porngif"), Description("lewd")] - public static async Task PornGif(CommandContext ctx) + public async Task PornGif(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); DiscordMessageBuilder builder = new(); @@ -77,7 +77,7 @@ public static async Task PornGif(CommandContext ctx) } [Command("pussy"), Description("lewd")] - public static async Task Pussy(CommandContext ctx) + public async Task Pussy(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); DiscordMessageBuilder builder = new(); @@ -87,7 +87,7 @@ public static async Task Pussy(CommandContext ctx) } [Command("thighs"), Aliases("thigh"), Description("lewd")] - public static async Task Thighs(CommandContext ctx) + public async Task Thighs(CommandContext ctx) { var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); DiscordMessageBuilder builder = new(); From 21f297b32fc279e99e8e5c92c1e5daf8b1605cfa Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 04:13:09 +0100 Subject: [PATCH 045/113] Update Action.cs --- MikuSharp/Commands/Action.cs | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 97074604..86a6c813 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -1,4 +1,4 @@ -using HeyRed.Mime; +using HeyRed.Mime; using MikuSharp.Utilities; @@ -17,9 +17,9 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("kiss", "Kiss someone!")] @@ -32,9 +32,9 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("lick", "Lick someone!")] @@ -47,9 +47,9 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("pat", "Pat someone!")] @@ -66,9 +66,9 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("poke", "Poke someone!")] @@ -85,9 +85,9 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("slap", "Slap someone!")] @@ -104,9 +104,9 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("bite", "Bite someone!")] @@ -123,9 +123,9 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("nom", "Nom someone!")] @@ -142,9 +142,9 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } [SlashCommand("stare", "Stare at someone!")] @@ -161,8 +161,8 @@ public static async Task StateAsync(InteractionContext ctx, [Option("user", "The DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); await ctx.EditResponseAsync(builder); - if (ctx.Interaction.Context is InteractionContextType.Guild) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(user.Mention).WithAllowedMention(new UserMention(user))); } } From 7cc0c3227d06880475d3ae8434e1ff1a978fc581 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 04:37:17 +0100 Subject: [PATCH 046/113] Update MikuBot.cs --- MikuSharp/MikuBot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/MikuBot.cs index 65938a93..cef5cb37 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/MikuBot.cs @@ -97,7 +97,7 @@ internal MikuBot() this.ApplicationCommandsModules = ShardedClient.UseApplicationCommandsAsync(new() { EnableDefaultHelp = true, - DebugStartup = true, + DebugStartup = false, EnableLocalization = false, GenerateTranslationFilesOnly = false }).Result; From b2929183f9d54acb0fdffa140db9c24edeb3ea96 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 05:08:09 +0100 Subject: [PATCH 047/113] fun stuff --- .../Music/MusicCommands.OptionsCommands.cs | 31 ++--- .../Music/MusicCommands.PlaybackCommands.cs | 119 ++++++------------ MikuSharp/Commands/Music/MusicCommands.cs | 4 +- MikuSharp/Events/Old/Lavalink.cs | 85 ------------- MikuSharp/Utilities/Other.cs | 38 ++++++ 5 files changed, 87 insertions(+), 190 deletions(-) delete mode 100644 MikuSharp/Events/Old/Lavalink.cs diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 4e0e64b9..53df4b8f 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -18,35 +18,22 @@ public static async Task RepeatAsync( RepeatMode mode ) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - musicSession.UpdateRepeatMode(mode); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); - } - } + musicSession.UpdateRepeatMode(mode); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set repeat mode to: **{mode}**")); + }); } [SlashCommand("shuffle", "Shuffle the queue")] public static async Task ShuffleAsync(InteractionContext ctx) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - musicSession.LavalinkGuildPlayer.ShuffleQueue(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); - } - } + musicSession.LavalinkGuildPlayer.ShuffleQueue(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); + }); } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 6458807e..620a7a14 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -19,19 +19,13 @@ public class PlaybackCommands : ApplicationCommandsModule [SlashCommand("pause", "Pauses the playback"), RequirePlaybackState(PlaybackState.Playing)] public async Task PauseAsync(InteractionContext ctx) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - await musicSession.LavalinkGuildPlayer.PauseAsync(); - musicSession.UpdatePlaybackState(PlaybackState.Paused); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync("Paused the playback! "); - } - } + await musicSession.LavalinkGuildPlayer.PauseAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Paused); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Paused the playback! "); + }); } /// @@ -41,39 +35,27 @@ public async Task PauseAsync(InteractionContext ctx) [SlashCommand("resume", "Resumes the playback"), RequirePlaybackState(PlaybackState.Paused)] public async Task ResumeAsync(InteractionContext ctx) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - await musicSession.LavalinkGuildPlayer.ResumeAsync(); - musicSession.UpdatePlaybackState(PlaybackState.Playing); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync("Resumed the playback!"); - } - } + await musicSession.LavalinkGuildPlayer.ResumeAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Playing); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync("Resumed the playback!"); + }); } [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task StopAsync(InteractionContext ctx) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - musicSession.UpdateRepeatMode(RepeatMode.None); - musicSession.LavalinkGuildPlayer.ClearQueue(); - await musicSession.LavalinkGuildPlayer.StopAsync(); - musicSession.UpdatePlaybackState(PlaybackState.Stopped); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing")); - await ctx.EditResponseAsync("Stopped the playback!"); - } - } + musicSession.UpdateRepeatMode(RepeatMode.None); + musicSession.LavalinkGuildPlayer.ClearQueue(); + await musicSession.LavalinkGuildPlayer.StopAsync(); + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing")); + await ctx.EditResponseAsync("Stopped the playback!"); + }); } [SlashCommand("volume", "Change the music volume")] @@ -83,70 +65,45 @@ public static async Task ModifyVolumeAsync( int volume = 100 ) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); - } - } + await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); + }); } [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - var targetSeek = TimeSpan.FromSeconds(position); - await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); - } - } + var targetSeek = TimeSpan.FromSeconds(position); + await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); + }); } [SlashCommand("play", "Plays a url")] public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) - { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - await musicSession.LoadAndPlayTrackAsync(ctx, url); - } + await ctx.ExecuteWithMusicSessionAsync(async musicSession => await musicSession.LoadAndPlayTrackAsync(ctx, url)); } [SlashCommand("skip", "Skips to the next song")] public async Task SkipAsync(InteractionContext ctx) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await ctx.ExecuteWithMusicSessionAsync(async musicSession => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) { - if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) - { - await musicSession.LavalinkGuildPlayer.SkipAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); - } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + await musicSession.LavalinkGuildPlayer.SkipAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); } - } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + }); } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 21583ada..78141c6e 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -15,7 +15,7 @@ public partial class MusicCommands : ApplicationCommandsModule /// /// The interaction context. [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] - public static async Task JoinAsync(InteractionContext ctx) + public async Task JoinAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); ArgumentNullException.ThrowIfNull(ctx.Guild); @@ -46,7 +46,7 @@ public static async Task JoinAsync(InteractionContext ctx) /// /// The interaction context. [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection] - public static async Task LeaveAsync(InteractionContext ctx) + public async Task LeaveAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); diff --git a/MikuSharp/Events/Old/Lavalink.cs b/MikuSharp/Events/Old/Lavalink.cs deleted file mode 100644 index 6ca14388..00000000 --- a/MikuSharp/Events/Old/Lavalink.cs +++ /dev/null @@ -1,85 +0,0 @@ -/*using System; -using System.Threading.Tasks; - -using DisCatSharp.Entities; -using DisCatSharp.Lavalink; -using DisCatSharp.Lavalink.Enums; -using DisCatSharp.Lavalink.EventArgs; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Enums; -using MikuSharp.Utilities; - -namespace MikuSharp.Events; - -public class Lavalink -{ - public static async Task LavalinkTrackFinish(LavalinkGuildPlayer lava, LavalinkTrackEndedEventArgs e) - { - try - { - var g = MikuBot.Guilds[e.GuildId]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(e.Guild); - if (g.MusicInstance == null!) - return; - - switch (e.Reason) - { - case LavalinkTrackEndReason.Stopped: - { - g.MusicInstance.Playstate = Playstate.Stopped; - g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - break; - } - case LavalinkTrackEndReason.Replaced: - { - break; - } - case LavalinkTrackEndReason.LoadFailed: - { - await g.MusicInstance.UsedChannel.SendMessageAsync(new DiscordEmbedBuilder().WithTitle("Track failed to play") - .WithDescription($"**{g.MusicInstance.CurrentSong.Track.Info.Title}**\nby {g.MusicInstance.CurrentSong.Track.Info.Author}\n" + $"**Failed to load, Skipping to next Track**")); - g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; - await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - var queue = await Database.GetQueueAsync(e.Guild); - if (queue.Count != 0) await g.MusicInstance.PlaySong(); - else g.MusicInstance.Playstate = Playstate.NotPlaying; - break; - } - case LavalinkTrackEndReason.Finished: - { - g.MusicInstance.GuildConnection.TrackEnded -= LavalinkTrackFinish; - if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) - await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, e.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - else if (lastPlayedSongs[0].Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) - await Database.AddToLastPlayingListAsync(e.GuildId, g.MusicInstance.CurrentSong.Track.Encoded); - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - var queue = await Database.GetQueueAsync(e.Guild); - if (queue.Count != 0) await g.MusicInstance.PlaySong(); - else g.MusicInstance.Playstate = Playstate.NotPlaying; - break; - } - } - } - catch (Exception ex) - { - MikuBot.ShardedClient.Logger.LogError(ex.Message); - MikuBot.ShardedClient.Logger.LogError(ex.StackTrace); - } - } -} -*/ - - diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index cd1b56ea..f4d9dd59 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -135,4 +135,42 @@ public static async Task LoadAndPlayTrackAsync(this MusicSession m return musicSession; } + + /// + /// Executes an action with the music session. + /// + /// The interaction context. + /// The action to execute. + /// A task that represents the asynchronous operation. + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func action) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(ctx.GuildId.Value, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(ctx.GuildId.Value, out var musicSession)) + await action(musicSession); + } + } + + /// + /// Executes an action with the music session. + /// + /// The type of the result. + /// The interaction context. + /// The action to execute. + /// The default value of to return if the music session does not exist. + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func> action, T? defaultValue = default) + { + ArgumentNullException.ThrowIfNull(ctx.GuildId); + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(ctx.GuildId.Value, _ => new()); + using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + { + if (MikuBot.MusicSessions.TryGetValue(ctx.GuildId.Value, out var musicSession)) + return await action(musicSession); + } + + return defaultValue; + } } From dff31161899d2569eb4744e737eec99c45fc3951 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 05:12:04 +0100 Subject: [PATCH 048/113] rawr --- .../Music/MusicCommands.OptionsCommands.cs | 9 +++++ .../Music/MusicCommands.PlaybackCommands.cs | 34 +++++++++++-------- .../Music/MusicCommands.QueueCommands.cs | 20 +++++++++++ 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 53df4b8f..7650f673 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -11,6 +11,11 @@ public partial class MusicCommands [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection] public class OptionsCommands : ApplicationCommandsModule { + /// + /// Changes the repeat mode. + /// + /// The interaction context. + /// The new repeat mode. [SlashCommand("repeat", "Repeat the current song or the entire queue")] public static async Task RepeatAsync( InteractionContext ctx, @@ -26,6 +31,10 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => }); } + /// + /// Shuffles the queue. + /// + /// The interaction context. [SlashCommand("shuffle", "Shuffle the queue")] public static async Task ShuffleAsync(InteractionContext ctx) { diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 620a7a14..52aa39f6 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -44,6 +44,10 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => }); } + /// + /// Stops the playback. + /// + /// The interaction context. [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task StopAsync(InteractionContext ctx) { @@ -58,6 +62,11 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => }); } + /// + /// Changes the volume of the music player. + /// + /// The interaction context. + /// The volume to set. [SlashCommand("volume", "Change the music volume")] public static async Task ModifyVolumeAsync( InteractionContext ctx, @@ -73,6 +82,11 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => }); } + /// + /// Seeks the currently playing song to given position. + /// + /// The interaction context. + /// The position to seek to. [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) { @@ -84,26 +98,16 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => }); } + /// + /// Plays a url. + /// + /// The interaction context. + /// The url to play. [SlashCommand("play", "Plays a url")] public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); await ctx.ExecuteWithMusicSessionAsync(async musicSession => await musicSession.LoadAndPlayTrackAsync(ctx, url)); } - - [SlashCommand("skip", "Skips to the next song")] - public async Task SkipAsync(InteractionContext ctx) - { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => - { - if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) - { - await musicSession.LavalinkGuildPlayer.SkipAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); - } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); - }); - } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index 974a10b4..aa22b42d 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -1,4 +1,5 @@ using MikuSharp.Attributes; +using MikuSharp.Utilities; namespace MikuSharp.Commands.Music; @@ -17,5 +18,24 @@ public class QueueCommands : ApplicationCommandsModule [SlashCommand("dummy", "Dummy command")] public async Task DummyCommand(InteractionContext ctx) => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + + /// + /// Skips to the next song. + /// + /// The interaction context. + [SlashCommand("skip", "Skips to the next song")] + public async Task SkipAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async musicSession => + { + if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) + { + await musicSession.LavalinkGuildPlayer.SkipAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + }); + } } } From 53355c9eb7015540659ef83583cc995e514327fa Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 06:07:48 +0100 Subject: [PATCH 049/113] aaaaaaaaaaaaaaaaaaaa --- .../Music/MusicCommands.OptionsCommands.cs | 4 +- .../Music/MusicCommands.PlaybackCommands.cs | 12 ++-- .../Music/MusicCommands.QueueCommands.cs | 2 +- MikuSharp/Commands/Music/MusicCommands.cs | 40 ++++------- MikuSharp/Entities/MusicQueueEntry.cs | 22 ++---- MikuSharp/Entities/MusicSession.cs | 2 + MikuSharp/Utilities/Other.cs | 72 +++++++++++++++---- 7 files changed, 87 insertions(+), 67 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 7650f673..1c2cf4df 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -23,7 +23,7 @@ public static async Task RepeatAsync( RepeatMode mode ) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { musicSession.UpdateRepeatMode(mode); await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); @@ -38,7 +38,7 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => [SlashCommand("shuffle", "Shuffle the queue")] public static async Task ShuffleAsync(InteractionContext ctx) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { musicSession.LavalinkGuildPlayer.ShuffleQueue(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 52aa39f6..1ea6b9f7 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -19,7 +19,7 @@ public class PlaybackCommands : ApplicationCommandsModule [SlashCommand("pause", "Pauses the playback"), RequirePlaybackState(PlaybackState.Playing)] public async Task PauseAsync(InteractionContext ctx) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await musicSession.LavalinkGuildPlayer.PauseAsync(); musicSession.UpdatePlaybackState(PlaybackState.Paused); @@ -35,7 +35,7 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => [SlashCommand("resume", "Resumes the playback"), RequirePlaybackState(PlaybackState.Paused)] public async Task ResumeAsync(InteractionContext ctx) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await musicSession.LavalinkGuildPlayer.ResumeAsync(); musicSession.UpdatePlaybackState(PlaybackState.Playing); @@ -51,7 +51,7 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => [SlashCommand("stop", "Stop Playback"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task StopAsync(InteractionContext ctx) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { musicSession.UpdateRepeatMode(RepeatMode.None); musicSession.LavalinkGuildPlayer.ClearQueue(); @@ -74,7 +74,7 @@ public static async Task ModifyVolumeAsync( int volume = 100 ) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); @@ -90,7 +90,7 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { var targetSeek = TimeSpan.FromSeconds(position); await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); @@ -107,7 +107,7 @@ await ctx.ExecuteWithMusicSessionAsync(async musicSession => public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); - await ctx.ExecuteWithMusicSessionAsync(async musicSession => await musicSession.LoadAndPlayTrackAsync(ctx, url)); + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await musicSession.LoadAndPlayTrackAsync(ctx, url)); } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index aa22b42d..c8605873 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -26,7 +26,7 @@ public async Task DummyCommand(InteractionContext ctx) [SlashCommand("skip", "Skips to the next song")] public async Task SkipAsync(InteractionContext ctx) { - await ctx.ExecuteWithMusicSessionAsync(async musicSession => + await ctx.ExecuteWithMusicSessionAsync(async (discard, musicSession) => { if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) { diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 78141c6e..be96eae4 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -19,26 +19,18 @@ public async Task JoinAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); ArgumentNullException.ThrowIfNull(ctx.Guild); - ArgumentNullException.ThrowIfNull(ctx.GuildId); - - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) - { - if (!MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + await ctx.ExecuteWithMusicSessionAsync(async (_, _) => await ctx.EditResponseAsync("I'm already connected"), + async guildId => { var session = ctx.Client.GetLavalink().DefaultSession(); ArgumentNullException.ThrowIfNull(session); await session.ConnectAsync(ctx.Member.VoiceState.Channel); - musicSession = await new MusicSession(ctx.Member.VoiceState.Channel, ctx.Guild, session).InjectPlayerAsync(); + var musicSession = await new MusicSession(ctx.Member.VoiceState.Channel, ctx.Guild, session).InjectPlayerAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); + await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); MikuBot.MusicSessions[guildId] = musicSession; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); - } + }); } /// @@ -48,24 +40,16 @@ public async Task JoinAsync(InteractionContext ctx) [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection] public async Task LeaveAsync(InteractionContext ctx) { - ArgumentNullException.ThrowIfNull(ctx.GuildId); - - var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) - { - if (MikuBot.MusicSessions.TryRemove(ctx.GuildId.Value, out var musicSession)) + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { if (musicSession.LavalinkGuildPlayer is not null) await musicSession.LavalinkGuildPlayer.DisconnectAsync(); await musicSession.CurrentChannel.SendMessageAsync("Bye bye humans 💙"); if (musicSession.StatusMessage is not null) await musicSession.StatusMessage.DeleteAsync("Miku disconnected"); - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } - - MikuBot.MusicSessionLocks.TryRemove(guildId, out _); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); + }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")), + guildId => Task.FromResult(MikuBot.MusicSessionLocks.TryRemove(guildId, out _))); } } diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index f97df291..828287e9 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -10,32 +10,22 @@ internal sealed class MusicQueueEntry : IQueueEntry /// public async Task BeforePlayingAsync(LavalinkGuildPlayer player) { - var guildId = player.GuildId; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + return await player.GuildId.ExecuteWithMusicSessionAsync(async (_, musicSession) => { - if (!MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - return false; - musicSession.UpdatePlaybackState(PlaybackState.Playing); await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed($"Playing {this.Track.Info.Title.Bold()} from {this.Track.Info.Author.Italic()}")); return true; - } + }, defaultValue: false); } /// public async Task AfterPlayingAsync(LavalinkGuildPlayer player) { - var guildId = player.GuildId; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + await player.GuildId.ExecuteWithMusicSessionAsync(async (_, musicSession) => { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) - { - musicSession.UpdatePlaybackState(PlaybackState.Stopped); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - } - } + musicSession.UpdatePlaybackState(PlaybackState.Stopped); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + }); } /// diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index b1bb3fe5..735ef896 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -1,5 +1,7 @@ using DisCatSharp.Exceptions; +using Microsoft.CodeAnalysis.CSharp.Syntax; + using MikuSharp.Enums; namespace MikuSharp.Entities; diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index f4d9dd59..e9e9111b 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -137,38 +137,82 @@ public static async Task LoadAndPlayTrackAsync(this MusicSession m } /// - /// Executes an action with the music session. + /// Executes an action with the music session. /// /// The interaction context. - /// The action to execute. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// The action to execute after the success or failure action. /// A task that represents the asynchronous operation. - public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func action) + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func successAction, Func? failureAction = null, Func? finalAction = null) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(ctx.GuildId.Value, _ => new()); + await ctx.GuildId.Value.ExecuteWithMusicSessionAsync(successAction, failureAction, finalAction); + } + + /// + /// Executes an action with the music session. + /// + /// The guild ID. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// The action to execute after the success or failure action. + /// A task that represents the asynchronous operation. + public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func successAction, Func? failureAction = null, Func? finalAction = null) + { + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); using (await asyncLock.LockAsync(MikuBot.Cts.Token)) { - if (MikuBot.MusicSessions.TryGetValue(ctx.GuildId.Value, out var musicSession)) - await action(musicSession); + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + await successAction(guildId, musicSession); + else if (failureAction is not null) + await failureAction(guildId); } + + if (finalAction is not null) + await finalAction(guildId); } /// - /// Executes an action with the music session. + /// Executes an action with the music session. /// /// The type of the result. /// The interaction context. - /// The action to execute. - /// The default value of to return if the music session does not exist. - /// The result of the action or the default value with given type from . - public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func> action, T? defaultValue = default) + /// The action to execute. + /// The action to execute if the music session does not exist. + /// + /// The default value of to return if the music session does not + /// exist. + /// + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func> successAction, Func>? failureAction = null, T? defaultValue = default) { ArgumentNullException.ThrowIfNull(ctx.GuildId); - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(ctx.GuildId.Value, _ => new()); + return await ctx.GuildId.Value.ExecuteWithMusicSessionAsync(successAction, failureAction, defaultValue); + } + + /// + /// Executes an action with the music session. + /// + /// The type of the result. + /// The guild ID. + /// The action to execute. + /// + /// The action to execute if the music session does not exist. + /// + /// The default value of to return if the music session does not + /// exist. + /// + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func> successAction, Func>? failureAction = null, T? defaultValue = default) + { + var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); using (await asyncLock.LockAsync(MikuBot.Cts.Token)) { - if (MikuBot.MusicSessions.TryGetValue(ctx.GuildId.Value, out var musicSession)) - return await action(musicSession); + if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + return await successAction(guildId, musicSession); + if (failureAction is not null) + return await failureAction(guildId); } return defaultValue; From 57751b8a2fba0ed39f3bd724f928789584cc50f0 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 06:23:33 +0100 Subject: [PATCH 050/113] i hate this shit --- .../Commands/Music/MusicCommands.PlaybackCommands.cs | 8 ++++---- MikuSharp/Utilities/Other.cs | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 1ea6b9f7..76c9598b 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -86,14 +86,14 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => /// Seeks the currently playing song to given position. /// /// The interaction context. - /// The position to seek to. + /// The seconds to seek to. [SlashCommand("seek", "Seeks the currently playing song to given position"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] - public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) + public static async Task SeekAsync(InteractionContext ctx, [Option("seconds", "Seconds to seek to")] int seconds) { await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { - var targetSeek = TimeSpan.FromSeconds(position); - await musicSession.LavalinkGuildPlayer.SeekAsync(targetSeek); + var targetSeek = TimeSpan.FromSeconds(seconds); + await musicSession.LavalinkGuildPlayer.SeekAsync(seconds); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked to **{targetSeek.FormatTimeSpan()}**!")); }); } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/Other.cs index e9e9111b..f41b92b8 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/Other.cs @@ -74,10 +74,10 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List /// The formatted time span. public static string FormatTimeSpan(this TimeSpan timeSpan) => timeSpan.TotalHours >= 1 - ? $"{(int)timeSpan.TotalHours:D2}:{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}" + ? $"{(int)timeSpan.TotalHours:D2}h:{timeSpan.Minutes:D2}m:{timeSpan.Seconds:D2}s" : timeSpan.TotalMinutes >= 1 - ? $"{timeSpan.Minutes:D2}:{timeSpan.Seconds:D2}" - : $"{timeSpan.Seconds:D2} sec"; + ? $"{(int)timeSpan.TotalMinutes:D2}m:{timeSpan.Seconds:D2}s" + : $"{(int)timeSpan.TotalSeconds:D2}s"; /// /// Loads and plays an . From c203062d8a25ad7092d6a550c02206f59fecbde4 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 09:20:12 +0100 Subject: [PATCH 051/113] i hate this shit --- ...MikuAttributes.cs => CommandAttributes.cs} | 0 MikuSharp/Commands/Action.cs | 84 ++++++++++--- MikuSharp/Commands/Fun.cs | 29 ++++- .../Music/MusicCommands.QueueCommands.cs | 27 +++- MikuSharp/Commands/Weeb.cs | 100 ++++++++++++--- MikuSharp/Entities/MusicQueueEntry.cs | 4 +- MikuSharp/GlobalSuppressions.cs | 6 +- MikuSharp/GlobalUsings.cs | 1 + MikuSharp/Utilities/DiscordOptionProviders.cs | 39 ++++-- MikuSharp/Utilities/Formatters.cs | 27 ++++ .../Utilities/LavalinkExtensionMethods.cs | 7 ++ ...her.cs => MusicSessionExtensionMethods.cs} | 62 +++++---- MikuSharp/Utilities/NND.cs | 51 -------- MikuSharp/Utilities/NndExtensionMethods.cs | 62 +++++++++ MikuSharp/Utilities/Web.cs | 76 ----------- MikuSharp/Utilities/WebExtensionMethods.cs | 118 ++++++++++++++++++ 16 files changed, 476 insertions(+), 217 deletions(-) rename MikuSharp/Attributes/{CustomMikuAttributes.cs => CommandAttributes.cs} (100%) create mode 100644 MikuSharp/Utilities/Formatters.cs create mode 100644 MikuSharp/Utilities/LavalinkExtensionMethods.cs rename MikuSharp/Utilities/{Other.cs => MusicSessionExtensionMethods.cs} (83%) delete mode 100644 MikuSharp/Utilities/NND.cs create mode 100644 MikuSharp/Utilities/NndExtensionMethods.cs delete mode 100644 MikuSharp/Utilities/Web.cs create mode 100644 MikuSharp/Utilities/WebExtensionMethods.cs diff --git a/MikuSharp/Attributes/CustomMikuAttributes.cs b/MikuSharp/Attributes/CommandAttributes.cs similarity index 100% rename from MikuSharp/Attributes/CustomMikuAttributes.cs rename to MikuSharp/Attributes/CommandAttributes.cs diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 86a6c813..e57c295f 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -11,7 +11,13 @@ internal class Action : ApplicationCommandsModule public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug", [""]); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug"); + if (wsh is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); DiscordWebhookBuilder builder = new(); @@ -26,7 +32,13 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss", [""]); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss"); + if (wsh is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); DiscordWebhookBuilder builder = new(); @@ -41,7 +53,13 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); - var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick", [""]); + var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick"); + if (wsh is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); DiscordWebhookBuilder builder = new(); @@ -56,8 +74,14 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", []); + if (weeurl is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -75,8 +99,14 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", []); + if (weeurl is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -94,8 +124,14 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", []); + if (weeurl is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -113,8 +149,14 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", []); + if (weeurl is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} bites {user.Mention} x~x"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -132,8 +174,14 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", []); + if (weeurl is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} noms {user.Mention} >:3c"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); @@ -151,8 +199,14 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} stares {user.Mention} O.o")); - var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", [""]); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(weeurl.Url))); + var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", []); + if (weeurl is null) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} stares at {user.Mention} O.o"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 44b3547c..8ea402bf 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -20,6 +20,11 @@ public static async Task EightBallAsync(InteractionContext ctx, [Option("text", public static async Task CatAsync(InteractionContext ctx) { var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + if (imgUrl is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); @@ -31,7 +36,13 @@ public static async Task CatAsync(InteractionContext ctx) public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); + if (e is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); DiscordWebhookBuilder builder = new(); builder.AddFile("clyde.png", img); @@ -50,7 +61,13 @@ public static async Task CoinflipAsync(InteractionContext ctx) public static async Task DogAsync(InteractionContext ctx) { var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(dc.Message))); + if (dc is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); @@ -93,7 +110,13 @@ public static async Task Lion(InteractionContext ctx) public static async Task LizardAsync(InteractionContext ctx) { var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(get.Url))); + if (get is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(get.Url.ResizeLink())); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index c8605873..c64d1e13 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -28,7 +28,7 @@ public async Task SkipAsync(InteractionContext ctx) { await ctx.ExecuteWithMusicSessionAsync(async (discard, musicSession) => { - if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out _)) + if (musicSession.LavalinkGuildPlayer!.TryPeekQueue(out _)) { await musicSession.LavalinkGuildPlayer.SkipAsync(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully skipped the song!")); @@ -37,5 +37,30 @@ await ctx.ExecuteWithMusicSessionAsync(async (discard, musicSession) => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); }); } + + /// + /// Skips to the given queue position. + /// + /// The interaction context. + /// The position in the queue. + [SlashCommand("skip_to", "Skips to given queue position")] + public async Task SkipToAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.QueueProvider)), Option("position", "Position in queue", true)] int position) + { + if (position is -1) + { + await ctx.EditResponseAsync("Something went wrong while parsing the queue position."); + return; + } + + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.LavalinkGuildPlayer!.RemoveFromQueueAtRange(0, position); + await musicSession.LavalinkGuildPlayer.SkipAsync(); + if (musicSession.LavalinkGuildPlayer.TryPeekQueue(out var nextTrack)) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Successfully skipped to {nextTrack.Info.Title.Bold()}!")); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); + }); + } } } diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index f3491430..47a52896 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -13,15 +13,27 @@ internal class Weeb : ApplicationCommandsModule public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) { var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); + if (e is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); } [SlashCommand("diva", "Radnom PJD Loading image")] public static async Task DivaPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -43,8 +55,14 @@ public static async Task DivaPic(InteractionContext ctx) [SlashCommand("gumi", "Random Gumi image")] public static async Task GumiPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -67,8 +85,14 @@ public static async Task GumiPic(InteractionContext ctx) [SlashCommand("kaito", "Random Kaito image")] public static async Task KaitoPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -91,8 +115,14 @@ public static async Task KaitoPic(InteractionContext ctx) [SlashCommand("len", "Random Len image")] public static async Task KLenPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -115,8 +145,14 @@ public static async Task KLenPic(InteractionContext ctx) [SlashCommand("luka", "Random Luka image")] public static async Task LukaPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -139,8 +175,8 @@ public static async Task LukaPic(InteractionContext ctx) [SlashCommand("meiko", "Random Meiko image")] public static async Task MeikoPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -163,8 +199,14 @@ public static async Task MeikoPic(InteractionContext ctx) [SlashCommand("miku", "Random Miku image")] public static async Task HMikuPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -188,7 +230,13 @@ public static async Task HMikuPic(InteractionContext ctx) public static async Task Cat(InteractionContext ctx) { var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(imgUrl.Url))); + if (imgUrl is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(imgUrl.Url.ResizeLink())); var em = new DiscordEmbedBuilder(); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); @@ -202,8 +250,14 @@ public static async Task Cat(InteractionContext ctx) [SlashCommand("rin", "Random Rin image")] public static async Task KRinPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; @@ -226,8 +280,14 @@ public static async Task KRinPic(InteractionContext ctx) [SlashCommand("teto", "Random Teto image")] public static async Task KTetoPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.ResizeLink(res.Url))) + var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); + if (res is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) { Position = 0 }; diff --git a/MikuSharp/Entities/MusicQueueEntry.cs b/MikuSharp/Entities/MusicQueueEntry.cs index 828287e9..f2d61189 100644 --- a/MikuSharp/Entities/MusicQueueEntry.cs +++ b/MikuSharp/Entities/MusicQueueEntry.cs @@ -1,5 +1,3 @@ -using DisCatSharp.Lavalink.Entities; - using MikuSharp.Enums; using MikuSharp.Utilities; @@ -24,7 +22,7 @@ public async Task AfterPlayingAsync(LavalinkGuildPlayer player) await player.GuildId.ExecuteWithMusicSessionAsync(async (_, musicSession) => { musicSession.UpdatePlaybackState(PlaybackState.Stopped); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing")); }); } diff --git a/MikuSharp/GlobalSuppressions.cs b/MikuSharp/GlobalSuppressions.cs index 197c10f2..2374d7af 100644 --- a/MikuSharp/GlobalSuppressions.cs +++ b/MikuSharp/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// This file is used by Code Analysis to maintain SuppressMessage +// This file is used by Code Analysis to maintain SuppressMessage // attributes that are applied to this project. // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. @@ -399,7 +399,7 @@ [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.NND")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Other")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.PlaylistDB")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.Web")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Utilities.WebExtensionMethods")] [assembly: SuppressMessage("DocumentationHeader", "MethodDocumentationHeader:The method must have a documentation header.", Justification = "", Scope = "member", Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] @@ -446,7 +446,7 @@ "~M:MikuSharp.Utilities.Music.GetUrlPlayingInformationAsync(DisCatSharp.Entities.DiscordEmbedBuilder,DisCatSharp.DiscordClient,MikuSharp.Entities.Guild,System.Collections.Generic.List{MikuSharp.Entities.Entry})~System.Threading.Tasks.Task{DisCatSharp.Entities.DiscordEmbedBuilder}")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", - Target = "~M:MikuSharp.Utilities.Web.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] + Target = "~M:MikuSharp.Utilities.WebExtensionMethods.GetKsoftSiRanImgAsync(System.Net.Http.HttpClient,System.String,System.Boolean)~System.Threading.Tasks.Task{MikuSharp.Entities.KsoftSiRanImg}")] [assembly: SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "", Scope = "member", Target = diff --git a/MikuSharp/GlobalUsings.cs b/MikuSharp/GlobalUsings.cs index 8aae8949..194523a7 100644 --- a/MikuSharp/GlobalUsings.cs +++ b/MikuSharp/GlobalUsings.cs @@ -19,6 +19,7 @@ global using DisCatSharp.Interactivity.Enums; global using DisCatSharp.Interactivity.Extensions; global using DisCatSharp.Lavalink; +global using DisCatSharp.Lavalink.Entities; global using DisCatSharp.Lavalink.Enums; global using Microsoft.Extensions.Logging; diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index e377ba29..120557fb 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,3 +1,5 @@ +using NuGet.Packaging; + namespace MikuSharp.Utilities; internal class FixedOptionProviders @@ -81,22 +83,33 @@ public async Task> Prov return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); } } + */ internal sealed class QueueProvider : IAutocompleteProvider { - public async Task> Provider(AutocompleteContext ctx) - { - var queue = await Database.GetQueueAsync(ctx.Guild); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(queue.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(queue.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(queue.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); + public async Task> Provider(AutocompleteContext ctx) + { + var queue = await ctx.ExecuteWithMusicSessionAsync((_, musicSession) => Task.FromResult(musicSession.LavalinkGuildPlayer?.Queue.ToList()), null, []); + if (queue is null) + return [new("The queue is empty", -1)]; - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); - } + Dictionary queueEntries = []; + var i = 1; + foreach (var entry in queue) + { + queueEntries[i] = entry; + i++; + } + + Dictionary songs = []; + + var value = ctx.FocusedOption.Value as string; + + songs.AddRange(string.IsNullOrEmpty(value) + ? queueEntries.Take(25) + : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value)!.ToLower())).Take(25)); + + return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key)); + } } - */ } diff --git a/MikuSharp/Utilities/Formatters.cs b/MikuSharp/Utilities/Formatters.cs new file mode 100644 index 00000000..924d1e6c --- /dev/null +++ b/MikuSharp/Utilities/Formatters.cs @@ -0,0 +1,27 @@ +namespace MikuSharp.Utilities; + +/// +/// Represents a collection of formatters. +/// +internal static class Formatters +{ + /// + /// Resizes an image link. + /// + /// The url of the image to resize. + /// The resized image. + public static string ResizeLink(this string url) + => $"https://api.meek.moe/im/?image={url}&resize=500"; + + /// + /// Formats a into a human-readable string. + /// + /// The time span to format. + /// The formatted time span. + public static string FormatTimeSpan(this TimeSpan timeSpan) + => timeSpan.TotalHours >= 1 + ? $"{(int)timeSpan.TotalHours:D2}h:{timeSpan.Minutes:D2}m:{timeSpan.Seconds:D2}s" + : timeSpan.TotalMinutes >= 1 + ? $"{(int)timeSpan.TotalMinutes:D2}m:{timeSpan.Seconds:D2}s" + : $"{(int)timeSpan.TotalSeconds:D2}s"; +} diff --git a/MikuSharp/Utilities/LavalinkExtensionMethods.cs b/MikuSharp/Utilities/LavalinkExtensionMethods.cs new file mode 100644 index 00000000..6eb55a76 --- /dev/null +++ b/MikuSharp/Utilities/LavalinkExtensionMethods.cs @@ -0,0 +1,7 @@ +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for Lavalink-related operations. +/// +internal static class LavalinkExtensionMethods +{ } diff --git a/MikuSharp/Utilities/Other.cs b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs similarity index 83% rename from MikuSharp/Utilities/Other.cs rename to MikuSharp/Utilities/MusicSessionExtensionMethods.cs index f41b92b8..65665b12 100644 --- a/MikuSharp/Utilities/Other.cs +++ b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs @@ -1,28 +1,13 @@ -using DisCatSharp.Lavalink.Entities; - using MikuSharp.Entities; using MikuSharp.Enums; namespace MikuSharp.Utilities; -public static class Other +/// +/// Provides extension methods for use with s. +/// +internal static class MusicSessionExtensionMethods { - /// - /// Resizes an image link. - /// - /// The url of the image to resize. - /// The resized image. - public static string ResizeLink(string url) - => $"https://api.meek.moe/im/?image={url}&resize=500"; - - public static async Task DeferAsync(this InteractionContext ctx, bool ephemeral = true) - { - var builder = new DiscordInteractionResponseBuilder(); - if (ephemeral) - builder.AsEphemeral(); - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); - } - /// /// Gets the default session. /// @@ -67,18 +52,6 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, stri public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List? additionalEmbedFields = null) => session.StatusMessage is not null ? BuildMusicStatusEmbed(session, session.StatusMessage.Embeds[0].Description, additionalEmbedFields) : throw new NullReferenceException(); - /// - /// Formats a into a human-readable string. - /// - /// The time span to format. - /// The formatted time span. - public static string FormatTimeSpan(this TimeSpan timeSpan) - => timeSpan.TotalHours >= 1 - ? $"{(int)timeSpan.TotalHours:D2}h:{timeSpan.Minutes:D2}m:{timeSpan.Seconds:D2}s" - : timeSpan.TotalMinutes >= 1 - ? $"{(int)timeSpan.TotalMinutes:D2}m:{timeSpan.Seconds:D2}s" - : $"{(int)timeSpan.TotalSeconds:D2}s"; - /// /// Loads and plays an . /// @@ -150,6 +123,17 @@ public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ct await ctx.GuildId.Value.ExecuteWithMusicSessionAsync(successAction, failureAction, finalAction); } + /// + /// Executes an action with the music session. + /// + /// The autocomplete context. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// The action to execute after the success or failure action. + /// A task that represents the asynchronous operation. + public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext ctx, Func successAction, Func? failureAction = null, Func? finalAction = null) + => await ctx.Guild.Id.ExecuteWithMusicSessionAsync(successAction, failureAction, finalAction); + /// /// Executes an action with the music session. /// @@ -191,13 +175,27 @@ public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func + /// Executes an action with the music session. + /// + /// The type of the result. + /// The autocomplete context. + /// The action to execute. + /// The action to execute if the music session does not exist. + /// + /// The default value of to return if the music session does not + /// exist. + /// + /// The result of the action or the default value with given type from . + public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext ctx, Func> successAction, Func>? failureAction = null, T? defaultValue = default) + => await ctx.Guild.Id.ExecuteWithMusicSessionAsync(successAction, failureAction, defaultValue); + /// /// Executes an action with the music session. /// /// The type of the result. /// The guild ID. /// The action to execute. - /// /// The action to execute if the music session does not exist. /// /// The default value of to return if the music session does not diff --git a/MikuSharp/Utilities/NND.cs b/MikuSharp/Utilities/NND.cs deleted file mode 100644 index 58939730..00000000 --- a/MikuSharp/Utilities/NND.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Diagnostics; - -using NicoNicoNii; - -namespace MikuSharp.Utilities; - -public static class Nnd -{ - public static async Task GetNndAsync(this InteractionContext ctx, string n, string s, ulong msgId) - { - try - { - NndClient nndClient = new(); - NicoVideoClient videoClient = new(nndClient); - var videoPage = await videoClient.GetWatchPageInfoAsync(s); - var downloadExe = "nnd.exe"; - var linuxExe = "nndownload.py"; - var cmd = downloadExe; - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); - if (OperatingSystem.IsLinux()) - cmd = linuxExe; - Process downloadProcess = new(); - downloadProcess.StartInfo.FileName = cmd; - downloadProcess.StartInfo.Arguments = $"-g -o {$@"{s}"}.mp4 {$@"{n}"}"; - downloadProcess.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); }; - downloadProcess.Start(); - await downloadProcess.WaitForExitAsync(); - var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; - var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); - Process convertProgress = new(); - convertProgress.StartInfo.FileName = "ffmpeg"; - convertProgress.StartInfo.Arguments = $"-i {$@"{s}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{s}"}.mp3"; - convertProgress.OutputDataReceived += (d, f) => { ctx.Client.Logger.LogDebug("{data}", f.Data); }; - convertProgress.Start(); - await convertProgress.WaitForExitAsync(); - File.Delete($@"{s}.mp4"); - MemoryStream ms = new(await File.ReadAllBytesAsync($@"{s}.mp3")); - File.Delete($@"{s}.mp3"); - ms.Position = 0; - return ms; - } - catch (Exception ex) - { - ctx.Client.Logger.LogDebug("{ex}", ex.Message); - ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); - await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); - return null; - } - } -} diff --git a/MikuSharp/Utilities/NndExtensionMethods.cs b/MikuSharp/Utilities/NndExtensionMethods.cs new file mode 100644 index 00000000..3e47ff67 --- /dev/null +++ b/MikuSharp/Utilities/NndExtensionMethods.cs @@ -0,0 +1,62 @@ +using System.Diagnostics; + +using NicoNicoNii; + +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for NND-related operations. +/// +public static class NndExtensionMethods +{ + /// + /// Gets a NND video. + /// + /// The interaction context. + /// The name of the video. + /// The ID of the video. + /// The ID of the message. + /// The video as a memory stream. + public static async Task GetNndAsync(this InteractionContext ctx, string name, string videoId, ulong msgId) + { + try + { + NndClient nndClient = new(); + NicoVideoClient videoClient = new(nndClient); + var videoPage = await videoClient.GetWatchPageInfoAsync(videoId); + var downloadExe = "nnd.exe"; + var linuxExe = "nndownload.py"; + var cmd = downloadExe; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Downloading video (this may take up to 10 min)")); + if (OperatingSystem.IsLinux()) + cmd = linuxExe; + Process downloadProcess = new(); + downloadProcess.StartInfo.FileName = cmd; + downloadProcess.StartInfo.Arguments = $"-g -o {$@"{videoId}"}.mp4 {$@"{name}"}"; + downloadProcess.OutputDataReceived += (_, f) => ctx.Client.Logger.LogDebug("{data}", $"\n{f.Data}\n"); + downloadProcess.Start(); + await downloadProcess.WaitForExitAsync(); + var songTitle = videoPage?.Video?.Title ?? "[NND] Unknown Title"; + var songArtist = videoPage?.Owner?.Nickname ?? "Unknown Artist"; + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Converting")); + Process convertProgress = new(); + convertProgress.StartInfo.FileName = "ffmpeg"; + convertProgress.StartInfo.Arguments = $"-i {$@"{videoId}"}.mp4 -metadata title=\"{songTitle}\" -metadata artist=\"{songArtist}\" {$@"{videoId}"}.mp3"; + convertProgress.OutputDataReceived += (_, f) => ctx.Client.Logger.LogDebug("{data}", f.Data); + convertProgress.Start(); + await convertProgress.WaitForExitAsync(); + File.Delete($@"{videoId}.mp4"); + MemoryStream ms = new(await File.ReadAllBytesAsync($@"{videoId}.mp3")); + File.Delete($@"{videoId}.mp3"); + ms.Position = 0; + return ms; + } + catch (Exception ex) + { + ctx.Client.Logger.LogDebug("{ex}", ex.Message); + ctx.Client.Logger.LogDebug("{ex}", ex.StackTrace); + await ctx.EditFollowupAsync(msgId, new DiscordWebhookBuilder().WithContent("Encountered error")); + return null; + } + } +} diff --git a/MikuSharp/Utilities/Web.cs b/MikuSharp/Utilities/Web.cs deleted file mode 100644 index c9de0e00..00000000 --- a/MikuSharp/Utilities/Web.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Net.Http; - -using HeyRed.Mime; - -using MikuSharp.Entities; - -using Weeb.net; - -namespace MikuSharp.Utilities; - -public static class Web -{ - public static async Task GetNekosLifeAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Url))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekos.life"); - dl.Embed = em.Build(); - return dl; - } - - public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag, bool nsfw = false) - { - client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); - var v = JsonConvert.DeserializeObject(await client.GetStringAsync("https://api.ksoft.si/images/random-image?tag=hentai_gif&nsfw=true")); - MemoryStream img = new(await client.GetByteArrayAsync(Other.ResizeLink(v.Url))); - v.Data = img; - v.Filetype = MimeGuesser.GuessExtension(img); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{v.Filetype}"); - em.WithFooter("by KSoft.si"); - v.Embed = em.Build(); - return v; - } - - public static async Task GetNekobotAsync(this HttpClient client, string url) - { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); - MemoryStream str = new(await client.GetByteArrayAsync(Other.ResizeLink(dl.Message))) - { - Position = 0 - }; - dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekobot.xyz"); - dl.Embed = em.Build(); - return dl; - } - - public static async Task GetWeebShAsync(this HttpClient client, string query, string[] tags = null, NsfwSearch nsfw = NsfwSearch.False) - { - var weeurl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); - MemoryStream img = new(await client.GetByteArrayAsync(weeurl.Url)) - { - Position = 0 - }; - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by weeb.sh"); - return new() - { - ImgData = img, - Extension = MimeGuesser.GuessExtension(img), - Embed = em - }; - } -} diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs new file mode 100644 index 00000000..d8035c4e --- /dev/null +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -0,0 +1,118 @@ +using System.Net.Http; + +using HeyRed.Mime; + +using MikuSharp.Entities; + +using Weeb.net; + +namespace MikuSharp.Utilities; + +/// +/// Provides extension methods for web-related operations. +/// +public static class WebExtensionMethods +{ + /// + /// Gets a random image from nekos.life. + /// + /// The http client. + /// The url. + /// The nekos.life response. + public static async Task GetNekosLifeAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + if (dl is null) + return null; + + MemoryStream str = new(await client.GetByteArrayAsync(dl.Url.ResizeLink())) + { + Position = 0 + }; + dl.Data = str; + dl.Filetype = MimeGuesser.GuessExtension(str); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by nekos.life"); + dl.Embed = em.Build(); + return dl; + } + + /// + /// Gets a random image from ksoft.si. + /// + /// The http client. + /// The tag. + /// Whether the search should include NSFW results. + /// The ksoft.si response. + public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag = "hentai_gif", bool nsfw = true) + { + client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync($"https://api.ksoft.si/images/random-image?tag={tag}&nsfw={nsfw.ToString().ToLowerInvariant()}")); + if (dl is null) + return null; + + MemoryStream img = new(await client.GetByteArrayAsync(dl.Url.ResizeLink())); + dl.Data = img; + dl.Filetype = MimeGuesser.GuessExtension(img); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by KSoft.si"); + dl.Embed = em.Build(); + return dl; + } + + /// + /// Gets a random image from nekobot.xyz. + /// + /// The http client. + /// The url. + /// The nekobot response. + public static async Task GetNekobotAsync(this HttpClient client, string url) + { + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + if (dl is null) + return null; + + MemoryStream str = new(await client.GetByteArrayAsync(dl.Message.ResizeLink())) + { + Position = 0 + }; + dl.Data = str; + dl.Filetype = MimeGuesser.GuessExtension(str); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{dl.Filetype}"); + em.WithFooter("by nekobot.xyz"); + dl.Embed = em.Build(); + return dl; + } + + /// + /// Gets a random image from weeb.sh. + /// + /// The http client. + /// The query. + /// The optional tags. + /// Whether the search should include NSFW results. + /// The weeb.sh response. + public static async Task GetWeebShAsync(this HttpClient client, string query, IEnumerable? tags = null, NsfwSearch nsfw = NsfwSearch.False) + { + var dl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); + if (dl is null) + return null; + + MemoryStream img = new(await client.GetByteArrayAsync(dl.Url)) + { + Position = 0 + }; + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by weeb.sh"); + return new() + { + ImgData = img, + Extension = MimeGuesser.GuessExtension(img), + Embed = em + }; + } +} From da164fa7d7afc7aeb1ef5427aec288b2a3a128ea Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 09:46:48 +0100 Subject: [PATCH 052/113] nom --- MikuSharp/Utilities/DiscordOptionProviders.cs | 36 ++++++++++--------- .../Utilities/MusicSessionExtensionMethods.cs | 8 +++-- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 120557fb..041bab6b 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -89,27 +89,31 @@ internal sealed class QueueProvider : IAutocompleteProvider { public async Task> Provider(AutocompleteContext ctx) { - var queue = await ctx.ExecuteWithMusicSessionAsync((_, musicSession) => Task.FromResult(musicSession.LavalinkGuildPlayer?.Queue.ToList()), null, []); - if (queue is null) - return [new("The queue is empty", -1)]; - - Dictionary queueEntries = []; - var i = 1; - foreach (var entry in queue) + return await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { - queueEntries[i] = entry; - i++; - } + await Task.Delay(0); + + Dictionary queueEntries = []; + Dictionary filteredQueueEntries = []; - Dictionary songs = []; + var queue = musicSession.LavalinkGuildPlayer?.Queue.ToList(); + if (queue is null) + return [new("The queue is empty", -1)]; - var value = ctx.FocusedOption.Value as string; + var i = 1; + foreach (var entry in queue) + { + queueEntries[i] = entry; + i++; + } - songs.AddRange(string.IsNullOrEmpty(value) - ? queueEntries.Take(25) - : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value)!.ToLower())).Take(25)); + var value = ctx.FocusedOption.Value as string; + filteredQueueEntries.AddRange(string.IsNullOrEmpty(value) + ? queueEntries.Take(25) + : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value)!.ToLower())).Take(25)); - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key)); + return filteredQueueEntries.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key - 1)); + }, null, [new("The queue is empty", -1)]); } } } diff --git a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs index 65665b12..44528899 100644 --- a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs +++ b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + using MikuSharp.Entities; using MikuSharp.Enums; @@ -169,7 +171,7 @@ public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func /// The result of the action or the default value with given type from . - public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func> successAction, Func>? failureAction = null, T? defaultValue = default) + public static async Task ExecuteWithMusicSessionAsync(this InteractionContext ctx, Func> successAction, Func>? failureAction = null, T defaultValue = default) { ArgumentNullException.ThrowIfNull(ctx.GuildId); return await ctx.GuildId.Value.ExecuteWithMusicSessionAsync(successAction, failureAction, defaultValue); @@ -187,7 +189,7 @@ public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func /// The result of the action or the default value with given type from . - public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext ctx, Func> successAction, Func>? failureAction = null, T? defaultValue = default) + public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext ctx, Func> successAction, Func>? failureAction = null, T defaultValue = default) => await ctx.Guild.Id.ExecuteWithMusicSessionAsync(successAction, failureAction, defaultValue); /// @@ -202,7 +204,7 @@ public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func /// The result of the action or the default value with given type from . - public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func> successAction, Func>? failureAction = null, T? defaultValue = default) + public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func> successAction, Func>? failureAction = null, T defaultValue = default) { var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); using (await asyncLock.LockAsync(MikuBot.Cts.Token)) From e3bb6ca46e084494ec87ada970a05e43363e52a6 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 4 Feb 2025 16:06:19 +0100 Subject: [PATCH 053/113] Update DiscordOptionProviders.cs --- MikuSharp/Utilities/DiscordOptionProviders.cs | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 041bab6b..10424f78 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,5 +1,3 @@ -using NuGet.Packaging; - namespace MikuSharp.Utilities; internal class FixedOptionProviders @@ -27,9 +25,9 @@ public async Task> Prov { var bans = await ctx.Guild.GetBansAsync(); List bannedUsers = new(25); - bannedUsers.AddRange(ctx.FocusedOption.Value is null + bannedUsers.AddRange(ctx.FocusedOption.Value is not string value ? bans.Take(25) - : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value))).Take(25)); + : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(value))).Take(25)); return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); } @@ -89,30 +87,20 @@ internal sealed class QueueProvider : IAutocompleteProvider { public async Task> Provider(AutocompleteContext ctx) { - return await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + return await ctx.ExecuteWithMusicSessionAsync((_, musicSession) => { - await Task.Delay(0); - - Dictionary queueEntries = []; - Dictionary filteredQueueEntries = []; - var queue = musicSession.LavalinkGuildPlayer?.Queue.ToList(); - if (queue is null) - return [new("The queue is empty", -1)]; - - var i = 1; - foreach (var entry in queue) - { - queueEntries[i] = entry; - i++; - } + if (queue is null || queue.Count is 0) + return Task.FromResult>([new("The queue is empty", -1)]); + var queueEntries = queue + .Select((entry, index) => (index: index + 1, entry)) + .ToDictionary(x => x.index, x => x.entry); var value = ctx.FocusedOption.Value as string; - filteredQueueEntries.AddRange(string.IsNullOrEmpty(value) + var filteredQueueEntries = string.IsNullOrEmpty(value) ? queueEntries.Take(25) - : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value)!.ToLower())).Take(25)); - - return filteredQueueEntries.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key - 1)); + : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(value.ToLower())).Take(25); + return Task.FromResult(filteredQueueEntries.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key - 1))); }, null, [new("The queue is empty", -1)]); } } From d32ca683227fe2b2e8808ebab62aa0583b0c4fa1 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 5 Feb 2025 02:33:20 +0100 Subject: [PATCH 054/113] fix --- MikuSharp/Commands/Action.cs | 147 +++++++++++++++++---- MikuSharp/Utilities/WebExtensionMethods.cs | 2 +- 2 files changed, 120 insertions(+), 29 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index e57c295f..8978d282 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -1,26 +1,37 @@ using HeyRed.Mime; +using MikuSharp.Attributes; using MikuSharp.Utilities; namespace MikuSharp.Commands; -[SlashCommandGroup("action", "Actions", false, [InteractionContextType.Guild, InteractionContextType.PrivateChannel], [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall])] +[SlashCommandGroup("action", "Actions", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] internal class Action : ApplicationCommandsModule { [SlashCommand("hug", "Hug someone!")] public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug"); if (wsh is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } - wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); - DiscordWebhookBuilder builder = new(); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A wild hug appears!"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} hugs {user.Mention} uwu")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + + wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); builder.WithContent(user.Mention); @@ -31,17 +42,27 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u [SlashCommand("kiss", "Kiss someone!")] public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss"); if (wsh is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } - wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); - DiscordWebhookBuilder builder = new(); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A kiss!"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} kisses {user.Mention} >~<")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + + wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); builder.WithContent(user.Mention); @@ -52,17 +73,27 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("lick", "Lick someone!")] public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo")); var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick"); if (wsh is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } - wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); - DiscordWebhookBuilder builder = new(); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slurp~"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} licks {user.Mention} owo")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + + wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); builder.AddEmbed(wsh.Embed.Build()); builder.WithContent(user.Mention); @@ -73,21 +104,31 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("pat", "Pat someone!")] public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", []); if (weeurl is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } + DiscordWebhookBuilder builder = new(); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Pat pat :3"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} pats {user.Mention} #w#")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); builder.WithContent(user.Mention); @@ -98,21 +139,31 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u [SlashCommand("poke", "Poke someone!")] public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", []); if (weeurl is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } + DiscordWebhookBuilder builder = new(); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Poke poke :p"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); builder.WithContent(user.Mention); @@ -123,21 +174,31 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("slap", "Slap someone!")] public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", []); if (weeurl is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } + DiscordWebhookBuilder builder = new(); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slap x~x"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); builder.WithContent(user.Mention); @@ -148,21 +209,31 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("bite", "Bite someone!")] public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", []); if (weeurl is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } + DiscordWebhookBuilder builder = new(); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Bite~"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} bites {user.Mention} x~x")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} bites {user.Mention} x~x"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); builder.WithContent(user.Mention); @@ -173,21 +244,31 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The [SlashCommand("nom", "Nom someone!")] public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", []); if (weeurl is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } + DiscordWebhookBuilder builder = new(); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Noms~"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} noms {user.Mention} >:3c")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} noms {user.Mention} >:3c"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); builder.WithContent(user.Mention); @@ -198,21 +279,31 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u [SlashCommand("stare", "Stare at someone!")] public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"{ctx.User.Mention} stares {user.Mention} O.o")); var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", []); if (weeurl is null) { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} stares at {user.Mention} O.o").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } + DiscordWebhookBuilder builder = new(); var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); + + if (ctx.GuildId is 1317206872763404478) + { + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Stare O.o"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} stares at {user.Mention} O.o")])); + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + await ctx.EditResponseAsync(builder); + return; + } + var em = new DiscordEmbedBuilder(); em.WithDescription($"{ctx.User.Mention} stares at {user.Mention} O.o"); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); builder.AddEmbed(em.Build()); builder.WithContent(user.Mention); diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index d8035c4e..fa47721f 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -97,7 +97,7 @@ public static class WebExtensionMethods /// The weeb.sh response. public static async Task GetWeebShAsync(this HttpClient client, string query, IEnumerable? tags = null, NsfwSearch nsfw = NsfwSearch.False) { - var dl = await MikuBot.WeebClient.GetRandomAsync(query, tags, nsfw: nsfw); + var dl = await MikuBot.WeebClient.GetRandomAsync(query, tags ?? [""], nsfw: nsfw); if (dl is null) return null; From 56d313baa3b7b42b4b543e145ec319a911ffd136 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 5 Feb 2025 03:12:19 +0100 Subject: [PATCH 055/113] Update Action.cs --- MikuSharp/Commands/Action.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index 8978d282..e257e19e 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -25,7 +25,7 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u { builder.WithV2Components(); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A wild hug appears!"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} hugs {user.Mention} uwu")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A wild hug appears!"), new DiscordTextDisplayComponent($"{ctx.User.Mention} hugs {user.Mention} uwu"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -45,7 +45,7 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss"); if (wsh is null) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); return; } @@ -56,7 +56,7 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The { builder.WithV2Components(); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A kiss!"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} kisses {user.Mention} >~<")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A kiss!"), new DiscordTextDisplayComponent($"{ctx.User.Mention} kisses {user.Mention} >~<"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -87,7 +87,7 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The { builder.WithV2Components(); builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slurp~"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} licks {user.Mention} owo")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slurp~"), new DiscordTextDisplayComponent($"{ctx.User.Mention} licks {user.Mention} owo"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -119,7 +119,7 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u { builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Pat pat :3"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} pats {user.Mention} #w#")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Pat pat :3"), new DiscordTextDisplayComponent($"{ctx.User.Mention} pats {user.Mention} #w#"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -154,7 +154,7 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The { builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Poke poke :p"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Poke poke :p"), new DiscordTextDisplayComponent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -189,7 +189,7 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The { builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slap x~x"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slap x~x"), new DiscordTextDisplayComponent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -224,7 +224,7 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The { builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Bite~"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} bites {user.Mention} x~x")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Bite~"), new DiscordTextDisplayComponent($"{ctx.User.Mention} bites {user.Mention} x~x"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -259,7 +259,7 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u { builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Noms~"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} noms {user.Mention} >:3c")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Noms~"), new DiscordTextDisplayComponent($"{ctx.User.Mention} noms {user.Mention} >:3c"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; @@ -294,7 +294,7 @@ public static async Task StateAsync(InteractionContext ctx, [Option("user", "The { builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Stare O.o"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")]), new DiscordTextDisplayComponent($"{ctx.User.Mention} stares at {user.Mention} O.o")])); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Stare O.o"), new DiscordTextDisplayComponent($"{ctx.User.Mention} stares at {user.Mention} O.o"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); await ctx.EditResponseAsync(builder); return; From 06b58e1ce0466cc5fa58b4b390bcfd5733b8e2ec Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 5 Feb 2025 07:59:53 +0100 Subject: [PATCH 056/113] raaaaaaaaa --- MikuSharp/Commands/Music/MusicCommands.cs | 79 ++++++++++++++++++++++- 1 file changed, 76 insertions(+), 3 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index be96eae4..dc06c750 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -1,3 +1,5 @@ +using DisCatSharp.Exceptions; + using MikuSharp.Attributes; using MikuSharp.Entities; using MikuSharp.Utilities; @@ -7,14 +9,14 @@ namespace MikuSharp.Commands.Music; /// /// The music commands /// -[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true), EnsureLavalinkSession] +[SlashCommandGroup("music", "Music commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), EnsureLavalinkSession] public partial class MusicCommands : ApplicationCommandsModule { /// /// Joins a voice channel the user is in. /// /// The interaction context. - [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession] + [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection, AutomaticallyDisconnectExistingSession, DeferResponseAsync(true)] public async Task JoinAsync(InteractionContext ctx) { ArgumentNullException.ThrowIfNull(ctx.Member?.VoiceState?.Channel); @@ -37,7 +39,7 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, _) => await ctx.EditResponseAsy /// Leaves a voice channel. /// /// The interaction context. - [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection] + [SlashCommand("leave", "Leaves the voice channel"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public async Task LeaveAsync(InteractionContext ctx) { await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => @@ -52,4 +54,75 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")), guildId => Task.FromResult(MikuBot.MusicSessionLocks.TryRemove(guildId, out _))); } + + [SlashCommand("test", "Test UI Kit"), ApplicationCommandRequireTeamMember, DeferResponseAsync] + public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The identifier (lavalink identifier, url, etc..)")] string identifier) + { + LavalinkTrack track; + var session = ctx.Client.GetLavalink().DefaultSession()!; + var loadResult = await session.LoadTracksAsync(identifier); + switch (loadResult.LoadType) + { + case LavalinkLoadResultType.Track: + track = loadResult.GetResultAs(); + break; + case LavalinkLoadResultType.Search: + var tracks = loadResult.GetResultAs>(); + track = tracks.First(); + break; + case LavalinkLoadResultType.Playlist: + case LavalinkLoadResultType.Empty: + case LavalinkLoadResultType.Error: + default: + throw new ArgumentOutOfRangeException(null, "Unsupported result type"); + } + + var artistArtworkUrl = track.PluginInfo.AdditionalProperties.TryGetValue("artistArtworkUrl", out var url1) ? url1.ToString()! : null; + var albumName = track.PluginInfo.AdditionalProperties.TryGetValue("albumName", out var url2) ? url2.ToString() : null; + var albumUrl = track.PluginInfo.AdditionalProperties.TryGetValue("albumUrl", out var url3) ? url3.ToString() : null; + var artistUrl = track.PluginInfo.AdditionalProperties.TryGetValue("artistUrl", out var url4) ? url4.ToString() : null; + + DiscordSeparatorComponent separator = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent trackInfo = new($"### {track.Info.Title}\n**Artist:** {track.Info.Author.InlineCode()}\n**Length:** {track.Info.Length.FormatTimeSpan().InlineCode()}{(!string.IsNullOrEmpty(albumName) ? $"\n**Album:** {albumName.InlineCode()}" : "")}"); + DiscordSectionComponent trackInfoSection = new([trackInfo]); + trackInfoSection.WithThumbnailComponent(artistArtworkUrl!, "Artist Artwork"); + DiscordTextDisplayComponent additionalTrackInfo = new($"-# **ISrc:** {track.Info.Isrc?.InlineCode() ?? "none".InlineCode()} **Identifier:** {track.Info.Identifier.InlineCode()}"); + DiscordMediaGalleryItem trackCover = new(track.Info.ArtworkUrl!.AbsoluteUri, "Song Artwork"); + DiscordMediaGalleryComponent trackMediaGallery = new([trackCover]); + DiscordLinkButtonComponent trackLink = new(track.Info.Uri.AbsoluteUri, "View Track", emoji: new(GetEmojiBasedOnIdentifier(identifier))); + DiscordActionRowComponent links1 = new([trackLink]); + List infoComponents = [string.IsNullOrEmpty(artistArtworkUrl) ? trackInfo : trackInfoSection, separator, trackMediaGallery, separator, links1]; + if (!string.IsNullOrEmpty(albumUrl)) + { + DiscordLinkButtonComponent albumLink = new(albumUrl, "View Album", emoji: new(GetEmojiBasedOnIdentifier(identifier))); + DiscordActionRowComponent links2 = new([albumLink]); + infoComponents.Add(links2); + } + + if (!string.IsNullOrEmpty(artistUrl)) + { + DiscordLinkButtonComponent artistLink = new(artistUrl, "View Artist", emoji: new(GetEmojiBasedOnIdentifier(identifier))); + DiscordActionRowComponent links3 = new([artistLink]); + infoComponents.Add(links3); + } + + infoComponents.AddRange([separator, additionalTrackInfo]); + + DiscordContainerComponent infoContainer = new([..infoComponents]); + try + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(infoContainer)); + } + catch (BadRequestException ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.Errors?.BlockCode("json") ?? ex.Message)); + } + + return; + + ulong GetEmojiBasedOnIdentifier(string ident) + => ident.ToLowerInvariant().Contains("spotify") + ? 1336571943687688252 + : (ulong)1336587088132440115; + } } From 89256db1a13edda4a65fc0688741e7a05ae32689 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 5 Feb 2025 09:46:49 +0100 Subject: [PATCH 057/113] Update MusicCommands.cs --- MikuSharp/Commands/Music/MusicCommands.cs | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index dc06c750..046c1ef0 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -58,6 +58,7 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => [SlashCommand("test", "Test UI Kit"), ApplicationCommandRequireTeamMember, DeferResponseAsync] public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The identifier (lavalink identifier, url, etc..)")] string identifier) { + DiscordWebhookBuilder builder = new(); LavalinkTrack track; var session = ctx.Client.GetLavalink().DefaultSession()!; var loadResult = await session.LoadTracksAsync(identifier); @@ -77,6 +78,8 @@ public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The i throw new ArgumentOutOfRangeException(null, "Unsupported result type"); } + var lyrics = await session.GetLyricsAsync(track); + var artistArtworkUrl = track.PluginInfo.AdditionalProperties.TryGetValue("artistArtworkUrl", out var url1) ? url1.ToString()! : null; var albumName = track.PluginInfo.AdditionalProperties.TryGetValue("albumName", out var url2) ? url2.ToString() : null; var albumUrl = track.PluginInfo.AdditionalProperties.TryGetValue("albumUrl", out var url3) ? url3.ToString() : null; @@ -108,19 +111,36 @@ public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The i infoComponents.AddRange([separator, additionalTrackInfo]); + MemoryStream? ms = null; + if (lyrics is not null) + { + var lyricString = $"Lyrics for {lyrics.SourceName} by {lyrics.Provider}\n\n"; + lyricString += string.Join("\n", lyrics.Lines.Select(line => string.IsNullOrEmpty(line.Line) ? string.Empty : $"{TimeSpan.FromMilliseconds(line.Timestamp).FormatTimeSpan()}{(line.Duration.HasValue ? $" {TimeSpan.FromMilliseconds(line.Duration.Value).FormatTimeSpan()}" : string.Empty)}: {line.Line}")); + ms = new(Encoding.UTF8.GetBytes(lyricString)); + ms.Position = 0; + builder.AddFile("lyrics.txt", ms, description: $"Lyrics for {track.Info.Title}"); + DiscordFileDisplayComponent lyricsComponent = new("attachment://lyrics.txt", null); + infoComponents.AddRange([separator, lyricsComponent]); + } + DiscordContainerComponent infoContainer = new([..infoComponents]); try { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(infoContainer)); + await ctx.EditResponseAsync(builder.WithV2Components().AddComponents(infoContainer)); } catch (BadRequestException ex) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(ex.Errors?.BlockCode("json") ?? ex.Message)); + await ctx.EditResponseAsync(builder.WithContent(ex.Errors?.BlockCode("json") ?? ex.Message)); + } + finally + { + if (ms is not null) + await ms.DisposeAsync(); } return; - ulong GetEmojiBasedOnIdentifier(string ident) + static ulong GetEmojiBasedOnIdentifier(string ident) => ident.ToLowerInvariant().Contains("spotify") ? 1336571943687688252 : (ulong)1336587088132440115; From 7486e08f47bb06246f58b4ea7b5117a8d27113c0 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 6 Feb 2025 00:49:51 +0100 Subject: [PATCH 058/113] aaaaa --- MikuSharp/Commands/Music/MusicCommands.cs | 36 ++++++++-------------- MikuSharp/Utilities/Formatters.cs | 37 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 046c1ef0..5cca0569 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -86,47 +86,44 @@ public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The i var artistUrl = track.PluginInfo.AdditionalProperties.TryGetValue("artistUrl", out var url4) ? url4.ToString() : null; DiscordSeparatorComponent separator = new(false, SeparatorSpacingSize.Small); - DiscordTextDisplayComponent trackInfo = new($"### {track.Info.Title}\n**Artist:** {track.Info.Author.InlineCode()}\n**Length:** {track.Info.Length.FormatTimeSpan().InlineCode()}{(!string.IsNullOrEmpty(albumName) ? $"\n**Album:** {albumName.InlineCode()}" : "")}"); + DiscordTextDisplayComponent trackInfo = new($"### {track.Info.Title}\n**Artist:** {track.Info.Author.InlineCode()}\n**Length:** {track.Info.Length.FormatTimeSpan().InlineCode()}{(!string.IsNullOrEmpty(albumName) ? $"\n**Album:** {albumName.InlineCode()}" : "")}\n**Stream:** {track.Info.IsStream.ToString().ToLowerInvariant().InlineCode()}"); DiscordSectionComponent trackInfoSection = new([trackInfo]); trackInfoSection.WithThumbnailComponent(artistArtworkUrl!, "Artist Artwork"); DiscordTextDisplayComponent additionalTrackInfo = new($"-# **ISrc:** {track.Info.Isrc?.InlineCode() ?? "none".InlineCode()} **Identifier:** {track.Info.Identifier.InlineCode()}"); DiscordMediaGalleryItem trackCover = new(track.Info.ArtworkUrl!.AbsoluteUri, "Song Artwork"); DiscordMediaGalleryComponent trackMediaGallery = new([trackCover]); - DiscordLinkButtonComponent trackLink = new(track.Info.Uri.AbsoluteUri, "View Track", emoji: new(GetEmojiBasedOnIdentifier(identifier))); + DiscordLinkButtonComponent trackLink = new(track.Info.Uri.AbsoluteUri, "View", emoji: new(track.Info.SourceName.GetEmojiBasedOnSourceName())); DiscordActionRowComponent links1 = new([trackLink]); List infoComponents = [string.IsNullOrEmpty(artistArtworkUrl) ? trackInfo : trackInfoSection, separator, trackMediaGallery, separator, links1]; if (!string.IsNullOrEmpty(albumUrl)) { - DiscordLinkButtonComponent albumLink = new(albumUrl, "View Album", emoji: new(GetEmojiBasedOnIdentifier(identifier))); + DiscordLinkButtonComponent albumLink = new(albumUrl, "View Album", emoji: new(track.Info.SourceName.GetEmojiBasedOnSourceName())); DiscordActionRowComponent links2 = new([albumLink]); infoComponents.Add(links2); } if (!string.IsNullOrEmpty(artistUrl)) { - DiscordLinkButtonComponent artistLink = new(artistUrl, "View Artist", emoji: new(GetEmojiBasedOnIdentifier(identifier))); + DiscordLinkButtonComponent artistLink = new(artistUrl, "View Artist", emoji: new(track.Info.SourceName.GetEmojiBasedOnSourceName())); DiscordActionRowComponent links3 = new([artistLink]); infoComponents.Add(links3); } infoComponents.AddRange([separator, additionalTrackInfo]); - MemoryStream? ms = null; - if (lyrics is not null) - { - var lyricString = $"Lyrics for {lyrics.SourceName} by {lyrics.Provider}\n\n"; - lyricString += string.Join("\n", lyrics.Lines.Select(line => string.IsNullOrEmpty(line.Line) ? string.Empty : $"{TimeSpan.FromMilliseconds(line.Timestamp).FormatTimeSpan()}{(line.Duration.HasValue ? $" {TimeSpan.FromMilliseconds(line.Duration.Value).FormatTimeSpan()}" : string.Empty)}: {line.Line}")); - ms = new(Encoding.UTF8.GetBytes(lyricString)); - ms.Position = 0; - builder.AddFile("lyrics.txt", ms, description: $"Lyrics for {track.Info.Title}"); - DiscordFileDisplayComponent lyricsComponent = new("attachment://lyrics.txt", null); - infoComponents.AddRange([separator, lyricsComponent]); - } - DiscordContainerComponent infoContainer = new([..infoComponents]); + MemoryStream? ms = null; try { await ctx.EditResponseAsync(builder.WithV2Components().AddComponents(infoContainer)); + + if (lyrics is not null && lyrics.Lines.Count > 0) + { + var lyricString = string.Join("\n", lyrics.Lines.Select(line => string.IsNullOrEmpty(line.Line) ? string.Empty : $"{TimeSpan.FromMilliseconds(line.Timestamp).FormatTimeSpan()}{(line.Duration.HasValue ? $" {TimeSpan.FromMilliseconds(line.Duration.Value).FormatTimeSpan()}" : string.Empty)}: {line.Line}")); + ms = new(Encoding.UTF8.GetBytes(lyricString)); + ms.Position = 0; + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Lyrics for {lyrics.SourceName}{(!string.IsNullOrEmpty(lyrics.Provider) ? $" by {lyrics.Provider}" : string.Empty)}").AddFile("SPOILER_lyrics.txt", ms, description: $"Lyrics for {track.Info.Title}")); + } } catch (BadRequestException ex) { @@ -137,12 +134,5 @@ public async Task TestAsync(InteractionContext ctx, [Option("identifier", "The i if (ms is not null) await ms.DisposeAsync(); } - - return; - - static ulong GetEmojiBasedOnIdentifier(string ident) - => ident.ToLowerInvariant().Contains("spotify") - ? 1336571943687688252 - : (ulong)1336587088132440115; } } diff --git a/MikuSharp/Utilities/Formatters.cs b/MikuSharp/Utilities/Formatters.cs index 924d1e6c..1aef21a5 100644 --- a/MikuSharp/Utilities/Formatters.cs +++ b/MikuSharp/Utilities/Formatters.cs @@ -24,4 +24,41 @@ public static string FormatTimeSpan(this TimeSpan timeSpan) : timeSpan.TotalMinutes >= 1 ? $"{(int)timeSpan.TotalMinutes:D2}m:{timeSpan.Seconds:D2}s" : $"{(int)timeSpan.TotalSeconds:D2}s"; + + /// + /// Maps a source name to an emoji. + /// + /// The source name. + /// The id of the mapped emoji. + public static ulong GetEmojiBasedOnSourceName(this string source) + { +#if DEBUG + return source.ToLowerInvariant() switch + { + "applemusic" => 1336837190805885050, + "yandexmusic" => 1336836824420716545, + "flowerytts" => 1336836652911300680, + "deezer" => 1336836375948824576, + "vkmusic" => 1336836363265511455, + "soundcloud" => 1336836056225681522, + "bandcamp" => 1336835936499011615, + "http" => 1336835679748882433, + "twitch" => 1336835463293702267, + "vimeo" => 1336835333358092411, + "nico" => 1336835252076941342, + "youtube" => 1336834499903881327, + "spotify" => 1336834471277760582, + "local" => 1336838168170987520, + "bilibili" => 1336840488786985013, + _ => 1336839088678113482 + }; +#else + return source.ToLowerInvariant() switch + { + "spotify" => 1336571943687688252, + "youtube" => 1336587088132440115, + _ => 1336624959207903283 + }; +#endif + } } From cfa3654b23d4f8ba177070372569da8852b55ed0 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 6 Feb 2025 06:37:11 +0100 Subject: [PATCH 059/113] nom --- MikuSharp.sln | 3 +++ MikuSharp/Commands/Fun.cs | 6 +++++- MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs | 2 +- MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs | 7 ++++--- MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs | 2 +- MikuSharp/MikuSharp.csproj | 2 ++ MikuSharp/Utilities/DiscordOptionProviders.cs | 2 +- MikuSharp/Utilities/MusicSessionExtensionMethods.cs | 5 ++++- 8 files changed, 21 insertions(+), 8 deletions(-) diff --git a/MikuSharp.sln b/MikuSharp.sln index 17cf08fe..186ede68 100644 --- a/MikuSharp.sln +++ b/MikuSharp.sln @@ -15,6 +15,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Lavalink", "..\DisCatSharp\DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DisCatSharp", "DisCatSharp", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" + ProjectSection(SolutionItems) = preProject + ..\DisCatSharp\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj = ..\DisCatSharp\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp", "..\DisCatSharp\DisCatSharp\DisCatSharp.csproj", "{5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}" EndProject diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 8ea402bf..67379595 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -1,3 +1,7 @@ +using System.Net; + +using AngleSharp.Text; + using HeyRed.Mime; using MikuSharp.Attributes; @@ -35,7 +39,7 @@ public static async Task CatAsync(InteractionContext ctx) [SlashCommand("clyde", "Say something as clyde bot")] public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={text}")); + var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={WebUtility.UrlEncode(text)}")); if (e is null) { await ctx.EditResponseAsync("Something went wrong while fetching the image."); diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index 1c2cf4df..b8e34a4b 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -8,7 +8,7 @@ public partial class MusicCommands /// /// The options commands. /// - [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection] + [SlashCommandGroup("options", "Music options commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public class OptionsCommands : ApplicationCommandsModule { /// diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index 76c9598b..c0fcffb5 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -9,7 +9,7 @@ public partial class MusicCommands /// /// The playback commands. /// - [SlashCommandGroup("playback", "Music playback commands"), RequireUserAndBotVoicechatConnection] + [SlashCommandGroup("playback", "Music playback commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public class PlaybackCommands : ApplicationCommandsModule { /// @@ -103,11 +103,12 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => /// /// The interaction context. /// The url to play. + /// Whether to shuffle playlists. [SlashCommand("play", "Plays a url")] - public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url) + public async Task PlayUrlAsync(InteractionContext ctx, [Option("url", "The url to play")] string url, [Option("shuffle_playlist", "Shuffle playlists")] bool shufflePlaylists = false) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Searching for `{url}`..")); - await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await musicSession.LoadAndPlayTrackAsync(ctx, url)); + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await musicSession.LoadAndPlayTrackAsync(ctx, url, shufflePlaylists)); } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index c64d1e13..35bc0cf7 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -8,7 +8,7 @@ public partial class MusicCommands /// /// The queue commands. /// - [SlashCommandGroup("queue", "Music queue commands"), RequireUserAndBotVoicechatConnection] + [SlashCommandGroup("queue", "Music queue commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public class QueueCommands : ApplicationCommandsModule { /// diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 10c859aa..0c9badea 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -48,6 +48,7 @@ + @@ -61,6 +62,7 @@ + diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 10424f78..5d1c3cba 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -89,6 +89,7 @@ public async Task> Prov { return await ctx.ExecuteWithMusicSessionAsync((_, musicSession) => { + var value = ctx.FocusedOption.Value?.ToString(); var queue = musicSession.LavalinkGuildPlayer?.Queue.ToList(); if (queue is null || queue.Count is 0) return Task.FromResult>([new("The queue is empty", -1)]); @@ -96,7 +97,6 @@ public async Task> Prov var queueEntries = queue .Select((entry, index) => (index: index + 1, entry)) .ToDictionary(x => x.index, x => x.entry); - var value = ctx.FocusedOption.Value as string; var filteredQueueEntries = string.IsNullOrEmpty(value) ? queueEntries.Take(25) : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(value.ToLower())).Take(25); diff --git a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs index 44528899..8ebe02ea 100644 --- a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs +++ b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs @@ -60,10 +60,11 @@ public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, List /// The music session. /// The interaction context. /// The identifier to load. + /// Whether to shuffle playlists. Defaults to . /// The optional search type. Defaults to . /// Whether the track was successfully loaded and added to the queue. /// - public static async Task LoadAndPlayTrackAsync(this MusicSession musicSession, InteractionContext ctx, string identifier, LavalinkSearchType searchType = LavalinkSearchType.Plain) + public static async Task LoadAndPlayTrackAsync(this MusicSession musicSession, InteractionContext ctx, string identifier, bool shufflePlaylists = false, LavalinkSearchType searchType = LavalinkSearchType.Plain) { var loadResult = await musicSession.LavalinkGuildPlayer.LoadTracksAsync(searchType, identifier); switch (loadResult.LoadType) @@ -76,6 +77,8 @@ public static async Task LoadAndPlayTrackAsync(this MusicSession m case LavalinkLoadResultType.Playlist: var playlist = loadResult.GetResultAs(); musicSession.LavalinkGuildPlayer.AddToQueue(playlist); + if (shufflePlaylists) + musicSession.LavalinkGuildPlayer.ShuffleQueue(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Added playlist {playlist.Info.Name.Bold()} to the queue.")); break; case LavalinkLoadResultType.Search: From 371594f85052e48d3857b9ff3cfdc8f909b52d4a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 6 Feb 2025 07:42:43 +0100 Subject: [PATCH 060/113] feat: more fun commands from nekobot.xyz --- MikuSharp/Commands/Fun.cs | 372 +++++++++++++----- MikuSharp/Commands/Weeb.cs | 14 - MikuSharp/Utilities/DiscordOptionProviders.cs | 4 +- 3 files changed, 279 insertions(+), 111 deletions(-) diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 67379595..fab1092b 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -1,7 +1,3 @@ -using System.Net; - -using AngleSharp.Text; - using HeyRed.Mime; using MikuSharp.Attributes; @@ -16,8 +12,75 @@ internal class Fun : ApplicationCommandsModule [SlashCommand("8ball", "Yes? No? Maybe?")] public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - var responses = new[] { "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No." }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{responses[new Random().Next(0, responses.Length)]}")); + var responses = new[] + { + "It is certain.", + "It is decidedly so.", + "Without a doubt.", + "Yes - definitely.", + "You may rely on it.", + "As I see it, yes.", + "Most likely.", + "Outlook good.", + "Yes.", + "Signs point to yes.", + "Absolutely!", + "Of course!", + "No doubt about it.", + "The universe says yes.", + "You got it!", + "Definitely!", + "All signs point to yes.", + "The answer is crystal clear.", + "Yes, in due time.", + "The stars align in your favor.", + "Reply hazy, try again.", + "Ask again later.", + "Better not tell you now.", + "Cannot predict now.", + "Concentrate and ask again.", + "Maybe, maybe not.", + "Uncertain, check back later.", + "Hard to say.", + "Try flipping a coin.", + "Your guess is as good as mine.", + "The future is unclear.", + "I can't say for sure.", + "It's a mystery.", + "Only time will tell.", + "50/50 chance.", + "Don't count on it.", + "My reply is no.", + "My sources say no.", + "Outlook not so good.", + "Very doubtful.", + "No.", + "Absolutely not.", + "I wouldn’t bet on it.", + "No way!", + "Highly unlikely.", + "Not in a million years.", + "Doubtful at best.", + "The odds aren’t in your favor.", + "The universe says no.", + "Nope." + }; + + var chosenAnswer = responses[new Random().Next(0, responses.Length)]; + if (ctx.GuildId is 1317206872763404478) + { + DiscordTextDisplayComponent question = new($"### Question\n{text}"); + DiscordSectionComponent questionComponent = new([question]); + questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); + DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent answer = new($"### Answer\n{chosenAnswer}"); + DiscordSectionComponent answerComponent = new([answer]); + answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([questionComponent]), seperator, new DiscordContainerComponent([answerComponent]))); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{chosenAnswer}")); } [SlashCommand("cat", "Get a random cat image!")] @@ -36,23 +99,6 @@ public static async Task CatAsync(InteractionContext ctx) await ctx.EditResponseAsync(builder); } - [SlashCommand("clyde", "Say something as clyde bot")] - public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=clyde&text={WebUtility.UrlEncode(text)}")); - if (e is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; - } - - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.Message)); - - DiscordWebhookBuilder builder = new(); - builder.AddFile("clyde.png", img); - await ctx.EditResponseAsync(builder); - } - [SlashCommand("coinflip", "Flip a coin lol")] public static async Task CoinflipAsync(InteractionContext ctx) { @@ -82,33 +128,28 @@ public static async Task DogAsync(InteractionContext ctx) builder.AddEmbed(em.Build()); await ctx.EditResponseAsync(builder); } - /* + [SlashCommand("duck", "Random duck image")] public static async Task DuckAsync(InteractionContext ctx) { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(dc.message))); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); - em.WithDescription($"[Full Image]({dc.message})"); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.WithEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - }*/ - /* - [SlashCommand("lion", "Get a random lion image")] - public static async Task Lion(InteractionContext ctx) - { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/lion")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); - - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ + var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); + if (dc is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); + em.WithDescription($"[Full Image]({dc.Message})"); + + var builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + } [SlashCommand("lizard", "Get a random lizard image")] public static async Task LizardAsync(InteractionContext ctx) @@ -126,69 +167,210 @@ public static async Task LizardAsync(InteractionContext ctx) builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); await ctx.EditResponseAsync(builder); } - /* - [SlashCommand("panda", "Random panda image")] - public static async Task PandaAsync(InteractionContext ctx) + + [SlashCommand("rps", "Play rock paper scissors!")] + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/panda")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); + } - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); + [SlashCommand("stickbug", "Get stickbugged!")] + public static async Task StickbugAsync(InteractionContext ctx, [Option("user", "User to stickbug")] DiscordUser? user = null) + { + user ??= ctx.User; + await GenerateImageAsync(ctx, "stickbug", new() + { + { "url", user.AvatarUrl } + }); } - [SlashCommand("penguin", "Radnom penguin image")] - public static async Task PenguinAsync(InteractionContext ctx) + [SlashCommand("trash", "Trash waifu image generator.")] + public static async Task TrashAsync(InteractionContext ctx, [Option("user", "User to trash")] DiscordUser? user = null) { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/penguin")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + user ??= ctx.User; + await GenerateImageAsync(ctx, "trash", new() + { + { "url", user.AvatarUrl } + }); + } - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ + [SlashCommand("magik", "Magikify an image!")] + public static async Task MagikAsync(InteractionContext ctx, [Option("user", "User to magikify")] DiscordUser? user = null, [Option("intensity", "Magik intensity (0-10)")] int intensity = 5) + { + user ??= ctx.User; + await GenerateImageAsync(ctx, "magik", new() + { + { "image", user.AvatarUrl }, + { "intensity", intensity.ToString() } + }); + } - /* - [SlashCommand("redpanda", "Random red panda image")] - public static async Task RedPandaAsync(InteractionContext ctx) + [SlashCommand("phcomment", "Make a PH comment!")] + public static async Task PhCommentAsync(InteractionContext ctx, [Option("text", "Text to comment.")] string text, [Option("username", "Username to use")] string? username = null) { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/red_panda")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + username ??= ctx.User.Username; + await GenerateImageAsync(ctx, "phcomment", new() + { + { "image", ctx.User.AvatarUrl }, + { "text", text }, + { "username", username } + }); + } - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ + [SlashCommand("blurpify", "Blurpify an image!")] + public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", "User to blurpify")] DiscordUser? user = null) + { + user ??= ctx.User; + await GenerateImageAsync(ctx, "blurpify", new() + { + { "url", user.AvatarUrl } + }); + } - [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) + [SlashCommand("deepfry", "Deepfry an image!")] + public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "User to deepfry")] DiscordUser? user = null) { - var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); + user ??= ctx.User; + await GenerateImageAsync(ctx, "deepfry", new() + { + { "image", user.AvatarUrl } + }); } - /* - [SlashCommand("tiger", "Random tiger image")] - public static async Task TigerAsync(InteractionContext ctx) + + [SlashCommand("kidnap", "Kidnap a user!")] + public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "User to kidnap")] DiscordUser? user = null) { - var ImgLink = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://animals.anidiots.guide/tiger")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(Other.resizeLink(ImgLink.link))); + user ??= ctx.User; + await GenerateImageAsync(ctx, "kidnap", new() + { + { "image", user.AvatarUrl } + }); + } - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - }*/ + [SlashCommand("tweet", "Generate a fake tweet!")] + public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("username", "Username for tweet")] string? username = null) + { + username ??= ctx.User.Username; + await GenerateImageAsync(ctx, "tweet", new() + { + { "username", username }, + { "text", text } + }); + } - /* - [SlashCommand("trumptweet", "generate a tweet by Trump")] - public static async Task TrumpTweetAsync(InteractionContext ctx, [RemainingText]string text) + [SlashCommand("trap", "Trap someone!")] + public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User to trap")] DiscordUser user, [Option("author", "User trapping them")] DiscordUser? author = null) { - //https://nekobot.xyz/api/imagegen?type=trumptweet&text= - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=trumptweet&text={text}")); - Stream img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(e.message)); + author ??= ctx.User; + await GenerateImageAsync(ctx, "trap", new() + { + { "name", user.Username }, + { "author", author.Username }, + { "image", user.AvatarUrl } + }); + } + + [SlashCommand("iphonex", "Insert an image into an iPhone X frame.")] + public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "User to insert")] DiscordUser? user = null) + { + user ??= ctx.User; + await GenerateImageAsync(ctx, "iphonex", new() + { + { "url", user.AvatarUrl } + }); + } - DiscordWebhookBuilder builder = new DiscordWebhookBuilder(); - builder.AddFile($"trump.png", img); - await ctx.EditResponseAsync(builder); - }*/ + [SlashCommand("lolice", "Call the lolice!")] + public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "User to call lolice on")] DiscordUser? user = null) + { + user ??= ctx.User; + await GenerateImageAsync(ctx, "lolice", new() + { + { "url", user.AvatarUrl } + }); + } + + [SlashCommand("kannagen", "Kanna says something!")] + public static async Task KannaGenAsync(InteractionContext ctx, [Option("text", "Text for Kanna")] string text) + { + await GenerateImageAsync(ctx, "kannagen", new() + { + { "text", text } + }); + } + + [SlashCommand("changemymind", "Change my mind meme generator.")] + public static async Task ChangeMyMindAsync(InteractionContext ctx, [Option("text", "Change my mind text")] string text) + { + await GenerateImageAsync(ctx, "changemymind", new() + { + { "text", text } + }); + } + + [SlashCommand("whowouldwin", "Who would win?")] + public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) + { + await GenerateImageAsync(ctx, "whowouldwin", new() + { + { "user1", user1.AvatarUrl }, + { "user2", user2.AvatarUrl } + }); + } + + [SlashCommand("captcha", "Generate a fake captcha!")] + public static async Task CaptchaAsync(InteractionContext ctx, [Option("username", "Username to display")] string? username = null) + { + username ??= ctx.User.Username; + await GenerateImageAsync(ctx, "captcha", new() + { + { "url", ctx.User.AvatarUrl }, + { "username", username } + }); + } + + [SlashCommand("ship", "Ship two users!")] + public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) + { + await GenerateImageAsync(ctx, "ship", new() + { + { "user1", user1.AvatarUrl }, + { "user2", user2.AvatarUrl } + }); + } + + [SlashCommand("baguette", "Baguette someone!")] + public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", "User to baguette")] DiscordUser? user = null) + { + user ??= ctx.User; + await GenerateImageAsync(ctx, "baguette", new() + { + { "url", user.AvatarUrl } + }); + } + + [SlashCommand("clyde", "Say something as clyde bot")] + public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + { + await GenerateImageAsync(ctx, "clyde", new() + { + { "text", text } + }); + } + + private static async Task GenerateImageAsync(BaseContext ctx, string type, Dictionary parameters) + { + var query = string.Join("&", parameters.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value)}")); + var response = await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type={type}&{query}"); + var result = JsonConvert.DeserializeObject(response); + + if (result is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + await ctx.EditResponseAsync(result.Message); + } } diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 47a52896..471726cd 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -9,20 +9,6 @@ namespace MikuSharp.Commands; [SlashCommandGroup("weeb", "Weeb Stuff!", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] internal class Weeb : ApplicationCommandsModule { - [SlashCommand("awooify", "Awooify your or someones avatar!")] - public static async Task AwooifyAsync(InteractionContext ctx, [Option("user", "User to awooify")] DiscordUser? user = null) - { - var url = (await (user ?? ctx.User).ConvertToMember(ctx.Guild)).GuildAvatarUrl; - var e = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type=awooify&url={url}")); - if (e is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(e.Message).Build())); - } - [SlashCommand("diva", "Radnom PJD Loading image")] public static async Task DivaPic(InteractionContext ctx) { diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index 5d1c3cba..c5ab3f27 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -27,7 +27,7 @@ public async Task> Prov List bannedUsers = new(25); bannedUsers.AddRange(ctx.FocusedOption.Value is not string value ? bans.Take(25) - : bans.Where(x => x.User.Username.ToLower().Contains(Convert.ToString(value))).Take(25)); + : bans.Where(x => x.User.Username.ToLowerInvariant().Contains(Convert.ToString(value))).Take(25)); return bannedUsers.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.User.UsernameWithGlobalName, x.User.Id.ToString())); } @@ -99,7 +99,7 @@ public async Task> Prov .ToDictionary(x => x.index, x => x.entry); var filteredQueueEntries = string.IsNullOrEmpty(value) ? queueEntries.Take(25) - : queueEntries.Where(x => x.Value.Info.Title.ToLower().Contains(value.ToLower())).Take(25); + : queueEntries.Where(x => x.Value.Info.Title.ToLowerInvariant().Contains(value.ToLower())).Take(25); return Task.FromResult(filteredQueueEntries.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Key}: {x.Value.Info.Title}", x.Key - 1))); }, null, [new("The queue is empty", -1)]); } From 78b8799c64febf9c2f340e085b2f2bd4c0a42f18 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 6 Feb 2025 08:04:30 +0100 Subject: [PATCH 061/113] Update Fun.cs --- MikuSharp/Commands/Fun.cs | 75 +++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index fab1092b..f2152e71 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -181,7 +181,7 @@ public static async Task StickbugAsync(InteractionContext ctx, [Option("user", " user ??= ctx.User; await GenerateImageAsync(ctx, "stickbug", new() { - { "url", user.AvatarUrl } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -191,7 +191,7 @@ public static async Task TrashAsync(InteractionContext ctx, [Option("user", "Use user ??= ctx.User; await GenerateImageAsync(ctx, "trash", new() { - { "url", user.AvatarUrl } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -201,20 +201,19 @@ public static async Task MagikAsync(InteractionContext ctx, [Option("user", "Use user ??= ctx.User; await GenerateImageAsync(ctx, "magik", new() { - { "image", user.AvatarUrl }, + { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) }, { "intensity", intensity.ToString() } }); } [SlashCommand("phcomment", "Make a PH comment!")] - public static async Task PhCommentAsync(InteractionContext ctx, [Option("text", "Text to comment.")] string text, [Option("username", "Username to use")] string? username = null) + public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", "User to write as")] DiscordUser user, [Option("text", "Text to comment.")] string text) { - username ??= ctx.User.Username; await GenerateImageAsync(ctx, "phcomment", new() { - { "image", ctx.User.AvatarUrl }, + { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) }, { "text", text }, - { "username", username } + { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) } }); } @@ -224,7 +223,7 @@ public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", " user ??= ctx.User; await GenerateImageAsync(ctx, "blurpify", new() { - { "url", user.AvatarUrl } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -234,7 +233,7 @@ public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "U user ??= ctx.User; await GenerateImageAsync(ctx, "deepfry", new() { - { "image", user.AvatarUrl } + { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -244,17 +243,17 @@ public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "Us user ??= ctx.User; await GenerateImageAsync(ctx, "kidnap", new() { - { "image", user.AvatarUrl } + { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } [SlashCommand("tweet", "Generate a fake tweet!")] - public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("username", "Username for tweet")] string? username = null) + public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("user", "User for tweet")] DiscordUser? user = null) { - username ??= ctx.User.Username; + user ??= ctx.User; await GenerateImageAsync(ctx, "tweet", new() { - { "username", username }, + { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) }, { "text", text } }); } @@ -265,9 +264,9 @@ public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User author ??= ctx.User; await GenerateImageAsync(ctx, "trap", new() { - { "name", user.Username }, - { "author", author.Username }, - { "image", user.AvatarUrl } + { "name", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) }, + { "author", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, author) }, + { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -277,7 +276,7 @@ public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "U user ??= ctx.User; await GenerateImageAsync(ctx, "iphonex", new() { - { "url", user.AvatarUrl } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -287,7 +286,7 @@ public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "Us user ??= ctx.User; await GenerateImageAsync(ctx, "lolice", new() { - { "url", user.AvatarUrl } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -314,19 +313,19 @@ public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1 { await GenerateImageAsync(ctx, "whowouldwin", new() { - { "user1", user1.AvatarUrl }, - { "user2", user2.AvatarUrl } + { "user1", await GetGuildAvatarIfPossibleAsync(ctx, user1) }, + { "user2", await GetGuildAvatarIfPossibleAsync(ctx, user2) } }); } [SlashCommand("captcha", "Generate a fake captcha!")] - public static async Task CaptchaAsync(InteractionContext ctx, [Option("username", "Username to display")] string? username = null) + public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "User to display (their name)")] DiscordUser? user = null) { - username ??= ctx.User.Username; + user ??= ctx.User; await GenerateImageAsync(ctx, "captcha", new() { - { "url", ctx.User.AvatarUrl }, - { "username", username } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, ctx.User) }, + { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) } }); } @@ -335,8 +334,8 @@ public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "Fir { await GenerateImageAsync(ctx, "ship", new() { - { "user1", user1.AvatarUrl }, - { "user2", user2.AvatarUrl } + { "user1", await GetGuildAvatarIfPossibleAsync(ctx, user1) }, + { "user2", await GetGuildAvatarIfPossibleAsync(ctx, user2) } }); } @@ -346,7 +345,7 @@ public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", " user ??= ctx.User; await GenerateImageAsync(ctx, "baguette", new() { - { "url", user.AvatarUrl } + { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); } @@ -373,4 +372,26 @@ private static async Task GenerateImageAsync(BaseContext ctx, string type, Dicti await ctx.EditResponseAsync(result.Message); } + + private static async Task GetGuildAvatarIfPossibleAsync(BaseContext ctx, DiscordUser user) + { + if (ctx.Guild is null) + return user.AvatarUrl; + + var member = await ctx.Guild.TryGetMemberAsync(user.Id); + return member is null + ? user.AvatarUrl + : member.GuildAvatarUrl; + } + + private static async Task GetGuildOrGlobalDisplayNameIfPossibleAsync(BaseContext ctx, DiscordUser user) + { + if (ctx.Guild is null) + return user.GlobalName ?? user.Username; + + var member = await ctx.Guild.TryGetMemberAsync(user.Id); + return member is null + ? user.GlobalName ?? user.Username + : member.DisplayName; + } } From 5c35d204900d2922d276af66cbfe5ff96e0cde9b Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 6 Feb 2025 08:21:56 +0100 Subject: [PATCH 062/113] tuning :3 --- MikuSharp/Commands/Fun.cs | 51 ++++++++-------------- MikuSharp/Entities/ImgData.cs | 25 +++++++++++ MikuSharp/Entities/Img_Data.cs | 9 ---- MikuSharp/Entities/KsoftSiImage.cs | 31 +++++++++++++ MikuSharp/Entities/KsoftSiRanImg.cs | 9 ---- MikuSharp/Entities/NekoBotImage.cs | 33 ++++++++++++++ MikuSharp/Entities/Nekobot.cs | 8 ---- MikuSharp/Entities/NekosLifeImage.cs | 13 ++++++ MikuSharp/Entities/Nekos_Life.cs | 6 --- MikuSharp/Utilities/WebExtensionMethods.cs | 33 +++++++++++--- 10 files changed, 147 insertions(+), 71 deletions(-) create mode 100644 MikuSharp/Entities/ImgData.cs delete mode 100644 MikuSharp/Entities/Img_Data.cs create mode 100644 MikuSharp/Entities/KsoftSiImage.cs delete mode 100644 MikuSharp/Entities/KsoftSiRanImg.cs create mode 100644 MikuSharp/Entities/NekoBotImage.cs delete mode 100644 MikuSharp/Entities/Nekobot.cs create mode 100644 MikuSharp/Entities/NekosLifeImage.cs delete mode 100644 MikuSharp/Entities/Nekos_Life.cs diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index f2152e71..2df2f72a 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -179,7 +179,7 @@ public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your r public static async Task StickbugAsync(InteractionContext ctx, [Option("user", "User to stickbug")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "stickbug", new() + await ctx.GenerateNekobotImageAsync("stickbug", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -189,7 +189,7 @@ public static async Task StickbugAsync(InteractionContext ctx, [Option("user", " public static async Task TrashAsync(InteractionContext ctx, [Option("user", "User to trash")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "trash", new() + await ctx.GenerateNekobotImageAsync("trash", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -199,7 +199,7 @@ public static async Task TrashAsync(InteractionContext ctx, [Option("user", "Use public static async Task MagikAsync(InteractionContext ctx, [Option("user", "User to magikify")] DiscordUser? user = null, [Option("intensity", "Magik intensity (0-10)")] int intensity = 5) { user ??= ctx.User; - await GenerateImageAsync(ctx, "magik", new() + await ctx.GenerateNekobotImageAsync( "magik", new() { { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) }, { "intensity", intensity.ToString() } @@ -209,7 +209,7 @@ public static async Task MagikAsync(InteractionContext ctx, [Option("user", "Use [SlashCommand("phcomment", "Make a PH comment!")] public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", "User to write as")] DiscordUser user, [Option("text", "Text to comment.")] string text) { - await GenerateImageAsync(ctx, "phcomment", new() + await ctx.GenerateNekobotImageAsync( "phcomment", new() { { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) }, { "text", text }, @@ -221,7 +221,7 @@ public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", "User to blurpify")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "blurpify", new() + await ctx.GenerateNekobotImageAsync( "blurpify", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -231,7 +231,7 @@ public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", " public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "User to deepfry")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "deepfry", new() + await ctx.GenerateNekobotImageAsync("deepfry", new() { { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -241,7 +241,7 @@ public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "U public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "User to kidnap")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "kidnap", new() + await ctx.GenerateNekobotImageAsync("kidnap", new() { { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -251,7 +251,7 @@ public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "Us public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("user", "User for tweet")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "tweet", new() + await ctx.GenerateNekobotImageAsync("tweet", new() { { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) }, { "text", text } @@ -262,7 +262,7 @@ public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "Us public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User to trap")] DiscordUser user, [Option("author", "User trapping them")] DiscordUser? author = null) { author ??= ctx.User; - await GenerateImageAsync(ctx, "trap", new() + await ctx.GenerateNekobotImageAsync("trap", new() { { "name", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) }, { "author", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, author) }, @@ -274,7 +274,7 @@ public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "User to insert")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "iphonex", new() + await ctx.GenerateNekobotImageAsync("iphonex", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -284,7 +284,7 @@ public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "U public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "User to call lolice on")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "lolice", new() + await ctx.GenerateNekobotImageAsync("lolice", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -293,7 +293,7 @@ public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "Us [SlashCommand("kannagen", "Kanna says something!")] public static async Task KannaGenAsync(InteractionContext ctx, [Option("text", "Text for Kanna")] string text) { - await GenerateImageAsync(ctx, "kannagen", new() + await ctx.GenerateNekobotImageAsync("kannagen", new() { { "text", text } }); @@ -302,7 +302,7 @@ public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "Us [SlashCommand("changemymind", "Change my mind meme generator.")] public static async Task ChangeMyMindAsync(InteractionContext ctx, [Option("text", "Change my mind text")] string text) { - await GenerateImageAsync(ctx, "changemymind", new() + await ctx.GenerateNekobotImageAsync("changemymind", new() { { "text", text } }); @@ -311,7 +311,7 @@ public static async Task ChangeMyMindAsync(InteractionContext ctx, [Option("text [SlashCommand("whowouldwin", "Who would win?")] public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) { - await GenerateImageAsync(ctx, "whowouldwin", new() + await ctx.GenerateNekobotImageAsync("whowouldwin", new() { { "user1", await GetGuildAvatarIfPossibleAsync(ctx, user1) }, { "user2", await GetGuildAvatarIfPossibleAsync(ctx, user2) } @@ -322,7 +322,7 @@ public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1 public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "User to display (their name)")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "captcha", new() + await ctx.GenerateNekobotImageAsync("captcha", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, ctx.User) }, { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) } @@ -332,7 +332,7 @@ public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "U [SlashCommand("ship", "Ship two users!")] public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) { - await GenerateImageAsync(ctx, "ship", new() + await ctx.GenerateNekobotImageAsync("ship", new() { { "user1", await GetGuildAvatarIfPossibleAsync(ctx, user1) }, { "user2", await GetGuildAvatarIfPossibleAsync(ctx, user2) } @@ -343,7 +343,7 @@ public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "Fir public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", "User to baguette")] DiscordUser? user = null) { user ??= ctx.User; - await GenerateImageAsync(ctx, "baguette", new() + await ctx.GenerateNekobotImageAsync("baguette", new() { { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } }); @@ -352,27 +352,12 @@ public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", " [SlashCommand("clyde", "Say something as clyde bot")] public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - await GenerateImageAsync(ctx, "clyde", new() + await ctx.GenerateNekobotImageAsync("clyde", new() { { "text", text } }); } - private static async Task GenerateImageAsync(BaseContext ctx, string type, Dictionary parameters) - { - var query = string.Join("&", parameters.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value)}")); - var response = await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type={type}&{query}"); - var result = JsonConvert.DeserializeObject(response); - - if (result is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; - } - - await ctx.EditResponseAsync(result.Message); - } - private static async Task GetGuildAvatarIfPossibleAsync(BaseContext ctx, DiscordUser user) { if (ctx.Guild is null) diff --git a/MikuSharp/Entities/ImgData.cs b/MikuSharp/Entities/ImgData.cs new file mode 100644 index 00000000..275ad71b --- /dev/null +++ b/MikuSharp/Entities/ImgData.cs @@ -0,0 +1,25 @@ +namespace MikuSharp.Entities; + +/// +/// Represents image data. +/// +public class ImgData +{ + /// + /// Gets the data. + /// + [JsonIgnore] + public Stream Data { get; set; } + + /// + /// Gets the file type. + /// + [JsonIgnore] + public string Filetype { get; set; } + + /// + /// Gets the embed. + /// + [JsonIgnore] + public DiscordEmbed Embed { get; set; } +} diff --git a/MikuSharp/Entities/Img_Data.cs b/MikuSharp/Entities/Img_Data.cs deleted file mode 100644 index 5ab88651..00000000 --- a/MikuSharp/Entities/Img_Data.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MikuSharp.Entities; - -public class ImgData -{ - public Stream Data { get; set; } - public string Filetype { get; set; } - - public DiscordEmbed Embed { get; set; } -} diff --git a/MikuSharp/Entities/KsoftSiImage.cs b/MikuSharp/Entities/KsoftSiImage.cs new file mode 100644 index 00000000..41c3dd76 --- /dev/null +++ b/MikuSharp/Entities/KsoftSiImage.cs @@ -0,0 +1,31 @@ +namespace MikuSharp.Entities; + +/// +/// Represents a KSoft.si image. +/// +public sealed class KsoftSiImage : ImgData +{ + /// + /// Gets the url. + /// + [JsonProperty("url")] + public string Url { get; set; } + + /// + /// Gets the snowflake. + /// + [JsonProperty("snowflake")] + public string Snowflake { get; set; } + + /// + /// Gets whether the image is NSFW. + /// + [JsonProperty("nsfw")] + public bool Nsfw { get; set; } + + /// + /// Gets the tag. + /// + [JsonProperty("tag")] + public string Tag { get; set; } +} diff --git a/MikuSharp/Entities/KsoftSiRanImg.cs b/MikuSharp/Entities/KsoftSiRanImg.cs deleted file mode 100644 index ad0c2a52..00000000 --- a/MikuSharp/Entities/KsoftSiRanImg.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace MikuSharp.Entities; - -public sealed class KsoftSiRanImg : ImgData -{ - public string Url { get; set; } - public string Snowflake { get; set; } - public bool Nsfw { get; set; } - public string Tag { get; set; } -} diff --git a/MikuSharp/Entities/NekoBotImage.cs b/MikuSharp/Entities/NekoBotImage.cs new file mode 100644 index 00000000..374a9721 --- /dev/null +++ b/MikuSharp/Entities/NekoBotImage.cs @@ -0,0 +1,33 @@ +using System.Net; + +namespace MikuSharp.Entities; + +/// +/// Represents a Nekobot response. +/// +public sealed class NekoBotImage : ImgData +{ + /// + /// Gets the message. + /// + [JsonProperty("message", NullValueHandling = NullValueHandling.Include)] + public string Message { get; internal set; } + + /// + /// Gets the status. + /// + [JsonProperty("status")] + public HttpStatusCode Status { get; internal set; } + + /// + /// Gets whether the request was successful. + /// + [JsonProperty("success")] + public bool Success { get; internal set; } + + /// + /// Gets the version. + /// + [JsonProperty("version")] + public string Version { get; internal set; } +} diff --git a/MikuSharp/Entities/Nekobot.cs b/MikuSharp/Entities/Nekobot.cs deleted file mode 100644 index af79d18a..00000000 --- a/MikuSharp/Entities/Nekobot.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace MikuSharp.Entities; - -public sealed class NekoBot : ImgData -{ - public string Message { get; set; } - public int Status { get; set; } - public bool Success { get; set; } -} diff --git a/MikuSharp/Entities/NekosLifeImage.cs b/MikuSharp/Entities/NekosLifeImage.cs new file mode 100644 index 00000000..eb937a3f --- /dev/null +++ b/MikuSharp/Entities/NekosLifeImage.cs @@ -0,0 +1,13 @@ +namespace MikuSharp.Entities; + +/// +/// Represents a nekos.life response. +/// +public sealed class NekosLifeImage : ImgData +{ + /// + /// Gets the URL. + /// + [JsonProperty("url")] + public string Url { get; set; } +} diff --git a/MikuSharp/Entities/Nekos_Life.cs b/MikuSharp/Entities/Nekos_Life.cs deleted file mode 100644 index 235044e5..00000000 --- a/MikuSharp/Entities/Nekos_Life.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace MikuSharp.Entities; - -public sealed class NekosLife : ImgData -{ - public string Url { get; set; } -} diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index fa47721f..a906b461 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -19,9 +19,9 @@ public static class WebExtensionMethods /// The http client. /// The url. /// The nekos.life response. - public static async Task GetNekosLifeAsync(this HttpClient client, string url) + public static async Task GetNekosLifeAsync(this HttpClient client, string url) { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); if (dl is null) return null; @@ -45,10 +45,10 @@ public static class WebExtensionMethods /// The tag. /// Whether the search should include NSFW results. /// The ksoft.si response. - public static async Task GetKsoftSiRanImgAsync(this HttpClient client, string tag = "hentai_gif", bool nsfw = true) + public static async Task GetKsoftSiImgageAsync(this HttpClient client, string tag = "hentai_gif", bool nsfw = true) { client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync($"https://api.ksoft.si/images/random-image?tag={tag}&nsfw={nsfw.ToString().ToLowerInvariant()}")); + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync($"https://api.ksoft.si/images/random-image?tag={tag}&nsfw={nsfw.ToString().ToLowerInvariant()}")); if (dl is null) return null; @@ -62,15 +62,36 @@ public static class WebExtensionMethods return dl; } + /// + /// Generates an image using the Nekobot API. + /// + /// The context. + /// The type of image to generate. + /// The parameters. + public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string type, Dictionary parameters) + { + var query = string.Join("&", parameters.Select(kvp => $"{kvp.Key}={Uri.EscapeDataString(kvp.Value)}")); + var response = await ctx.Client.RestClient.GetStringAsync($"https://nekobot.xyz/api/imagegen?type={type}&{query}"); + var result = JsonConvert.DeserializeObject(response); + + if (result is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + await ctx.EditResponseAsync(result.Message); + } + /// /// Gets a random image from nekobot.xyz. /// /// The http client. /// The url. /// The nekobot response. - public static async Task GetNekobotAsync(this HttpClient client, string url) + public static async Task GetNekobotAsync(this HttpClient client, string url) { - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); if (dl is null) return null; From 733054de327d585954f4a293e581cf20a3cbcfea Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 7 Feb 2025 12:05:41 +0100 Subject: [PATCH 063/113] a --- MikuSharp/Commands/Fun.cs | 70 +++++++------------ .../Utilities/DiscordExtensionMethods.cs | 37 ++++++++++ .../Utilities/LavalinkExtensionMethods.cs | 3 +- 3 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 MikuSharp/Utilities/DiscordExtensionMethods.cs diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 2df2f72a..7d31a172 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -181,7 +181,7 @@ public static async Task StickbugAsync(InteractionContext ctx, [Option("user", " user ??= ctx.User; await ctx.GenerateNekobotImageAsync("stickbug", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -191,7 +191,7 @@ public static async Task TrashAsync(InteractionContext ctx, [Option("user", "Use user ??= ctx.User; await ctx.GenerateNekobotImageAsync("trash", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -199,9 +199,9 @@ public static async Task TrashAsync(InteractionContext ctx, [Option("user", "Use public static async Task MagikAsync(InteractionContext ctx, [Option("user", "User to magikify")] DiscordUser? user = null, [Option("intensity", "Magik intensity (0-10)")] int intensity = 5) { user ??= ctx.User; - await ctx.GenerateNekobotImageAsync( "magik", new() + await ctx.GenerateNekobotImageAsync("magik", new() { - { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) }, + { "image", ctx.GetGuildAvatarIfPossible(user) }, { "intensity", intensity.ToString() } }); } @@ -209,11 +209,11 @@ public static async Task MagikAsync(InteractionContext ctx, [Option("user", "Use [SlashCommand("phcomment", "Make a PH comment!")] public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", "User to write as")] DiscordUser user, [Option("text", "Text to comment.")] string text) { - await ctx.GenerateNekobotImageAsync( "phcomment", new() + await ctx.GenerateNekobotImageAsync("phcomment", new() { - { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) }, + { "image", ctx.GetGuildAvatarIfPossible(user) }, { "text", text }, - { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) } + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } }); } @@ -221,9 +221,9 @@ public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", "User to blurpify")] DiscordUser? user = null) { user ??= ctx.User; - await ctx.GenerateNekobotImageAsync( "blurpify", new() + await ctx.GenerateNekobotImageAsync("blurpify", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -233,7 +233,7 @@ public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "U user ??= ctx.User; await ctx.GenerateNekobotImageAsync("deepfry", new() { - { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "image", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -243,7 +243,7 @@ public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "Us user ??= ctx.User; await ctx.GenerateNekobotImageAsync("kidnap", new() { - { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "image", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -253,7 +253,7 @@ public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "Us user ??= ctx.User; await ctx.GenerateNekobotImageAsync("tweet", new() { - { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) }, + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, { "text", text } }); } @@ -264,9 +264,9 @@ public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User author ??= ctx.User; await ctx.GenerateNekobotImageAsync("trap", new() { - { "name", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) }, - { "author", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, author) }, - { "image", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "name", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, + { "author", ctx.GetGuildOrGlobalDisplayNameIfPossible(author) }, + { "image", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -276,7 +276,7 @@ public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "U user ??= ctx.User; await ctx.GenerateNekobotImageAsync("iphonex", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -286,7 +286,7 @@ public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "Us user ??= ctx.User; await ctx.GenerateNekobotImageAsync("lolice", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -313,8 +313,8 @@ public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1 { await ctx.GenerateNekobotImageAsync("whowouldwin", new() { - { "user1", await GetGuildAvatarIfPossibleAsync(ctx, user1) }, - { "user2", await GetGuildAvatarIfPossibleAsync(ctx, user2) } + { "user1", ctx.GetGuildAvatarIfPossible(user1) }, + { "user2", ctx.GetGuildAvatarIfPossible(user2) } }); } @@ -324,8 +324,8 @@ public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "U user ??= ctx.User; await ctx.GenerateNekobotImageAsync("captcha", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, ctx.User) }, - { "username", await GetGuildOrGlobalDisplayNameIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(ctx.User) }, + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } }); } @@ -334,8 +334,8 @@ public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "Fir { await ctx.GenerateNekobotImageAsync("ship", new() { - { "user1", await GetGuildAvatarIfPossibleAsync(ctx, user1) }, - { "user2", await GetGuildAvatarIfPossibleAsync(ctx, user2) } + { "user1", ctx.GetGuildAvatarIfPossible(user1) }, + { "user2", ctx.GetGuildAvatarIfPossible(user2) } }); } @@ -345,7 +345,7 @@ public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", " user ??= ctx.User; await ctx.GenerateNekobotImageAsync("baguette", new() { - { "url", await GetGuildAvatarIfPossibleAsync(ctx, user) } + { "url", ctx.GetGuildAvatarIfPossible(user) } }); } @@ -357,26 +357,4 @@ public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Tex { "text", text } }); } - - private static async Task GetGuildAvatarIfPossibleAsync(BaseContext ctx, DiscordUser user) - { - if (ctx.Guild is null) - return user.AvatarUrl; - - var member = await ctx.Guild.TryGetMemberAsync(user.Id); - return member is null - ? user.AvatarUrl - : member.GuildAvatarUrl; - } - - private static async Task GetGuildOrGlobalDisplayNameIfPossibleAsync(BaseContext ctx, DiscordUser user) - { - if (ctx.Guild is null) - return user.GlobalName ?? user.Username; - - var member = await ctx.Guild.TryGetMemberAsync(user.Id); - return member is null - ? user.GlobalName ?? user.Username - : member.DisplayName; - } } diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs new file mode 100644 index 00000000..33f51eee --- /dev/null +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -0,0 +1,37 @@ +namespace MikuSharp.Utilities; + +/// +/// Contains extension methods for Discord-related classes. +/// +public static class DiscordExtensionMethods +{ + /// + /// Gets the avatar URL of the user, using the guild avatar URL if possible. + /// + /// The context. + /// The user. + /// The avatar URL. + public static string GetGuildAvatarIfPossible(this BaseContext ctx, DiscordUser user) + => ctx.Guild is not null && ctx.Guild.TryGetMember(user.Id, out var member) + ? member.GuildAvatarUrl + : user.AvatarUrl; + + /// + /// Gets the display name of the user, using the guild display name if possible. + /// + /// The context. + /// The user. + /// The display name. + public static string GetGuildOrGlobalDisplayNameIfPossible(this BaseContext ctx, DiscordUser user) + => ctx.Guild is not null && ctx.Guild.TryGetMember(user.Id, out var member) + ? member.DisplayName + : user.GetGlobalOrUsername(); + + /// + /// Gets the global name of the user, using the username if the global name is not set. + /// + /// The user. + /// The global name or username. + public static string GetGlobalOrUsername(this DiscordUser user) + => user.GlobalName ?? user.Username; +} diff --git a/MikuSharp/Utilities/LavalinkExtensionMethods.cs b/MikuSharp/Utilities/LavalinkExtensionMethods.cs index 6eb55a76..9b6f54a2 100644 --- a/MikuSharp/Utilities/LavalinkExtensionMethods.cs +++ b/MikuSharp/Utilities/LavalinkExtensionMethods.cs @@ -3,5 +3,4 @@ namespace MikuSharp.Utilities; /// /// Provides extension methods for Lavalink-related operations. /// -internal static class LavalinkExtensionMethods -{ } +internal static class LavalinkExtensionMethods; From 56951233857e5fe0d1922b1a1ea4e382e9c0ccf5 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 7 Feb 2025 12:49:01 +0100 Subject: [PATCH 064/113] refactor: move common action parts into extension methods --- MikuSharp/Commands/Action.cs | 314 +++--------------- .../Utilities/DiscordExtensionMethods.cs | 103 +++++- 2 files changed, 156 insertions(+), 261 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index e257e19e..acd6d185 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -1,5 +1,3 @@ -using HeyRed.Mime; - using MikuSharp.Attributes; using MikuSharp.Utilities; @@ -11,303 +9,99 @@ internal class Action : ApplicationCommandsModule [SlashCommand("hug", "Hug someone!")] public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var wsh = await ctx.Client.RestClient.GetWeebShAsync("hug"); - if (wsh is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} hugs {user.Mention} uwu").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A wild hug appears!"), new DiscordTextDisplayComponent($"{ctx.User.Mention} hugs {user.Mention} uwu"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - wsh.Embed.WithDescription($"{ctx.User.Mention} hugs {user.Mention} uwu"); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## A wild hug appears!"; + var content = $"{ctx.User.Mention} hugs {user.Mention} uwu"; + if (!(await ctx.Client.RestClient.GetWeebShAsync("hug")).TryGetWeebShImage(out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("kiss", "Kiss someone!")] public static async Task KissAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var wsh = await ctx.Client.RestClient.GetWeebShAsync("kiss"); - if (wsh is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} kisses {user.Mention} >~<").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## A kiss!"), new DiscordTextDisplayComponent($"{ctx.User.Mention} kisses {user.Mention} >~<"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - wsh.Embed.WithDescription($"{ctx.User.Mention} kisses {user.Mention} >~<"); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## A kiss!"; + var content = $"{ctx.User.Mention} kisses {user.Mention} >~<"; + if (!(await ctx.Client.RestClient.GetWeebShAsync("kiss")).TryGetWeebShImage(out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("lick", "Lick someone!")] public static async Task LickAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var wsh = await ctx.Client.RestClient.GetWeebShAsync("lick"); - if (wsh is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} licks {user.Mention} owo").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slurp~"), new DiscordTextDisplayComponent($"{ctx.User.Mention} licks {user.Mention} owo"), new DiscordMediaGalleryComponent([new($"attachment://image.{wsh.Extension}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - wsh.Embed.WithDescription($"{ctx.User.Mention} licks {user.Mention} owo"); - builder.AddFile($"image.{wsh.Extension}", wsh.ImgData); - builder.AddEmbed(wsh.Embed.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Slurp~"; + var content = $"{ctx.User.Mention} licks {user.Mention} owo"; + if (!(await ctx.Client.RestClient.GetWeebShAsync("lick")).TryGetWeebShImage(out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("pat", "Pat someone!")] public static async Task PatAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var weeurl = await MikuBot.WeebClient.GetRandomAsync("pat", []); - if (weeurl is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} pats {user.Mention} #w#").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Pat pat :3"), new DiscordTextDisplayComponent($"{ctx.User.Mention} pats {user.Mention} #w#"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pats {user.Mention} #w#"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Pat pat~"; + var content = $"{ctx.User.Mention} pats {user.Mention} #w#"; + if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("pat", []), out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("poke", "Poke someone!")] public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var weeurl = await MikuBot.WeebClient.GetRandomAsync("poke", []); - if (weeurl is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Poke poke :p"), new DiscordTextDisplayComponent($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Poke poke!"; + var content = $"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"; + if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("poke", []), out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("slap", "Slap someone!")] public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var weeurl = await MikuBot.WeebClient.GetRandomAsync("slap", []); - if (weeurl is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Slap x~x"), new DiscordTextDisplayComponent($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Slap!"; + var content = $"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"; + if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("slap", []), out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("bite", "Bite someone!")] public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var weeurl = await MikuBot.WeebClient.GetRandomAsync("bite", []); - if (weeurl is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} bites {user.Mention} x~x").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Bite~"), new DiscordTextDisplayComponent($"{ctx.User.Mention} bites {user.Mention} x~x"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} bites {user.Mention} x~x"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Bite >:3"; + var content = $"{ctx.User.Mention} bites {user.Mention} >:3"; + if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("bite", []), out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("nom", "Nom someone!")] public static async Task NomAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var weeurl = await MikuBot.WeebClient.GetRandomAsync("nom", []); - if (weeurl is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} noms {user.Mention} >:3c").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Noms~"), new DiscordTextDisplayComponent($"{ctx.User.Mention} noms {user.Mention} >:3c"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} noms {user.Mention} >:3c"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Nom nom~"; + var content = $"{ctx.User.Mention} noms {user.Mention} >:3c"; + if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("nom", []), out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } [SlashCommand("stare", "Stare at someone!")] public static async Task StateAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) { - var weeurl = await MikuBot.WeebClient.GetRandomAsync("stare", []); - if (weeurl is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} stares at {user.Mention} O.o").WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); - return; - } - - DiscordWebhookBuilder builder = new(); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(weeurl.Url.ResizeLink())); - - if (ctx.GuildId is 1317206872763404478) - { - builder.WithV2Components(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## Stare O.o"), new DiscordTextDisplayComponent($"{ctx.User.Mention} stares at {user.Mention} O.o"), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(img)}")])])); - builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); - await ctx.EditResponseAsync(builder); - return; - } - - var em = new DiscordEmbedBuilder(); - em.WithDescription($"{ctx.User.Mention} stares at {user.Mention} O.o"); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); - await ctx.EditResponseAsync(builder); + var title = "## Stare O.o"; + var content = $"{ctx.User.Mention} stares at {user.Mention} O.o"; + if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("stare", []), out var img)) + await ctx.ActionResponseWithErrorAsync(content, user); + else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) + await ctx.SendOldStyleMessageAsync(img, content, user); } } diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 33f51eee..ed698039 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -1,3 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +using HeyRed.Mime; + +using MikuSharp.Entities; + +using Weeb.net.Data; + namespace MikuSharp.Utilities; /// @@ -28,10 +36,103 @@ public static string GetGuildOrGlobalDisplayNameIfPossible(this BaseContext ctx, : user.GetGlobalOrUsername(); /// - /// Gets the global name of the user, using the username if the global name is not set. + /// Gets the global name of the user, using the username if the global name is not set. /// /// The user. /// The global name or username. public static string GetGlobalOrUsername(this DiscordUser user) => user.GlobalName ?? user.Username; + + /// + /// Tries to build and send a components V2 action message. + /// + /// The context. + /// The image. + /// The title. + /// The content. + /// The user. + /// Whether the message was sent successfully. + public static async Task TryBuildV2ActionMessageAsync(this BaseContext context, MemoryStream image, string title, string content, DiscordUser user) + { + if (context.GuildId is not 1317206872763404478) + return false; + + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); + builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent(title), new DiscordTextDisplayComponent(content), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])])); + builder.WithAllowedMentions([new UserMention(context.User), new UserMention(user)]); + await context.EditResponseAsync(builder); + return true; + } + + /// + /// Sends an old-style embed message. + /// + /// The context. + /// The image. + /// The content. + /// The user. + public static async Task SendOldStyleMessageAsync(this BaseContext context, MemoryStream image, string content, DiscordUser user) + { + DiscordWebhookBuilder builder = new(); + var em = new DiscordEmbedBuilder(); + em.WithDescription(content); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(image)}"); + em.WithFooter("by nekos.life"); + builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); + builder.AddEmbed(em.Build()); + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(user)); + await context.EditResponseAsync(builder); + } + + /// + /// Tries to get an image from the Weeb.net API. + /// + /// The context. + /// The data. + /// The stream. + /// Whether the image was successfully retrieved. + public static bool TryGetWeebNetImage(this BaseContext context, RandomData? data, [NotNullWhen(true)] out MemoryStream? stream) + { + if (data is null) + { + stream = null; + return false; + } + + stream = new(context.Client.RestClient.GetByteArrayAsync(data.Url.ResizeLink()).Result); + return true; + } + + /// + /// Tries to get an image from the Weeb.sh API. + /// + /// The data. + /// The stream. + /// Whether the image was successfully retrieved. + public static bool TryGetWeebShImage(this WeebSh? data, [NotNullWhen(true)] out MemoryStream? stream) + { + if (data is null) + { + stream = null; + return false; + } + + stream = data.ImgData; + return true; + } + + /// + /// Responds with an error message. + /// + /// The context. + /// The content. + /// The user. + public static async Task ActionResponseWithErrorAsync(this BaseContext ctx, string content, DiscordUser user) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(content).WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); + } } From 0c18b99edcb098af64b7f57da99ba4a8f03ad3fa Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 04:47:09 +0100 Subject: [PATCH 065/113] group commands better & use capv2 if possible --- MikuSharp/Commands/Fun.cs | 440 +++++++++--------- .../Utilities/DiscordExtensionMethods.cs | 40 +- MikuSharp/Utilities/WebExtensionMethods.cs | 10 +- 3 files changed, 260 insertions(+), 230 deletions(-) diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 7d31a172..6d67af54 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -83,22 +83,6 @@ public static async Task EightBallAsync(InteractionContext ctx, [Option("text", await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{chosenAnswer}")); } - [SlashCommand("cat", "Get a random cat image!")] - public static async Task CatAsync(InteractionContext ctx) - { - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); - if (imgUrl is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; - } - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); - builder.AddEmbed(imgUrl.Embed); - await ctx.EditResponseAsync(builder); - } - [SlashCommand("coinflip", "Flip a coin lol")] public static async Task CoinflipAsync(InteractionContext ctx) { @@ -107,254 +91,278 @@ public static async Task CoinflipAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); } - [SlashCommand("dog", "Random Dog Image")] - public static async Task DogAsync(InteractionContext ctx) + [SlashCommand("rps", "Play rock paper scissors!")] + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - if (dc is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; - } - - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); - em.WithDescription($"[Full Image]({dc.Message})"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); + var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); } - [SlashCommand("duck", "Random duck image")] - public static async Task DuckAsync(InteractionContext ctx) + [SlashCommandGroup("random_images", "Random images")] + public class RandomImages : ApplicationCommandsModule { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); - if (dc is null) + [SlashCommand("cat", "Get a random cat image!")] + public static async Task CatAsync(InteractionContext ctx) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; + var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + if (imgUrl is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); + builder.AddEmbed(imgUrl.Embed); + await ctx.EditResponseAsync(builder); } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); - em.WithDescription($"[Full Image]({dc.Message})"); - - var builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("lizard", "Get a random lizard image")] - public static async Task LizardAsync(InteractionContext ctx) - { - var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - if (get is null) + [SlashCommand("dog", "Random Dog Image")] + public static async Task DogAsync(InteractionContext ctx) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); - return; + var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); + if (dc is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); + em.WithDescription($"[Full Image]({dc.Message})"); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(get.Url.ResizeLink())); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); - } + [SlashCommand("duck", "Random duck image")] + public static async Task DuckAsync(InteractionContext ctx) + { + var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); + if (dc is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); + var em = new DiscordEmbedBuilder(); + em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); + em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); + em.WithDescription($"[Full Image]({dc.Message})"); + + var builder = new DiscordWebhookBuilder(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + builder.AddEmbed(em.Build()); + await ctx.EditResponseAsync(builder); + } - [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) - { - var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); + [SlashCommand("lizard", "Get a random lizard image")] + public static async Task LizardAsync(InteractionContext ctx) + { + var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); + if (get is null) + { + await ctx.EditResponseAsync("Something went wrong while fetching the image."); + return; + } + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(get.Url.ResizeLink())); + + DiscordWebhookBuilder builder = new(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); + await ctx.EditResponseAsync(builder); + } } - [SlashCommand("stickbug", "Get stickbugged!")] - public static async Task StickbugAsync(InteractionContext ctx, [Option("user", "User to stickbug")] DiscordUser? user = null) + [SlashCommandGroup("memes", "Meme commands (powered by nekos.life and nekobot.xyz)")] + public class Memes : ApplicationCommandsModule { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("stickbug", new() + [SlashCommand("stickbug", "Get stickbugged!")] + public static async Task StickbugAsync(InteractionContext ctx, [Option("user", "User to stickbug")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("stickbug", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("trash", "Trash waifu image generator.")] - public static async Task TrashAsync(InteractionContext ctx, [Option("user", "User to trash")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("trash", new() + [SlashCommand("trash", "Trash waifu image generator.")] + public static async Task TrashAsync(InteractionContext ctx, [Option("user", "User to trash")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("trash", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("magik", "Magikify an image!")] - public static async Task MagikAsync(InteractionContext ctx, [Option("user", "User to magikify")] DiscordUser? user = null, [Option("intensity", "Magik intensity (0-10)")] int intensity = 5) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("magik", new() + [SlashCommand("magik", "Magikify an image!")] + public static async Task MagikAsync(InteractionContext ctx, [Option("user", "User to magikify")] DiscordUser? user = null, [Option("intensity", "Magik intensity (0-10)")] int intensity = 5) { - { "image", ctx.GetGuildAvatarIfPossible(user) }, - { "intensity", intensity.ToString() } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("magik", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) }, + { "intensity", intensity.ToString() } + }); + } - [SlashCommand("phcomment", "Make a PH comment!")] - public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", "User to write as")] DiscordUser user, [Option("text", "Text to comment.")] string text) - { - await ctx.GenerateNekobotImageAsync("phcomment", new() + [SlashCommand("phcomment", "Make a PH comment!")] + public static async Task PhCommentAsync(InteractionContext ctx, [Option("user", "User to write as")] DiscordUser user, [Option("text", "Text to comment.")] string text) { - { "image", ctx.GetGuildAvatarIfPossible(user) }, - { "text", text }, - { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } - }); - } + await ctx.GenerateNekobotImageAsync("phcomment", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) }, + { "text", text }, + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } + }); + } - [SlashCommand("blurpify", "Blurpify an image!")] - public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", "User to blurpify")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("blurpify", new() + [SlashCommand("blurpify", "Blurpify an image!")] + public static async Task BlurpifyAsync(InteractionContext ctx, [Option("user", "User to blurpify")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("blurpify", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("deepfry", "Deepfry an image!")] - public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "User to deepfry")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("deepfry", new() + [SlashCommand("deepfry", "Deepfry an image!")] + public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "User to deepfry")] DiscordUser? user = null) { - { "image", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("deepfry", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("kidnap", "Kidnap a user!")] - public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "User to kidnap")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("kidnap", new() + [SlashCommand("kidnap", "Kidnap a user!")] + public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "User to kidnap")] DiscordUser? user = null) { - { "image", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("kidnap", new() + { + { "image", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("tweet", "Generate a fake tweet!")] - public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("user", "User for tweet")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("tweet", new() + [SlashCommand("tweet", "Generate a fake tweet!")] + public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("user", "User for tweet")] DiscordUser? user = null) { - { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, - { "text", text } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("tweet", new() + { + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, + { "text", text } + }); + } - [SlashCommand("trap", "Trap someone!")] - public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User to trap")] DiscordUser user, [Option("author", "User trapping them")] DiscordUser? author = null) - { - author ??= ctx.User; - await ctx.GenerateNekobotImageAsync("trap", new() + [SlashCommand("trap", "Trap someone!")] + public static async Task TrapAsync(InteractionContext ctx, [Option("user", "User to trap")] DiscordUser user, [Option("author", "User trapping them")] DiscordUser? author = null) { - { "name", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, - { "author", ctx.GetGuildOrGlobalDisplayNameIfPossible(author) }, - { "image", ctx.GetGuildAvatarIfPossible(user) } - }); - } + author ??= ctx.User; + await ctx.GenerateNekobotImageAsync("trap", new() + { + { "name", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) }, + { "author", ctx.GetGuildOrGlobalDisplayNameIfPossible(author) }, + { "image", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("iphonex", "Insert an image into an iPhone X frame.")] - public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "User to insert")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("iphonex", new() + [SlashCommand("iphonex", "Insert an image into an iPhone X frame.")] + public static async Task IPhoneXAsync(InteractionContext ctx, [Option("user", "User to insert")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("iphonex", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("lolice", "Call the lolice!")] - public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "User to call lolice on")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("lolice", new() + [SlashCommand("lolice", "Call the lolice!")] + public static async Task LoliceAsync(InteractionContext ctx, [Option("user", "User to call lolice on")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("lolice", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("kannagen", "Kanna says something!")] - public static async Task KannaGenAsync(InteractionContext ctx, [Option("text", "Text for Kanna")] string text) - { - await ctx.GenerateNekobotImageAsync("kannagen", new() + [SlashCommand("kannagen", "Kanna says something!")] + public static async Task KannaGenAsync(InteractionContext ctx, [Option("text", "Text for Kanna")] string text) { - { "text", text } - }); - } + await ctx.GenerateNekobotImageAsync("kannagen", new() + { + { "text", text } + }); + } - [SlashCommand("changemymind", "Change my mind meme generator.")] - public static async Task ChangeMyMindAsync(InteractionContext ctx, [Option("text", "Change my mind text")] string text) - { - await ctx.GenerateNekobotImageAsync("changemymind", new() + [SlashCommand("changemymind", "Change my mind meme generator.")] + public static async Task ChangeMyMindAsync(InteractionContext ctx, [Option("text", "Change my mind text")] string text) { - { "text", text } - }); - } + await ctx.GenerateNekobotImageAsync("changemymind", new() + { + { "text", text } + }); + } - [SlashCommand("whowouldwin", "Who would win?")] - public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) - { - await ctx.GenerateNekobotImageAsync("whowouldwin", new() + [SlashCommand("whowouldwin", "Who would win?")] + public static async Task WhoWouldWinAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) { - { "user1", ctx.GetGuildAvatarIfPossible(user1) }, - { "user2", ctx.GetGuildAvatarIfPossible(user2) } - }); - } + await ctx.GenerateNekobotImageAsync("whowouldwin", new() + { + { "user1", ctx.GetGuildAvatarIfPossible(user1) }, + { "user2", ctx.GetGuildAvatarIfPossible(user2) } + }); + } - [SlashCommand("captcha", "Generate a fake captcha!")] - public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "User to display (their name)")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("captcha", new() + [SlashCommand("captcha", "Generate a fake captcha!")] + public static async Task CaptchaAsync(InteractionContext ctx, [Option("user", "User to display (their name)")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(ctx.User) }, - { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("captcha", new() + { + { "url", ctx.GetGuildAvatarIfPossible(ctx.User) }, + { "username", ctx.GetGuildOrGlobalDisplayNameIfPossible(user) } + }); + } - [SlashCommand("ship", "Ship two users!")] - public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) - { - await ctx.GenerateNekobotImageAsync("ship", new() + [SlashCommand("ship", "Ship two users!")] + public static async Task ShipAsync(InteractionContext ctx, [Option("user1", "First user")] DiscordUser user1, [Option("user2", "Second user")] DiscordUser user2) { - { "user1", ctx.GetGuildAvatarIfPossible(user1) }, - { "user2", ctx.GetGuildAvatarIfPossible(user2) } - }); - } + await ctx.GenerateNekobotImageAsync("ship", new() + { + { "user1", ctx.GetGuildAvatarIfPossible(user1) }, + { "user2", ctx.GetGuildAvatarIfPossible(user2) } + }); + } - [SlashCommand("baguette", "Baguette someone!")] - public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", "User to baguette")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("baguette", new() + [SlashCommand("baguette", "Baguette someone!")] + public static async Task BaguetteAsync(InteractionContext ctx, [Option("user", "User to baguette")] DiscordUser? user = null) { - { "url", ctx.GetGuildAvatarIfPossible(user) } - }); - } + user ??= ctx.User; + await ctx.GenerateNekobotImageAsync("baguette", new() + { + { "url", ctx.GetGuildAvatarIfPossible(user) } + }); + } - [SlashCommand("clyde", "Say something as clyde bot")] - public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) - { - await ctx.GenerateNekobotImageAsync("clyde", new() + [SlashCommand("clyde", "Say something as clyde bot")] + public static async Task ClydeAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - { "text", text } - }); + await ctx.GenerateNekobotImageAsync("clyde", new() + { + { "text", text } + }); + } } } diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index ed698039..e95b6895 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.IO; using HeyRed.Mime; @@ -48,11 +49,11 @@ public static string GetGlobalOrUsername(this DiscordUser user) /// /// The context. /// The image. - /// The title. - /// The content. - /// The user. + /// The optional title. + /// The optional content. + /// The additional user to allow to be pinged (author is already added). /// Whether the message was sent successfully. - public static async Task TryBuildV2ActionMessageAsync(this BaseContext context, MemoryStream image, string title, string content, DiscordUser user) + public static async Task TryBuildV2ActionMessageAsync(this BaseContext context, MemoryStream image, string? title = null, string? content = null, DiscordUser? user = null) { if (context.GuildId is not 1317206872763404478) return false; @@ -60,9 +61,18 @@ public static async Task TryBuildV2ActionMessageAsync(this BaseContext con DiscordWebhookBuilder builder = new(); builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); - builder.AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent(title), new DiscordTextDisplayComponent(content), new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])])); - builder.WithAllowedMentions([new UserMention(context.User), new UserMention(user)]); + DiscordContainerComponent container = new(); + if (title is not null) + container.AddComponent(new DiscordTextDisplayComponent(title)); + if (content is not null) + container.AddComponent(new DiscordTextDisplayComponent(content)); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])); + builder.AddComponents(container); + builder.WithAllowedMention(new UserMention(context.User)); + if (user is not null) + builder.WithAllowedMention(new UserMention(user)); await context.EditResponseAsync(builder); + await image.DisposeAsync(); return true; } @@ -71,20 +81,24 @@ public static async Task TryBuildV2ActionMessageAsync(this BaseContext con /// /// The context. /// The image. - /// The content. - /// The user. - public static async Task SendOldStyleMessageAsync(this BaseContext context, MemoryStream image, string content, DiscordUser user) + /// The optional content. + /// The additional user to allow to be pinged (author is already added). + public static async Task SendOldStyleMessageAsync(this BaseContext context, MemoryStream image, string? content = null, DiscordUser? user = null) { DiscordWebhookBuilder builder = new(); var em = new DiscordEmbedBuilder(); - em.WithDescription(content); + if (content is not null) + em.WithDescription(content); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(image)}"); - em.WithFooter("by nekos.life"); builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); builder.AddEmbed(em.Build()); - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(user)); + if (user is not null) + builder.WithContent(user.Mention); + builder.WithAllowedMention(new UserMention(context.User)); + if (user is not null) + builder.WithAllowedMention(new UserMention(user)); await context.EditResponseAsync(builder); + await image.DisposeAsync(); } /// diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index a906b461..c86cdc2e 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -80,7 +80,15 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string return; } - await ctx.EditResponseAsync(result.Message); + MemoryStream stream = new(await ctx.Client.RestClient.GetByteArrayAsync(result.Message.ResizeLink())) + { + Position = 0 + }; + + if (!await ctx.TryBuildV2ActionMessageAsync(stream)) + await ctx.SendOldStyleMessageAsync(stream); + + await stream.DisposeAsync(); } /// From c49dbab5f9d1564f42eb8961f6cc8215fc4e5fbc Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 05:35:39 +0100 Subject: [PATCH 066/113] meow --- MikuSharp/Commands/Action.cs | 18 ++-- MikuSharp/Commands/Fun.cs | 85 +++++-------------- MikuSharp/Entities/DogCeo.cs | 7 +- MikuSharp/Entities/ImgData.cs | 2 +- MikuSharp/Entities/Random_D.cs | 7 +- .../Utilities/DiscordExtensionMethods.cs | 31 ++++++- 6 files changed, 69 insertions(+), 81 deletions(-) diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/Action.cs index acd6d185..1cfe44a0 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/Action.cs @@ -12,7 +12,7 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u var title = "## A wild hug appears!"; var content = $"{ctx.User.Mention} hugs {user.Mention} uwu"; if (!(await ctx.Client.RestClient.GetWeebShAsync("hug")).TryGetWeebShImage(out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -23,7 +23,7 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The var title = "## A kiss!"; var content = $"{ctx.User.Mention} kisses {user.Mention} >~<"; if (!(await ctx.Client.RestClient.GetWeebShAsync("kiss")).TryGetWeebShImage(out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -34,7 +34,7 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The var title = "## Slurp~"; var content = $"{ctx.User.Mention} licks {user.Mention} owo"; if (!(await ctx.Client.RestClient.GetWeebShAsync("lick")).TryGetWeebShImage(out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -45,7 +45,7 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u var title = "## Pat pat~"; var content = $"{ctx.User.Mention} pats {user.Mention} #w#"; if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("pat", []), out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -56,7 +56,7 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The var title = "## Poke poke!"; var content = $"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"; if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("poke", []), out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -67,7 +67,7 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The var title = "## Slap!"; var content = $"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"; if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("slap", []), out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -78,7 +78,7 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The var title = "## Bite >:3"; var content = $"{ctx.User.Mention} bites {user.Mention} >:3"; if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("bite", []), out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -89,7 +89,7 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u var title = "## Nom nom~"; var content = $"{ctx.User.Mention} noms {user.Mention} >:3c"; if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("nom", []), out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } @@ -100,7 +100,7 @@ public static async Task StateAsync(InteractionContext ctx, [Option("user", "The var title = "## Stare O.o"; var content = $"{ctx.User.Mention} stares at {user.Mention} O.o"; if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("stare", []), out var img)) - await ctx.ActionResponseWithErrorAsync(content, user); + await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); } diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 6d67af54..88fe99cb 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -1,5 +1,3 @@ -using HeyRed.Mime; - using MikuSharp.Attributes; using MikuSharp.Entities; using MikuSharp.Utilities; @@ -86,7 +84,6 @@ public static async Task EightBallAsync(InteractionContext ctx, [Option("text", [SlashCommand("coinflip", "Flip a coin lol")] public static async Task CoinflipAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new()); var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); } @@ -104,78 +101,50 @@ public class RandomImages : ApplicationCommandsModule [SlashCommand("cat", "Get a random cat image!")] public static async Task CatAsync(InteractionContext ctx) { - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); - if (imgUrl is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + var nekosLifeImage = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/meow"); + if (!await ctx.CheckForProperImageResultAsync(nekosLifeImage)) return; - } - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{imgUrl.Filetype}", imgUrl.Data); - builder.AddEmbed(imgUrl.Embed); - await ctx.EditResponseAsync(builder); + if (!await ctx.TryBuildV2ActionMessageAsync(nekosLifeImage!.Data, content: $"[Full Image]({nekosLifeImage.Url})", footer: "by nekos.life")) + await ctx.SendOldStyleMessageAsync(nekosLifeImage.Data, $"[Full Image]({nekosLifeImage.Url})", footer: "by nekos.life"); } [SlashCommand("dog", "Random Dog Image")] public static async Task DogAsync(InteractionContext ctx) { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); - if (dc is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + var dogCeo = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://dog.ceo/api/breeds/image/random")); + if (!await ctx.CheckForProperImageResultAsync(dogCeo)) return; - } - - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by dog.ceo", "https://dog.ceo/img/favicon.png"); - em.WithDescription($"[Full Image]({dc.Message})"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dogCeo.Message.ResizeLink())); + + if (!await ctx.TryBuildV2ActionMessageAsync(img, content: $"[Full Image]({dogCeo.Message})", footer: "by dog.ceo")) + await ctx.SendOldStyleMessageAsync(img, $"[Full Image]({dogCeo.Message})", footer: "by dog.ceo"); } [SlashCommand("duck", "Random duck image")] public static async Task DuckAsync(InteractionContext ctx) { - var dc = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); - if (dc is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + var randomData = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://random-d.uk/api/v1/random")); + if (!await ctx.CheckForProperImageResultAsync(randomData)) return; - } - - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dc.Message.ResizeLink())); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by random-d.uk", "https://random-d.uk/favicon.png"); - em.WithDescription($"[Full Image]({dc.Message})"); - - var builder = new DiscordWebhookBuilder(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); + + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(randomData.Message.ResizeLink())); + if (!await ctx.TryBuildV2ActionMessageAsync(img, content: $"[Full Image]({randomData.Message})", footer: "by random-d.uk")) + await ctx.SendOldStyleMessageAsync(img, $"[Full Image]({randomData.Message})", footer: "by random-d.uk"); } [SlashCommand("lizard", "Get a random lizard image")] public static async Task LizardAsync(InteractionContext ctx) { - var get = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); - if (get is null) - { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + var nekosLifeImage = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/lizard"); + if (!await ctx.CheckForProperImageResultAsync(nekosLifeImage)) return; - } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(get.Url.ResizeLink())); + var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(nekosLifeImage.Url.ResizeLink())); - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - await ctx.EditResponseAsync(builder); + if (!await ctx.TryBuildV2ActionMessageAsync(img, footer: "by nekos.life")) + await ctx.SendOldStyleMessageAsync(img, footer: "by nekos.life"); } } @@ -244,16 +213,6 @@ public static async Task DeepfryAsync(InteractionContext ctx, [Option("user", "U }); } - [SlashCommand("kidnap", "Kidnap a user!")] - public static async Task KidnapAsync(InteractionContext ctx, [Option("user", "User to kidnap")] DiscordUser? user = null) - { - user ??= ctx.User; - await ctx.GenerateNekobotImageAsync("kidnap", new() - { - { "image", ctx.GetGuildAvatarIfPossible(user) } - }); - } - [SlashCommand("tweet", "Generate a fake tweet!")] public static async Task TweetAsync(InteractionContext ctx, [Option("text", "Text of the tweet")] string text, [Option("user", "User for tweet")] DiscordUser? user = null) { diff --git a/MikuSharp/Entities/DogCeo.cs b/MikuSharp/Entities/DogCeo.cs index 4cfeb3fe..198da4ed 100644 --- a/MikuSharp/Entities/DogCeo.cs +++ b/MikuSharp/Entities/DogCeo.cs @@ -1,7 +1,10 @@ -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; -public sealed class DogCeo +public sealed class DogCeo : ImgData { + [JsonProperty("status")] public string Status { get; set; } + + [JsonProperty("message")] public string Message { get; set; } } diff --git a/MikuSharp/Entities/ImgData.cs b/MikuSharp/Entities/ImgData.cs index 275ad71b..30b7c615 100644 --- a/MikuSharp/Entities/ImgData.cs +++ b/MikuSharp/Entities/ImgData.cs @@ -9,7 +9,7 @@ public class ImgData /// Gets the data. /// [JsonIgnore] - public Stream Data { get; set; } + public MemoryStream Data { get; set; } /// /// Gets the file type. diff --git a/MikuSharp/Entities/Random_D.cs b/MikuSharp/Entities/Random_D.cs index c5214332..d32ce98d 100644 --- a/MikuSharp/Entities/Random_D.cs +++ b/MikuSharp/Entities/Random_D.cs @@ -1,7 +1,10 @@ -namespace MikuSharp.Entities; +namespace MikuSharp.Entities; -public class RandomD +public sealed class RandomD : ImgData { + [JsonProperty("url")] public string Url { get; set; } + + [JsonProperty("message")] public string Message { get; set; } } diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index e95b6895..95caa090 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.IO; using HeyRed.Mime; @@ -52,8 +51,12 @@ public static string GetGlobalOrUsername(this DiscordUser user) /// The optional title. /// The optional content. /// The additional user to allow to be pinged (author is already added). + /// + /// The optional footer. Tho footer is not really the correct word for the new components. We use a + /// section instead. + /// /// Whether the message was sent successfully. - public static async Task TryBuildV2ActionMessageAsync(this BaseContext context, MemoryStream image, string? title = null, string? content = null, DiscordUser? user = null) + public static async Task TryBuildV2ActionMessageAsync(this BaseContext context, MemoryStream image, string? title = null, string? content = null, DiscordUser? user = null, string? footer = null) { if (context.GuildId is not 1317206872763404478) return false; @@ -67,6 +70,8 @@ public static async Task TryBuildV2ActionMessageAsync(this BaseContext con if (content is not null) container.AddComponent(new DiscordTextDisplayComponent(content)); container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])); + if (footer is not null) + container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); builder.AddComponents(container); builder.WithAllowedMention(new UserMention(context.User)); if (user is not null) @@ -83,13 +88,16 @@ public static async Task TryBuildV2ActionMessageAsync(this BaseContext con /// The image. /// The optional content. /// The additional user to allow to be pinged (author is already added). - public static async Task SendOldStyleMessageAsync(this BaseContext context, MemoryStream image, string? content = null, DiscordUser? user = null) + /// The optional footer. + public static async Task SendOldStyleMessageAsync(this BaseContext context, MemoryStream image, string? content = null, DiscordUser? user = null, string? footer = null) { DiscordWebhookBuilder builder = new(); var em = new DiscordEmbedBuilder(); if (content is not null) em.WithDescription(content); em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(image)}"); + if (footer is not null) + em.WithFooter(footer); builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); builder.AddEmbed(em.Build()); if (user is not null) @@ -144,9 +152,24 @@ public static bool TryGetWeebShImage(this WeebSh? data, [NotNullWhen(true)] out /// The context. /// The content. /// The user. - public static async Task ActionResponseWithErrorAsync(this BaseContext ctx, string content, DiscordUser user) + public static async Task ActionRespondWithErrorAsync(this BaseContext ctx, string content, DiscordUser user) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(content).WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); } + + /// + /// Responds with an error message if the is null. + /// + /// The context. + /// The image data to check. + /// Whether the image data is not null. + public static async Task CheckForProperImageResultAsync(this BaseContext ctx, [NotNullWhen(true)] ImgData? imgData) + { + if (imgData is not null) + return true; + + await ctx.FollowUpAsync("Something went wrong while fetching the image"); + return false; + } } From b0529819e06418cdab73c6dfca722fa9fa231f7a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 05:41:19 +0100 Subject: [PATCH 067/113] Update Fun.cs --- MikuSharp/Commands/Fun.cs | 61 ++++----------------------------------- 1 file changed, 6 insertions(+), 55 deletions(-) diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/Fun.cs index 88fe99cb..cc5da028 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/Fun.cs @@ -10,61 +10,12 @@ internal class Fun : ApplicationCommandsModule [SlashCommand("8ball", "Yes? No? Maybe?")] public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - var responses = new[] - { - "It is certain.", - "It is decidedly so.", - "Without a doubt.", - "Yes - definitely.", - "You may rely on it.", - "As I see it, yes.", - "Most likely.", - "Outlook good.", - "Yes.", - "Signs point to yes.", - "Absolutely!", - "Of course!", - "No doubt about it.", - "The universe says yes.", - "You got it!", - "Definitely!", - "All signs point to yes.", - "The answer is crystal clear.", - "Yes, in due time.", - "The stars align in your favor.", - "Reply hazy, try again.", - "Ask again later.", - "Better not tell you now.", - "Cannot predict now.", - "Concentrate and ask again.", - "Maybe, maybe not.", - "Uncertain, check back later.", - "Hard to say.", - "Try flipping a coin.", - "Your guess is as good as mine.", - "The future is unclear.", - "I can't say for sure.", - "It's a mystery.", - "Only time will tell.", - "50/50 chance.", - "Don't count on it.", - "My reply is no.", - "My sources say no.", - "Outlook not so good.", - "Very doubtful.", - "No.", - "Absolutely not.", - "I wouldn’t bet on it.", - "No way!", - "Highly unlikely.", - "Not in a million years.", - "Doubtful at best.", - "The odds aren’t in your favor.", - "The universe says no.", - "Nope." - }; - - var chosenAnswer = responses[new Random().Next(0, responses.Length)]; + List responses = + [ + "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Absolutely!", "Of course!", "No doubt about it.", "The universe says yes.", "You got it!", "Definitely!", "All signs point to yes.", "The answer is crystal clear.", "Yes, in due time.", "The stars align in your favor.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Maybe, maybe not.", "Uncertain, check back later.", "Hard to say.", "Try flipping a coin.", "Your guess is as good as mine.", "The future is unclear.", "I can't say for sure.", "It's a mystery.", "Only time will tell.", "50/50 chance.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No.", "Absolutely not.", "I wouldn’t bet on it.", "No way!", "Highly unlikely.", "Not in a million years.", "Doubtful at best.", "The odds aren’t in your favor.", "The universe says no.", "Nope." + ]; + + var chosenAnswer = responses[new Random().Next(0, responses.Count - 1)]; if (ctx.GuildId is 1317206872763404478) { DiscordTextDisplayComponent question = new($"### Question\n{text}"); From aa1495dc5dae7bb8d9f5b824a30012131139b8f6 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 05:56:26 +0100 Subject: [PATCH 068/113] Update Developer.cs --- MikuSharp/Commands/Developer.cs | 241 ++++++++++++++------------------ 1 file changed, 106 insertions(+), 135 deletions(-) diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index bfb01274..84b8ea8f 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -1,6 +1,8 @@ using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; +using MikuSharp.Attributes; + namespace MikuSharp.Commands; /// @@ -11,85 +13,6 @@ public class Developer : ApplicationCommandsModule { private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; - [SlashCommand("test", "Testing")] - public static async Task TestAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - - [SlashCommand("guild_shard_test", "Testing")] - public static async Task GuildTestAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); - } - - [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] - public static async Task DeleteMessageAsync(ContextMenuContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.TargetMessage.DeleteAsync(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } - - /// - /// Gets the debug log. - /// - /// The interaction context. - [SlashCommand("dbg", "Get the logs of today")] - public static async Task GetDebugLogAsync(InteractionContext ctx) - { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request")); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); - var now = DateTime.Now; - var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; - - if (!File.Exists(targetFile)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); - - try - { - if (!File.Exists($"temp-{targetFile}")) - File.Copy(targetFile, $"temp-{targetFile}"); - else - { - File.Delete($"temp-{targetFile}"); - File.Copy(targetFile, $"temp-{targetFile}"); - } - - FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); - log.Close(); - await log.DisposeAsync(); - File.Delete($"temp-{targetFile}"); - } - catch (Exception ex) - { - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); - } - /// /// Evals the csharp script. /// @@ -98,13 +21,6 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) public static async Task EvalCsAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); - - if (ctx.Client.CurrentApplication.Team.Members.All(x => x.User != ctx.User) && ctx.User.Id != 856780995629154305) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You are not allowed to execute this request!")); - return; - } - var msg = ctx.TargetMessage; var code = ctx.TargetMessage.Content; var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; @@ -165,21 +81,12 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe } } - [SlashCommand("lstats", "Displays Lavalink statistics"), ApplicationCommandRequireTeamDeveloper] - public static async Task GetLavalinkStatsAsync(InteractionContext ctx) + [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] + public static async Task DeleteMessageAsync(ContextMenuContext ctx) { - var stats = ctx.Client.GetLavalink().ConnectedSessions.First().Value.Statistics; - var sb = new StringBuilder(); - sb.Append("Lavalink resources usage statistics: ```") - .Append("Uptime: ").Append(stats.Uptime).AppendLine() - .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() - .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() - .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() - .Append("RAM Usage: ") - .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() - .Append("Audio frames (per minute): ").Append($"{stats.Frames.Sent:#,##0} sent / {stats.Frames.Nulled:#,##0} nulled / {stats.Frames.Deficit:#,##0} deficit").AppendLine() - .Append("```"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); + await ctx.TargetMessage.DeleteAsync(); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); } private static string SizeToString(long l) @@ -195,68 +102,132 @@ private static string SizeToString(long l) return $"{d:#,##0.00} {s_units[u]}B"; } -} -/// -/// The test variables. -/// -public sealed class SgTestVariables -{ - //public Dictionary Bot = MikuBot.Guilds; - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The client. - /// The context menu context. - public SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) + [SlashCommandGroup("dev", "Developer commands")] + public class DeveloperCommands : ApplicationCommandsModule { - this.Client = client; - this.ShardClient = shard; - - this.Message = msg; - this.Channel = ctx.Channel; - this.Guild = ctx.Guild; - this.User = ctx.User; - this.Member = ctx.Member; - this.Context = ctx; - this.Inter = this.Client.GetInteractivity(); + [SlashCommand("test", "Testing")] + public static async Task TestAsync(InteractionContext ctx) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + + [SlashCommand("guild_shard_test", "Testing")] + public static async Task GuildTestAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); + } + + [SlashCommand("lstats", "Displays Lavalink statistics"), DeferResponseAsync(true)] + public static async Task GetLavalinkStatsAsync(InteractionContext ctx) + { + var stats = ctx.Client.GetLavalink().ConnectedSessions.First().Value.Statistics; + var sb = new StringBuilder(); + sb.Append("Lavalink resources usage statistics: ```") + .Append("Uptime: ").Append(stats.Uptime).AppendLine() + .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() + .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() + .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() + .Append("RAM Usage: ") + .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() + .Append("Audio frames (per minute): ").Append($"{stats.Frames?.Sent:#,##0} sent / {stats.Frames?.Nulled:#,##0} nulled / {stats.Frames?.Deficit:#,##0} deficit").AppendLine() + .Append("```"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); + } + + /// + /// Gets the debug log. + /// + /// The interaction context. + [SlashCommand("dbg", "Get the logs of today")] + public static async Task GetDebugLogAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Log request")); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Trying to get log")); + var now = DateTime.Now; + var targetFile = $"miku_log{now.ToString("yyyy/MM/dd").Replace("/", "")}.txt"; + + if (!File.Exists(targetFile)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get log")); + return; + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Found log {targetFile.Bold()}")); + + try + { + if (!File.Exists($"temp-{targetFile}")) + File.Copy(targetFile, $"temp-{targetFile}"); + else + { + File.Delete($"temp-{targetFile}"); + File.Copy(targetFile, $"temp-{targetFile}"); + } + + FileStream log = new($"temp-{targetFile}", FileMode.Open, FileAccess.Read); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddFile(targetFile, log, true).WithContent($"Log {targetFile.Bold()}").AsEphemeral()); + log.Close(); + await log.DisposeAsync(); + File.Delete($"temp-{targetFile}"); + } + catch (Exception ex) + { + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.Message).AsEphemeral()); + if (ex.StackTrace is not null) + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent(ex.StackTrace).AsEphemeral()); + } + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); + } } +} +/// +/// Initializes a new instance of the class. +/// +/// The message. +/// The client. +/// The context menu context. +public sealed class SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) +{ /// /// Gets or sets the message. /// - public DiscordMessage Message { get; set; } - - public InteractivityExtension Inter { get; set; } + public DiscordMessage Message { get; set; } = msg; /// /// Gets or sets the channel. /// - public DiscordChannel Channel { get; set; } + public DiscordChannel Channel { get; set; } = ctx.Channel; /// /// Gets or sets the guild. /// - public DiscordGuild Guild { get; set; } + public DiscordGuild? Guild { get; set; } = ctx.Guild; /// /// Gets or sets the user. /// - public DiscordUser User { get; set; } + public DiscordUser User { get; set; } = ctx.User; /// /// Gets or sets the member. /// - public DiscordMember Member { get; set; } + public DiscordMember? Member { get; set; } = ctx.Member; /// /// Gets or sets the context menu context. /// - public ContextMenuContext Context { get; set; } + public ContextMenuContext Context { get; set; } = ctx; - public DiscordShardedClient ShardClient { get; set; } + /// + /// Gets or sets the shard client. + /// + public DiscordShardedClient ShardClient { get; set; } = shard; - public DiscordClient Client { get; set; } + /// + /// Gets or sets the client. + /// + public DiscordClient Client { get; set; } = client; } From c543a3fb08b4e1c36f285f1ed126f3d9f92204a1 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 06:05:45 +0100 Subject: [PATCH 069/113] Update Utility.cs --- MikuSharp/Commands/Utility.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index 402070d7..d5264fb2 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -4,6 +4,7 @@ using Kitsu.Manga; using MikuSharp.Attributes; +using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -130,10 +131,16 @@ internal class DiscordUtility : ApplicationCommandsModule { [SlashCommand("avatar", "Get the avatar of someone or yourself")] public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user != null + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null ? user.AvatarUrl : ctx.User.AvatarUrl).Build())); + [SlashCommand("guild_avatar", "Get the guild avatar of someone or yourself")] + public static async Task GetGuildAvatarAsync(InteractionContext ctx, [Option("user", "User to get the guild avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null + ? ctx.GetGuildAvatarIfPossible(user) + : ctx.GetGuildAvatarIfPossible(ctx.User)).Build())); + [SlashCommand("server_info", "Get information about the server"), DeferResponseAsync(true)] public static async Task GuildInfoAsync(InteractionContext ctx) { @@ -149,9 +156,10 @@ public static async Task GuildInfoAsync(InteractionContext ctx) var emb = new DiscordEmbedBuilder(); emb.WithTitle(ctx.Guild.Name); emb.WithColor(new(0212255)); - emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new("Owner", ctx.Guild.Owner.Mention, true)); - emb.AddField(new("Language", ctx.Guild.PreferredLocale, true)); + if (ctx.Guild.IconUrl is not null) + emb.WithThumbnail(ctx.Guild.IconUrl); + emb.AddField(new("Owner", ctx.Guild.Owner?.UsernameWithGlobalName ?? "unknown??".Italic(), true)); + emb.AddField(new("Language", ctx.Guild.PreferredLocale ?? "Not set".Italic(), true)); emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); @@ -196,7 +204,7 @@ public static async Task EmojiListAsync(InteractionContext ctx) var wat = "You have to execute this command in a server!"; if (ctx.Guild is not null && ctx.Guild.Emojis.Any()) - wat = ctx.Guild.Emojis.Values.Aggregate("**Emojies:** ", (current, em) => current + (em + " ")); + wat = ctx.Guild.Emojis.Values.Aggregate("**Emojies:** ", (current, em) => current + em + " "); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); } From c5db68677ad2f6c8bc13427f7a4c525bb4388cbe Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 06:05:47 +0100 Subject: [PATCH 070/113] Update Weeb.cs --- MikuSharp/Commands/Weeb.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/Weeb.cs index 471726cd..0006ea34 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/Weeb.cs @@ -30,7 +30,6 @@ public static async Task DivaPic(InteractionContext ctx) }; emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - //ctx.Client.Logger.LogDebug(MimeGuesser.GuessExtension(img)); DiscordWebhookBuilder builder = new(); builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); From fb4656d809fe7d72627b8cdfdcb9654f76177483 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 06:12:18 +0100 Subject: [PATCH 071/113] Update About.cs --- MikuSharp/Commands/About.cs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index b8a77c9a..2e8e58ba 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -19,12 +19,16 @@ public static async Task DonateAsync(InteractionContext ctx) public static async Task BotAsync(InteractionContext ctx) { var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) - .WithDescription(ctx.Client.CurrentApplication.Description); - foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id - ? "Owner" - : "Developer", member.User.UsernameWithGlobalName)); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")); + if (ctx.Client.CurrentApplication.Description is not null) + emb.WithDescription(ctx.Client.CurrentApplication.Description); + if (ctx.Client.CurrentApplication.Team is not null) + foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) + emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id + ? "Owner" + : "Developer", member.User.UsernameWithGlobalName)); + else + emb.AddField(new("Owner", ctx.Client.CurrentApplication.Owner.UsernameWithGlobalName)); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } @@ -59,8 +63,8 @@ public static async Task FeedbackAsync(InteractionContext ctx) { DiscordInteractionModalBuilder modalBuilder = new(); modalBuilder.WithTitle("Feedback modal"); - modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "feedbacktitle", "Title of feedback", null, 5, null, true, "Feedback")); - modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "feedbackbody", "Your feedback", null, 20)); + modalBuilder.AddTextComponent(new(TextComponentStyle.Small, "Title of feedback", "feedbacktitle", null, 5, null, true, "Feedback")); + modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "Your feedback", "feedbackbody", null, 20)); await ctx.CreateModalResponseAsync(modalBuilder); var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); @@ -68,19 +72,19 @@ public static async Task FeedbackAsync(InteractionContext ctx) if (!res.TimedOut) { await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); - var title = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbacktitle").Value; - var body = res.Result.Interaction.Data.Components.First(x => x.CustomId == "feedbackbody").Value; + var title = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbacktitle").Value; + var body = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbackbody").Value; var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var emb = new DiscordEmbedBuilder(); emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); - if (ctx.Guild != null) + if (ctx.Guild is not null) emb.AddField(new("Guild", $"{ctx.Guild.Id}", true)); - var forum = guild.GetChannel(1020433162662322257); + var forum = await ctx.Client.GetChannelAsync(1020433162662322257, true); List tags = [ - ctx.Guild != null - ? forum.AvailableTags.First(x => x.Id == 1020434799493648404) - : forum.AvailableTags.First(x => x.Id == 1020434935502360576) + ctx.Guild is not null + ? forum.AvailableTags.First(x => x.Id is 1020434799493648404) + : forum.AvailableTags.First(x => x.Id is 1020434935502360576) ]; var thread = await forum.CreatePostAsync("Feedback", new DiscordMessageBuilder().AddEmbed(emb.Build()).WithContent($"Feedback from {ctx.User.UsernameWithGlobalName}"), null, tags, "Feedback"); var msg = await thread.GetMessageAsync(thread.Id); From 9e42fa93c0f321bf7d03b0bd96d0ad52df980eea Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 09:29:47 +0100 Subject: [PATCH 072/113] rework stats output --- MikuSharp/Commands/About.cs | 59 ++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 2e8e58ba..c3aaec35 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -67,7 +67,7 @@ public static async Task FeedbackAsync(InteractionContext ctx) modalBuilder.AddTextComponent(new(TextComponentStyle.Paragraph, "Your feedback", "feedbackbody", null, 20)); await ctx.CreateModalResponseAsync(modalBuilder); - var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(1)); + var res = await ctx.Client.GetInteractivity().WaitForModalAsync(modalBuilder.CustomId, TimeSpan.FromMinutes(2)); if (!res.TimedOut) { @@ -93,7 +93,7 @@ ctx.Guild is not null await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); } else - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is one minute.").AsEphemeral()); + await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is two minutes.").AsEphemeral()); } [SlashCommand("ping", "Current ping to discord's services")] @@ -107,25 +107,48 @@ public static async Task GetExecutingShardAsync(InteractionContext ctx) [SlashCommand("stats", "Some stats of the MikuBot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { - var guildCount = 0; - var userCount = 0; - var channelCount = 0; - - foreach (var client in MikuBot.ShardedClient.ShardClients) + var guildCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Count); + var userCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.MemberCount ?? 0)); + var channelCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Channels.Count)); + var threadCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Threads.Count)); + var roleCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Roles.Count)); + var emojiCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Emojis.Count)); + var stickerCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Stickers.Count)); + var soundboardSoundsCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.SoundboardSoundsInternal.Count)); + var stageInstancesCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.StageInstances.Count)); + var scheduledEventsCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.ScheduledEvents.Count)); + Dictionary counts = new() { - guildCount += client.Value.Guilds.Count; + ["Guilds"] = guildCount, + ["Users (no distinct)"] = userCount, + ["Channels"] = channelCount, + ["Known Threads"] = threadCount, + ["Roles"] = roleCount, + ["Emojis"] = emojiCount, + ["Stickers"] = stickerCount, + ["Soundboard Sounds"] = soundboardSoundsCount, + ["Stage Instances"] = stageInstancesCount, + ["Scheduled Events"] = scheduledEventsCount + }; - foreach (var guild in client.Value.Guilds) - { - userCount += guild.Value.MemberCount!.Value; - channelCount += guild.Value.Channels.Count; - } - } + var knownGuildFeatures = MikuBot.ShardedClient.ShardClients.Values.SelectMany(client => client.Guilds.Values).SelectMany(guild => guild.RawFeatures ?? ["None"]).Distinct().ToList(); - var emb = new DiscordEmbedBuilder().WithTitle("Stats").WithDescription("Some stats of the MikuBot!").AddField(new("Guilds", guildCount.ToString(), true)).AddField(new("Users", userCount.ToString(), true)) - .AddField(new("Channels", channelCount.ToString(), true)).AddField(new("Ping", ctx.Client.Ping.ToString(), true)) - .AddField(new("Lib (Version)", ctx.Client.BotLibrary + " " + ctx.Client.VersionString, true)).WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + var averagePing = (int)MikuBot.ShardedClient.ShardClients.Values.Average(client => client.Ping); + + DiscordEmbedBuilder builder = new(); + builder.WithTitle("Stats"); + builder.WithDescription($"Some stats of the MikuBot!\n\nKnown Guild Features:\n{string.Join("\n", knownGuildFeatures.Where(feature => feature is not "None")).BlockCode()}"); + foreach (var (key, value) in counts) + builder.AddField(new(key, value.ToString(), true)); + builder.AddField(new("Ping", $"{averagePing.ToString()}ms".InlineCode(), true)); + if (ctx.Client.VersionString.Contains('+')) + builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/{ctx.Client.VersionString.Split('+').Last()}")), true)); + else + builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/v{ctx.Client.VersionString.Trim()}")), true)); + builder.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); + if (ctx.Client.CurrentUser.BannerUrl is not null) + builder.WithImageUrl(ctx.Client.CurrentUser.BannerUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); } [SlashCommand("support", "Link to my support server"), DeferResponseAsync(true)] From 27fe8b5646a6c44f758677f43abe7ab6bdd8eac0 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 09:57:59 +0100 Subject: [PATCH 073/113] fix: pomelo again.. --- MikuSharp/Commands/Utility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/Utility.cs index d5264fb2..a1fd46dd 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/Utility.cs @@ -186,7 +186,7 @@ public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", " var emb = new DiscordEmbedBuilder(); emb.WithColor(new(0212255)); emb.WithTitle("User Info"); - emb.AddField(new("Username", $"{user.Username}#{user.Discriminator}", true)); + emb.AddField(new("Username", $"{user.UsernameWithGlobalName}", true)); if (member is not null) if (member.DisplayName != user.Username) emb.AddField(new("Nickname", $"{member.DisplayName}", true)); From dc90a3ab79ffc9d6ae8ceb7b891102440997b8ba Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 09:58:13 +0100 Subject: [PATCH 074/113] fix: improve linq, update links --- MikuSharp/Commands/About.cs | 46 ++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index c3aaec35..398b4ea8 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -1,4 +1,5 @@ using MikuSharp.Attributes; +using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -9,9 +10,9 @@ internal class About : ApplicationCommandsModule public static async Task DonateAsync(InteractionContext ctx) { var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")) - .WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon", "[Link](https://patreon.com/sekoree)", true)) - .AddField(new("PayPal", "[Link](https://paypal.me/speyd3r)", true)); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithColor(new("#348573")) + .WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon (Owner)", "[sekoree](https://patreon.com/sekoree)")) + .AddField(new("PayPal (Owner)", "[speyd3r](https://paypal.me/speyd3r)")).AddField(new("PayPal (Current Developer)", "[aitsys](https://paypal.me/aitsys)")).AddField(new("GitHub Sponsers (Current Developer)", "[Lulalaby](https://github.com/sponsors/Lulalaby)")); await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); } @@ -19,7 +20,7 @@ public static async Task DonateAsync(InteractionContext ctx) public static async Task BotAsync(InteractionContext ctx) { var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentUser.UsernameWithGlobalName}!").WithAuthor("Miku MikuBot uwu").WithUrl("https://meek.moe/").WithColor(new("#348573")); + emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentApplication.Name}!").WithAuthor("Miku MikuBot uwu").WithColor(new("#348573")); if (ctx.Client.CurrentApplication.Description is not null) emb.WithDescription(ctx.Client.CurrentApplication.Description); if (ctx.Client.CurrentApplication.Team is not null) @@ -98,25 +99,27 @@ ctx.Guild is not null [SlashCommand("ping", "Current ping to discord's services")] public static async Task PingAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {ctx.Client.Ping}ms")); + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {$"{ctx.Client.Ping}ms".InlineCode()}")); [SlashCommand("which_shard", "What shard am I on?")] public static async Task GetExecutingShardAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard {ctx.Client.ShardId}")); + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard: {ctx.Client.ShardId.ToString().InlineCode()}")); [SlashCommand("stats", "Some stats of the MikuBot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { - var guildCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Count); - var userCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.MemberCount ?? 0)); - var channelCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Channels.Count)); - var threadCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Threads.Count)); - var roleCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Roles.Count)); - var emojiCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Emojis.Count)); - var stickerCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.Stickers.Count)); - var soundboardSoundsCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.SoundboardSoundsInternal.Count)); - var stageInstancesCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.StageInstances.Count)); - var scheduledEventsCount = MikuBot.ShardedClient.ShardClients.Values.Sum(client => client.Guilds.Values.Sum(guild => guild.ScheduledEvents.Count)); +#pragma warning disable IDE0004 + var guildCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Count)); + var userCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.MemberCount ?? 0)))); + var channelCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Channels.Count)))); + var threadCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Threads.Count)))); + var roleCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Roles.Count)))); + var emojiCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Emojis.Count)))); + var stickerCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Stickers.Count)))); + var soundboardSoundsCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.SoundboardSoundsInternal.Count)))); + var stageInstancesCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.StageInstances.Count)))); + var scheduledEventsCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.ScheduledEvents.Count)))); +#pragma warning restore IDE0004 Dictionary counts = new() { ["Guilds"] = guildCount, @@ -137,14 +140,19 @@ public static async Task StatsAsync(InteractionContext ctx) DiscordEmbedBuilder builder = new(); builder.WithTitle("Stats"); - builder.WithDescription($"Some stats of the MikuBot!\n\nKnown Guild Features:\n{string.Join("\n", knownGuildFeatures.Where(feature => feature is not "None")).BlockCode()}"); + builder.WithDescription($"Some stats about {ctx.Client.CurrentApplication.Name}!\n\nKnown Guild Features:\n{string.Join("\n", knownGuildFeatures.Where(feature => feature is not "None")).BlockCode()}"); foreach (var (key, value) in counts) - builder.AddField(new(key, value.ToString(), true)); - builder.AddField(new("Ping", $"{averagePing.ToString()}ms".InlineCode(), true)); + builder.AddField(new(key, value.ToString().InlineCode(), true)); + builder.AddField(new("Ping", $"{averagePing}ms".InlineCode(), true)); if (ctx.Client.VersionString.Contains('+')) builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/{ctx.Client.VersionString.Split('+').Last()}")), true)); else builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/v{ctx.Client.VersionString.Trim()}")), true)); + builder.AddField(new("API Channel (Discord)", ctx.Client.Configuration.ApiChannel.ToString().InlineCode(), true)); + builder.AddField(new("API Version (Discord)", ctx.Client.Configuration.ApiVersion.InlineCode(), true)); + var lavalinkDefaultSession = ctx.Client.GetLavalink()?.DefaultSession(); + if (lavalinkDefaultSession is not null) + builder.AddField(new("Lavalink Version", $"{await lavalinkDefaultSession.GetLavalinkVersionAsync()}".InlineCode(), true)); builder.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); if (ctx.Client.CurrentUser.BannerUrl is not null) builder.WithImageUrl(ctx.Client.CurrentUser.BannerUrl); From c56dccf9142cc376bfee84de8b047f00312130f5 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 10:00:00 +0100 Subject: [PATCH 075/113] Update Developer.cs --- MikuSharp/Commands/Developer.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/Developer.cs index 84b8ea8f..16d1752f 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/Developer.cs @@ -18,15 +18,14 @@ public class Developer : ApplicationCommandsModule /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] - public static async Task EvalCsAsync(ContextMenuContext ctx) + public static async Task EvalAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); var msg = ctx.TargetMessage; - var code = ctx.TargetMessage.Content; + var code = msg.Content; var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; cs1 = code.IndexOf('\n', cs1) + 1; var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); - var c = await ctx.Guild.GetActiveThreadsAsync(); if (cs1 == -1 || cs2 == -1) { @@ -44,14 +43,14 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe try { - var globals = new SgTestVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); + var globals = new MikuDeveloperEvalVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); var sopts = ScriptOptions.Default; sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); - var script = CSharpScript.Create(cs, sopts, typeof(SgTestVariables)); + var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); script.Compile(); var result = await script.RunAsync(globals).ConfigureAwait(false); @@ -184,12 +183,12 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) } /// -/// Initializes a new instance of the class. +/// Initializes a new instance of the class. /// /// The message. /// The client. /// The context menu context. -public sealed class SgTestVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) +public sealed class MikuDeveloperEvalVariables(DiscordMessage msg, DiscordClient client, ContextMenuContext ctx, DiscordShardedClient shard) { /// /// Gets or sets the message. From 190a280a5fd037e304c42dd6f504218c280f598a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 10:58:52 +0100 Subject: [PATCH 076/113] Update About.cs --- MikuSharp/Commands/About.cs | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 398b4ea8..53fd5ee3 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -108,31 +108,7 @@ public static async Task GetExecutingShardAsync(InteractionContext ctx) [SlashCommand("stats", "Some stats of the MikuBot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { -#pragma warning disable IDE0004 - var guildCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Count)); - var userCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.MemberCount ?? 0)))); - var channelCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Channels.Count)))); - var threadCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Threads.Count)))); - var roleCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Roles.Count)))); - var emojiCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Emojis.Count)))); - var stickerCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.Stickers.Count)))); - var soundboardSoundsCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.SoundboardSoundsInternal.Count)))); - var stageInstancesCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.StageInstances.Count)))); - var scheduledEventsCount = MikuBot.ShardedClient.ShardClients.Values.Sum((Func)(client => client.Guilds.Values.Sum((Func)(guild => guild.ScheduledEvents.Count)))); -#pragma warning restore IDE0004 - Dictionary counts = new() - { - ["Guilds"] = guildCount, - ["Users (no distinct)"] = userCount, - ["Channels"] = channelCount, - ["Known Threads"] = threadCount, - ["Roles"] = roleCount, - ["Emojis"] = emojiCount, - ["Stickers"] = stickerCount, - ["Soundboard Sounds"] = soundboardSoundsCount, - ["Stage Instances"] = stageInstancesCount, - ["Scheduled Events"] = scheduledEventsCount - }; + var statsitcs = MikuBot.ShardedClient.Statistics; var knownGuildFeatures = MikuBot.ShardedClient.ShardClients.Values.SelectMany(client => client.Guilds.Values).SelectMany(guild => guild.RawFeatures ?? ["None"]).Distinct().ToList(); @@ -141,8 +117,8 @@ public static async Task StatsAsync(InteractionContext ctx) DiscordEmbedBuilder builder = new(); builder.WithTitle("Stats"); builder.WithDescription($"Some stats about {ctx.Client.CurrentApplication.Name}!\n\nKnown Guild Features:\n{string.Join("\n", knownGuildFeatures.Where(feature => feature is not "None")).BlockCode()}"); - foreach (var (key, value) in counts) - builder.AddField(new(key, value.ToString().InlineCode(), true)); + foreach (var (key, value) in statsitcs) + builder.AddField(new(key.ToString(), value.ToString().InlineCode(), true)); builder.AddField(new("Ping", $"{averagePing}ms".InlineCode(), true)); if (ctx.Client.VersionString.Contains('+')) builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/{ctx.Client.VersionString.Split('+').Last()}")), true)); From 54584003f383c7c4c80c4ec352908778aed22423 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 11:09:08 +0100 Subject: [PATCH 077/113] Update About.cs --- MikuSharp/Commands/About.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 53fd5ee3..94db5e3e 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -124,8 +124,8 @@ public static async Task StatsAsync(InteractionContext ctx) builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/{ctx.Client.VersionString.Split('+').Last()}")), true)); else builder.AddField(new("Lib (Version)", $"{ctx.Client.BotLibrary}@{ctx.Client.VersionString}".MaskedUrl(new($"https://github.com/Aiko-IT-Systems/DisCatSharp/tree/v{ctx.Client.VersionString.Trim()}")), true)); - builder.AddField(new("API Channel (Discord)", ctx.Client.Configuration.ApiChannel.ToString().InlineCode(), true)); - builder.AddField(new("API Version (Discord)", ctx.Client.Configuration.ApiVersion.InlineCode(), true)); + builder.AddField(new("API Channel (Discord)", ctx.Client.ApiChannel.ToString().InlineCode(), true)); + builder.AddField(new("API Version (Discord)", ctx.Client.ApiVersion.InlineCode(), true)); var lavalinkDefaultSession = ctx.Client.GetLavalink()?.DefaultSession(); if (lavalinkDefaultSession is not null) builder.AddField(new("Lavalink Version", $"{await lavalinkDefaultSession.GetLavalinkVersionAsync()}".InlineCode(), true)); From 33e06a8c58e61af95858c222c5456e1e919bef4f Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 11:10:55 +0100 Subject: [PATCH 078/113] Update About.cs --- MikuSharp/Commands/About.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/About.cs index 94db5e3e..fa27c70f 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/About.cs @@ -16,7 +16,7 @@ public static async Task DonateAsync(InteractionContext ctx) await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); } - [SlashCommand("bot", "About the bot"), DeferResponseAsync(true)] + [SlashCommand("bot", "Information about the bot"), DeferResponseAsync(true)] public static async Task BotAsync(InteractionContext ctx) { var emb = new DiscordEmbedBuilder(); @@ -59,7 +59,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( $"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); } - [SlashCommand("feedback", "Send feedback!")] + [SlashCommand("feedback", "Send feedback to the developers")] public static async Task FeedbackAsync(InteractionContext ctx) { DiscordInteractionModalBuilder modalBuilder = new(); @@ -101,11 +101,11 @@ ctx.Guild is not null public static async Task PingAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {$"{ctx.Client.Ping}ms".InlineCode()}")); - [SlashCommand("which_shard", "What shard am I on?")] + [SlashCommand("which_shard", "Gets the id of current shard you're using me on")] public static async Task GetExecutingShardAsync(InteractionContext ctx) => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard: {ctx.Client.ShardId.ToString().InlineCode()}")); - [SlashCommand("stats", "Some stats of the MikuBot!"), DeferResponseAsync(true)] + [SlashCommand("stats", "Statistics about the bot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { var statsitcs = MikuBot.ShardedClient.Statistics; @@ -135,7 +135,7 @@ public static async Task StatsAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(builder.Build())); } - [SlashCommand("support", "Link to my support server"), DeferResponseAsync(true)] + [SlashCommand("support", "Link to the support server"), DeferResponseAsync(true)] public static async Task SupportAsybc(InteractionContext ctx) { var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); From b2f8cdf7be25fe2ab36534b2451487d29edb0093 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 11:31:11 +0100 Subject: [PATCH 079/113] nom --- MikuSharp/Attributes/CommandAttributes.cs | 14 +- MikuSharp/Commands/{Old => 0-Old}/Music.cs | 0 MikuSharp/Commands/{Old => 0-Old}/Playlist.cs | 0 .../Commands/{About.cs => AboutCommands.cs} | 16 +- .../Commands/{Action.cs => ActionCommands.cs} | 14 +- .../{Developer.cs => DeveloperCommands.cs} | 6 +- MikuSharp/Commands/{Fun.cs => FunCommands.cs} | 6 +- .../{MikuGuild.cs => MikuGuildCommands.cs} | 11 +- .../{Moderation.cs => ModerationCommands.cs} | 2 +- MikuSharp/Commands/Music/MusicCommands.cs | 4 +- MikuSharp/Commands/NSFW.cs | 98 ----------- MikuSharp/Commands/NsfwCommands.cs | 161 ++++++++++++++++++ .../{Utility.cs => UtilityCommands.cs} | 2 +- .../Commands/{Weeb.cs => WeebCommands.cs} | 2 +- MikuSharp/Entities/MusicSession.cs | 2 +- MikuSharp/GlobalSuppressions.cs | 14 +- MikuSharp/{MikuBot.cs => HatsuneMikuBot.cs} | 142 +++++++++------ MikuSharp/MikuSharp.csproj | 12 +- MikuSharp/Program.cs | 17 +- .../Utilities/MusicSessionExtensionMethods.cs | 14 +- MikuSharp/Utilities/WebExtensionMethods.cs | 4 +- 21 files changed, 329 insertions(+), 212 deletions(-) rename MikuSharp/Commands/{Old => 0-Old}/Music.cs (100%) rename MikuSharp/Commands/{Old => 0-Old}/Playlist.cs (100%) rename MikuSharp/Commands/{About.cs => AboutCommands.cs} (90%) rename MikuSharp/Commands/{Action.cs => ActionCommands.cs} (87%) rename MikuSharp/Commands/{Developer.cs => DeveloperCommands.cs} (97%) rename MikuSharp/Commands/{Fun.cs => FunCommands.cs} (98%) rename MikuSharp/Commands/{MikuGuild.cs => MikuGuildCommands.cs} (78%) rename MikuSharp/Commands/{Moderation.cs => ModerationCommands.cs} (98%) delete mode 100644 MikuSharp/Commands/NSFW.cs create mode 100644 MikuSharp/Commands/NsfwCommands.cs rename MikuSharp/Commands/{Utility.cs => UtilityCommands.cs} (99%) rename MikuSharp/Commands/{Weeb.cs => WeebCommands.cs} (99%) rename MikuSharp/{MikuBot.cs => HatsuneMikuBot.cs} (75%) diff --git a/MikuSharp/Attributes/CommandAttributes.cs b/MikuSharp/Attributes/CommandAttributes.cs index 4e7cb564..f77d1ddf 100644 --- a/MikuSharp/Attributes/CommandAttributes.cs +++ b/MikuSharp/Attributes/CommandAttributes.cs @@ -120,16 +120,16 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) ArgumentNullException.ThrowIfNull(ctx.GuildId); ArgumentNullException.ThrowIfNull(ctx.Guild); var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) { - if (!MikuBot.MusicSessions.TryGetValue(guildId, out _)) + if (!HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out _)) return true; var player = ctx.Client.GetLavalink().GetGuildPlayer(ctx.Guild); if (player is not null) await player.DisconnectAsync(); - MikuBot.MusicSessions.TryRemove(ctx.GuildId.Value, out _); + HatsuneMikuBot.MusicSessions.TryRemove(ctx.GuildId.Value, out _); } return true; @@ -153,10 +153,10 @@ public override async Task ExecuteChecksAsync(BaseContext ctx) { ArgumentNullException.ThrowIfNull(ctx.GuildId); var guildId = ctx.GuildId.Value; - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) { - return MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession) && this.TargetStates.Contains(musicSession.PlaybackState); + return HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out var musicSession) && this.TargetStates.Contains(musicSession.PlaybackState); } } } diff --git a/MikuSharp/Commands/Old/Music.cs b/MikuSharp/Commands/0-Old/Music.cs similarity index 100% rename from MikuSharp/Commands/Old/Music.cs rename to MikuSharp/Commands/0-Old/Music.cs diff --git a/MikuSharp/Commands/Old/Playlist.cs b/MikuSharp/Commands/0-Old/Playlist.cs similarity index 100% rename from MikuSharp/Commands/Old/Playlist.cs rename to MikuSharp/Commands/0-Old/Playlist.cs diff --git a/MikuSharp/Commands/About.cs b/MikuSharp/Commands/AboutCommands.cs similarity index 90% rename from MikuSharp/Commands/About.cs rename to MikuSharp/Commands/AboutCommands.cs index fa27c70f..7b07344e 100644 --- a/MikuSharp/Commands/About.cs +++ b/MikuSharp/Commands/AboutCommands.cs @@ -4,7 +4,7 @@ namespace MikuSharp.Commands; [SlashCommandGroup("about", "About")] -internal class About : ApplicationCommandsModule +internal class AboutCommands : ApplicationCommandsModule { [SlashCommand("donate", "Financial support information")] public static async Task DonateAsync(InteractionContext ctx) @@ -56,7 +56,7 @@ public static async Task FollowNewsAsync( memoryStream.Position = 0; await webhook.ModifyAsync(name, memoryStream, reason: "Dev update follow"); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( - $"News setup complete {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); + $"News setup complete {DiscordEmoji.FromGuildEmote(HatsuneMikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); } [SlashCommand("feedback", "Send feedback to the developers")] @@ -75,7 +75,7 @@ public static async Task FeedbackAsync(InteractionContext ctx) await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var title = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbacktitle").Value; var body = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbackbody").Value; - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var emb = new DiscordEmbedBuilder(); emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); if (ctx.Guild is not null) @@ -91,7 +91,7 @@ ctx.Guild is not null var msg = await thread.GetMessageAsync(thread.Id); await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsdown:")); await msg.CreateReactionAsync(DiscordEmoji.FromName(ctx.Client, ":thumbsup:")); - await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(MikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); + await res.Result.Interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().WithContent($"Feedback sent {DiscordEmoji.FromGuildEmote(HatsuneMikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}")); } else await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().WithContent("You were too slow :(\nThe time limit is two minutes.").AsEphemeral()); @@ -108,11 +108,11 @@ public static async Task GetExecutingShardAsync(InteractionContext ctx) [SlashCommand("stats", "Statistics about the bot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { - var statsitcs = MikuBot.ShardedClient.Statistics; + var statsitcs = HatsuneMikuBot.ShardedClient.Statistics; - var knownGuildFeatures = MikuBot.ShardedClient.ShardClients.Values.SelectMany(client => client.Guilds.Values).SelectMany(guild => guild.RawFeatures ?? ["None"]).Distinct().ToList(); + var knownGuildFeatures = HatsuneMikuBot.ShardedClient.ShardClients.Values.SelectMany(client => client.Guilds.Values).SelectMany(guild => guild.RawFeatures ?? ["None"]).Distinct().ToList(); - var averagePing = (int)MikuBot.ShardedClient.ShardClients.Values.Average(client => client.Ping); + var averagePing = (int)HatsuneMikuBot.ShardedClient.ShardClients.Values.Average(client => client.Ping); DiscordEmbedBuilder builder = new(); builder.WithTitle("Stats"); @@ -138,7 +138,7 @@ public static async Task StatsAsync(InteractionContext ctx) [SlashCommand("support", "Link to the support server"), DeferResponseAsync(true)] public static async Task SupportAsybc(InteractionContext ctx) { - var guild = await MikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var widget = await guild.GetWidgetAsync(); var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); diff --git a/MikuSharp/Commands/Action.cs b/MikuSharp/Commands/ActionCommands.cs similarity index 87% rename from MikuSharp/Commands/Action.cs rename to MikuSharp/Commands/ActionCommands.cs index 1cfe44a0..c0517588 100644 --- a/MikuSharp/Commands/Action.cs +++ b/MikuSharp/Commands/ActionCommands.cs @@ -4,7 +4,7 @@ namespace MikuSharp.Commands; [SlashCommandGroup("action", "Actions", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] -internal class Action : ApplicationCommandsModule +internal class ActionCommands : ApplicationCommandsModule { [SlashCommand("hug", "Hug someone!")] public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) @@ -44,7 +44,7 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u { var title = "## Pat pat~"; var content = $"{ctx.User.Mention} pats {user.Mention} #w#"; - if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("pat", []), out var img)) + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("pat", []), out var img)) await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); @@ -55,7 +55,7 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The { var title = "## Poke poke!"; var content = $"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"; - if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("poke", []), out var img)) + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("poke", []), out var img)) await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); @@ -66,7 +66,7 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The { var title = "## Slap!"; var content = $"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"; - if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("slap", []), out var img)) + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("slap", []), out var img)) await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); @@ -77,7 +77,7 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The { var title = "## Bite >:3"; var content = $"{ctx.User.Mention} bites {user.Mention} >:3"; - if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("bite", []), out var img)) + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("bite", []), out var img)) await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); @@ -88,7 +88,7 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u { var title = "## Nom nom~"; var content = $"{ctx.User.Mention} noms {user.Mention} >:3c"; - if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("nom", []), out var img)) + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("nom", []), out var img)) await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); @@ -99,7 +99,7 @@ public static async Task StateAsync(InteractionContext ctx, [Option("user", "The { var title = "## Stare O.o"; var content = $"{ctx.User.Mention} stares at {user.Mention} O.o"; - if (!ctx.TryGetWeebNetImage(await MikuBot.WeebClient.GetRandomAsync("stare", []), out var img)) + if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("stare", []), out var img)) await ctx.ActionRespondWithErrorAsync(content, user); else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) await ctx.SendOldStyleMessageAsync(img, content, user); diff --git a/MikuSharp/Commands/Developer.cs b/MikuSharp/Commands/DeveloperCommands.cs similarity index 97% rename from MikuSharp/Commands/Developer.cs rename to MikuSharp/Commands/DeveloperCommands.cs index 16d1752f..e47bb238 100644 --- a/MikuSharp/Commands/Developer.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -9,7 +9,7 @@ namespace MikuSharp.Commands; /// The developer commands. /// [ApplicationCommandRequireTeamMember] -public class Developer : ApplicationCommandsModule +public class DeveloperOnlyCommands : ApplicationCommandsModule { private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; @@ -43,7 +43,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe try { - var globals = new MikuDeveloperEvalVariables(ctx.TargetMessage, ctx.Client, ctx, MikuBot.ShardedClient); + var globals = new MikuDeveloperEvalVariables(ctx.TargetMessage, ctx.Client, ctx, HatsuneMikuBot.ShardedClient); var sopts = ScriptOptions.Default; sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", @@ -113,7 +113,7 @@ public static async Task TestAsync(InteractionContext ctx) public static async Task GuildTestAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); - foreach (var shard in MikuBot.ShardedClient.ShardClients.Values) + foreach (var shard in HatsuneMikuBot.ShardedClient.ShardClients.Values) await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); } diff --git a/MikuSharp/Commands/Fun.cs b/MikuSharp/Commands/FunCommands.cs similarity index 98% rename from MikuSharp/Commands/Fun.cs rename to MikuSharp/Commands/FunCommands.cs index cc5da028..274598d7 100644 --- a/MikuSharp/Commands/Fun.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -5,7 +5,7 @@ namespace MikuSharp.Commands; [SlashCommandGroup("fun", "Fun commands", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] -internal class Fun : ApplicationCommandsModule +internal class FunCommands : ApplicationCommandsModule { [SlashCommand("8ball", "Yes? No? Maybe?")] public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) @@ -47,7 +47,7 @@ public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your r } [SlashCommandGroup("random_images", "Random images")] - public class RandomImages : ApplicationCommandsModule + public class RandomImagesCommands : ApplicationCommandsModule { [SlashCommand("cat", "Get a random cat image!")] public static async Task CatAsync(InteractionContext ctx) @@ -100,7 +100,7 @@ public static async Task LizardAsync(InteractionContext ctx) } [SlashCommandGroup("memes", "Meme commands (powered by nekos.life and nekobot.xyz)")] - public class Memes : ApplicationCommandsModule + public class MemesCommands : ApplicationCommandsModule { [SlashCommand("stickbug", "Get stickbugged!")] public static async Task StickbugAsync(InteractionContext ctx, [Option("user", "User to stickbug")] DiscordUser? user = null) diff --git a/MikuSharp/Commands/MikuGuild.cs b/MikuSharp/Commands/MikuGuildCommands.cs similarity index 78% rename from MikuSharp/Commands/MikuGuild.cs rename to MikuSharp/Commands/MikuGuildCommands.cs index 42b66f17..949500f3 100644 --- a/MikuSharp/Commands/MikuGuild.cs +++ b/MikuSharp/Commands/MikuGuildCommands.cs @@ -1,18 +1,21 @@ -namespace MikuSharp.Commands; +namespace MikuSharp.Commands; -public class MikuGuild : ApplicationCommandsModule +public class MikuGuildCommands : ApplicationCommandsModule { [SlashCommand("smolcar", "#SmolArmy")] public static async Task SmolCarAsync(InteractionContext ctx) { + if (ctx.Guild is null || ctx.Member is null) + return; + if (ctx.Member.Roles.Any(x => x.Id == 607989212696018945)) { - await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.Member.RevokeRoleAsync(ctx.Guild.GetRole(607989212696018945)!); await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent(":(")); } else { - await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)); + await ctx.Member.GrantRoleAsync(ctx.Guild.GetRole(607989212696018945)!); await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Welcome to smolcar")); } } diff --git a/MikuSharp/Commands/Moderation.cs b/MikuSharp/Commands/ModerationCommands.cs similarity index 98% rename from MikuSharp/Commands/Moderation.cs rename to MikuSharp/Commands/ModerationCommands.cs index a3c11728..37e8f780 100644 --- a/MikuSharp/Commands/Moderation.cs +++ b/MikuSharp/Commands/ModerationCommands.cs @@ -6,7 +6,7 @@ namespace MikuSharp.Commands; [SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall])] -internal class Moderation : ApplicationCommandsModule +internal class ModerationCommands : ApplicationCommandsModule { [SlashCommand("disable_invites", "Disable invites usage for guild")] public static async Task DisableInvitesAsync(InteractionContext ctx, [Option("reason", "Auditlog reason")] string? reason = null) diff --git a/MikuSharp/Commands/Music/MusicCommands.cs b/MikuSharp/Commands/Music/MusicCommands.cs index 5cca0569..cd5f6328 100644 --- a/MikuSharp/Commands/Music/MusicCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.cs @@ -31,7 +31,7 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, _) => await ctx.EditResponseAsy await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); await musicSession.CurrentChannel.SendMessageAsync("Hatsune Miku at your service!"); await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed("Nothing playing yet")); - MikuBot.MusicSessions[guildId] = musicSession; + HatsuneMikuBot.MusicSessions[guildId] = musicSession; }); } @@ -52,7 +52,7 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); }, async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")), - guildId => Task.FromResult(MikuBot.MusicSessionLocks.TryRemove(guildId, out _))); + guildId => Task.FromResult(HatsuneMikuBot.MusicSessionLocks.TryRemove(guildId, out _))); } [SlashCommand("test", "Test UI Kit"), ApplicationCommandRequireTeamMember, DeferResponseAsync] diff --git a/MikuSharp/Commands/NSFW.cs b/MikuSharp/Commands/NSFW.cs deleted file mode 100644 index 60d6fcd8..00000000 --- a/MikuSharp/Commands/NSFW.cs +++ /dev/null @@ -1,98 +0,0 @@ -using MikuSharp.Attributes; -using MikuSharp.Utilities; - -namespace MikuSharp.Commands; - -[RequireNsfw, NotDiscordStaff] -public class Nsfw : BaseCommandModule -{ - [Command("4k"), Description("lewd")] - public async Task FourK(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("anal"), Description("lewd")] - public async Task Anal(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("ass"), Description("lewd")] - public async Task Ass(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("gonewild"), Description("lewd")] - public async Task Gonewild(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdkitsune"), Description("lewd")] - public async Task LewdKitsune(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("lewdneko"), Description("lewd")] - public async Task LewdNeko(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("porngif"), Description("lewd")] - public async Task PornGif(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("pussy"), Description("lewd")] - public async Task Pussy(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } - - [Command("thighs"), Aliases("thigh"), Description("lewd")] - public async Task Thighs(CommandContext ctx) - { - var d = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); - DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{d.Filetype}", d.Data); - builder.AddEmbed(d.Embed); - await ctx.RespondAsync(builder); - } -} diff --git a/MikuSharp/Commands/NsfwCommands.cs b/MikuSharp/Commands/NsfwCommands.cs new file mode 100644 index 00000000..f00fd30e --- /dev/null +++ b/MikuSharp/Commands/NsfwCommands.cs @@ -0,0 +1,161 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[RequireNsfw, NotDiscordStaff] +public class NsfwCommands : BaseCommandModule +{ + [Command("4k"), Description("lewd")] + public async Task FourK(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("anal"), Description("lewd")] + public async Task Anal(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("ass"), Description("lewd")] + public async Task Ass(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("gonewild"), Description("lewd")] + public async Task Gonewild(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdkitsune"), Description("lewd")] + public async Task LewdKitsune(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("lewdneko"), Description("lewd")] + public async Task LewdNeko(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("porngif"), Description("lewd")] + public async Task PornGif(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("pussy"), Description("lewd")] + public async Task Pussy(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } + + [Command("thighs"), Aliases("thigh"), Description("lewd")] + public async Task Thighs(CommandContext ctx) + { + var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); + if (img is null) + { + await ctx.RespondAsync("Failed to get image"); + await ctx.Message.DeleteAsync("Error while executing command"); + return; + } + + DiscordMessageBuilder builder = new(); + builder.AddFile($"image.{img.Filetype}", img.Data); + builder.AddEmbed(img.Embed); + await ctx.RespondAsync(builder); + } +} diff --git a/MikuSharp/Commands/Utility.cs b/MikuSharp/Commands/UtilityCommands.cs similarity index 99% rename from MikuSharp/Commands/Utility.cs rename to MikuSharp/Commands/UtilityCommands.cs index a1fd46dd..ce84c8a4 100644 --- a/MikuSharp/Commands/Utility.cs +++ b/MikuSharp/Commands/UtilityCommands.cs @@ -9,7 +9,7 @@ namespace MikuSharp.Commands; [SlashCommandGroup("utility", "Utilities")] -internal class Utility : ApplicationCommandsModule +internal class UtilityCommands : ApplicationCommandsModule { [SlashCommandGroup("am", "Anime & Mange"), DeferResponseAsync] internal class AnimeMangaUtility : ApplicationCommandsModule diff --git a/MikuSharp/Commands/Weeb.cs b/MikuSharp/Commands/WeebCommands.cs similarity index 99% rename from MikuSharp/Commands/Weeb.cs rename to MikuSharp/Commands/WeebCommands.cs index 0006ea34..c7bfa782 100644 --- a/MikuSharp/Commands/Weeb.cs +++ b/MikuSharp/Commands/WeebCommands.cs @@ -7,7 +7,7 @@ namespace MikuSharp.Commands; [SlashCommandGroup("weeb", "Weeb Stuff!", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] -internal class Weeb : ApplicationCommandsModule +internal class WeebCommands : ApplicationCommandsModule { [SlashCommand("diva", "Radnom PJD Loading image")] public static async Task DivaPic(InteractionContext ctx) diff --git a/MikuSharp/Entities/MusicSession.cs b/MikuSharp/Entities/MusicSession.cs index 735ef896..1587e0d6 100644 --- a/MikuSharp/Entities/MusicSession.cs +++ b/MikuSharp/Entities/MusicSession.cs @@ -109,7 +109,7 @@ public async Task UpdateStatusMessageAsync(DiscordEmbed embed) else { var messages = await this.CurrentChannel.GetMessagesAsync(50); - var mikuMessages = messages.Where(msg => msg.Author.Id == MikuBot.ShardedClient.CurrentUser.Id).OrderByDescending(msg => msg.CreationTimestamp).ToList(); + var mikuMessages = messages.Where(msg => msg.Author.Id == HatsuneMikuBot.ShardedClient.CurrentUser.Id).OrderByDescending(msg => msg.CreationTimestamp).ToList(); var targetMessage = mikuMessages.FirstOrDefault(msg => msg.Embeds.Count is 1); if (targetMessage is not null) await targetMessage.DeleteAsync("Updating miku status"); diff --git a/MikuSharp/GlobalSuppressions.cs b/MikuSharp/GlobalSuppressions.cs index 2374d7af..19415cd5 100644 --- a/MikuSharp/GlobalSuppressions.cs +++ b/MikuSharp/GlobalSuppressions.cs @@ -354,14 +354,14 @@ [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.WeebSh.Extension")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.WeebSh.ImgData")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.MikuBot")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Action")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Fun")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.About")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuild")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Moderation")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.ActionCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.FunCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.AboutCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuildCommands")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.ModerationCommands")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Music")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.NSFW")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Utility")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.UtilityCommands")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.Weeb")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.BiliJson")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.BiliPlayinfo")] @@ -405,7 +405,7 @@ Target = "~M:MikuSharp.Commands.Music.SizeToString(System.Int64)~System.String")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.message")] [assembly: SuppressMessage("DocumentationHeader", "PropertyDocumentationHeader:The property must have a documentation header.", Justification = "", Scope = "member", Target = "~P:MikuSharp.Entities.Random_D.url")] -[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuild")] +[assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Commands.MikuGuildCommands")] [assembly: SuppressMessage("DocumentationHeader", "ClassDocumentationHeader:The class must have a documentation header.", Justification = "", Scope = "type", Target = "~T:MikuSharp.Entities.Random_D")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "", Scope = "member", diff --git a/MikuSharp/MikuBot.cs b/MikuSharp/HatsuneMikuBot.cs similarity index 75% rename from MikuSharp/MikuBot.cs rename to MikuSharp/HatsuneMikuBot.cs index cef5cb37..3b696fcc 100644 --- a/MikuSharp/MikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -11,31 +11,38 @@ using Weeb.net; -using Action = MikuSharp.Commands.Action; +using ActionCommands = MikuSharp.Commands.ActionCommands; using MikuGuild = MikuSharp.Events.MikuGuild; using PlaylistCommands = MikuSharp.Commands.Playlist.PlaylistCommands; using TokenType = DisCatSharp.Enums.TokenType; namespace MikuSharp; -internal sealed class MikuBot : IDisposable +/// +/// The Hatsune Miku bot. +/// +public sealed class HatsuneMikuBot : IDisposable { + /// + /// Gets the Weeb client. + /// internal static readonly WeebClient WeebClient = new("Hatsune Miku Bot", "5.0.0"); - //internal static Playstate Ps = Playstate.Playing; - //internal static Stopwatch Psc = new(); - /// /// Gets the music sessions. /// internal static readonly ConcurrentDictionary MusicSessions = []; /// - /// Gets the music session locks. + /// Gets the music session locks. /// internal static readonly ConcurrentDictionary MusicSessionLocks = []; - internal MikuBot() + /// + /// Initializes a new instance of the class. + /// + /// Thrown when the config file is null or missing. + public HatsuneMikuBot() { var fileData = File.ReadAllText("config.json") ?? throw new ArgumentNullException(null, "config.json is null or missing"); @@ -44,7 +51,7 @@ internal MikuBot() Config = config; Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - Cts = new(); + MikuCancellationTokenSource = new(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() @@ -132,40 +139,76 @@ internal MikuBot() }; this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; + + DiscordBotListApi = new(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); } - internal static CancellationTokenSource Cts { get; set; } + /// + /// Gets the cancellation token source. + /// + internal static CancellationTokenSource MikuCancellationTokenSource { get; set; } + /// + /// Gets the bot configuration. + /// internal static BotConfig Config { get; set; } + /// + /// Gets the Lavalink configuration. + /// internal LavalinkConfiguration LavalinkConfig { get; set; } - internal Task GameSetThread { get; set; } - - internal Task StatusThread { get; set; } + /// + /// Gets or sets the game set thread. + /// + internal Task? GameSetThread { get; set; } - internal Task BotListThread { get; set; } + /// + /// Gets or sets the bot list thread. + /// + internal Task? BotListThread { get; set; } + /// + /// Gets the Discord Bot List API. + /// internal static AuthDiscordBotListApi DiscordBotListApi { get; set; } + /// + /// Gets the sharded client. + /// internal static DiscordShardedClient ShardedClient { get; set; } + /// + /// Gets the interactivity modules. + /// internal IReadOnlyDictionary InteractivityModules { get; set; } + /// + /// Gets the application commands modules. + /// internal IReadOnlyDictionary ApplicationCommandsModules { get; set; } + /// + /// Gets the commands next modules. + /// internal IReadOnlyDictionary CommandsNextModules { get; set; } + /// + /// Gets the Lavalink modules. + /// internal IReadOnlyDictionary LavalinkModules { get; set; } + /// public void Dispose() { -#pragma warning disable IDE0022 // Use expression body for method GC.SuppressFinalize(this); -#pragma warning restore IDE0022 // Use expression body for method + ShardedClient = null!; } - internal static async Task RegisterEvents() + /// + /// Registers the events. + /// + internal static async Task RegisterEventsAsync() { ShardedClient.ClientErrored += (sender, args) => { @@ -230,17 +273,10 @@ internal static async Task RegisterEvents() } } - /*internal async Task ShowConnections() - { - while (true) - { - var al = Guilds.Where(x => x.Value?.MusicInstance != null); - ShardedClient.Logger.LogInformation("Voice Connections: " + al.Count(x => x.Value.MusicInstance.GuildConnection?.IsConnected == true)); - await Task.Delay(15000); - } - }*/ - - internal static async Task UpdateBotList() + /// + /// Updates the bot list statistics. + /// + internal static async Task UpdateBotListStatisticsAsync() { await Task.Delay(15000); @@ -255,53 +291,62 @@ internal static async Task UpdateBotList() } } - internal static async Task SetActivity() + /// + /// Rotates the activity every 20 minutes. + /// + internal static async Task RotateActivityAsync() { while (true) { - DiscordActivity test = new() + DiscordActivity firstActivity = new() { Name = "New music system coming up soon!", ActivityType = ActivityType.Playing }; - await ShardedClient.UpdateStatusAsync(test, UserStatus.Online); + await ShardedClient.UpdateStatusAsync(firstActivity, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test2 = new() + DiscordActivity secondActivity = new() { Name = "Mention me with help for other commands!", ActivityType = ActivityType.Playing }; - await ShardedClient.UpdateStatusAsync(test2, UserStatus.Online); + await ShardedClient.UpdateStatusAsync(secondActivity, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); - DiscordActivity test3 = new() + DiscordActivity thirdActivity = new() { Name = "Full NND support!", ActivityType = ActivityType.Playing }; - await ShardedClient.UpdateStatusAsync(test3, UserStatus.Online); + await ShardedClient.UpdateStatusAsync(thirdActivity, UserStatus.Online); await Task.Delay(TimeSpan.FromMinutes(20)); } } + /// + /// Registers the commands. + /// internal void RegisterCommands() { // Nsfw stuff needs to be hidden, that's why we use commands next - this.CommandsNextModules.RegisterCommands(); + this.CommandsNextModules.RegisterCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); - this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); // Smolcar command, miku discord guild command - this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); + this.ApplicationCommandsModules.RegisterGuildCommands(483279257431441410); } + /// + /// Runs the bot. + /// internal async Task RunAsync() { await WeebClient.Authenticate(Config.WeebShToken, Weeb.net.TokenType.Wolke); @@ -310,7 +355,6 @@ internal async Task RunAsync() var success = false; while (!success) - { try { foreach (var lavalinkShard in this.LavalinkModules) @@ -321,19 +365,17 @@ internal async Task RunAsync() { success = false; } - } - this.GameSetThread = Task.Run(SetActivity); - //StatusThread = Task.Run(ShowConnections); - //DiscordBotListApi = new AuthDiscordBotListApi(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); + this.GameSetThread = Task.Run(RotateActivityAsync); //BotListThread = Task.Run(UpdateBotList); - while (!Cts.IsCancellationRequested) + while (!MikuCancellationTokenSource.IsCancellationRequested) await Task.Delay(25); _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); await ShardedClient.StopAsync(); } - ~MikuBot() + /// + ~HatsuneMikuBot() { this.Dispose(); } diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 0c9badea..f2230163 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -5,17 +5,17 @@ net9.0 MikuSharp.Program miku.ico - 4.0.0 - MikuSharp Team + 5.0.0 + aiko, MikuSharp Team https://github.com/Sekoree/MikuSharp/ git https://github.com/Sekoree/MikuSharp/ MIT - Full Hatsune Miku Discord bot C# Rewrite! + The One And Only Hatsune Miku Bot! enable - Hatsune Miku Discord MikuBot + Hatsune Miku Discord Bot MikuSharp Team - miku.jpg + miku.png README.md discord bot; discatsharp; hatsune miku; miku; bot true @@ -126,7 +126,7 @@ Always - + True \ diff --git a/MikuSharp/Program.cs b/MikuSharp/Program.cs index 27e439bd..06621575 100644 --- a/MikuSharp/Program.cs +++ b/MikuSharp/Program.cs @@ -1,12 +1,21 @@ namespace MikuSharp; -internal class Program +/// +/// The main program class. +/// +public class Program { - private static void Main(string[] args) + /// + /// The main entry point of the application. + /// + /// The optional command-line arguments. + public static void Main(string[]? args = null) { - using (var bot = new MikuBot()) + Log.Logger.Information("Startup!"); + + using (var bot = new HatsuneMikuBot()) { - MikuBot.RegisterEvents().Wait(); + HatsuneMikuBot.RegisterEventsAsync().Wait(); bot.RegisterCommands(); bot.RunAsync().Wait(); } diff --git a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs index 8ebe02ea..7a0e552b 100644 --- a/MikuSharp/Utilities/MusicSessionExtensionMethods.cs +++ b/MikuSharp/Utilities/MusicSessionExtensionMethods.cs @@ -28,7 +28,7 @@ internal static class MusicSessionExtensionMethods public static DiscordEmbed BuildMusicStatusEmbed(this MusicSession session, string description, List? additionalEmbedFields = null) { var builder = new DiscordEmbedBuilder() - .WithAuthor(MikuBot.ShardedClient.CurrentUser.UsernameWithGlobalName, iconUrl: MikuBot.ShardedClient.CurrentUser.AvatarUrl) + .WithAuthor(HatsuneMikuBot.ShardedClient.CurrentUser.UsernameWithGlobalName, iconUrl: HatsuneMikuBot.ShardedClient.CurrentUser.AvatarUrl) .WithColor(DiscordColor.Black) .WithTitle("Miku Music Status") .WithDescription(description); @@ -149,10 +149,10 @@ public static async Task ExecuteWithMusicSessionAsync(this AutocompleteContext c /// A task that represents the asynchronous operation. public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func successAction, Func? failureAction = null, Func? finalAction = null) { - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + if (HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) await successAction(guildId, musicSession); else if (failureAction is not null) await failureAction(guildId); @@ -209,10 +209,10 @@ public static async Task ExecuteWithMusicSessionAsync(this AutocompleteCon /// The result of the action or the default value with given type from . public static async Task ExecuteWithMusicSessionAsync(this ulong guildId, Func> successAction, Func>? failureAction = null, T defaultValue = default) { - var asyncLock = MikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); - using (await asyncLock.LockAsync(MikuBot.Cts.Token)) + var asyncLock = HatsuneMikuBot.MusicSessionLocks.GetOrAdd(guildId, _ => new()); + using (await asyncLock.LockAsync(HatsuneMikuBot.MikuCancellationTokenSource.Token)) { - if (MikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) + if (HatsuneMikuBot.MusicSessions.TryGetValue(guildId, out var musicSession)) return await successAction(guildId, musicSession); if (failureAction is not null) return await failureAction(guildId); diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index c86cdc2e..ee42af32 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -47,7 +47,7 @@ public static class WebExtensionMethods /// The ksoft.si response. public static async Task GetKsoftSiImgageAsync(this HttpClient client, string tag = "hentai_gif", bool nsfw = true) { - client.DefaultRequestHeaders.Authorization = new("Bearer", MikuBot.Config.KsoftSiToken); + client.DefaultRequestHeaders.Authorization = new("Bearer", HatsuneMikuBot.Config.KsoftSiToken); var dl = JsonConvert.DeserializeObject(await client.GetStringAsync($"https://api.ksoft.si/images/random-image?tag={tag}&nsfw={nsfw.ToString().ToLowerInvariant()}")); if (dl is null) return null; @@ -126,7 +126,7 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string /// The weeb.sh response. public static async Task GetWeebShAsync(this HttpClient client, string query, IEnumerable? tags = null, NsfwSearch nsfw = NsfwSearch.False) { - var dl = await MikuBot.WeebClient.GetRandomAsync(query, tags ?? [""], nsfw: nsfw); + var dl = await HatsuneMikuBot.WeebClient.GetRandomAsync(query, tags ?? [""], nsfw: nsfw); if (dl is null) return null; From dbeec54643408f55ff1336c51a508daf1f8bab1e Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 11:52:27 +0100 Subject: [PATCH 080/113] fuck me --- MikuSharp/HatsuneMikuBot.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index 3b696fcc..925c1448 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -139,8 +139,6 @@ public HatsuneMikuBot() }; this.LavalinkModules = ShardedClient.UseLavalinkAsync().Result; - - DiscordBotListApi = new(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); } /// @@ -366,6 +364,7 @@ internal async Task RunAsync() success = false; } + DiscordBotListApi = new(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); this.GameSetThread = Task.Run(RotateActivityAsync); //BotListThread = Task.Run(UpdateBotList); while (!MikuCancellationTokenSource.IsCancellationRequested) From e2a967908b09520b078693f6ce62b223b03b6f70 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 12:20:52 +0100 Subject: [PATCH 081/113] Update HatsuneMikuBot.cs --- MikuSharp/HatsuneMikuBot.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index 925c1448..428b923a 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -5,17 +5,14 @@ using MikuSharp.Attributes; using MikuSharp.Commands; using MikuSharp.Commands.Music; +using MikuSharp.Commands.Playlist; using MikuSharp.Entities; +using MikuSharp.Events; using Serilog.Events; using Weeb.net; -using ActionCommands = MikuSharp.Commands.ActionCommands; -using MikuGuild = MikuSharp.Events.MikuGuild; -using PlaylistCommands = MikuSharp.Commands.Playlist.PlaylistCommands; -using TokenType = DisCatSharp.Enums.TokenType; - namespace MikuSharp; /// @@ -63,7 +60,7 @@ public HatsuneMikuBot() ShardedClient = new(new() { Token = Config.DiscordToken, - TokenType = TokenType.Bot, + TokenType = DisCatSharp.Enums.TokenType.Bot, MinimumLogLevel = LogLevel.Debug, AutoReconnect = true, ApiChannel = ApiChannel.Canary, From 58267ef85b26da0108409e78c7c764cb13760888 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 13:22:27 +0100 Subject: [PATCH 082/113] mhh --- MikuSharp/Commands/DeveloperCommands.cs | 35 +++++++++++++++++++++++++ MikuSharp/HatsuneMikuBot.cs | 3 ++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index e47bb238..0463caf4 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -179,6 +179,41 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); } + + [SlashCommandGroup("monetization", "Monetization tests")] + public class Monetization : ApplicationCommandsModule + { + private const ulong CONSUMABLE_SKU_ID = 1337743977473900555; + private const ulong DURABLE_SKU_ID = 1337744226666151949; + + [SlashCommand("consume_consumable", "Consume a consumable"), ApplicationCommandRequireSkuEntitlement(CONSUMABLE_SKU_ID)] + public async Task ConsumeConsumableAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Testing..")); + + if (ctx.EntitlementSkuIds.Contains(CONSUMABLE_SKU_ID)) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable works")); + if (await ctx.Entitlements.First(x => x.Id == CONSUMABLE_SKU_ID).ConsumeAsync()) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable consumed")); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable failed to consume")); + } + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Huh?!")); + } + + [SlashCommand("use_durable", "Use a durable"), ApplicationCommandRequireSkuEntitlement(DURABLE_SKU_ID)] + public async Task UseDurableAsync(InteractionContext ctx) + { + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Testing..")); + + if (ctx.EntitlementSkuIds.Contains(DURABLE_SKU_ID)) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Durable works")); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Huh?!")); + } + } } } diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index 428b923a..c083ca48 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -75,7 +75,8 @@ public HatsuneMikuBot() FeedbackEmail = "aiko@aitsys.dev", DeveloperUserId = 856780995629154305, AttachUserInfo = true, - ReconnectIndefinitely = true + ReconnectIndefinitely = true, + EnableLibraryDeveloperMode = true }); this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() From 06dda78a9c96b0e19a88c9b896fb6fe396c21be8 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 15:42:14 +0100 Subject: [PATCH 083/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 0463caf4..bf190d97 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -187,11 +187,11 @@ public class Monetization : ApplicationCommandsModule private const ulong DURABLE_SKU_ID = 1337744226666151949; [SlashCommand("consume_consumable", "Consume a consumable"), ApplicationCommandRequireSkuEntitlement(CONSUMABLE_SKU_ID)] - public async Task ConsumeConsumableAsync(InteractionContext ctx) + public static async Task ConsumeConsumableAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Testing..")); - if (ctx.EntitlementSkuIds.Contains(CONSUMABLE_SKU_ID)) + if (ctx.Entitlements.Any(x => x.Id == CONSUMABLE_SKU_ID)) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Consumable works")); if (await ctx.Entitlements.First(x => x.Id == CONSUMABLE_SKU_ID).ConsumeAsync()) @@ -204,11 +204,11 @@ public async Task ConsumeConsumableAsync(InteractionContext ctx) } [SlashCommand("use_durable", "Use a durable"), ApplicationCommandRequireSkuEntitlement(DURABLE_SKU_ID)] - public async Task UseDurableAsync(InteractionContext ctx) + public static async Task UseDurableAsync(InteractionContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent("Testing..")); - if (ctx.EntitlementSkuIds.Contains(DURABLE_SKU_ID)) + if (ctx.Entitlements.Any(x => x.Id == DURABLE_SKU_ID)) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Durable works")); else await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Huh?!")); From bae7548acd461641847a9f91fafd0e114f7bc1d6 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sat, 8 Feb 2025 20:28:29 +0100 Subject: [PATCH 084/113] Update FunCommands.cs --- MikuSharp/Commands/FunCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs index 274598d7..93dafa82 100644 --- a/MikuSharp/Commands/FunCommands.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -42,8 +42,8 @@ public static async Task CoinflipAsync(InteractionContext ctx) [SlashCommand("rps", "Play rock paper scissors!")] public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) { - var rock = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} choose {rps}!\n\nI choose {rock[new Random().Next(0, rock.Length)]}")); + var rpsChoices = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} chooses {rps}!\n\nI choose {rpsChoices[new Random().Next(0, rpsChoices.Length)]}")); } [SlashCommandGroup("random_images", "Random images")] From 8f9e2585c1630abfc85e890096abb4b2d95b7fcf Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 08:59:02 +0100 Subject: [PATCH 085/113] games --- MikuSharp/Commands/FunCommands.cs | 71 ++++--- .../Entities/Games/RockPaperScissorsGame.cs | 192 ++++++++++++++++++ 2 files changed, 231 insertions(+), 32 deletions(-) create mode 100644 MikuSharp/Entities/Games/RockPaperScissorsGame.cs diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs index 93dafa82..8859ade8 100644 --- a/MikuSharp/Commands/FunCommands.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -1,5 +1,6 @@ using MikuSharp.Attributes; using MikuSharp.Entities; +using MikuSharp.Entities.Games; using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -7,43 +8,49 @@ namespace MikuSharp.Commands; [SlashCommandGroup("fun", "Fun commands", allowedContexts: [InteractionContextType.Guild, InteractionContextType.PrivateChannel], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall, ApplicationCommandIntegrationTypes.UserInstall]), DeferResponseAsync] internal class FunCommands : ApplicationCommandsModule { - [SlashCommand("8ball", "Yes? No? Maybe?")] - public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + [SlashCommandGroup("games", "Games")] + public class GamesCommands : ApplicationCommandsModule { - List responses = - [ - "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Absolutely!", "Of course!", "No doubt about it.", "The universe says yes.", "You got it!", "Definitely!", "All signs point to yes.", "The answer is crystal clear.", "Yes, in due time.", "The stars align in your favor.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Maybe, maybe not.", "Uncertain, check back later.", "Hard to say.", "Try flipping a coin.", "Your guess is as good as mine.", "The future is unclear.", "I can't say for sure.", "It's a mystery.", "Only time will tell.", "50/50 chance.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No.", "Absolutely not.", "I wouldn’t bet on it.", "No way!", "Highly unlikely.", "Not in a million years.", "Doubtful at best.", "The odds aren’t in your favor.", "The universe says no.", "Nope." - ]; - - var chosenAnswer = responses[new Random().Next(0, responses.Count - 1)]; - if (ctx.GuildId is 1317206872763404478) + [SlashCommand("8ball", "Yes? No? Maybe?")] + public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) { - DiscordTextDisplayComponent question = new($"### Question\n{text}"); - DiscordSectionComponent questionComponent = new([question]); - questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); - DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); - DiscordTextDisplayComponent answer = new($"### Answer\n{chosenAnswer}"); - DiscordSectionComponent answerComponent = new([answer]); - answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([questionComponent]), seperator, new DiscordContainerComponent([answerComponent]))); - return; - } + List responses = + [ + "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Absolutely!", "Of course!", "No doubt about it.", "The universe says yes.", "You got it!", "Definitely!", "All signs point to yes.", "The answer is crystal clear.", "Yes, in due time.", "The stars align in your favor.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Maybe, maybe not.", "Uncertain, check back later.", "Hard to say.", "Try flipping a coin.", "Your guess is as good as mine.", "The future is unclear.", "I can't say for sure.", "It's a mystery.", "Only time will tell.", "50/50 chance.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No.", "Absolutely not.", "I wouldn’t bet on it.", "No way!", "Highly unlikely.", + "Not in a million years.", "Doubtful at best.", "The odds aren’t in your favor.", "The universe says no.", "Nope." + ]; + + var chosenAnswer = responses[new Random().Next(0, responses.Count - 1)]; + if (ctx.GuildId is 1317206872763404478) + { + DiscordTextDisplayComponent question = new($"### Question\n{text}"); + DiscordSectionComponent questionComponent = new([question]); + questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); + DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent answer = new($"### Answer\n{chosenAnswer}"); + DiscordSectionComponent answerComponent = new([answer]); + answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([questionComponent]), seperator, new DiscordContainerComponent([answerComponent]))); + return; + } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{chosenAnswer}")); - } + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{chosenAnswer}")); + } - [SlashCommand("coinflip", "Flip a coin lol")] - public static async Task CoinflipAsync(InteractionContext ctx) - { - var flip = new[] { $"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Length)])); - } + [SlashCommand("coinflip", "Flip a coin!")] + public static async Task CoinflipAsync(InteractionContext ctx) + { + List flip = [$"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}"]; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Count - 1)])); + } - [SlashCommand("rps", "Play rock paper scissors!")] - public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] string rps) - { - var rpsChoices = new[] { $"Rock {DiscordEmoji.FromName(ctx.Client, ":black_circle:")}", $"Paper {DiscordEmoji.FromName(ctx.Client, ":pencil:")}", $"Scissors {DiscordEmoji.FromName(ctx.Client, ":scissors:")}" }; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{ctx.User.Mention} chooses {rps}!\n\nI choose {rpsChoices[new Random().Next(0, rpsChoices.Length)]}")); + [SlashCommand("rps", "Play rock paper scissors!")] + public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] RockPaperScissorsChoiceType userChoice) + { + var game = userChoice.ResolveRps(); + if (!await game.TryBuildV2RpsMessageAsync(ctx)) + await game.SendOldStyleRpsMessageAsync(ctx); + } } [SlashCommandGroup("random_images", "Random images")] diff --git a/MikuSharp/Entities/Games/RockPaperScissorsGame.cs b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs new file mode 100644 index 00000000..7bb308c4 --- /dev/null +++ b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs @@ -0,0 +1,192 @@ +namespace MikuSharp.Entities.Games; + +/// +/// Represents a rock-paper-scissors game. +/// +public static class RockPaperScissorsGame +{ + /// + /// The rock–paper–scissors assets. + /// + public static IEnumerable Assets + => [new(RockPaperScissorsChoiceType.Rock, "https://miku-cdn.aitsys.dev/assets/miku/rps/noun-rock-88661.png", "Rock by Studio Fibonacci from Noun Project (CC BY 3.0)"), new(RockPaperScissorsChoiceType.Paper, "https://miku-cdn.aitsys.dev/assets/miku/rps/noun-paper-88662.png", "Paper by Studio Fibonacci from Noun Project (CC BY 3.0)"), new(RockPaperScissorsChoiceType.Scissors, "https://miku-cdn.aitsys.dev/assets/miku/rps/noun-scissors-88666.png", "Scissors by Studio Fibonacci from Noun Project (CC BY 3.0)")]; + + /// + /// Plays a round of rock–paper–scissors against the user. + /// + /// The user's choice. + /// A representing the outcome. + public static RockPaperScissorsResponse ResolveRps(this RockPaperScissorsChoiceType userChoice) + { + var random = new Random(); + var values = Enum.GetValues(); + var computerChoice = (RockPaperScissorsChoiceType)values.GetValue(random.Next(values.Length))!; + + return new(userChoice, computerChoice, userChoice == computerChoice + ? RockPaperScissorsWinType.Tie + : (userChoice is RockPaperScissorsChoiceType.Rock && computerChoice is RockPaperScissorsChoiceType.Scissors) || + (userChoice is RockPaperScissorsChoiceType.Paper && computerChoice is RockPaperScissorsChoiceType.Rock) || + (userChoice is RockPaperScissorsChoiceType.Scissors && computerChoice is RockPaperScissorsChoiceType.Paper) + ? RockPaperScissorsWinType.User + : RockPaperScissorsWinType.Computer); + } +} + +/// +/// Represents a rock-paper-scissors asset. +/// +/// The type. +/// The image URL. +/// The creative commons license. +public sealed class RockPaperScissorsAsset(RockPaperScissorsChoiceType choiceType, string imgUrl, string ccl) +{ + /// + /// The type. + /// + public RockPaperScissorsChoiceType ChoiceType { get; } = choiceType; + + /// + /// The image URL. + /// + public string ImageUrl { get; } = imgUrl; + + /// + /// The creative commons license. + /// + public string CreativeCommonsLicense { get; } = ccl; + + /// + /// Maps a to a . + /// + /// The choice type. + /// The mapped . + public static implicit operator RockPaperScissorsAsset(RockPaperScissorsChoiceType type) + => RockPaperScissorsGame.Assets.First(x => x.ChoiceType == type); +} + +/// +/// Constructs a new . +/// +/// The user choice asset. +/// The computer choice asset. +/// The win type. +public sealed class RockPaperScissorsResponse(RockPaperScissorsAsset userChoiceAsset, RockPaperScissorsAsset computerChoiceAsset, RockPaperScissorsWinType winType) +{ + /// + /// Gets the computer's choice asset. + /// + public RockPaperScissorsAsset UserChoiceAsset { get; } = userChoiceAsset; + + /// + /// Gets the computer's choice asset. + /// + public RockPaperScissorsAsset ComputerChoiceAsset { get; } = computerChoiceAsset; + + /// + /// Gets the result. + /// + public RockPaperScissorsWinType WinType { get; } = winType; + + /// + /// Gets the winner asset. + /// + public (string ImageUrl, string CreativeCommonsLicense) WinnerAsset + => this.WinType is RockPaperScissorsWinType.Computer or RockPaperScissorsWinType.Tie + ? (this.ComputerChoiceAsset.ImageUrl, this.ComputerChoiceAsset.CreativeCommonsLicense) + : (this.UserChoiceAsset.ImageUrl, this.UserChoiceAsset.CreativeCommonsLicense); + + /// + /// Tries to build and send a components V2 rock-paper-scissors message. + /// + /// The context. + /// Whether the message was sent successfully. + public async Task TryBuildV2RpsMessageAsync(InteractionContext ctx) + { + if (ctx.GuildId is not 1317206872763404478) + return false; + + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + DiscordTextDisplayComponent userChoiceComponent = new($"### {ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType.ToString().InlineCode()}"); + DiscordSectionComponent userChoiceSection = new([userChoiceComponent]); + userChoiceSection.WithThumbnailComponent(ctx.User.AvatarUrl); + DiscordSeparatorComponent seperator1 = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent computerChoiceComponent = new($"### I choose {this.ComputerChoiceAsset.ChoiceType.ToString().InlineCode()}"); + DiscordSectionComponent computerChoiceSection = new([computerChoiceComponent]); + computerChoiceSection.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); + DiscordSeparatorComponent seperator2 = new(true, SeparatorSpacingSize.Large); + DiscordTextDisplayComponent resultTextComponent = new($"### {this}"); + DiscordSectionComponent resultSectionComponent = new([resultTextComponent]); + resultSectionComponent.WithThumbnailComponent(this.WinnerAsset.ImageUrl, this.WinnerAsset.CreativeCommonsLicense); + builder.AddComponents(new DiscordContainerComponent([userChoiceSection, seperator1, computerChoiceSection, seperator2, resultSectionComponent])); + builder.WithAllowedMention(new UserMention(ctx.User)); + await ctx.EditResponseAsync(builder); + return true; + } + + /// + /// Sends an old-style embed rock-paper-scissors message. + /// + /// The context. + public async Task SendOldStyleRpsMessageAsync(InteractionContext ctx) + { + var emb = new DiscordEmbedBuilder() + .WithTitle("Rock Paper Scissors") + .WithDescription($"{ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType} and I choose {this.ComputerChoiceAsset.ChoiceType}.\n{this}") + .WithThumbnail(this.WinnerAsset.ImageUrl) + .WithFooter(this.WinnerAsset.CreativeCommonsLicense, "https://mirrors.creativecommons.org/presskit/icons/cc.xlarge.png"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb)); + } + + /// + public override string? ToString() + => this.WinType switch + { + RockPaperScissorsWinType.User => "You win :3", + RockPaperScissorsWinType.Computer => "I win ^~^", + RockPaperScissorsWinType.Tie => "It's a tie O.o", + _ => null + }; +} + +/// +/// Represents a rock paper scissors type. +/// +public enum RockPaperScissorsChoiceType +{ + /// + /// The rock. + /// + Rock, + + /// + /// The paper. + /// + Paper, + + /// + /// The scissors. + /// + Scissors +} + +/// +/// Represents a rock paper scissors win type. +/// +public enum RockPaperScissorsWinType +{ + /// + /// The user wins. + /// + User, + + /// + /// The computer wins. + /// + Computer, + + /// + /// It's a tie. + /// + Tie +} From c04a315affb10651bc470bd63de690a07b4724c5 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 11:43:30 +0100 Subject: [PATCH 086/113] make 8ball & cointoss a game --- MikuSharp/Commands/FunCommands.cs | 31 ++------- MikuSharp/Entities/Games/CointossGame.cs | 84 +++++++++++++++++++++++ MikuSharp/Entities/Games/EightBallGame.cs | 78 +++++++++++++++++++++ 3 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 MikuSharp/Entities/Games/CointossGame.cs create mode 100644 MikuSharp/Entities/Games/EightBallGame.cs diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs index 8859ade8..92dee618 100644 --- a/MikuSharp/Commands/FunCommands.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -12,36 +12,19 @@ internal class FunCommands : ApplicationCommandsModule public class GamesCommands : ApplicationCommandsModule { [SlashCommand("8ball", "Yes? No? Maybe?")] - public static async Task EightBallAsync(InteractionContext ctx, [Option("text", "Text to modify")] string text) + public static async Task EightBallAsync(InteractionContext ctx, [Option("question", "The question")] string question) { - List responses = - [ - "It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Absolutely!", "Of course!", "No doubt about it.", "The universe says yes.", "You got it!", "Definitely!", "All signs point to yes.", "The answer is crystal clear.", "Yes, in due time.", "The stars align in your favor.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Maybe, maybe not.", "Uncertain, check back later.", "Hard to say.", "Try flipping a coin.", "Your guess is as good as mine.", "The future is unclear.", "I can't say for sure.", "It's a mystery.", "Only time will tell.", "50/50 chance.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No.", "Absolutely not.", "I wouldn’t bet on it.", "No way!", "Highly unlikely.", - "Not in a million years.", "Doubtful at best.", "The odds aren’t in your favor.", "The universe says no.", "Nope." - ]; - - var chosenAnswer = responses[new Random().Next(0, responses.Count - 1)]; - if (ctx.GuildId is 1317206872763404478) - { - DiscordTextDisplayComponent question = new($"### Question\n{text}"); - DiscordSectionComponent questionComponent = new([question]); - questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); - DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); - DiscordTextDisplayComponent answer = new($"### Answer\n{chosenAnswer}"); - DiscordSectionComponent answerComponent = new([answer]); - answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([questionComponent]), seperator, new DiscordContainerComponent([answerComponent]))); - return; - } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {text}\n\n{chosenAnswer}")); + EightBallGame eightBall = new(question); + if (!await eightBall.TryBuildV28BallMessageAsync(ctx)) + await eightBall.SendOldStyle8BallMessageAsync(ctx); } [SlashCommand("coinflip", "Flip a coin!")] public static async Task CoinflipAsync(InteractionContext ctx) { - List flip = [$"Heads {DiscordEmoji.FromName(ctx.Client, ":arrow_up_small:")}", $"Tails {DiscordEmoji.FromName(ctx.Client, ":arrow_down_small:")}"]; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(flip[new Random().Next(0, flip.Count - 1)])); + var game = new CointossGame(ctx).TossCoin(); + if (!await game.TryBuildV2CointossMessageAsync()) + await game.SendOldStyleCointossMessageAsync(); } [SlashCommand("rps", "Play rock paper scissors!")] diff --git a/MikuSharp/Entities/Games/CointossGame.cs b/MikuSharp/Entities/Games/CointossGame.cs new file mode 100644 index 00000000..f993feda --- /dev/null +++ b/MikuSharp/Entities/Games/CointossGame.cs @@ -0,0 +1,84 @@ +namespace MikuSharp.Entities.Games; + +/// +/// Represents a coin toss game. +/// +/// The context. +public sealed class CointossGame(InteractionContext context) +{ + /// + /// Gets the interaction context. + /// + public InteractionContext Context { get; } = context; + + /// + /// Gets the dictionary of coin toss side emojis. + /// + public Dictionary CoinTossSideEmojis + => new() + { + { CointossSide.Heads, DiscordEmoji.FromApplicationEmote(this.Context.Client, 1338091202188279818) }, + { CointossSide.Tails, DiscordEmoji.FromApplicationEmote(this.Context.Client, 1338091187009228882) } + }; + + /// + /// Gets the dictionary of coin toss side images. + /// + public Dictionary CoinTossSideImages { get; } = new() + { + { CointossSide.Heads, "https://miku-cdn.aitsys.dev/assets/miku/cointoss/heads.png" }, + { CointossSide.Tails, "https://miku-cdn.aitsys.dev/assets/miku/cointoss/tails.png" } + }; + + /// + /// Gets or sets the result of the coin toss. + /// + public CointossSide Result { get; internal set; } + + /// + /// Tosses the coin and sets the result. + /// + /// The current instance of . + public CointossGame TossCoin() + { + Random random = new(); + this.Result = Enum.GetValues()[random.Next(0, 20) > 10 ? 1 : 0]; + return this; + } + + /// + /// Tries to build and send a components V2 cointoss message. + /// + /// Whether the message was sent successfully. + public async Task TryBuildV2CointossMessageAsync() + { + if (this.Context.GuildId is not 1317206872763404478) + return false; + + DiscordMediaGalleryComponent galleryComponent = new([new(this.CoinTossSideImages[this.Result], $"It's {this.Result}!")]); + await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([galleryComponent]))); + return true; + } + + /// + /// Sends an old-style embed cointoss message. + /// + public async Task SendOldStyleCointossMessageAsync() + => await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{this.CoinTossSideEmojis[this.Result]} It's {this.Result}!")); +} + +/// +/// Represents the sides of a coin. +/// +public enum CointossSide +{ + /// + /// Heads side of the coin. + /// + Heads, + + /// + /// Tails side of the coin. + /// + Tails +} diff --git a/MikuSharp/Entities/Games/EightBallGame.cs b/MikuSharp/Entities/Games/EightBallGame.cs new file mode 100644 index 00000000..533d250e --- /dev/null +++ b/MikuSharp/Entities/Games/EightBallGame.cs @@ -0,0 +1,78 @@ +namespace MikuSharp.Entities.Games; + +/// +/// Represents the 8ball game. +/// +public sealed class EightBallGame +{ + /// + /// Gets a random response from the 8ball. + /// + /// The user question. + /// The response. + public EightBallGame(string userQuestion) + { + Random random = new(); + var responseList = Responses[random.Next(Responses.Count)].ToList(); + this.ChosenAnswer = responseList[random.Next(responseList.Count)]; + this.UserQuestion = userQuestion; + } + + /// + /// A list of negative responses that the 8ball can give. + /// + public static IEnumerable NegativeResponses { get; } = ["Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful.", "No.", "Absolutely not.", "I wouldn’t bet on it.", "No way!", "Highly unlikely.", "Not in a million years.", "Doubtful at best.", "The odds aren’t in your favor.", "The universe says no.", "Nope."]; + + /// + /// A list of positive responses that the 8ball can give. + /// + public static IEnumerable PositiveResponses { get; } = ["It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.", "Absolutely!", "Of course!", "No doubt about it.", "The universe says yes.", "You got it!", "Definitely!", "All signs point to yes.", "The answer is crystal clear.", "Yes, in due time.", "The stars align in your favor."]; + + /// + /// A list of neutral responses that the 8ball can give. + /// + public static IEnumerable NeutralResponse { get; } = ["Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.", "Concentrate and ask again.", "Maybe, maybe not.", "Uncertain, check back later.", "Hard to say.", "Try flipping a coin.", "Your guess is as good as mine.", "The future is unclear.", "I can't say for sure.", "It's a mystery.", "Only time will tell.", "50/50 chance."]; + + /// + /// A list of all the responses that the 8ball can give. + /// + public static IReadOnlyList> Responses { get; } = [NegativeResponses, PositiveResponses, NeutralResponse]; + + /// + /// Gets the chosen answer. + /// + public string ChosenAnswer { get; } + + /// + /// Gets the user question. + /// + public string UserQuestion { get; } + + /// + /// Tries to build and send a components V2 8ball message. + /// + /// The context. + /// Whether the message was sent successfully. + public async Task TryBuildV28BallMessageAsync(InteractionContext ctx) + { + if (ctx.GuildId is not 1317206872763404478) + return false; + + DiscordTextDisplayComponent question = new($"### Question\n{this.UserQuestion}"); + DiscordSectionComponent questionComponent = new([question]); + questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); + DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); + DiscordTextDisplayComponent answer = new($"### Answer\n{this.ChosenAnswer}"); + DiscordSectionComponent answerComponent = new([answer]); + answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([questionComponent, seperator, answerComponent]))); + return true; + } + + /// + /// Sends an old-style embed 8ball message. + /// + /// The context. + public async Task SendOldStyle8BallMessageAsync(InteractionContext ctx) + => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {this.UserQuestion}\n\n{this.ChosenAnswer}")); +} From 70f004c70e68154c72d71b7c88fb3eed72a0f369 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 11:48:10 +0100 Subject: [PATCH 087/113] Update CointossGame.cs --- MikuSharp/Entities/Games/CointossGame.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MikuSharp/Entities/Games/CointossGame.cs b/MikuSharp/Entities/Games/CointossGame.cs index f993feda..03233115 100644 --- a/MikuSharp/Entities/Games/CointossGame.cs +++ b/MikuSharp/Entities/Games/CointossGame.cs @@ -55,8 +55,10 @@ public async Task TryBuildV2CointossMessageAsync() if (this.Context.GuildId is not 1317206872763404478) return false; - DiscordMediaGalleryComponent galleryComponent = new([new(this.CoinTossSideImages[this.Result], $"It's {this.Result}!")]); - await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([galleryComponent]))); + DiscordTextDisplayComponent textDisplayComponent = new($"### And the winner is... {this.Result.ToString().InlineCode()}!"); + DiscordSectionComponent sectionComponent = new([textDisplayComponent]); + sectionComponent.WithThumbnailComponent(this.CoinTossSideImages[this.Result], this.Result.ToString()); + await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([sectionComponent]))); return true; } From 8dd48c7398776f6a8327e6cd97c144b58b6076bd Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 11:48:35 +0100 Subject: [PATCH 088/113] Update HatsuneMikuBot.cs --- MikuSharp/HatsuneMikuBot.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index c083ca48..c0a07520 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -13,6 +13,8 @@ using Weeb.net; +using TokenType = DisCatSharp.Enums.TokenType; + namespace MikuSharp; /// @@ -60,7 +62,7 @@ public HatsuneMikuBot() ShardedClient = new(new() { Token = Config.DiscordToken, - TokenType = DisCatSharp.Enums.TokenType.Bot, + TokenType = TokenType.Bot, MinimumLogLevel = LogLevel.Debug, AutoReconnect = true, ApiChannel = ApiChannel.Canary, @@ -77,6 +79,8 @@ public HatsuneMikuBot() AttachUserInfo = true, ReconnectIndefinitely = true, EnableLibraryDeveloperMode = true + //Proxy = new WebProxy("127.0.0.1", 8004), + //GatewayCompressionLevel = GatewayCompressionLevel.None }); this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() @@ -362,6 +366,9 @@ internal async Task RunAsync() success = false; } + foreach (var client in ShardedClient.ShardClients.Values) + await client.GetApplicationEmojisAsync(true); + DiscordBotListApi = new(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); this.GameSetThread = Task.Run(RotateActivityAsync); //BotListThread = Task.Run(UpdateBotList); From 64bb3c2cec8ec299eb7617ecaf978367e8082d2e Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 13:02:11 +0100 Subject: [PATCH 089/113] mewh --- MikuSharp/Commands/DeveloperCommands.cs | 6 +++- MikuSharp/Commands/FunCommands.cs | 2 +- MikuSharp/Entities/Games/CointossGame.cs | 5 ++-- MikuSharp/Entities/Games/EightBallGame.cs | 8 ++++-- .../Entities/Games/RockPaperScissorsGame.cs | 28 +++++++++++-------- .../Utilities/DiscordExtensionMethods.cs | 7 ++++- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index bf190d97..155a9bfa 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -107,7 +107,11 @@ public class DeveloperCommands : ApplicationCommandsModule { [SlashCommand("test", "Testing")] public static async Task TestAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Meep meep. Shard {ctx.Client.ShardId}")); + { + await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + List items = [new("https://cdn.discordapp.com/attachments/1211030818533937362/1338113601453686835/lulalaby_Catgirls_5e44ded1-2d0d-4be7-8e1e-b08400429ec3.png?ex=67a9e6e7&is=67a89567&hm=c2de0d5bedf5f981dd66e707ed9805c4c72d0ae66161ff064a7b698e686d729c&")]; + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])]))); + } [SlashCommand("guild_shard_test", "Testing")] public static async Task GuildTestAsync(InteractionContext ctx) diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs index 92dee618..bf07c1e7 100644 --- a/MikuSharp/Commands/FunCommands.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -30,7 +30,7 @@ public static async Task CoinflipAsync(InteractionContext ctx) [SlashCommand("rps", "Play rock paper scissors!")] public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] RockPaperScissorsChoiceType userChoice) { - var game = userChoice.ResolveRps(); + var game = userChoice.ResolveRps(ctx.User); if (!await game.TryBuildV2RpsMessageAsync(ctx)) await game.SendOldStyleRpsMessageAsync(ctx); } diff --git a/MikuSharp/Entities/Games/CointossGame.cs b/MikuSharp/Entities/Games/CointossGame.cs index 03233115..0b60aad0 100644 --- a/MikuSharp/Entities/Games/CointossGame.cs +++ b/MikuSharp/Entities/Games/CointossGame.cs @@ -1,3 +1,5 @@ +using MikuSharp.Utilities; + namespace MikuSharp.Entities.Games; /// @@ -55,8 +57,7 @@ public async Task TryBuildV2CointossMessageAsync() if (this.Context.GuildId is not 1317206872763404478) return false; - DiscordTextDisplayComponent textDisplayComponent = new($"### And the winner is... {this.Result.ToString().InlineCode()}!"); - DiscordSectionComponent sectionComponent = new([textDisplayComponent]); + DiscordSectionComponent sectionComponent = new([DiscordExtensionMethods.EmptyComponent, new("And the winner..."), new($"### {this.Result}")]); sectionComponent.WithThumbnailComponent(this.CoinTossSideImages[this.Result], this.Result.ToString()); await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([sectionComponent]))); return true; diff --git a/MikuSharp/Entities/Games/EightBallGame.cs b/MikuSharp/Entities/Games/EightBallGame.cs index 533d250e..c5a3849b 100644 --- a/MikuSharp/Entities/Games/EightBallGame.cs +++ b/MikuSharp/Entities/Games/EightBallGame.cs @@ -1,3 +1,5 @@ +using MikuSharp.Utilities; + namespace MikuSharp.Entities.Games; /// @@ -59,13 +61,13 @@ public async Task TryBuildV28BallMessageAsync(InteractionContext ctx) return false; DiscordTextDisplayComponent question = new($"### Question\n{this.UserQuestion}"); - DiscordSectionComponent questionComponent = new([question]); + DiscordSectionComponent questionComponent = new([DiscordExtensionMethods.EmptyComponent, question]); questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); DiscordSeparatorComponent seperator = new(false, SeparatorSpacingSize.Small); DiscordTextDisplayComponent answer = new($"### Answer\n{this.ChosenAnswer}"); - DiscordSectionComponent answerComponent = new([answer]); + DiscordSectionComponent answerComponent = new([DiscordExtensionMethods.EmptyComponent, answer]); answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([questionComponent, seperator, answerComponent]))); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## 8Ball"), new DiscordSeparatorComponent(true, SeparatorSpacingSize.Large), questionComponent, seperator, answerComponent]))); return true; } diff --git a/MikuSharp/Entities/Games/RockPaperScissorsGame.cs b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs index 7bb308c4..039b5d79 100644 --- a/MikuSharp/Entities/Games/RockPaperScissorsGame.cs +++ b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs @@ -1,3 +1,5 @@ +using MikuSharp.Utilities; + namespace MikuSharp.Entities.Games; /// @@ -16,13 +18,13 @@ public static IEnumerable Assets /// /// The user's choice. /// A representing the outcome. - public static RockPaperScissorsResponse ResolveRps(this RockPaperScissorsChoiceType userChoice) + public static RockPaperScissorsResponse ResolveRps(this RockPaperScissorsChoiceType userChoice, DiscordUser player) { var random = new Random(); var values = Enum.GetValues(); var computerChoice = (RockPaperScissorsChoiceType)values.GetValue(random.Next(values.Length))!; - return new(userChoice, computerChoice, userChoice == computerChoice + return new(player, userChoice, computerChoice, userChoice == computerChoice ? RockPaperScissorsWinType.Tie : (userChoice is RockPaperScissorsChoiceType.Rock && computerChoice is RockPaperScissorsChoiceType.Scissors) || (userChoice is RockPaperScissorsChoiceType.Paper && computerChoice is RockPaperScissorsChoiceType.Rock) || @@ -70,7 +72,7 @@ public static implicit operator RockPaperScissorsAsset(RockPaperScissorsChoiceTy /// The user choice asset. /// The computer choice asset. /// The win type. -public sealed class RockPaperScissorsResponse(RockPaperScissorsAsset userChoiceAsset, RockPaperScissorsAsset computerChoiceAsset, RockPaperScissorsWinType winType) +public sealed class RockPaperScissorsResponse(DiscordUser player, RockPaperScissorsAsset userChoiceAsset, RockPaperScissorsAsset computerChoiceAsset, RockPaperScissorsWinType winType) { /// /// Gets the computer's choice asset. @@ -95,6 +97,8 @@ public sealed class RockPaperScissorsResponse(RockPaperScissorsAsset userChoiceA ? (this.ComputerChoiceAsset.ImageUrl, this.ComputerChoiceAsset.CreativeCommonsLicense) : (this.UserChoiceAsset.ImageUrl, this.UserChoiceAsset.CreativeCommonsLicense); + public DiscordUser Player { get; } = player; + /// /// Tries to build and send a components V2 rock-paper-scissors message. /// @@ -107,16 +111,16 @@ public async Task TryBuildV2RpsMessageAsync(InteractionContext ctx) DiscordWebhookBuilder builder = new(); builder.WithV2Components(); - DiscordTextDisplayComponent userChoiceComponent = new($"### {ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType.ToString().InlineCode()}"); - DiscordSectionComponent userChoiceSection = new([userChoiceComponent]); - userChoiceSection.WithThumbnailComponent(ctx.User.AvatarUrl); - DiscordSeparatorComponent seperator1 = new(false, SeparatorSpacingSize.Small); - DiscordTextDisplayComponent computerChoiceComponent = new($"### I choose {this.ComputerChoiceAsset.ChoiceType.ToString().InlineCode()}"); - DiscordSectionComponent computerChoiceSection = new([computerChoiceComponent]); - computerChoiceSection.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); + DiscordTextDisplayComponent userChoiceComponent = new($"### {ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType}"); + DiscordSectionComponent userChoiceSection = new([DiscordExtensionMethods.EmptyComponent, userChoiceComponent]); + userChoiceSection.WithThumbnailComponent(ctx.User.AvatarUrl, "User Avatar"); + DiscordSeparatorComponent seperator1 = new(false, SeparatorSpacingSize.Large); + DiscordTextDisplayComponent computerChoiceComponent = new($"### I choose {this.ComputerChoiceAsset.ChoiceType}"); + DiscordSectionComponent computerChoiceSection = new([DiscordExtensionMethods.EmptyComponent, computerChoiceComponent]); + computerChoiceSection.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl, "Bot Avatar"); DiscordSeparatorComponent seperator2 = new(true, SeparatorSpacingSize.Large); DiscordTextDisplayComponent resultTextComponent = new($"### {this}"); - DiscordSectionComponent resultSectionComponent = new([resultTextComponent]); + DiscordSectionComponent resultSectionComponent = new([DiscordExtensionMethods.EmptyComponent, resultTextComponent]); resultSectionComponent.WithThumbnailComponent(this.WinnerAsset.ImageUrl, this.WinnerAsset.CreativeCommonsLicense); builder.AddComponents(new DiscordContainerComponent([userChoiceSection, seperator1, computerChoiceSection, seperator2, resultSectionComponent])); builder.WithAllowedMention(new UserMention(ctx.User)); @@ -142,7 +146,7 @@ public async Task SendOldStyleRpsMessageAsync(InteractionContext ctx) public override string? ToString() => this.WinType switch { - RockPaperScissorsWinType.User => "You win :3", + RockPaperScissorsWinType.User => $"{this.Player.Mention} wins :3", RockPaperScissorsWinType.Computer => "I win ^~^", RockPaperScissorsWinType.Tie => "It's a tie O.o", _ => null diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 95caa090..8c060f61 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -13,6 +13,11 @@ namespace MikuSharp.Utilities; /// public static class DiscordExtensionMethods { + /// + /// Gets an empty component using the \u200e annotation. + /// + public static DiscordTextDisplayComponent EmptyComponent { get; } = new("\u200e"); + /// /// Gets the avatar URL of the user, using the guild avatar URL if possible. /// @@ -159,7 +164,7 @@ public static async Task ActionRespondWithErrorAsync(this BaseContext ctx, strin } /// - /// Responds with an error message if the is null. + /// Responds with an error message if the is null. /// /// The context. /// The image data to check. From bdba4af9ec58ebe02d1b1e0a262b8aff9259a4f5 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 13:40:00 +0100 Subject: [PATCH 090/113] Update DiscordExtensionMethods.cs --- MikuSharp/Utilities/DiscordExtensionMethods.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 8c060f61..b9ba481d 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -13,10 +13,12 @@ namespace MikuSharp.Utilities; /// public static class DiscordExtensionMethods { + private const string INVISIBLE_CHARACTER = "\u200e"; + /// /// Gets an empty component using the \u200e annotation. /// - public static DiscordTextDisplayComponent EmptyComponent { get; } = new("\u200e"); + public static DiscordTextDisplayComponent EmptyComponent { get; } = new(INVISIBLE_CHARACTER); /// /// Gets the avatar URL of the user, using the guild avatar URL if possible. From 522ce3e56cad64422ee7bacfa9696a0da4e7e905 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 9 Feb 2025 13:41:12 +0100 Subject: [PATCH 091/113] Update DiscordExtensionMethods.cs --- MikuSharp/Utilities/DiscordExtensionMethods.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index b9ba481d..5491b254 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -13,6 +13,9 @@ namespace MikuSharp.Utilities; /// public static class DiscordExtensionMethods { + /// + /// Gets the invisible space string. + /// private const string INVISIBLE_CHARACTER = "\u200e"; /// From 549a8a0e0cf217088bf8ff0b6a047985267ae2c8 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 10 Feb 2025 14:05:57 +0100 Subject: [PATCH 092/113] feat: prepare missing music & playlist commands --- MikuSharp/Commands/0-Old/Music.cs | 790 ------------------ MikuSharp/Commands/0-Old/Playlist.cs | 497 ----------- .../Music/MusicCommands.InfoCommands.cs | 38 +- .../Music/MusicCommands.OptionsCommands.cs | 20 + .../Music/MusicCommands.PlaybackCommands.cs | 20 - .../Music/MusicCommands.QueueCommands.cs | 51 +- .../PlaylistCommands.CreateCommands.cs | 57 ++ .../PlaylistCommands.ManageCommands.cs | 80 +- .../Playlist/PlaylistCommands.SongCommands.cs | 93 ++- .../Commands/Playlist/PlaylistCommands.cs | 11 +- MikuSharp/Utilities/DiscordOptionProviders.cs | 107 ++- 11 files changed, 381 insertions(+), 1383 deletions(-) delete mode 100644 MikuSharp/Commands/0-Old/Music.cs delete mode 100644 MikuSharp/Commands/0-Old/Playlist.cs create mode 100644 MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs diff --git a/MikuSharp/Commands/0-Old/Music.cs b/MikuSharp/Commands/0-Old/Music.cs deleted file mode 100644 index ede34e0f..00000000 --- a/MikuSharp/Commands/0-Old/Music.cs +++ /dev/null @@ -1,790 +0,0 @@ -/*using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Enums; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Extensions; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Attributes; -using MikuSharp.Enums; -using MikuSharp.Events; -using MikuSharp.Utilities; - -namespace MikuSharp.Commands; - -/// -/// The music commands -/// -[SlashCommandGroup("music", "Music commands", dmPermission: false)] -public class Music : ApplicationCommandsModule -{ - private static readonly string[] Units = - [ - "", "ki", "Mi", "Gi" - ]; - - private static string SizeToString(long l) - { - double d = l; - var u = 0; - - while (d >= 900 && u < Units.Length - 2) - { - u++; - d /= 1024; - } - - return $"{d:#,##0.00} {Units[u]}B"; - } - - [SlashCommandGroup("base", "Base commands")] - public class Base : ApplicationCommandsModule - { - [SlashCommand("join", "Joins the voice channel you're in"), RequireUserVoicechatConnection] - public static async Task JoinAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); - await g.ConditionalConnect(ctx); - g.MusicInstance.UsedChannel = ctx.Channel; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Heya {ctx.Member.Mention}!")); - } - - [SlashCommand("leave", "Leaves the channel")] - public static async Task LeaveAsync(InteractionContext ctx, [Option("keep", "Whether to keep the queue")] bool keep = false) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - - if (g.MusicInstance == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not in a voice channel")); - return; - } - - g.MusicInstance.Playstate = Playstate.NotPlaying; - - try - { - if (keep) - await g.MusicInstance.GuildConnection.StopAsync(); - await g.MusicInstance.GuildConnection.DisconnectAsync(); - if (!keep) - await Database.ClearQueue(ctx.Guild); - g.MusicInstance = null; - } - catch (Exception) - { } - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cya! 💙")); - } - - [SlashCommand("lstats", "Displays Lavalink statistics"), ApplicationCommandRequireTeamDeveloper] - public static async Task GetLavalinkStatsAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var stats = MikuBot.LavalinkSessions[ctx.Client.ShardId].Statistics; - var sb = new StringBuilder(); - sb.Append("Lavalink resources usage statistics: ```") - .Append("Uptime: ").Append(stats.Uptime).AppendLine() - .Append("Players: ").Append($"{stats.PlayingPlayers} active / {stats.Players} total").AppendLine() - .Append("CPU Cores: ").Append(stats.Cpu.Cores).AppendLine() - .Append("CPU Usage: ").Append($"{stats.Cpu.LavalinkLoad:#,##0.0%} lavalink / {stats.Cpu.SystemLoad:#,##0.0%} system").AppendLine() - .Append("RAM Usage: ") - .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() - .Append("Audio frames (per minute): ").Append($"{stats.Frames.Sent:#,##0} sent / {stats.Frames.Nulled:#,##0} nulled / {stats.Frames.Deficit:#,##0} deficit").AppendLine() - .Append("```"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); - } - } - - [SlashCommandGroup("playback", "Playback controls")] - public class Playback : ApplicationCommandsModule - { - [SlashCommand("seek", "Seek a song"), RequireUserAndBotVoicechatConnection] - public static async Task SeekAsync(InteractionContext ctx, [Option("position", "Position to seek to")] double position) - { - await ctx.DeferAsync(); - if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - if (g.MusicInstance.Playstate != Playstate.Playing && g.MusicInstance.Playstate != Playstate.Paused) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I don't play anything right now")); - return; - } - - g.MusicInstance.UsedChannel = ctx.Channel; - var ts = TimeSpan.FromSeconds(position); - await g.MusicInstance.GuildConnection.SeekAsync(ts); - var pos = ts.Hours < 1 - ? ts.ToString(@"mm\:ss") - : ts.ToString(@"hh\:mm\:ss"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Seeked {g.MusicInstance.CurrentSong.Track.Info.Title} to {pos}")); - } - - [SlashCommand("play", "Play or queue a song"), RequireUserVoicechatConnection] - public static async Task PlayAsync( - InteractionContext ctx, - [Option("song", "Song name or url to play")] string nameOrUrl = null, - [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null - ) - { - await ctx.DeferAsync(); - if (MikuBot.Guilds.All(x => x.Key != ctx.Guild.Id)) - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new(ctx.Client.ShardId)); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); - var curq = await Database.GetQueueAsync(ctx.Guild); - - if (curq.Count != 0 && g.MusicInstance.Playstate == Playstate.NotPlaying) - { - var inter = ctx.Client.GetInteractivity(); - List buttons = [new(ButtonStyle.Success, "restore", "Restore old queue"), new(ButtonStyle.Danger, "clear", "Clear old queue")]; - var msg = await ctx.EditResponseAsync(new DiscordWebhookBuilder() - .AddEmbed(new DiscordEmbedBuilder().WithTitle("Recover Queue").WithDescription("The last time the bot disconnected the queue wasnt cleared, do you want to restore and play that old one?").Build()).AddComponents(buttons)); - var hmm = await inter.WaitForButtonAsync(msg, ctx.User, TimeSpan.FromSeconds(30)); - - if (hmm.TimedOut) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Timed out!")); - return; - } - - if (hmm.Result.Id == "restore") - { - await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - buttons.ForEach(x => x.Disable()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Restored").AddComponents(buttons)); - await g.MusicInstance.ConnectToChannel(ctx.Member.VoiceState.Channel); - await g.MusicInstance.PlaySong(); - return; - } - - await hmm.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate); - await Database.ClearQueue(ctx.Guild); - buttons.ForEach(x => x.Disable()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cleared").AddComponents(buttons)); - } - - await g.ConditionalConnect(ctx); - - if (musicFile == null && nameOrUrl == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); - return; - } - - g.MusicInstance.UsedChannel = ctx.Channel; - nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); - var oldState = g.MusicInstance.Playstate; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Trying to play/search {nameOrUrl}...")); - var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx); - - if (q == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); - return; - } - - var emb = new DiscordEmbedBuilder(); - - if (oldState == Playstate.Playing) - { - emb.AddField(new(q.Tracks.First().Info.Title + - "[" + - (q.Tracks.First().Info.Length.Hours != 0 - ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") - : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + - "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Added").Build()).AsEphemeral()); - } - else - { - if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new(q.Tracks.First().Info.Title + - "[" + - (q.Tracks.First().Info.Length.Hours != 0 - ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") - : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + - "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); - else - emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + - "[" + - (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 - ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") - : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + - "]", - $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AddEmbed(emb.WithTitle("Playing").Build()).AsEphemeral()); - } - } - - [SlashCommand("insert", "Queue a song at a specific position!"), RequireUserVoicechatConnection] - public static async Task InsertToQueueAsync( - InteractionContext ctx, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string posi, - [Option("song", "Song name or url to play")] string nameOrUrl = null, - [Option("music_file", "Music file to play")] DiscordAttachment musicFile = null - ) - { - await ctx.DeferAsync(); - var pos = Convert.ToInt32(posi); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (pos < 1) - return; - - g.MusicInstance ??= new(MikuBot.LavalinkSessions[ctx.Client.ShardId], ctx.Client.ShardId); - - await g.ConditionalConnect(ctx); - - if (musicFile == null && nameOrUrl == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: No song or file choosen")); - return; - } - - g.MusicInstance.UsedChannel = ctx.Channel; - nameOrUrl = musicFile.SearchUrlOrAttachment(nameOrUrl); - var oldState = g.MusicInstance.Playstate; - var q = await g.MusicInstance.QueueSong(nameOrUrl, ctx, pos); - - if (q == null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Error: Song not found")); - return; - } - - var emb = new DiscordEmbedBuilder(); - - if (oldState == Playstate.Playing) - { - emb.AddField(new(q.Tracks.First().Info.Title + - "[" + - (q.Tracks.First().Info.Length.Hours != 0 - ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") - : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + - "]", $"by {q.Tracks.First().Info.Author}\n" + $"Requested by {ctx.Member.Mention}\nAt position: {pos}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - emb.WithTitle("Playing"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - else - { - if (q.PlaylistInfo.SelectedTrack == -1 || q.PlaylistInfo.Name == null) - emb.AddField(new(q.Tracks.First().Info.Title + - "[" + - (q.Tracks.First().Info.Length.Hours != 0 - ? q.Tracks.First().Info.Length.ToString(@"hh\:mm\:ss") - : q.Tracks.First().Info.Length.ToString(@"mm\:ss")) + - "]", $"by {q.Tracks.First().Info.Author}\nRequested by {ctx.Member.Mention}")); - else - emb.AddField(new(q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Title + - "[" + - (q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.Hours != 0 - ? q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"hh\:mm\:ss") - : q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Length.ToString(@"mm\:ss")) + - "]", - $"by {q.Tracks[q.PlaylistInfo.SelectedTrack].Info.Author}\nRequested by {ctx.Member.Mention}At position: {pos}")); - if (q.Tracks.Count != 1) - emb.AddField(new("Playlist added:", $"added {q.Tracks.Count - 1} more")); - emb.WithTitle("Added"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - } - - [SlashCommand("skip", "Skip the current song"), RequireUserAndBotVoicechatConnection] - public static async Task SkipSongAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - g.MusicInstance.GuildConnection.TrackEnded -= Lavalink.LavalinkTrackFinish; - - if (g.MusicInstance.CurrentSong != null) - { - if (g.MusicInstance.RepeatMode != RepeatMode.On && g.MusicInstance.RepeatMode != RepeatMode.All) - await Database.RemoveFromQueueAsync(g.MusicInstance.CurrentSong.Position, ctx.Guild); - if (lastPlayedSongs.Count == 0) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); - else if (lastPlayedSongs[0]?.Track.Info.Uri != g.MusicInstance.CurrentSong.Track.Info.Uri) - await Database.AddToLastPlayingListAsync(ctx.Guild.Id, g.MusicInstance.CurrentSong.Track.Encoded); - } - - queue = await Database.GetQueueAsync(ctx.Guild); - g.MusicInstance.LastSong = g.MusicInstance.CurrentSong; - g.MusicInstance.CurrentSong = null; - - if (queue.Count != 0) - await g.MusicInstance.PlaySong(); - else - { - g.MusicInstance.Playstate = Playstate.NotPlaying; - await g.MusicInstance.GuildConnection.StopAsync(); - } - - if (g.MusicInstance.LastSong != null) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Skipped:**\n{g.MusicInstance.LastSong.Track.Info.Title}").Build())); - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Continued!**").Build())); - } - - [SlashCommand("stop", "Stop Playback"), RequireUserAndBotVoicechatConnection] - public static async Task StopAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - await Task.Run(async () => await g.MusicInstance.GuildConnection.StopAsync()); - var cmdId = ctx.Client.GetApplicationCommands().GlobalCommands.First(x => x.Name == "music").Id; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Stopped** (use to start playback again)").Build())); - } - - [SlashCommand("volume", "Change the music volume"), RequireUserAndBotVoicechatConnection] - public static async Task ModifyVolumeAsync( - InteractionContext ctx, - [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] - int vol = 100 - ) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - if (vol > 150) vol = 150; - await g.MusicInstance.GuildConnection.SetVolumeAsync(vol); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Set volume to {vol}**").Build())); - } - - [SlashCommand("pause", "Pauses playback"), RequireUserAndBotVoicechatConnection] - public static async Task PauseAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - - if (g.MusicInstance.Playstate == Playstate.Playing) - { - await g.MusicInstance.GuildConnection.PauseAsync(); - g.MusicInstance.Playstate = Playstate.Paused; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Paused**").Build())); - } - else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not playing anything right now")); - } - - [SlashCommand("resume", "Resumes paused playback"), RequireUserAndBotVoicechatConnection] - public static async Task ResumeAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - - if (g.MusicInstance.Playstate == Playstate.Stopped) - { - await g.MusicInstance.PlaySong(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Started Playback**").Build())); - } - else - { - await g.MusicInstance.GuildConnection.ResumeAsync(); - g.MusicInstance.Playstate = Playstate.Playing; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Resumed**").Build())); - } - } - } - - [SlashCommandGroup("queue", "Queue management"), RequireUserAndBotVoicechatConnection] - public class Queue : ApplicationCommandsModule - { - [SlashCommand("show", "Show the current queue")] - public static async Task ShowQueueAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var queue = await Database.GetQueueAsync(ctx.Guild); - - try - { - var g = MikuBot.Guilds[ctx.Guild.Id]; - - if (queue.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); - return; - } - - var inter = ctx.Client.GetInteractivity(); - var songsPerPage = 0; - var currentPage = 1; - var songAmount = 0; - var totalP = queue.Count / 5; - if (queue.Count % 5 != 0) - totalP++; - var emb = new DiscordEmbedBuilder(); - List pages = []; - - if (g.MusicInstance.RepeatMode == RepeatMode.All) - { - songAmount = g.MusicInstance.RepeatAllPos; - - foreach (var track in queue) - { - if (songsPerPage == 0 && currentPage == 1) - { - emb.WithTitle("Current Queue"); - g.GetPlayingState(out var time1, out var time2); - emb.AddField(new( - $"**{songAmount}.{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); - } - else - { - queue.ElementAt(songAmount).GetPlayingState(out var time); - emb.AddField(new($"**{songAmount}.{queue.ElementAt(songAmount).Track.Info.Title.Replace("*", "").Replace("|", "")}** by {queue.ElementAt(songAmount).Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{queue.ElementAt(songAmount).AddedBy}> [Link]({queue.ElementAt(songAmount).Track.Info.Uri.AbsoluteUri})")); - } - - songsPerPage++; - songAmount++; - if (songAmount == queue.Count) - songAmount = 0; - - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - - if (songAmount == g.MusicInstance.RepeatAllPos) - { - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - } - } - } - else - foreach (var track in queue) - { - if (songsPerPage == 0 && currentPage == 1) - { - emb.WithTitle("Current Queue"); - g.GetPlayingState(out var time1, out var time2); - emb.AddField(new($"**{g.MusicInstance.CurrentSong.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {g.MusicInstance.CurrentSong.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time1}/{time2}]", - $"Requested by <@{g.MusicInstance.CurrentSong.AddedBy}> [Link]({g.MusicInstance.CurrentSong.Track.Info.Uri.AbsoluteUri})\nˉˉˉˉˉ")); - } - else - { - track.GetPlayingState(out var time); - emb.AddField(new($"**{songAmount}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}** by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Requested by <@{track.AddedBy}> [Link]({track.Track.Info.Uri.AbsoluteUri})")); - } - - songsPerPage++; - songAmount++; - - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - pages.Add(new(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - - if (songAmount == queue.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - pages.Add(new(embed: emb)); - emb.ClearFields(); - } - } - - if (currentPage == 1) - { - emb.AddField(new("Playback options", g.MusicInstance.GetPlaybackOptions())); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - } - - if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - } - - foreach (var eP in pages.Where(x => x.Embed.Fields.All(y => y.Name == "Playback keep")).ToList()) - pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - } - } - - [SlashCommand("clear", "Clears the queue")] - public static async Task ClearQueueAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - await Database.ClearQueue(ctx.Guild); - if (g.MusicInstance.CurrentSong != null) - await Database.AddToQueue(ctx.Guild, g.MusicInstance.CurrentSong.AddedBy, g.MusicInstance.CurrentSong.Track.Encoded); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription("**Cleared queue!**").Build())); - } - - [SlashCommand("move", "Moves a specific song within the queue")] - public static async Task MoveWithinQueueAsync( - InteractionContext ctx, - [Option("song", "Song to move within the queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string oldPosi, - [Option("position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string newPosi - ) - { - await ctx.DeferAsync(); - var oldPos = Convert.ToInt32(oldPosi); - var newPos = Convert.ToInt32(newPosi); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - if (oldPos < 1 || newPos < 1 || oldPos == newPos || newPos >= queue.Count) - return; - - var oldSong = queue[oldPos]; - await Database.MoveQueueItems(ctx.Guild, oldPos, newPos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() - .WithDescription($"**Moved**:\n **{oldSong.Track.Info.Title}**\nby {oldSong.Track.Info.Author}\n from position **{oldPos}** to **{newPos}**!").Build())); - } - - [SlashCommand("remove", "Removes a name_or_url from queue")] - public static async Task RemoveFromQueueAsync( - InteractionContext ctx, - [Option("song", "Song to remove from queue", true), Autocomplete(typeof(AutocompleteProviders.QueueProvider))] - string posi - ) - { - var position = Convert.ToInt32(posi); - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - var queue = await Database.GetQueueAsync(ctx.Guild); - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - var old = queue[position]; - await Database.RemoveFromQueueAsync(position, ctx.Guild); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"**Removed:\n{old.Track.Info.Title}**\nby {old.Track.Info.Author}").Build())); - } - } - - [SlashCommandGroup("options", "Playback Options"), RequireUserAndBotVoicechatConnection] - public class PlaybackOptions : ApplicationCommandsModule - { - [SlashCommand("repeat", "Repeat the current song or the entire queue")] - public static async Task RepeatAsync( - InteractionContext ctx, - [Option("mode", "New repeat mode"), ChoiceProvider(typeof(FixedOptionProviders.RepeatModeProvider))] - RepeatMode mode - ) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - g.MusicInstance.RepeatMode = mode; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set repeat mode to:\n **{g.MusicInstance.RepeatMode}**").Build())); - } - - [SlashCommand("shuffle", "Play the queue in shuffle mode")] - public static async Task ShuffleAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - if (await g.IsNotConnected(ctx)) - return; - - g.MusicInstance.UsedChannel = ctx.Channel; - g.MusicInstance.ShuffleMode = g.MusicInstance.ShuffleMode == ShuffleMode.Off - ? ShuffleMode.On - : ShuffleMode.Off; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithDescription($"Set shuffle mode to:\n**{g.MusicInstance.ShuffleMode}**").Build())); - } - } - - [SlashCommandGroup("info", "Playing info")] - public class PlayingInfo : ApplicationCommandsModule - { - [SlashCommand("now_playing", "Show whats currently playing")] - public static async Task ShowNowPlaylingAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.ShardId = ctx.Client.ShardId; - var eb = new DiscordEmbedBuilder(); - eb.WithTitle("Now Playing"); - eb.WithDescription("**__Current Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g); - } - - [SlashCommand("last_playing", "Show what played before")] - public static async Task ShowLastPlaylingAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - - if (!lastPlayedSongs.Any()) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); - return; - } - - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.ShardId = ctx.Client.ShardId; - var eb = new DiscordEmbedBuilder(); - eb.WithTitle("Last playing"); - eb.WithDescription("**__Previous Song:__**"); - await ctx.SendPlayingInformationAsync(eb, g, lastPlayedSongs); - } - - [SlashCommand("last_playing_list", "Show what songs were played before")] - public static async Task ShowLastPlaylingListAsync(InteractionContext ctx) - { - await ctx.DeferAsync(); - var lastPlayedSongs = await Database.GetLastPlayingListAsync(ctx.Guild); - - if (!lastPlayedSongs.Any()) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I haven't played anything on this server yet.")); - return; - } - - try - { - var g = MikuBot.Guilds[ctx.Guild.Id]; - - if (lastPlayedSongs.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue empty")); - return; - } - - var inter = ctx.Client.GetInteractivity(); - var songsPerPage = 0; - var currentPage = 1; - var songAmount = 0; - var totalP = lastPlayedSongs.Count / 10; - if (lastPlayedSongs.Count % 10 != 0) - totalP++; - var emb = new DiscordEmbedBuilder(); - List pages = []; - - foreach (var track in lastPlayedSongs) - { - track.GetPlayingState(out var time); - emb.AddField(new($"{songAmount + 1}.{track.Track.Info.Title.Replace("*", "").Replace("|", "")}", $"by {track.Track.Info.Author.Replace("*", "").Replace("|", "")} [{time}] [Link]({track.Track.Info.Uri})")); - songsPerPage++; - songAmount++; - - if (songsPerPage == 10) - { - songsPerPage = 0; - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - emb.WithTitle("more™"); - currentPage++; - } - - if (songAmount != lastPlayedSongs.Count) - continue; - - emb.WithTitle("Last played songs in this server:\n"); - emb.WithFooter($"Page {currentPage}/{totalP}"); - pages.Add(new(embed: emb)); - emb.ClearFields(); - } - - switch (currentPage) - { - case 1: - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - case 2 when songsPerPage == 0: - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(pages.First().Embed)); - return; - } - - foreach (var eP in pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - pages.Remove(eP); - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, pages); - } - catch (Exception ex) - { - ctx.Client.Logger.LogError("{ex}", ex.Message); - ctx.Client.Logger.LogError("{ex}", ex.StackTrace); - } - } - } -} -*/ - - diff --git a/MikuSharp/Commands/0-Old/Playlist.cs b/MikuSharp/Commands/0-Old/Playlist.cs deleted file mode 100644 index 4037ebce..00000000 --- a/MikuSharp/Commands/0-Old/Playlist.cs +++ /dev/null @@ -1,497 +0,0 @@ -/*using DisCatSharp.ApplicationCommands; -using DisCatSharp.ApplicationCommands.Attributes; -using DisCatSharp.ApplicationCommands.Context; -using DisCatSharp.Entities; -using DisCatSharp.Interactivity; -using DisCatSharp.Interactivity.Extensions; -using DisCatSharp.Lavalink; - -using Microsoft.Extensions.Logging; - -using MikuSharp.Attributes; -using MikuSharp.Entities; -using MikuSharp.Enums; -using MikuSharp.Utilities; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace MikuSharp.Commands; - -[SlashCommandGroup("playlists", "Manage your playlists", dmPermission: false)] -public class Playlists : ApplicationCommandsModule -{ - [SlashCommandGroup("new", "Playlist creation")] - public class PlaylistCreation : ApplicationCommandsModule - { - [SlashCommand("copy_queue", "Copy the current queue to a playlist!")] - [RequireUserAndBotVoicechatConnection] - public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name - ) - { - await ctx.DeferAsync(true); - var q = await Database.GetQueueAsync(ctx.Guild); - if (q.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Nothing in queue")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Copy Queue").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.AddPlaylist(name, ctx.Member.Id); - foreach (var e in q) - { - await PlaylistDB.AddEntry(name, ctx.Member.Id, e.track.TrackString); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Queue Copy").WithDescription("Queue was saved to new playlist -> " + name).Build())); - } - - [SlashCommand("create", "Create a playlist")] - public static async Task CreatePlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name - ) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.AddPlaylist(name, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Playlist").WithDescription("New Playlist was created -> " + name).Build())); - } - - [SlashCommand("create_fixed", "Create a fixed playlist (linked to a Youtube or Soundcloud playlist)")] - public static async Task CreateFixedPlaylistAsync(InteractionContext ctx, - [Option("name", "Name of new playlist")] string name, - [Option("link", "Link to playlist")] string link - ) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (pls.Any(x => x == name)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** You already have a playlist with that playlist!").Build())); - return; - } - LavalinkLoadResult s = null; - try - { - s = await MikuBot.LavalinkNodeConnections[ctx.Client.ShardId].Rest.GetTracksAsync(new Uri(link)); - } - catch - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); - return; - } - if (s.LoadResultType != LavalinkLoadResultType.PlaylistLoaded) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription("**Error** Reasons could be:\n> The provided link was not a playlist\n> The playlist is unavailable (for example set to private)").Build())); - return; - } - if (link.Contains("youtu") && !link.Contains("soundcloud")) - { - await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Youtube, link); - } - else - { - await PlaylistDB.AddPlaylist(name, ctx.Member.Id, ExtService.Soundcloud, link); - } - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Create Fixed Playlist").WithDescription($"Fixed playlist created with playlist -> {name} and {s.Tracks.Count} Songs!").Build())); - } - } - - [SlashCommandGroup("manage", "Playlist management")] - public class PlaylistManagement : ApplicationCommandsModule - { - [SlashCommand("list", "List all your playlists")] - public static async Task ListPlaylistsAsync(InteractionContext ctx) - { - await ctx.DeferAsync(true); - var pls = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - if (pls.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You dont have any playlists")); - return; - } - //ctx.Client.Logger.LogDebug(pls.Count.ToString()); - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = pls.Count / 5; - if ((pls.Count % 5) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in pls) - { - //ctx.Client.Logger.LogDebug(Track.Value == null); - //ctx.Client.Logger.LogDebug(Track.Key); - int songam = 0; - var ent = await Track.Value.GetEntries(); - songam = ent.Count; - string sub = ""; - if (Track.Value.ExternalService == ExtService.None) - { - sub = $"Created on: {Track.Value.Creation}\n" + - $"Last modified on: {Track.Value.Modify}"; - } - else - { - sub = $"Created on: {Track.Value.Creation}\n" + - $"{Track.Value.ExternalService} [Link]({Track.Value.Url})"; - } - emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.Key}** ({songam} Songs)", sub)); - songsPerPage++; - songAmount++; - emb.WithTitle($"List Playlists"); - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - currentPage++; - } - if (songAmount == pls.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - { - Pages.Remove(eP); - } - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } - - [SlashCommand("show", "Show the contents of a playlist")] - public static async Task ShowPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to show", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!pls.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Show Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var q = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var queue = await q.GetEntries(); - if (queue.Count == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist empty!")); - return; - } - var inter = ctx.Client.GetInteractivity(); - int songsPerPage = 0; - int currentPage = 1; - int songAmount = 0; - int totalP = queue.Count / 5; - if ((queue.Count % 5) != 0) totalP++; - var emb = new DiscordEmbedBuilder(); - List Pages = new(); - foreach (var Track in queue) - { - string time = ""; - if (Track.track.Length.Hours < 1) time = Track.track.Length.ToString(@"mm\:ss"); - else time = Track.track.Length.ToString(@"hh\:mm\:ss"); - emb.AddField(new DiscordEmbedField($"**{songAmount + 1}.{Track.track.Title.Replace("*", "").Replace("|", "")}** by {Track.track.Author.Replace("*", "").Replace("|", "")} [{time}]", - $"Added on {Track.additionDate} [Link]({Track.track.Uri.AbsoluteUri})")); - songsPerPage++; - songAmount++; - emb.WithTitle($"Songs in {playlist}"); - if (songsPerPage == 5) - { - songsPerPage = 0; - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - currentPage++; - } - if (songAmount == queue.Count) - { - emb.WithFooter($"Page {currentPage}/{totalP}"); - Pages.Add(new Page(embed: emb)); - emb.ClearFields(); - } - } - if (currentPage == 1) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - else if (currentPage == 2 && songsPerPage == 0) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(Pages.First().Embed)); - return; - } - foreach (var eP in Pages.Where(x => x.Embed.Fields.Count == 0).ToList()) - { - Pages.Remove(eP); - } - await inter.SendPaginatedResponseAsync(ctx.Interaction, true, false, ctx.User, Pages); - } - - [SlashCommand("delete", "Delete a playlist")] - public static async Task DeletePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to delete", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!pls.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.RemovePlaylist(playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Delete Playlist").WithDescription("Deleted playlist -> " + playlist).Build())); - } - - [SlashCommand("rename", "Rename a playlist")] - public static async Task RenamePlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to rename", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("name", "New name for playlist")] string name - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription("**Error** You dont have a playlist with that name!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - await pls.GetEntries(); - - await PlaylistDB.RenameList(playlist, ctx.Member.Id, name); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Rename Playlist").WithDescription($"Renamed Playlist to {playlist} -> {name}!").Build())); - } - - [SlashCommand("clear", "Clear all entries from a playlist")] - public static async Task ClearPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to clear", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - await PlaylistDB.ClearList(playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Clear Playlist").WithDescription($"Cleared all songs from playlist -> {playlist}!").Build())); - } - - [SlashCommand("play", "Play a playlist/Add the songs to the queue")] - [RequireUserVoicechatConnection] - public static async Task PlayPlaylistAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to play", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist - ) - { - await ctx.DeferAsync(true); - var ps = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - if (!ps.Any(x => x.Key == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var p = await pls.GetEntries(); - if (!MikuBot.Guilds.Any(x => x.Key == ctx.Guild.Id)) - { - MikuBot.Guilds.TryAdd(ctx.Guild.Id, new Guild(ctx.Client.ShardId)); - } - var g = MikuBot.Guilds[ctx.Guild.Id]; - g.musicInstance ??= new MusicInstance(MikuBot.LavalinkNodeConnections[ctx.Client.ShardId], ctx.Client.ShardId); - await g.ConditionalConnect(ctx); - g.musicInstance.usedChannel = ctx.Channel; - await Database.AddToQueue(ctx.Guild, ctx.Member.Id, p); - if (g.musicInstance.guildConnection.IsConnected && (g.musicInstance.playstate == Playstate.NotPlaying || g.musicInstance.playstate == Playstate.Stopped)) - await g.musicInstance.PlaySong(); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Play Playlist").WithDescription($"Playing playlist/Added to queue!").Build())); - } - } - - [SlashCommandGroup("song", "Song management")] - public class SongOperations : ApplicationCommandsModule - { - [SlashCommand("add", "Add a song to a playlist")] - public static async Task AddSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("url_or_search", "Url or name of song to add")] string song - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); - return; - } - TrackResult got = await PlaylistDB.GetSong(song, ctx); - if (got == null) - return; - await PlaylistDB.AddEntry(playlist, ctx.Member.Id, got.Tracks); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Add Song").WithDescription($"Added entry -> {got.Tracks[0].Title}!").Build())); - } - - [SlashCommand("insert_at", "Insert a song into a playlist at a choosen position")] - public static async Task InsertAtAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("position", "Position to insert song at" ,true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi, - [Option("url_or_search", "Url or name of song to add")] string song - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pos = Convert.ToInt32(posi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant add songs to this!").Build())); - return; - } - TrackResult got = await PlaylistDB.GetSong(song, ctx); - if (got == null) - return; - got.Tracks.Reverse(); - await PlaylistDB.InsertEntry(ctx.Guild, playlist, ctx.Member.Id, got.Tracks, pos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription($"Inserted entry -> {got.Tracks[0].Title} at {pos}!").Build())); - } - - [SlashCommand("move", "Move a song to a specific position in your playlist")] - public static async Task MoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string oldposi, - [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string newposi - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var oldpos = Convert.ToInt32(oldposi); - var newpos = Convert.ToInt32(newposi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - if (pls.ExternalService != ExtService.None) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Insert Song").WithDescription("**Error** This playlist is a fixed one, you cant move songs!").Build())); - return; - } - var e = await pls.GetEntries(); - if (e[newpos] == null | e[oldpos] == null) - return; - await PlaylistDB.MoveListItems(ctx.Guild, playlist, ctx.Member.Id, oldpos, newpos); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Move Song").WithDescription($"Moved entry -> {e[oldpos].track.Title} to position {newpos}!").Build())); - } - - [SlashCommand("remove", "Remove a song from a playlist")] - public static async Task RemoveSongAsync(InteractionContext ctx, - [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] string playlist, - [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] string posi - ) - { - await ctx.DeferAsync(true); - if (CheckError(playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Detected error, do you have a playlist?")); - return; - } - var pos = Convert.ToInt32(posi); - var p = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (!p.Any(x => x == playlist)) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription("**Error** You dont have a playlist with that playlist!").Build())); - return; - } - var ents = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var en = await ents.GetEntries(); - await PlaylistDB.RemoveFromList(ctx.Guild, pos, playlist, ctx.Member.Id); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder().WithTitle("Remove Song").WithDescription($"Entry removed! -> {en[pos].track.Title}").Build())); - } - } - - public static bool CheckError(string playlist) - { - if (playlist == "error") - return true; - else - return false; - } -} -*/ - - diff --git a/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs index b23714a0..81db5735 100644 --- a/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.InfoCommands.cs @@ -1,4 +1,5 @@ -using MikuSharp.Attributes; +using MikuSharp.Attributes; +using MikuSharp.Utilities; namespace MikuSharp.Commands.Music; @@ -7,15 +8,40 @@ public partial class MusicCommands /// /// The info commands. /// - [SlashCommandGroup("info", "Music info commands"), RequireUserAndBotVoicechatConnection] + [SlashCommandGroup("info", "Music info commands"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public class InfoCommands : ApplicationCommandsModule { /// - /// A dummy command. + /// Shows the currently playing song. /// /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + [SlashCommand("now_playing", "Show whats currently playing")] + public static async Task ShowNowPlaylingAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Now playing")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Shows the last played song. + /// + /// The interaction context. + [SlashCommand("last_played", "Show what played before")] + public static async Task ShowLastPlayedAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Last played")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Shows the last playing songs. + /// + /// The interaction context. + [SlashCommand("last_playing", "Show what songs were played before")] + public static async Task ShowLastPlayingAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Last playing list")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs index b8e34a4b..da8665e9 100644 --- a/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.OptionsCommands.cs @@ -44,5 +44,25 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Shuffled the queue!")); }); } + + /// + /// Changes the volume of the music player. + /// + /// The interaction context. + /// The volume to set. + [SlashCommand("volume", "Change the music volume")] + public static async Task ModifyVolumeAsync( + InteractionContext ctx, + [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] + int volume = 100 + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); + await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); + }); + } } } diff --git a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs index c0fcffb5..906c1df3 100644 --- a/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.PlaybackCommands.cs @@ -62,26 +62,6 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => }); } - /// - /// Changes the volume of the music player. - /// - /// The interaction context. - /// The volume to set. - [SlashCommand("volume", "Change the music volume")] - public static async Task ModifyVolumeAsync( - InteractionContext ctx, - [Option("volume", "Level of volume to set (Percentage)"), MinimumValue(0), MaximumValue(150)] - int volume = 100 - ) - { - await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => - { - await musicSession.LavalinkGuildPlayer.SetVolumeAsync(volume); - await musicSession.UpdateStatusMessageAsync(musicSession.BuildMusicStatusEmbed()); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"Set the volume to **{volume}%**!")); - }); - } - /// /// Seeks the currently playing song to given position. /// diff --git a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs index 35bc0cf7..8b331a4d 100644 --- a/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs +++ b/MikuSharp/Commands/Music/MusicCommands.QueueCommands.cs @@ -1,4 +1,5 @@ using MikuSharp.Attributes; +using MikuSharp.Enums; using MikuSharp.Utilities; namespace MikuSharp.Commands.Music; @@ -12,19 +13,21 @@ public partial class MusicCommands public class QueueCommands : ApplicationCommandsModule { /// - /// A dummy command. + /// Shows the current queue. /// /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + [SlashCommand("show", "Show the current queue")] + public static async Task ShowQueueAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Queue list")); }); + } /// /// Skips to the next song. /// /// The interaction context. [SlashCommand("skip", "Skips to the next song")] - public async Task SkipAsync(InteractionContext ctx) + public static async Task SkipAsync(InteractionContext ctx) { await ctx.ExecuteWithMusicSessionAsync(async (discard, musicSession) => { @@ -44,7 +47,7 @@ await ctx.ExecuteWithMusicSessionAsync(async (discard, musicSession) => /// The interaction context. /// The position in the queue. [SlashCommand("skip_to", "Skips to given queue position")] - public async Task SkipToAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.QueueProvider)), Option("position", "Position in queue", true)] int position) + public static async Task SkipToAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.QueueProvider)), Option("position", "Position in queue", true)] int position) { if (position is -1) { @@ -62,5 +65,41 @@ await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Cannot skip as there are no more songs in the queue.")); }); } + + /// + /// Removes a song from the queue. + /// + /// The interaction context. + /// The position in the queue. + [SlashCommand("remove", "Remvoes a song from the queue")] + public static async Task RemoveAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.QueueProvider)), Option("song", "Song to remove", true)] int position) + { + if (position is -1) + { + await ctx.EditResponseAsync("Something went wrong while parsing the song."); + return; + } + + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.LavalinkGuildPlayer!.RemoveFromQueueAt(position); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Successfully removed the song from the queue!")); + }); + } + + /// + /// Clears the queue. + /// + /// The interaction context. + [SlashCommand("clear", "Clear Queue"), RequirePlaybackState(PlaybackState.Playing, PlaybackState.Paused)] + public static async Task StopAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => + { + musicSession.UpdateRepeatMode(RepeatMode.None); + musicSession.LavalinkGuildPlayer.ClearQueue(); + await ctx.EditResponseAsync("Cleared the queue!"); + }); + } } } diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs new file mode 100644 index 00000000..0ad47bfe --- /dev/null +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.CreateCommands.cs @@ -0,0 +1,57 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands.Playlist; + +public partial class PlaylistCommands +{ + /// + /// Provides commands for creating playlists. + /// + [SlashCommandGroup("create", "Playlist creation"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] + public class CreateCommands : ApplicationCommandsModule + { + /// + /// Copies the current queue to a new playlist. + /// + /// The interaction context. + /// The name of the new playlist. + [SlashCommand("from_queue", "Copy the current queue to a playlist!")] + public static async Task CopyQueueToNewPlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name) + { + await ctx.ExecuteWithMusicSessionAsync( + async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist from queue")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")) + ); + } + + /// + /// Creates a new blank playlist. + /// + /// The interaction context. + /// The name of the new playlist. + [SlashCommand("blank", "Create a new playlist from scratch")] + public static async Task CreateBlankPlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name) + { + await ctx.ExecuteWithMusicSessionAsync( + async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist from scratch")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")) + ); + } + + /// + /// Creates a new playlist from another playlist (e.g., YouTube, Soundcloud, or Spotify). + /// + /// The interaction context. + /// The name of the new playlist. + /// The link to the source playlist. + [SlashCommand("create", "Create a new playlist from another playlist (Like YouTube, Soundcloud or Spotify)")] + public static async Task CreatePlaylistAsync(InteractionContext ctx, [Option("name", "Name of new playlist")] string name, [Option("source", "The playlist to use")] string link) + { + await ctx.ExecuteWithMusicSessionAsync( + async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist from playlist")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o")) + ); + } + } +} diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs index 1fe14d7e..9a08061e 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.ManageCommands.cs @@ -1,16 +1,86 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + namespace MikuSharp.Commands.Playlist; public partial class PlaylistCommands { - [SlashCommandGroup("manage", "Playlist management")] + /// + /// Provides commands for managing playlists. + /// + [SlashCommandGroup("manage", "Playlist management"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public class ManageCommands : ApplicationCommandsModule { /// - /// A dummy command. + /// Lists all your playlists. + /// + /// The interaction context. + [SlashCommand("list", "List all your playlists")] + public static async Task ListPlaylistsAsync(InteractionContext ctx) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlists")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Shows the contents of a playlist. + /// + /// The interaction context. + /// The name of the playlist to show. + [SlashCommand("show", "Show the contents of a playlist")] + public static async Task ShowPlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to show", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Deletes a playlist. + /// + /// The interaction context. + /// The name of the playlist to delete. + [SlashCommand("delete", "Delete a playlist")] + public static async Task DeletePlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to delete", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist delete")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Renames a playlist. + /// + /// The interaction context. + /// The name of the playlist to rename. + /// The new name for the playlist. + [SlashCommand("rename", "Rename a playlist")] + public static async Task RenamePlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to rename", true)] string playlist, [Option("name", "New name for playlist")] string name) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist rename")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Clears all entries from a playlist. + /// + /// The interaction context. + /// The name of the playlist to clear. + [SlashCommand("clear", "Clear all entries from a playlist")] + public static async Task ClearPlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to clear", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist clear")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Plays a playlist or adds the songs to the queue. /// /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + /// The name of the playlist to play. + [SlashCommand("play", "Play a playlist/Add the songs to the queue")] + public static async Task PlayPlaylistAsync(InteractionContext ctx, [Autocomplete(typeof(AutocompleteProviders.PlaylistProvider)), Option("playlist", "Name of playlist to play", true)] string playlist) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Playlist play")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } } } diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs index ee9ab7af..4941564f 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.SongCommands.cs @@ -1,16 +1,99 @@ +using MikuSharp.Attributes; +using MikuSharp.Utilities; + namespace MikuSharp.Commands.Playlist; +/// +/// Contains commands for managing songs in playlists. +/// public partial class PlaylistCommands { - [SlashCommandGroup("song", "Song management")] + /// + /// Provides commands for managing songs in playlists. + /// + [SlashCommandGroup("song", "Song management"), RequireUserAndBotVoicechatConnection, DeferResponseAsync(true)] public class SongCommands : ApplicationCommandsModule { /// - /// A dummy command. + /// Adds a song to a playlist. + /// + /// The interaction context. + /// The name of the playlist to add the song to. + /// The URL or name of the song to add. + [SlashCommand("add", "Add a song to a playlist")] + public static async Task AddSongAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("url_or_name", "Url or name of song to add")] + string song + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Add song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Inserts a song into a playlist at a chosen position. + /// + /// The interaction context. + /// The name of the playlist to add the song to. + /// The position to insert the song at. + /// The URL or name of the song to add. + [SlashCommand("insert_at", "Insert a song into a playlist at a chosen position")] + public static async Task InsertSongAtAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to add song to", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("position", "Position to insert song at", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string posi, + [Option("url_or_name", "Url or name of song to add")] + string song + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Insert song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Moves a song to a specific position in a playlist. + /// + /// The interaction context. + /// The name of the playlist to move the song within. + /// The position to move the song from. + /// The position to move the song to. + [SlashCommand("move", "Move a song to a specific position in your playlist")] + public static async Task MoveSongAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to move the song within", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("old_position", "Position to move the song from", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string oldposi, + [Option("new_position", "Position to move song to", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string newposi + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Move song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } + + /// + /// Removes a song from a playlist. /// /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); + /// The name of the playlist to remove the song from. + /// The song to remove. + [SlashCommand("remove", "Remove a song from a playlist")] + public static async Task RemoveSongAsync( + InteractionContext ctx, + [Option("playlist", "Name of playlist to remove the song from", true), Autocomplete(typeof(AutocompleteProviders.PlaylistProvider))] + string playlist, + [Option("song", "Song to remove", true), Autocomplete(typeof(AutocompleteProviders.SongProvider))] + string posi + ) + { + await ctx.ExecuteWithMusicSessionAsync(async (_, musicSession) => { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Remove song")); }, + async _ => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("I'm not connected O.o"))); + } } } diff --git a/MikuSharp/Commands/Playlist/PlaylistCommands.cs b/MikuSharp/Commands/Playlist/PlaylistCommands.cs index 7807529c..fdc20e90 100644 --- a/MikuSharp/Commands/Playlist/PlaylistCommands.cs +++ b/MikuSharp/Commands/Playlist/PlaylistCommands.cs @@ -6,13 +6,4 @@ namespace MikuSharp.Commands.Playlist; /// The playlist commands /// [SlashCommandGroup("playlist", "Playlist commands", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), EnsureLavalinkSession] -public partial class PlaylistCommands : ApplicationCommandsModule -{ - /// - /// A dummy command. - /// - /// The interaction context. - [SlashCommand("dummy", "Dummy command")] - public async Task DummyCommand(InteractionContext ctx) - => await ctx.EditResponseAsync("This command is a placeholder and does nothing."); -} +public partial class PlaylistCommands : ApplicationCommandsModule; diff --git a/MikuSharp/Utilities/DiscordOptionProviders.cs b/MikuSharp/Utilities/DiscordOptionProviders.cs index c5ab3f27..7479afb7 100644 --- a/MikuSharp/Utilities/DiscordOptionProviders.cs +++ b/MikuSharp/Utilities/DiscordOptionProviders.cs @@ -1,9 +1,19 @@ namespace MikuSharp.Utilities; +/// +/// Provides fixed options for Discord commands. +/// internal class FixedOptionProviders { + /// + /// Provides choices for repeat modes. + /// internal sealed class RepeatModeProvider : ChoiceProvider { + /// + /// Provides the choices for repeat modes. + /// + /// A task that represents the asynchronous operation. The task result contains the choices for repeat modes. public override Task> Provider() { var list = new List(3) @@ -17,10 +27,24 @@ public override Task> Provide } } +/// +/// Provides autocomplete options for Discord commands. +/// internal class AutocompleteProviders { + /// + /// Provides autocomplete options for banned users. + /// internal sealed class BanProvider : IAutocompleteProvider { + /// + /// Provides the autocomplete options for banned users. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for + /// banned users. + /// public async Task> Provider(AutocompleteContext ctx) { var bans = await ctx.Guild.GetBansAsync(); @@ -33,58 +57,53 @@ public async Task> Prov } } - /* - internal class PlaylistProvider : IAutocompleteProvider + /// + /// Provides autocomplete options for playlists. + /// + internal sealed class PlaylistProvider : IAutocompleteProvider { - public async Task> Provider(AutocompleteContext ctx) - { - var plls = await PlaylistDB.GetPlaylistsSimple(ctx.Member.Id); - if (plls.Count == 0) - return new List() { new("You have no songs", "error") }; - - var DbPlaylists = await PlaylistDB.GetPlaylists(ctx.Guild, ctx.Member.Id); - - List> playlists = new(25); - if (ctx.FocusedOption.Value == null) - playlists.AddRange(DbPlaylists.Take(25)); - else - playlists.AddRange(DbPlaylists.Where(x => x.Value.Name.Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return playlists.Select(x => new DiscordApplicationCommandAutocompleteChoice(x.Value.Name, x.Key)); - } + /// + /// Provides the autocomplete options for playlists. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for + /// playlists. + /// + public async Task> Provider(AutocompleteContext ctx) + => []; } - internal class SongProvider : IAutocompleteProvider + /// + /// Provides autocomplete options for songs. + /// + internal sealed class SongProvider : IAutocompleteProvider { - public async Task> Provider(AutocompleteContext ctx) - { - var playlist = Convert.ToString(ctx.Options.First(x => x.Name == "playlist").Value); - - switch (playlist) - { - case null: - return new List() { new("You have no playlist selected", "error") }; - case "error": - return new List() { new("You have no valid playlist selected", "error") }; - } - - var pls = await PlaylistDB.GetPlaylist(ctx.Guild, ctx.Member.Id, playlist); - var tracks = await pls.GetEntries(); - List songs = new(25); - if (ctx.FocusedOption.Value == null) - songs.AddRange(tracks.Take(25)); - else if (int.TryParse(Convert.ToString(ctx.FocusedOption.Value), out var pos)) - songs.AddRange(tracks.Where(x => x.Position.ToString().StartsWith(pos.ToString())).Take(25)); - else - songs.AddRange(tracks.Where(x => x.Track.Info.Title.ToLower().Contains(Convert.ToString(ctx.FocusedOption.Value).ToLower())).Take(25)); - - return songs.Select(x => new DiscordApplicationCommandAutocompleteChoice($"{x.Position}: {x.Track.Info.Title}", x.Position.ToString())); - } + /// + /// Provides the autocomplete options for songs. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for + /// songs. + /// + public async Task> Provider(AutocompleteContext ctx) + => []; } - */ + /// + /// Provides autocomplete options for the queue. + /// internal sealed class QueueProvider : IAutocompleteProvider { + /// + /// Provides the autocomplete options for the queue. + /// + /// The autocomplete context. + /// + /// A task that represents the asynchronous operation. The task result contains the autocomplete options for the + /// queue. + /// public async Task> Provider(AutocompleteContext ctx) { return await ctx.ExecuteWithMusicSessionAsync((_, musicSession) => From ac5a2c895236b62de4c6627ad2c3c254ea8ccc13 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 10 Feb 2025 14:08:55 +0100 Subject: [PATCH 093/113] more docs --- MikuSharp/Commands/DeveloperCommands.cs | 53 ++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 155a9bfa..90a05e80 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -14,7 +14,7 @@ public class DeveloperOnlyCommands : ApplicationCommandsModule private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; /// - /// Evals the csharp script. + /// Evals a csharp script. /// /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] @@ -80,31 +80,53 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe } } + /// + /// Deletes a message sent by the bot. + /// + /// The context menu context. [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] public static async Task DeleteMessageAsync(ContextMenuContext ctx) { await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); + if (ctx.TargetMessage.Author.Id != ctx.Client.CurrentUser.Id) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You can only delete messages sent by me.")); + return; + } + await ctx.TargetMessage.DeleteAsync(); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); } - private static string SizeToString(long l) + /// + /// Converts a size to a human-readable string. + /// + /// The size to convert. + /// A human-readable string representing the size. + private static string SizeToString(long size) { - double d = l; + double convertedSize = size; var u = 0; - while (d >= 900 && u < s_units.Length - 2) + while (convertedSize >= 900 && u < s_units.Length - 2) { u++; - d /= 1024; + convertedSize /= 1024; } - return $"{d:#,##0.00} {s_units[u]}B"; + return $"{convertedSize:#,##0.00} {s_units[u]}B"; } + /// + /// The developer commands. + /// [SlashCommandGroup("dev", "Developer commands")] public class DeveloperCommands : ApplicationCommandsModule { + /// + /// A test command. + /// + /// The interaction context. [SlashCommand("test", "Testing")] public static async Task TestAsync(InteractionContext ctx) { @@ -113,6 +135,10 @@ public static async Task TestAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])]))); } + /// + /// Guild shard test command. + /// + /// The interaction context. [SlashCommand("guild_shard_test", "Testing")] public static async Task GuildTestAsync(InteractionContext ctx) { @@ -121,6 +147,10 @@ public static async Task GuildTestAsync(InteractionContext ctx) await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent($"Shard {shard.ShardId} has {shard.Guilds.Count} guilds.")); } + /// + /// Gets the Lavalink statistics. + /// + /// The interaction context. [SlashCommand("lstats", "Displays Lavalink statistics"), DeferResponseAsync(true)] public static async Task GetLavalinkStatsAsync(InteractionContext ctx) { @@ -184,12 +214,19 @@ public static async Task GetDebugLogAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Done")); } + /// + /// Monetization tests. + /// [SlashCommandGroup("monetization", "Monetization tests")] public class Monetization : ApplicationCommandsModule { private const ulong CONSUMABLE_SKU_ID = 1337743977473900555; private const ulong DURABLE_SKU_ID = 1337744226666151949; + /// + /// Consumes a consumable. + /// + /// The interaction context. [SlashCommand("consume_consumable", "Consume a consumable"), ApplicationCommandRequireSkuEntitlement(CONSUMABLE_SKU_ID)] public static async Task ConsumeConsumableAsync(InteractionContext ctx) { @@ -207,6 +244,10 @@ public static async Task ConsumeConsumableAsync(InteractionContext ctx) await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Huh?!")); } + /// + /// Uses a durable. + /// + /// The interaction context. [SlashCommand("use_durable", "Use a durable"), ApplicationCommandRequireSkuEntitlement(DURABLE_SKU_ID)] public static async Task UseDurableAsync(InteractionContext ctx) { From 71e3745287510cfbd61824715e076b665b5e325a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 10 Feb 2025 14:11:27 +0100 Subject: [PATCH 094/113] extract discord utility to own group --- MikuSharp/Commands/DiscordUtilityCommands.cs | 90 ++++++++++++++++++++ MikuSharp/Commands/UtilityCommands.cs | 87 ------------------- 2 files changed, 90 insertions(+), 87 deletions(-) create mode 100644 MikuSharp/Commands/DiscordUtilityCommands.cs diff --git a/MikuSharp/Commands/DiscordUtilityCommands.cs b/MikuSharp/Commands/DiscordUtilityCommands.cs new file mode 100644 index 00000000..34caf2ee --- /dev/null +++ b/MikuSharp/Commands/DiscordUtilityCommands.cs @@ -0,0 +1,90 @@ +using DisCatSharp.Exceptions; + +using MikuSharp.Attributes; +using MikuSharp.Utilities; + +namespace MikuSharp.Commands; + +[SlashCommandGroup("discord", "Discord Utilities")] +internal class DiscordUtilityCommands : ApplicationCommandsModule +{ + [SlashCommand("avatar", "Get the avatar of someone or yourself")] + public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null + ? user.AvatarUrl + : ctx.User.AvatarUrl).Build())); + + [SlashCommand("guild_avatar", "Get the guild avatar of someone or yourself")] + public static async Task GetGuildAvatarAsync(InteractionContext ctx, [Option("user", "User to get the guild avatar from")] DiscordUser? user = null) + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null + ? ctx.GetGuildAvatarIfPossible(user) + : ctx.GetGuildAvatarIfPossible(ctx.User)).Build())); + + [SlashCommand("server_info", "Get information about the server"), DeferResponseAsync(true)] + public static async Task GuildInfoAsync(InteractionContext ctx) + { + if (ctx.Guild is null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + var members = await ctx.Guild.GetAllMembersAsync(); + var bots = members.Count(x => x.IsBot); + + var emb = new DiscordEmbedBuilder(); + emb.WithTitle(ctx.Guild.Name); + emb.WithColor(new(0212255)); + if (ctx.Guild.IconUrl is not null) + emb.WithThumbnail(ctx.Guild.IconUrl); + emb.AddField(new("Owner", ctx.Guild.Owner?.UsernameWithGlobalName ?? "unknown??".Italic(), true)); + emb.AddField(new("Language", ctx.Guild.PreferredLocale ?? "Not set".Italic(), true)); + emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); + emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); + emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); + emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("user_info", "Get information about a user"), DeferResponseAsync(true)] + public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + { + user ??= ctx.User; + + DiscordMember? member = null; + + if (ctx.Guild is not null) + try + { + member = await user.ConvertToMember(ctx.Guild); + } + catch (NotFoundException) + { } + + var emb = new DiscordEmbedBuilder(); + emb.WithColor(new(0212255)); + emb.WithTitle("User Info"); + emb.AddField(new("Username", $"{user.UsernameWithGlobalName}", true)); + if (member is not null) + if (member.DisplayName != user.Username) + emb.AddField(new("Nickname", $"{member.DisplayName}", true)); + emb.AddField(new("ID", $"{user.Id}", true)); + emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); + if (member is not null) + emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); + emb.WithThumbnail(user.AvatarUrl); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + } + + [SlashCommand("emojilist", "Lists all custom emoji on this server")] + public static async Task EmojiListAsync(InteractionContext ctx) + { + var wat = "You have to execute this command in a server!"; + + if (ctx.Guild is not null && ctx.Guild.Emojis.Any()) + wat = ctx.Guild.Emojis.Values.Aggregate("**Emojies:** ", (current, em) => current + em + " "); + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); + } +} diff --git a/MikuSharp/Commands/UtilityCommands.cs b/MikuSharp/Commands/UtilityCommands.cs index ce84c8a4..6380e46c 100644 --- a/MikuSharp/Commands/UtilityCommands.cs +++ b/MikuSharp/Commands/UtilityCommands.cs @@ -1,10 +1,7 @@ -using DisCatSharp.Exceptions; - using Kitsu.Anime; using Kitsu.Manga; using MikuSharp.Attributes; -using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -125,88 +122,4 @@ public static async Task SearchMangaAsync(InteractionContext ctx, [Option("searc } } } - - [SlashCommandGroup("discord", "Discord Utilities")] - internal class DiscordUtility : ApplicationCommandsModule - { - [SlashCommand("avatar", "Get the avatar of someone or yourself")] - public static async Task GetAvatarAsync(InteractionContext ctx, [Option("user", "User to get the avatar from")] DiscordUser? user = null) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null - ? user.AvatarUrl - : ctx.User.AvatarUrl).Build())); - - [SlashCommand("guild_avatar", "Get the guild avatar of someone or yourself")] - public static async Task GetGuildAvatarAsync(InteractionContext ctx, [Option("user", "User to get the guild avatar from")] DiscordUser? user = null) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AddEmbed(new DiscordEmbedBuilder().WithImageUrl(user is not null - ? ctx.GetGuildAvatarIfPossible(user) - : ctx.GetGuildAvatarIfPossible(ctx.User)).Build())); - - [SlashCommand("server_info", "Get information about the server"), DeferResponseAsync(true)] - public static async Task GuildInfoAsync(InteractionContext ctx) - { - if (ctx.Guild is null) - { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); - return; - } - - var members = await ctx.Guild.GetAllMembersAsync(); - var bots = members.Count(x => x.IsBot); - - var emb = new DiscordEmbedBuilder(); - emb.WithTitle(ctx.Guild.Name); - emb.WithColor(new(0212255)); - if (ctx.Guild.IconUrl is not null) - emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new("Owner", ctx.Guild.Owner?.UsernameWithGlobalName ?? "unknown??".Italic(), true)); - emb.AddField(new("Language", ctx.Guild.PreferredLocale ?? "Not set".Italic(), true)); - emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); - emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); - emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("user_info", "Get information about a user"), DeferResponseAsync(true)] - public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) - { - user ??= ctx.User; - - DiscordMember? member = null; - - if (ctx.Guild is not null) - try - { - member = await user.ConvertToMember(ctx.Guild); - } - catch (NotFoundException) - { } - - var emb = new DiscordEmbedBuilder(); - emb.WithColor(new(0212255)); - emb.WithTitle("User Info"); - emb.AddField(new("Username", $"{user.UsernameWithGlobalName}", true)); - if (member is not null) - if (member.DisplayName != user.Username) - emb.AddField(new("Nickname", $"{member.DisplayName}", true)); - emb.AddField(new("ID", $"{user.Id}", true)); - emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); - if (member is not null) - emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); - emb.WithThumbnail(user.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); - } - - [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public static async Task EmojiListAsync(InteractionContext ctx) - { - var wat = "You have to execute this command in a server!"; - - if (ctx.Guild is not null && ctx.Guild.Emojis.Any()) - wat = ctx.Guild.Emojis.Values.Aggregate("**Emojies:** ", (current, em) => current + em + " "); - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); - } - } } From 3101a4ecaf26864dbe05d5805f27cf8b4d38b333 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 10 Feb 2025 14:16:04 +0100 Subject: [PATCH 095/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 90a05e80..f8a38ea2 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -11,6 +11,9 @@ namespace MikuSharp.Commands; [ApplicationCommandRequireTeamMember] public class DeveloperOnlyCommands : ApplicationCommandsModule { + /// + /// The units. + /// private static readonly string[] s_units = ["", "ki", "Mi", "Gi"]; /// From e1eedcb17c9d0cc7199edab72080c6e0b46b53dd Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 10 Feb 2025 14:28:44 +0100 Subject: [PATCH 096/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index f8a38ea2..5b01717e 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -30,7 +30,7 @@ public static async Task EvalAsync(ContextMenuContext ctx) cs1 = code.IndexOf('\n', cs1) + 1; var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); - if (cs1 == -1 || cs2 == -1) + if (cs1 is -1 || cs2 is -1) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); return; @@ -87,10 +87,9 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe /// Deletes a message sent by the bot. /// /// The context menu context. - [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev")] + [ContextMenu(ApplicationCommandType.Message, "Remove message - Miku Dev"), DeferResponseAsync(true)] public static async Task DeleteMessageAsync(ContextMenuContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Log request").AsEphemeral()); if (ctx.TargetMessage.Author.Id != ctx.Client.CurrentUser.Id) { await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You can only delete messages sent by me.")); From bd5a72e22ab9dc82e0271bdf0b4384bab1d92d76 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Mon, 10 Feb 2025 16:06:25 +0100 Subject: [PATCH 097/113] a --- MikuSharp/Commands/UtilityCommands.cs | 37 ++++++++--------- MikuSharp/HatsuneMikuBot.cs | 58 ++++++++++++++++++--------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/MikuSharp/Commands/UtilityCommands.cs b/MikuSharp/Commands/UtilityCommands.cs index 6380e46c..d828f546 100644 --- a/MikuSharp/Commands/UtilityCommands.cs +++ b/MikuSharp/Commands/UtilityCommands.cs @@ -26,24 +26,25 @@ public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("searc { emb.WithColor(new(0212255)); emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis.Length != 0) + if (aa.Attributes.Synopsis.Length is not 0) emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype.Length != 0) + if (aa.Attributes.Subtype.Length is not 0) emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.EpisodeCount != null) + if (aa.Attributes.EpisodeCount is not null) emb.AddField(new("Episodes", $"{aa.Attributes.EpisodeCount}", true)); - if (aa.Attributes.EpisodeLength != null) + if (aa.Attributes.EpisodeLength is not null) emb.AddField(new("Length", $"{aa.Attributes.EpisodeLength}", true)); - if (aa.Attributes.StartDate != null) + if (aa.Attributes.StartDate is not null) emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) + if (aa.Attributes.EndDate is not null) emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) + if (aa.Attributes.AgeRating is not null) emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) + if (aa.Attributes.AverageRating is not null) emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); emb.AddField(new("NSFW", $"{aa.Attributes.Nsfw}", true)); - if (aa.Attributes.CoverImage?.Small != null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); + if (aa.Attributes.CoverImage?.Small is not null) + emb.WithThumbnail(aa.Attributes.CoverImage.Small); res.Add(emb); emb = new(); } @@ -58,7 +59,7 @@ public static async Task SearchAnimeAsync(InteractionContext ctx, [Option("searc i++; } - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild is not null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); } catch (Exception ex) { @@ -83,19 +84,19 @@ public static async Task SearchMangaAsync(InteractionContext ctx, [Option("searc { emb.WithColor(new(0212255)); emb.WithTitle(aa.Attributes.Titles.EnJp); - if (aa.Attributes.Synopsis != null) + if (aa.Attributes.Synopsis is not null) emb.WithDescription(aa.Attributes.Synopsis); - if (aa.Attributes.Subtype != null) + if (aa.Attributes.Subtype is not null) emb.AddField(new("Type", $"{aa.Attributes.Subtype}", true)); - if (aa.Attributes.StartDate != null) + if (aa.Attributes.StartDate is not null) emb.AddField(new("Start Date", $"{aa.Attributes.StartDate}", true)); - if (aa.Attributes.EndDate != null) + if (aa.Attributes.EndDate is not null) emb.AddField(new("End Date", $"{aa.Attributes.EndDate}", true)); - if (aa.Attributes.AgeRating != null) + if (aa.Attributes.AgeRating is not null) emb.AddField(new("Age Rating", $"{aa.Attributes.AgeRating}", true)); - if (aa.Attributes.AverageRating != null) + if (aa.Attributes.AverageRating is not null) emb.AddField(new("Score", $"{aa.Attributes.AverageRating}", true)); - if (aa.Attributes.CoverImage?.Small != null) + if (aa.Attributes.CoverImage?.Small is not null) emb.WithThumbnail(aa.Attributes.CoverImage.Small); emb.WithFooter("via Kitsu.io", "https://kitsu.io/kitsu-256-ed442f7567271af715884ca3080e8240.png"); res.Add(emb); @@ -112,7 +113,7 @@ public static async Task SearchMangaAsync(InteractionContext ctx, [Option("searc i++; } - await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild != null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); + await ine.SendPaginatedResponseAsync(ctx.Interaction, true, ctx.Guild is not null, ctx.User, ress, behaviour: PaginationBehaviour.WrapAround, deletion: ButtonPaginationBehavior.Disable); } catch (Exception ex) { diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index c0a07520..58788b65 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -1,3 +1,5 @@ +using System.Net; + using DisCatSharp.ApplicationCommands.Exceptions; using DiscordBotsList.Api; @@ -22,6 +24,21 @@ namespace MikuSharp; /// public sealed class HatsuneMikuBot : IDisposable { + /// + /// Whether to enable the proxy. + /// + public const bool ENABLE_PROXY = false; + + /// + /// Whether to disable the gateway compression. + /// + public const bool DISABLE_GATEWAY_COMPRESSION = false; + + /// + /// Whether to disable Lavalink. + /// + public const bool DISABLE_LAVALINK = true; + /// /// Gets the Weeb client. /// @@ -50,7 +67,6 @@ public HatsuneMikuBot() Config = config; Config.DbConnectString = $"Host={Config.DbConfig.Hostname};Username={Config.DbConfig.User};Password={Config.DbConfig.Password};Database={Config.DbConfig.Database}"; - MikuCancellationTokenSource = new(); Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() @@ -78,9 +94,9 @@ public HatsuneMikuBot() DeveloperUserId = 856780995629154305, AttachUserInfo = true, ReconnectIndefinitely = true, - EnableLibraryDeveloperMode = true - //Proxy = new WebProxy("127.0.0.1", 8004), - //GatewayCompressionLevel = GatewayCompressionLevel.None + EnableLibraryDeveloperMode = true, + Proxy = ENABLE_PROXY ? new WebProxy("127.0.0.1", 8004) : null, + GatewayCompressionLevel = DISABLE_GATEWAY_COMPRESSION ? GatewayCompressionLevel.None : GatewayCompressionLevel.Stream }); this.InteractivityModules = ShardedClient.UseInteractivityAsync(new() @@ -146,7 +162,7 @@ public HatsuneMikuBot() /// /// Gets the cancellation token source. /// - internal static CancellationTokenSource MikuCancellationTokenSource { get; set; } + internal static CancellationTokenSource MikuCancellationTokenSource { get; set; } = new(); /// /// Gets the bot configuration. @@ -338,6 +354,7 @@ internal void RegisterCommands() this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); + this.ApplicationCommandsModules.RegisterGlobalCommands(); this.ApplicationCommandsModules.RegisterGlobalCommands(); // Smolcar command, miku discord guild command @@ -353,25 +370,30 @@ internal async Task RunAsync() await ShardedClient.StartAsync(); await Task.Delay(5000); - var success = false; - while (!success) - try - { - foreach (var lavalinkShard in this.LavalinkModules) - await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); - success = true; - } - catch - { - success = false; - } + if (!DISABLE_LAVALINK) + { + var success = false; + while (!success) + try + { + foreach (var lavalinkShard in this.LavalinkModules) + await lavalinkShard.Value.ConnectAsync(this.LavalinkConfig); + success = true; + } + catch + { + success = false; + } + } foreach (var client in ShardedClient.ShardClients.Values) await client.GetApplicationEmojisAsync(true); DiscordBotListApi = new(ShardedClient.CurrentApplication.Id, Config.DiscordBotListToken); this.GameSetThread = Task.Run(RotateActivityAsync); - //BotListThread = Task.Run(UpdateBotList); +#if !DEBUG + this.BotListThread = Task.Run(UpdateBotListStatisticsAsync); +#endif while (!MikuCancellationTokenSource.IsCancellationRequested) await Task.Delay(25); _ = this.LavalinkModules.Select(lavalinkShard => lavalinkShard.Value.ConnectedSessions.Select(async connectedSession => await connectedSession.Value.DestroyAsync())); From ac7f13e5901b7698d844720f9d354d8f6e374759 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Feb 2025 10:25:58 +0100 Subject: [PATCH 098/113] nom --- MikuSharp/Commands/AboutCommands.cs | 4 +- MikuSharp/Commands/DeveloperCommands.cs | 81 ++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/MikuSharp/Commands/AboutCommands.cs b/MikuSharp/Commands/AboutCommands.cs index 7b07344e..111f5f97 100644 --- a/MikuSharp/Commands/AboutCommands.cs +++ b/MikuSharp/Commands/AboutCommands.cs @@ -108,7 +108,7 @@ public static async Task GetExecutingShardAsync(InteractionContext ctx) [SlashCommand("stats", "Statistics about the bot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { - var statsitcs = HatsuneMikuBot.ShardedClient.Statistics; + var statistics = HatsuneMikuBot.ShardedClient.Statistics; var knownGuildFeatures = HatsuneMikuBot.ShardedClient.ShardClients.Values.SelectMany(client => client.Guilds.Values).SelectMany(guild => guild.RawFeatures ?? ["None"]).Distinct().ToList(); @@ -117,7 +117,7 @@ public static async Task StatsAsync(InteractionContext ctx) DiscordEmbedBuilder builder = new(); builder.WithTitle("Stats"); builder.WithDescription($"Some stats about {ctx.Client.CurrentApplication.Name}!\n\nKnown Guild Features:\n{string.Join("\n", knownGuildFeatures.Where(feature => feature is not "None")).BlockCode()}"); - foreach (var (key, value) in statsitcs) + foreach (var (key, value) in statistics) builder.AddField(new(key.ToString(), value.ToString().InlineCode(), true)); builder.AddField(new("Ping", $"{averagePing}ms".InlineCode(), true)); if (ctx.Client.VersionString.Contains('+')) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 5b01717e..720c9296 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -20,10 +20,10 @@ public class DeveloperOnlyCommands : ApplicationCommandsModule /// Evals a csharp script. /// /// The context menu context. - [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev")] + [ContextMenu(ApplicationCommandType.Message, "Eval - Miku Dev"), DeferResponseAsync(true)] public static async Task EvalAsync(ContextMenuContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().WithContent("Eval request").AsEphemeral()); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Eval request")); var msg = ctx.TargetMessage; var code = msg.Content; var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; @@ -42,7 +42,6 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe .WithColor(new("#FF007F")) .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") .Build())).ConfigureAwait(false); - await ctx.GetOriginalResponseAsync(); try { @@ -54,8 +53,73 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); - script.Compile(); - var result = await script.RunAsync(globals).ConfigureAwait(false); + script.Compile(HatsuneMikuBot.MikuCancellationTokenSource.Token); + var result = await script.RunAsync(globals, HatsuneMikuBot.MikuCancellationTokenSource.Token).ConfigureAwait(false); + + if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Result", + Description = result.ReturnValue.ToString(), + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + else + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Successful", + Description = "No result was returned.", + Color = new DiscordColor("#007FFF") + }.Build())).ConfigureAwait(false); + } + catch (Exception ex) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder + { + Title = "Evaluation Failure", + Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), + Color = new DiscordColor("#FF0000") + }.Build())).ConfigureAwait(false); + } + } + + /// + /// Evals a csharp script. Version 2 with the new components :eyes:. + /// + /// The context menu context. + [ContextMenu(ApplicationCommandType.Message, "Eval V2 - Miku Dev"), DeferResponseAsync] + public static async Task EvalV2Async(ContextMenuContext ctx) + { + var msg = ctx.TargetMessage; + var code = msg.Content; + var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; + cs1 = code.IndexOf('\n', cs1) + 1; + var cs2 = code.LastIndexOf("```", StringComparison.Ordinal); + + if (cs1 is -1 || cs2 is -1) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); + return; + } + + var cs = code[cs1..cs2]; + + await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() + .WithColor(new("#FF007F")) + .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") + .Build())).ConfigureAwait(false); + + try + { + var globals = new MikuDeveloperEvalVariables(ctx.TargetMessage, ctx.Client, ctx, HatsuneMikuBot.ShardedClient); + + var sopts = ScriptOptions.Default; + sopts = sopts.WithImports("System", "System.Collections.Generic", "System.Linq", "System.Text", "System.Threading.Tasks", "DisCatSharp", "DisCatSharp.Entities", "DisCatSharp.CommandsNext", "DisCatSharp.CommandsNext.Attributes", + "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); + sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + + var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); + script.Compile(HatsuneMikuBot.MikuCancellationTokenSource.Token); + var result = await script.RunAsync(globals, HatsuneMikuBot.MikuCancellationTokenSource.Token).ConfigureAwait(false); if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder @@ -132,9 +196,12 @@ public class DeveloperCommands : ApplicationCommandsModule [SlashCommand("test", "Testing")] public static async Task TestAsync(InteractionContext ctx) { - await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); + var x = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AsSilentMessage()); + Console.WriteLine(x.Message?.Flags?.ToString()); + await ctx.EditResponseAsync("a"); + /*await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); List items = [new("https://cdn.discordapp.com/attachments/1211030818533937362/1338113601453686835/lulalaby_Catgirls_5e44ded1-2d0d-4be7-8e1e-b08400429ec3.png?ex=67a9e6e7&is=67a89567&hm=c2de0d5bedf5f981dd66e707ed9805c4c72d0ae66161ff064a7b698e686d729c&")]; - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])]))); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])])));*/ } /// From 7b5cacb226371e613a03d86ea72f9505239d1cf4 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Feb 2025 14:05:38 +0100 Subject: [PATCH 099/113] feat: sticker command --- MikuSharp/Commands/DiscordUtilityCommands.cs | 68 +++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/MikuSharp/Commands/DiscordUtilityCommands.cs b/MikuSharp/Commands/DiscordUtilityCommands.cs index 34caf2ee..4925b151 100644 --- a/MikuSharp/Commands/DiscordUtilityCommands.cs +++ b/MikuSharp/Commands/DiscordUtilityCommands.cs @@ -21,7 +21,7 @@ public static async Task GetGuildAvatarAsync(InteractionContext ctx, [Option("us : ctx.GetGuildAvatarIfPossible(ctx.User)).Build())); [SlashCommand("server_info", "Get information about the server"), DeferResponseAsync(true)] - public static async Task GuildInfoAsync(InteractionContext ctx) + public static async Task GetGuildInfoAsync(InteractionContext ctx) { if (ctx.Guild is null) { @@ -48,7 +48,7 @@ public static async Task GuildInfoAsync(InteractionContext ctx) } [SlashCommand("user_info", "Get information about a user"), DeferResponseAsync(true)] - public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) + public static async Task GetUserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) { user ??= ctx.User; @@ -67,7 +67,7 @@ public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", " emb.WithTitle("User Info"); emb.AddField(new("Username", $"{user.UsernameWithGlobalName}", true)); if (member is not null) - if (member.DisplayName != user.Username) + if (member.DisplayName != (user.GlobalName ?? user.Username)) emb.AddField(new("Nickname", $"{member.DisplayName}", true)); emb.AddField(new("ID", $"{user.Id}", true)); emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); @@ -77,14 +77,64 @@ public static async Task UserInfoAsync(InteractionContext ctx, [Option("user", " await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } - [SlashCommand("emojilist", "Lists all custom emoji on this server")] - public static async Task EmojiListAsync(InteractionContext ctx) + [SlashCommand("emojis", "Lists all custom emoji on this server"), DeferResponseAsync(true)] + public static async Task ListEmojisAsync(InteractionContext ctx) { - var wat = "You have to execute this command in a server!"; + if (ctx.Guild is null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + if (ctx.Guild.Emojis.Count is 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("This server has no custom emojis!")); + return; + } + + var guildEmojis = ctx.Guild.Emojis.Values.ToList(); + var emojiGroups = guildEmojis.Select((emoji, index) => new + { + emoji, + index + }) + .GroupBy(x => x.index / 9) + .Select(g => g.Select(x => x.emoji).ToList()) + .ToList(); + + List pages = new(emojiGroups.Count); + foreach (var group in emojiGroups) + { + DiscordEmbedBuilder builder = new(); + builder.WithTitle($"Emojis in {ctx.Guild.Name}"); + foreach (var emoji in group) + builder.AddField(new(emoji.ToString(), $"{emoji.Name} ({emoji.Id})", true)); + pages.Add(new(embed: builder)); + } + + await ctx.Client.GetInteractivity().SendPaginatedResponseAsync(ctx.Interaction, true, true, ctx.User, pages.Recalculate()); + } + + [SlashCommand("stickers", "Lists all custom stickers on this server"), DeferResponseAsync(true)] + public static async Task ListStickersAsync(InteractionContext ctx) + { + if (ctx.Guild is null) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You have to execute this command on a server!")); + return; + } + + if (ctx.Guild.Stickers.Count is 0) + { + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("This server has no custom stickers!")); + return; + } + + var guildStickers = ctx.Guild.Stickers.Values.ToList(); - if (ctx.Guild is not null && ctx.Guild.Emojis.Any()) - wat = ctx.Guild.Emojis.Values.Aggregate("**Emojies:** ", (current, em) => current + em + " "); + List pages = new(guildStickers.Count); + pages.AddRange(guildStickers.Select(guildSticker => new Page(embed: new DiscordEmbedBuilder().WithTitle($"Stickers in {ctx.Guild.Name}").AddField(new("Name", guildSticker.Name)).AddField(new("ID", guildSticker.Id.ToString())).AddField(new("Description", string.IsNullOrEmpty(guildSticker.Description) ? "No description" : guildSticker.Description)).WithImageUrl(guildSticker.Url)))); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(wat)); + await ctx.Client.GetInteractivity().SendPaginatedResponseAsync(ctx.Interaction, true, true, ctx.User, pages.Recalculate()); } } From 2e1b8ea0af21b19648f15efb3ca9aac786afed0b Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Feb 2025 14:17:06 +0100 Subject: [PATCH 100/113] Update DiscordUtilityCommands.cs --- MikuSharp/Commands/DiscordUtilityCommands.cs | 55 +++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/MikuSharp/Commands/DiscordUtilityCommands.cs b/MikuSharp/Commands/DiscordUtilityCommands.cs index 4925b151..b819e6fa 100644 --- a/MikuSharp/Commands/DiscordUtilityCommands.cs +++ b/MikuSharp/Commands/DiscordUtilityCommands.cs @@ -1,5 +1,3 @@ -using DisCatSharp.Exceptions; - using MikuSharp.Attributes; using MikuSharp.Utilities; @@ -31,48 +29,46 @@ public static async Task GetGuildInfoAsync(InteractionContext ctx) var members = await ctx.Guild.GetAllMembersAsync(); var bots = members.Count(x => x.IsBot); - var emb = new DiscordEmbedBuilder(); emb.WithTitle(ctx.Guild.Name); + if (ctx.Guild.BannerUrl is not null) + emb.WithImageUrl(ctx.Guild.BannerUrl); + if (ctx.Guild.Description is not null) + emb.WithDescription(ctx.Guild.Description); emb.WithColor(new(0212255)); if (ctx.Guild.IconUrl is not null) emb.WithThumbnail(ctx.Guild.IconUrl); - emb.AddField(new("Owner", ctx.Guild.Owner?.UsernameWithGlobalName ?? "unknown??".Italic(), true)); - emb.AddField(new("Language", ctx.Guild.PreferredLocale ?? "Not set".Italic(), true)); - emb.AddField(new("ID", ctx.Guild.Id.ToString(), true)); - emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime), true)); - emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString(), true)); - emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})", true)); - + emb.AddField(new("Owner", ctx.Guild.Owner?.UsernameWithGlobalName ?? "Unknown??".Italic())); + emb.AddField(new("Language", ctx.Guild.PreferredLocale ?? "Not set".Italic())); + emb.AddField(new("ID", ctx.Guild.Id.ToString())); + emb.AddField(new("Created At", ctx.Guild.CreationTimestamp.Timestamp(TimestampFormat.LongDateTime))); + emb.AddField(new("Members (Bots)", $"{members.Count} ({bots})")); + emb.AddField(new("Emojis", ctx.Guild.Emojis.Count.ToString())); + emb.AddField(new("Stickers", ctx.Guild.Stickers.Count.ToString())); + emb.AddField(new("Soundboard Sounds", ctx.Guild.SoundboardSounds.Count.ToString())); + emb.AddField(new("Roles", ctx.Guild.Roles.Count.ToString())); + emb.AddField(new("Channels", ctx.Guild.Channels.Count.ToString())); + emb.AddField(new("Scheduled Events", ctx.Guild.ScheduledEvents.Count.ToString())); + emb.AddField(new("Threads", ctx.Guild.Threads.Count.ToString())); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } [SlashCommand("user_info", "Get information about a user"), DeferResponseAsync(true)] public static async Task GetUserInfoAsync(InteractionContext ctx, [Option("user", "The user to view")] DiscordUser? user = null) { + var member = user is not null && ctx.Guild is not null ? ctx.Guild.TryGetMember(user.Id, out var mem) ? mem : null : ctx.Member; user ??= ctx.User; - - DiscordMember? member = null; - - if (ctx.Guild is not null) - try - { - member = await user.ConvertToMember(ctx.Guild); - } - catch (NotFoundException) - { } - var emb = new DiscordEmbedBuilder(); emb.WithColor(new(0212255)); emb.WithTitle("User Info"); - emb.AddField(new("Username", $"{user.UsernameWithGlobalName}", true)); + emb.AddField(new("Username", $"{user.UsernameWithGlobalName}")); if (member is not null) if (member.DisplayName != (user.GlobalName ?? user.Username)) - emb.AddField(new("Nickname", $"{member.DisplayName}", true)); - emb.AddField(new("ID", $"{user.Id}", true)); - emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}", true)); + emb.AddField(new("Nickname", $"{member.DisplayName}")); + emb.AddField(new("ID", $"{user.Id}")); + emb.AddField(new("Account Creation", $"{user.CreationTimestamp.Timestamp()}")); if (member is not null) - emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}", true)); + emb.AddField(new("Join Date", $"{member.JoinedAt.Timestamp()}")); emb.WithThumbnail(user.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); } @@ -101,14 +97,13 @@ public static async Task ListEmojisAsync(InteractionContext ctx) .GroupBy(x => x.index / 9) .Select(g => g.Select(x => x.emoji).ToList()) .ToList(); - List pages = new(emojiGroups.Count); foreach (var group in emojiGroups) { DiscordEmbedBuilder builder = new(); builder.WithTitle($"Emojis in {ctx.Guild.Name}"); foreach (var emoji in group) - builder.AddField(new(emoji.ToString(), $"{emoji.Name} ({emoji.Id})", true)); + builder.AddField(new(emoji.ToString(), $"{emoji.Name} ({emoji.Id})")); pages.Add(new(embed: builder)); } @@ -131,10 +126,8 @@ public static async Task ListStickersAsync(InteractionContext ctx) } var guildStickers = ctx.Guild.Stickers.Values.ToList(); - List pages = new(guildStickers.Count); - pages.AddRange(guildStickers.Select(guildSticker => new Page(embed: new DiscordEmbedBuilder().WithTitle($"Stickers in {ctx.Guild.Name}").AddField(new("Name", guildSticker.Name)).AddField(new("ID", guildSticker.Id.ToString())).AddField(new("Description", string.IsNullOrEmpty(guildSticker.Description) ? "No description" : guildSticker.Description)).WithImageUrl(guildSticker.Url)))); - + pages.AddRange(guildStickers.Select(guildSticker => new Page(embed: new DiscordEmbedBuilder().WithTitle($"Stickers in {ctx.Guild.Name}").AddField(new("Name", guildSticker.Name)).AddField(new("ID", guildSticker.Id.ToString())).AddField(new("Description", string.IsNullOrEmpty(guildSticker.Description) ? "No description".Italic() : guildSticker.Description)).WithImageUrl(guildSticker.Url)))); await ctx.Client.GetInteractivity().SendPaginatedResponseAsync(ctx.Interaction, true, true, ctx.User, pages.Recalculate()); } } From 796d81748c125045fbf95e2d1f7502fcf6f54497 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Feb 2025 18:36:09 +0100 Subject: [PATCH 101/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 720c9296..88019cef 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -196,9 +196,9 @@ public class DeveloperCommands : ApplicationCommandsModule [SlashCommand("test", "Testing")] public static async Task TestAsync(InteractionContext ctx) { - var x = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().AsSilentMessage()); - Console.WriteLine(x.Message?.Flags?.ToString()); - await ctx.EditResponseAsync("a"); + var builder = new DiscordInteractionResponseBuilder().AsEphemeral().AsSilentMessage().SuppressEmbeds().AsVoiceMessage().WithV2Components(); + var x = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); + await ctx.EditResponseAsync($"Send the following flags: {x.SendFlags}\nReceived the following flags from the callback response: {x.Message?.Flags}"); /*await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); List items = [new("https://cdn.discordapp.com/attachments/1211030818533937362/1338113601453686835/lulalaby_Catgirls_5e44ded1-2d0d-4be7-8e1e-b08400429ec3.png?ex=67a9e6e7&is=67a89567&hm=c2de0d5bedf5f981dd66e707ed9805c4c72d0ae66161ff064a7b698e686d729c&")]; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])])));*/ From 9f21917cf4b7ba6e553bd038df5e5b743e0c43dd Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 19 Feb 2025 00:40:55 +0100 Subject: [PATCH 102/113] Update ModerationCommands.cs --- MikuSharp/Commands/ModerationCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MikuSharp/Commands/ModerationCommands.cs b/MikuSharp/Commands/ModerationCommands.cs index 37e8f780..7a94bbc7 100644 --- a/MikuSharp/Commands/ModerationCommands.cs +++ b/MikuSharp/Commands/ModerationCommands.cs @@ -5,7 +5,7 @@ namespace MikuSharp.Commands; -[SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall])] +[SlashCommandGroup("mod", "Moderation", (long)Permissions.BanMembers, allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true)] internal class ModerationCommands : ApplicationCommandsModule { [SlashCommand("disable_invites", "Disable invites usage for guild")] From 15ad06cadbb77ea6e497f7eb61b790cd3544c81a Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 19 Feb 2025 00:42:15 +0100 Subject: [PATCH 103/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 88019cef..af5c5fb6 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -197,8 +197,8 @@ public class DeveloperCommands : ApplicationCommandsModule public static async Task TestAsync(InteractionContext ctx) { var builder = new DiscordInteractionResponseBuilder().AsEphemeral().AsSilentMessage().SuppressEmbeds().AsVoiceMessage().WithV2Components(); - var x = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); - await ctx.EditResponseAsync($"Send the following flags: {x.SendFlags}\nReceived the following flags from the callback response: {x.Message?.Flags}"); + var response = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); + await ctx.EditResponseAsync($"Send the following flags: {response.SendFlags}\nReceived the following flags from the callback response: {response.Message?.Flags}"); /*await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); List items = [new("https://cdn.discordapp.com/attachments/1211030818533937362/1338113601453686835/lulalaby_Catgirls_5e44ded1-2d0d-4be7-8e1e-b08400429ec3.png?ex=67a9e6e7&is=67a89567&hm=c2de0d5bedf5f981dd66e707ed9805c4c72d0ae66161ff064a7b698e686d729c&")]; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])])));*/ From f6de2cb0e00dc033b687d0c4b002d0ba16be3337 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Wed, 19 Feb 2025 00:58:19 +0100 Subject: [PATCH 104/113] Update MikuSharp.csproj --- MikuSharp/MikuSharp.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index f2230163..db5c53ca 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -77,7 +77,7 @@ - + From 9071edf435ae378f7a995d13664df0cac63e5a86 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 23 Feb 2025 21:29:20 +0100 Subject: [PATCH 105/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index af5c5fb6..e605f0cb 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -198,7 +198,7 @@ public static async Task TestAsync(InteractionContext ctx) { var builder = new DiscordInteractionResponseBuilder().AsEphemeral().AsSilentMessage().SuppressEmbeds().AsVoiceMessage().WithV2Components(); var response = await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder); - await ctx.EditResponseAsync($"Send the following flags: {response.SendFlags}\nReceived the following flags from the callback response: {response.Message?.Flags}"); + //await ctx.EditResponseAsync($"Send the following flags: {response.SendFlags}\nReceived the following flags from the callback response: {response.Message?.Flags}"); /*await ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource); List items = [new("https://cdn.discordapp.com/attachments/1211030818533937362/1338113601453686835/lulalaby_Catgirls_5e44ded1-2d0d-4be7-8e1e-b08400429ec3.png?ex=67a9e6e7&is=67a89567&hm=c2de0d5bedf5f981dd66e707ed9805c4c72d0ae66161ff064a7b698e686d729c&")]; await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordSectionComponent([new("**Catgirlsdsaophifejkgü#äl,lsd gjf bgkfnd lög kjdf gdks flkds fujenaolsf ewj bfiew löf eroiwfb eikmfpsdnifb jkemds wflkoen uje fmj ewofn udesj fckmds mfgoe4wbrhjrf em, folewbf jew f --s 750 --v 6.1 --p x5nrtis** - <@856780995629154305> (turbo, stealth)".SingleQuote())]).WithThumbnailComponent("https://example.com/image.png"), new DiscordMediaGalleryComponent([..items])])));*/ From 6bf1be83ad4e3aabd1b658174cb0e99889306cf7 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Mar 2025 16:00:09 +0100 Subject: [PATCH 106/113] switch over to v2 components --- MikuSharp/Commands/ActionCommands.cs | 63 +++-- MikuSharp/Commands/DeveloperCommands.cs | 2 +- MikuSharp/Commands/DiscordUtilityCommands.cs | 2 + MikuSharp/Commands/FunCommands.cs | 23 +- MikuSharp/Commands/WeebCommands.cs | 236 +++--------------- MikuSharp/Entities/Games/CointossGame.cs | 12 +- MikuSharp/Entities/Games/EightBallGame.cs | 13 +- .../Entities/Games/RockPaperScissorsGame.cs | 20 +- MikuSharp/Entities/MeekMoe.cs | 7 - MikuSharp/Entities/MeekMoeImage.cs | 13 + MikuSharp/HatsuneMikuBot.cs | 4 +- .../Utilities/DiscordExtensionMethods.cs | 67 +++-- MikuSharp/Utilities/WebExtensionMethods.cs | 29 ++- 13 files changed, 168 insertions(+), 323 deletions(-) delete mode 100644 MikuSharp/Entities/MeekMoe.cs create mode 100644 MikuSharp/Entities/MeekMoeImage.cs diff --git a/MikuSharp/Commands/ActionCommands.cs b/MikuSharp/Commands/ActionCommands.cs index c0517588..258e6f82 100644 --- a/MikuSharp/Commands/ActionCommands.cs +++ b/MikuSharp/Commands/ActionCommands.cs @@ -12,9 +12,12 @@ public static async Task HugAsync(InteractionContext ctx, [Option("user", "The u var title = "## A wild hug appears!"; var content = $"{ctx.User.Mention} hugs {user.Mention} uwu"; if (!(await ctx.Client.RestClient.GetWeebShAsync("hug")).TryGetWeebShImage(out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("kiss", "Kiss someone!")] @@ -23,9 +26,12 @@ public static async Task KissAsync(InteractionContext ctx, [Option("user", "The var title = "## A kiss!"; var content = $"{ctx.User.Mention} kisses {user.Mention} >~<"; if (!(await ctx.Client.RestClient.GetWeebShAsync("kiss")).TryGetWeebShImage(out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("lick", "Lick someone!")] @@ -34,9 +40,12 @@ public static async Task LickAsync(InteractionContext ctx, [Option("user", "The var title = "## Slurp~"; var content = $"{ctx.User.Mention} licks {user.Mention} owo"; if (!(await ctx.Client.RestClient.GetWeebShAsync("lick")).TryGetWeebShImage(out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("pat", "Pat someone!")] @@ -45,9 +54,12 @@ public static async Task PatAsync(InteractionContext ctx, [Option("user", "The u var title = "## Pat pat~"; var content = $"{ctx.User.Mention} pats {user.Mention} #w#"; if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("pat", []), out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("poke", "Poke someone!")] @@ -56,9 +68,12 @@ public static async Task PokeAsync(InteractionContext ctx, [Option("user", "The var title = "## Poke poke!"; var content = $"{ctx.User.Mention} pokes {user.Mention} ÓwÒ"; if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("poke", []), out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("slap", "Slap someone!")] @@ -67,9 +82,12 @@ public static async Task SlapAsync(InteractionContext ctx, [Option("user", "The var title = "## Slap!"; var content = $"{ctx.User.Mention} slaps {user.Mention} ÒwÓ"; if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("slap", []), out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("bite", "Bite someone!")] @@ -78,9 +96,12 @@ public static async Task BiteAsync(InteractionContext ctx, [Option("user", "The var title = "## Bite >:3"; var content = $"{ctx.User.Mention} bites {user.Mention} >:3"; if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("bite", []), out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("nom", "Nom someone!")] @@ -89,9 +110,12 @@ public static async Task NomAsync(InteractionContext ctx, [Option("user", "The u var title = "## Nom nom~"; var content = $"{ctx.User.Mention} noms {user.Mention} >:3c"; if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("nom", []), out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } [SlashCommand("stare", "Stare at someone!")] @@ -100,8 +124,11 @@ public static async Task StateAsync(InteractionContext ctx, [Option("user", "The var title = "## Stare O.o"; var content = $"{ctx.User.Mention} stares at {user.Mention} O.o"; if (!ctx.TryGetWeebNetImage(await HatsuneMikuBot.WeebClient.GetRandomAsync("stare", []), out var img)) + { await ctx.ActionRespondWithErrorAsync(content, user); - else if (!await ctx.TryBuildV2ActionMessageAsync(img, title, content, user)) - await ctx.SendOldStyleMessageAsync(img, content, user); + return; + } + + await ctx.SendActionMessageAsync(img, title, content, user); } } diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index e605f0cb..b2401f9a 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -234,7 +234,7 @@ public static async Task GetLavalinkStatsAsync(InteractionContext ctx) .Append($"{SizeToString(stats.Memory.Allocated)} allocated / {SizeToString(stats.Memory.Used)} used / {SizeToString(stats.Memory.Free)} free / {SizeToString(stats.Memory.Reservable)} reservable").AppendLine() .Append("Audio frames (per minute): ").Append($"{stats.Frames?.Sent:#,##0} sent / {stats.Frames?.Nulled:#,##0} nulled / {stats.Frames?.Deficit:#,##0} deficit").AppendLine() .Append("```"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString())); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(sb.ToString().BlockCode())); } /// diff --git a/MikuSharp/Commands/DiscordUtilityCommands.cs b/MikuSharp/Commands/DiscordUtilityCommands.cs index b819e6fa..e97c3e26 100644 --- a/MikuSharp/Commands/DiscordUtilityCommands.cs +++ b/MikuSharp/Commands/DiscordUtilityCommands.cs @@ -33,8 +33,10 @@ public static async Task GetGuildInfoAsync(InteractionContext ctx) emb.WithTitle(ctx.Guild.Name); if (ctx.Guild.BannerUrl is not null) emb.WithImageUrl(ctx.Guild.BannerUrl); +#pragma warning disable DCS0200 if (ctx.Guild.Description is not null) emb.WithDescription(ctx.Guild.Description); +#pragma warning restore DCS0200 emb.WithColor(new(0212255)); if (ctx.Guild.IconUrl is not null) emb.WithThumbnail(ctx.Guild.IconUrl); diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs index bf07c1e7..a4f51832 100644 --- a/MikuSharp/Commands/FunCommands.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -15,24 +15,21 @@ public class GamesCommands : ApplicationCommandsModule public static async Task EightBallAsync(InteractionContext ctx, [Option("question", "The question")] string question) { EightBallGame eightBall = new(question); - if (!await eightBall.TryBuildV28BallMessageAsync(ctx)) - await eightBall.SendOldStyle8BallMessageAsync(ctx); + await eightBall.Send8BallMessageAsync(ctx); } [SlashCommand("coinflip", "Flip a coin!")] public static async Task CoinflipAsync(InteractionContext ctx) { var game = new CointossGame(ctx).TossCoin(); - if (!await game.TryBuildV2CointossMessageAsync()) - await game.SendOldStyleCointossMessageAsync(); + await game.SendCointossMessageAsync(); } [SlashCommand("rps", "Play rock paper scissors!")] public static async Task RpsAsync(InteractionContext ctx, [Option("rps", "Your rock paper scissor choice")] RockPaperScissorsChoiceType userChoice) { var game = userChoice.ResolveRps(ctx.User); - if (!await game.TryBuildV2RpsMessageAsync(ctx)) - await game.SendOldStyleRpsMessageAsync(ctx); + await game.SendRpsMessageAsync(ctx); } } @@ -46,8 +43,7 @@ public static async Task CatAsync(InteractionContext ctx) if (!await ctx.CheckForProperImageResultAsync(nekosLifeImage)) return; - if (!await ctx.TryBuildV2ActionMessageAsync(nekosLifeImage!.Data, content: $"[Full Image]({nekosLifeImage.Url})", footer: "by nekos.life")) - await ctx.SendOldStyleMessageAsync(nekosLifeImage.Data, $"[Full Image]({nekosLifeImage.Url})", footer: "by nekos.life"); + await ctx.SendActionMessageAsync(nekosLifeImage!.Data, content: $"[Full Image]({nekosLifeImage.Url})", footer: "by nekos.life"); } [SlashCommand("dog", "Random Dog Image")] @@ -58,9 +54,7 @@ public static async Task DogAsync(InteractionContext ctx) return; var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dogCeo.Message.ResizeLink())); - - if (!await ctx.TryBuildV2ActionMessageAsync(img, content: $"[Full Image]({dogCeo.Message})", footer: "by dog.ceo")) - await ctx.SendOldStyleMessageAsync(img, $"[Full Image]({dogCeo.Message})", footer: "by dog.ceo"); + await ctx.SendActionMessageAsync(img, content: $"[Full Image]({dogCeo.Message})", footer: "by dog.ceo"); } [SlashCommand("duck", "Random duck image")] @@ -71,8 +65,7 @@ public static async Task DuckAsync(InteractionContext ctx) return; var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(randomData.Message.ResizeLink())); - if (!await ctx.TryBuildV2ActionMessageAsync(img, content: $"[Full Image]({randomData.Message})", footer: "by random-d.uk")) - await ctx.SendOldStyleMessageAsync(img, $"[Full Image]({randomData.Message})", footer: "by random-d.uk"); + await ctx.SendActionMessageAsync(img, content: $"[Full Image]({randomData.Message})", footer: "by random-d.uk"); } [SlashCommand("lizard", "Get a random lizard image")] @@ -83,9 +76,7 @@ public static async Task LizardAsync(InteractionContext ctx) return; var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(nekosLifeImage.Url.ResizeLink())); - - if (!await ctx.TryBuildV2ActionMessageAsync(img, footer: "by nekos.life")) - await ctx.SendOldStyleMessageAsync(img, footer: "by nekos.life"); + await ctx.SendActionMessageAsync(img, footer: "by nekos.life"); } } diff --git a/MikuSharp/Commands/WeebCommands.cs b/MikuSharp/Commands/WeebCommands.cs index c7bfa782..f59a31f1 100644 --- a/MikuSharp/Commands/WeebCommands.cs +++ b/MikuSharp/Commands/WeebCommands.cs @@ -1,7 +1,4 @@ -using HeyRed.Mime; - using MikuSharp.Attributes; -using MikuSharp.Entities; using MikuSharp.Utilities; namespace MikuSharp.Commands; @@ -12,283 +9,108 @@ internal class WeebCommands : ApplicationCommandsModule [SlashCommand("diva", "Radnom PJD Loading image")] public static async Task DivaPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/diva")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/diva")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("gumi", "Random Gumi image")] public static async Task GumiPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/gumi")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/teto")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("kaito", "Random Kaito image")] public static async Task KaitoPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/kaito")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/teto")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("len", "Random Len image")] public static async Task KLenPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/len")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/len")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("luka", "Random Luka image")] public static async Task LukaPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/luka")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/luka")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("meiko", "Random Meiko image")] public static async Task MeikoPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/meiko")); - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); - } - - [SlashCommand("miku", "Random Miku image")] - public static async Task HMikuPic(InteractionContext ctx) - { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/miku")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/meiko")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } - [SlashCommand("neko", "Get a random neko image")] - public static async Task Cat(InteractionContext ctx) + [SlashCommand("miku", "Random Miku image")] + public static async Task HMikuPic(InteractionContext ctx) { - var imgUrl = await ctx.Client.RestClient.GetNekosLifeAsync("https://nekos.life/api/v2/img/neko"); - if (imgUrl is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/miku")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(imgUrl.Url.ResizeLink())); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(img)}"); - em.WithFooter("by nekos.life"); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(em.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("rin", "Random Rin image")] public static async Task KRinPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/rin")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/rin")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } [SlashCommand("teto", "Random Teto image")] public static async Task KTetoPic(InteractionContext ctx) { - var res = JsonConvert.DeserializeObject(await ctx.Client.RestClient.GetStringAsync("https://api.meek.moe/teto")); - if (res is null) + if (!(await ctx.Client.RestClient.GetMeekMoeAsync("https://api.meek.moe/teto")).TryGetMeekMoeImage(out var img)) { - await ctx.EditResponseAsync("Something went wrong while fetching the image."); + await ctx.ImageRespondWithErrorAsync(); return; } - var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(res.Url.ResizeLink())) - { - Position = 0 - }; - var emim = new DiscordEmbedBuilder - { - Description = $"[Full Source Image Link]({res.Url})", - ImageUrl = $"attachment://image.{MimeGuesser.GuessExtension(img)}" - }; - if (res.Creator.Length != 0) - emim.AddField(new("Creator", res.Creator)); - emim.WithAuthor("via api.meek.moe", "https://api.meek.moe/"); - emim.WithFooter("Requested by " + ctx.User.UsernameWithGlobalName, ctx.User.AvatarUrl); - - DiscordWebhookBuilder builder = new(); - builder.AddFile($"image.{MimeGuesser.GuessExtension(img)}", img); - builder.AddEmbed(emim.Build()); - await ctx.EditResponseAsync(builder); + await ctx.SendWeebMessageAsync(img, $"Via {"api.meek.moe".MaskedUrl(new("https://api.meek.moe/"))}"); } } diff --git a/MikuSharp/Entities/Games/CointossGame.cs b/MikuSharp/Entities/Games/CointossGame.cs index 0b60aad0..bf7a40ea 100644 --- a/MikuSharp/Entities/Games/CointossGame.cs +++ b/MikuSharp/Entities/Games/CointossGame.cs @@ -52,22 +52,12 @@ public CointossGame TossCoin() /// Tries to build and send a components V2 cointoss message. /// /// Whether the message was sent successfully. - public async Task TryBuildV2CointossMessageAsync() + public async Task SendCointossMessageAsync() { - if (this.Context.GuildId is not 1317206872763404478) - return false; - DiscordSectionComponent sectionComponent = new([DiscordExtensionMethods.EmptyComponent, new("And the winner..."), new($"### {this.Result}")]); sectionComponent.WithThumbnailComponent(this.CoinTossSideImages[this.Result], this.Result.ToString()); await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([sectionComponent]))); - return true; } - - /// - /// Sends an old-style embed cointoss message. - /// - public async Task SendOldStyleCointossMessageAsync() - => await this.Context.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"{this.CoinTossSideEmojis[this.Result]} It's {this.Result}!")); } /// diff --git a/MikuSharp/Entities/Games/EightBallGame.cs b/MikuSharp/Entities/Games/EightBallGame.cs index c5a3849b..3ab5ed9f 100644 --- a/MikuSharp/Entities/Games/EightBallGame.cs +++ b/MikuSharp/Entities/Games/EightBallGame.cs @@ -55,11 +55,8 @@ public EightBallGame(string userQuestion) /// /// The context. /// Whether the message was sent successfully. - public async Task TryBuildV28BallMessageAsync(InteractionContext ctx) + public async Task Send8BallMessageAsync(InteractionContext ctx) { - if (ctx.GuildId is not 1317206872763404478) - return false; - DiscordTextDisplayComponent question = new($"### Question\n{this.UserQuestion}"); DiscordSectionComponent questionComponent = new([DiscordExtensionMethods.EmptyComponent, question]); questionComponent.WithThumbnailComponent(ctx.User.AvatarUrl); @@ -68,13 +65,5 @@ public async Task TryBuildV28BallMessageAsync(InteractionContext ctx) DiscordSectionComponent answerComponent = new([DiscordExtensionMethods.EmptyComponent, answer]); answerComponent.WithThumbnailComponent(ctx.Client.CurrentUser.AvatarUrl); await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(new DiscordContainerComponent([new DiscordTextDisplayComponent("## 8Ball"), new DiscordSeparatorComponent(true, SeparatorSpacingSize.Large), questionComponent, seperator, answerComponent]))); - return true; } - - /// - /// Sends an old-style embed 8ball message. - /// - /// The context. - public async Task SendOldStyle8BallMessageAsync(InteractionContext ctx) - => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent($"> {this.UserQuestion}\n\n{this.ChosenAnswer}")); } diff --git a/MikuSharp/Entities/Games/RockPaperScissorsGame.cs b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs index 039b5d79..8fb49074 100644 --- a/MikuSharp/Entities/Games/RockPaperScissorsGame.cs +++ b/MikuSharp/Entities/Games/RockPaperScissorsGame.cs @@ -104,11 +104,8 @@ public sealed class RockPaperScissorsResponse(DiscordUser player, RockPaperSciss /// /// The context. /// Whether the message was sent successfully. - public async Task TryBuildV2RpsMessageAsync(InteractionContext ctx) + public async Task SendRpsMessageAsync(InteractionContext ctx) { - if (ctx.GuildId is not 1317206872763404478) - return false; - DiscordWebhookBuilder builder = new(); builder.WithV2Components(); DiscordTextDisplayComponent userChoiceComponent = new($"### {ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType}"); @@ -125,21 +122,6 @@ public async Task TryBuildV2RpsMessageAsync(InteractionContext ctx) builder.AddComponents(new DiscordContainerComponent([userChoiceSection, seperator1, computerChoiceSection, seperator2, resultSectionComponent])); builder.WithAllowedMention(new UserMention(ctx.User)); await ctx.EditResponseAsync(builder); - return true; - } - - /// - /// Sends an old-style embed rock-paper-scissors message. - /// - /// The context. - public async Task SendOldStyleRpsMessageAsync(InteractionContext ctx) - { - var emb = new DiscordEmbedBuilder() - .WithTitle("Rock Paper Scissors") - .WithDescription($"{ctx.User.Mention} chooses {this.UserChoiceAsset.ChoiceType} and I choose {this.ComputerChoiceAsset.ChoiceType}.\n{this}") - .WithThumbnail(this.WinnerAsset.ImageUrl) - .WithFooter(this.WinnerAsset.CreativeCommonsLicense, "https://mirrors.creativecommons.org/presskit/icons/cc.xlarge.png"); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb)); } /// diff --git a/MikuSharp/Entities/MeekMoe.cs b/MikuSharp/Entities/MeekMoe.cs deleted file mode 100644 index a50daed3..00000000 --- a/MikuSharp/Entities/MeekMoe.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MikuSharp.Entities; - -public sealed class MeekMoe -{ - public string Url { get; set; } - public string Creator { get; set; } -} diff --git a/MikuSharp/Entities/MeekMoeImage.cs b/MikuSharp/Entities/MeekMoeImage.cs new file mode 100644 index 00000000..f67a2355 --- /dev/null +++ b/MikuSharp/Entities/MeekMoeImage.cs @@ -0,0 +1,13 @@ +namespace MikuSharp.Entities; + +/// +/// Represents a meek moe image response. +/// +public sealed class MeekMoeImage : ImgData +{ + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("creator", NullValueHandling = NullValueHandling.Ignore)] + public string? Creator { get; set; } +} diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index 58788b65..00660808 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -228,8 +228,8 @@ internal static async Task RegisterEventsAsync() { ShardedClient.ClientErrored += (sender, args) => { - sender.Logger.LogError(args.Exception.Message); - sender.Logger.LogError(args.Exception.StackTrace); + sender.Logger.LogError(args.Exception, "{Message}", args.Exception.Message); + sender.Logger.LogError(args.Exception, "{Stack}", args.Exception.StackTrace); return Task.CompletedTask; }; diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 5491b254..580b6dfc 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -54,7 +54,7 @@ public static string GetGlobalOrUsername(this DiscordUser user) => user.GlobalName ?? user.Username; /// - /// Tries to build and send a components V2 action message. + /// Sends a Action message. /// /// The context. /// The image. @@ -65,12 +65,8 @@ public static string GetGlobalOrUsername(this DiscordUser user) /// The optional footer. Tho footer is not really the correct word for the new components. We use a /// section instead. /// - /// Whether the message was sent successfully. - public static async Task TryBuildV2ActionMessageAsync(this BaseContext context, MemoryStream image, string? title = null, string? content = null, DiscordUser? user = null, string? footer = null) + public static async Task SendActionMessageAsync(this BaseContext context, MemoryStream image, string? title = null, string? content = null, DiscordUser? user = null, string? footer = null) { - if (context.GuildId is not 1317206872763404478) - return false; - DiscordWebhookBuilder builder = new(); builder.WithV2Components(); builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); @@ -88,35 +84,29 @@ public static async Task TryBuildV2ActionMessageAsync(this BaseContext con builder.WithAllowedMention(new UserMention(user)); await context.EditResponseAsync(builder); await image.DisposeAsync(); - return true; } /// - /// Sends an old-style embed message. + /// Sends a Weeb message. /// /// The context. /// The image. - /// The optional content. - /// The additional user to allow to be pinged (author is already added). - /// The optional footer. - public static async Task SendOldStyleMessageAsync(this BaseContext context, MemoryStream image, string? content = null, DiscordUser? user = null, string? footer = null) + /// + /// The optional footer. Tho footer is not really the correct word for the new components. We use a + /// section instead. + /// + public static async Task SendWeebMessageAsync(this BaseContext context, MeekMoeImage image, string? footer = null) { DiscordWebhookBuilder builder = new(); - var em = new DiscordEmbedBuilder(); - if (content is not null) - em.WithDescription(content); - em.WithImageUrl($"attachment://image.{MimeGuesser.GuessExtension(image)}"); + builder.WithV2Components(); + builder.AddFile($"image.{image.Filetype}", image.Data); + DiscordContainerComponent container = new(); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{image.Filetype}")])); if (footer is not null) - em.WithFooter(footer); - builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); - builder.AddEmbed(em.Build()); - if (user is not null) - builder.WithContent(user.Mention); - builder.WithAllowedMention(new UserMention(context.User)); - if (user is not null) - builder.WithAllowedMention(new UserMention(user)); + container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); + builder.AddComponents(container); await context.EditResponseAsync(builder); - await image.DisposeAsync(); + await image.Data.DisposeAsync(); } /// @@ -156,6 +146,24 @@ public static bool TryGetWeebShImage(this WeebSh? data, [NotNullWhen(true)] out return true; } + /// + /// Tries to get an image from the Weeb.sh API. + /// + /// The data. + /// The image. + /// Whether the image was successfully retrieved. + public static bool TryGetMeekMoeImage(this MeekMoeImage? data, [NotNullWhen(true)] out MeekMoeImage? image) + { + if (data is null) + { + image = null; + return false; + } + + image = data; + return true; + } + /// /// Responds with an error message. /// @@ -168,6 +176,13 @@ public static async Task ActionRespondWithErrorAsync(this BaseContext ctx, strin await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); } + /// + /// Responds with an error message. + /// + /// The context. + public static async Task ImageRespondWithErrorAsync(this BaseContext ctx) + => await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get image")); + /// /// Responds with an error message if the is null. /// @@ -179,7 +194,7 @@ public static async Task CheckForProperImageResultAsync(this BaseContext c if (imgData is not null) return true; - await ctx.FollowUpAsync("Something went wrong while fetching the image"); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("Failed to get image")); return false; } } diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index ee42af32..60e1cc02 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -9,7 +9,7 @@ namespace MikuSharp.Utilities; /// -/// Provides extension methods for web-related operations. +/// Provides extension methods for web-related operations. /// public static class WebExtensionMethods { @@ -63,7 +63,7 @@ public static class WebExtensionMethods } /// - /// Generates an image using the Nekobot API. + /// Generates an image using the Nekobot API. /// /// The context. /// The type of image to generate. @@ -85,8 +85,7 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string Position = 0 }; - if (!await ctx.TryBuildV2ActionMessageAsync(stream)) - await ctx.SendOldStyleMessageAsync(stream); + await ctx.SendActionMessageAsync(stream); await stream.DisposeAsync(); } @@ -116,6 +115,28 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string return dl; } + /// + /// Gets an image from meek.moe. + /// + /// The http client. + /// The url. + /// The meek.moe response. + public static async Task GetMeekMoeAsync(this HttpClient client, string url) + { + var mm = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); + if (mm is null) + return null; + + var img = new MemoryStream(await client.GetByteArrayAsync(mm.Url.ResizeLink())) + { + Position = 0 + }; + + mm.Data = img; + mm.Filetype = MimeGuesser.GuessExtension(img); + return mm; + } + /// /// Gets a random image from weeb.sh. /// From e739c90c7572a0cba0e387dacc624b32675ab336 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Mar 2025 16:11:56 +0100 Subject: [PATCH 107/113] switch over to v2 components part 2 --- MikuSharp/Commands/FunCommands.cs | 8 +-- MikuSharp/Commands/NsfwCommands.cs | 54 +++++++++---------- MikuSharp/Entities/ImgData.cs | 8 +-- .../Utilities/DiscordExtensionMethods.cs | 27 +++++++++- MikuSharp/Utilities/WebExtensionMethods.cs | 34 +----------- 5 files changed, 58 insertions(+), 73 deletions(-) diff --git a/MikuSharp/Commands/FunCommands.cs b/MikuSharp/Commands/FunCommands.cs index a4f51832..766db173 100644 --- a/MikuSharp/Commands/FunCommands.cs +++ b/MikuSharp/Commands/FunCommands.cs @@ -43,7 +43,7 @@ public static async Task CatAsync(InteractionContext ctx) if (!await ctx.CheckForProperImageResultAsync(nekosLifeImage)) return; - await ctx.SendActionMessageAsync(nekosLifeImage!.Data, content: $"[Full Image]({nekosLifeImage.Url})", footer: "by nekos.life"); + await ctx.SendImageMessageAsync(nekosLifeImage!.Data, "by nekos.life"); } [SlashCommand("dog", "Random Dog Image")] @@ -54,7 +54,7 @@ public static async Task DogAsync(InteractionContext ctx) return; var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(dogCeo.Message.ResizeLink())); - await ctx.SendActionMessageAsync(img, content: $"[Full Image]({dogCeo.Message})", footer: "by dog.ceo"); + await ctx.SendImageMessageAsync(img, "by dog.ceo"); } [SlashCommand("duck", "Random duck image")] @@ -65,7 +65,7 @@ public static async Task DuckAsync(InteractionContext ctx) return; var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(randomData.Message.ResizeLink())); - await ctx.SendActionMessageAsync(img, content: $"[Full Image]({randomData.Message})", footer: "by random-d.uk"); + await ctx.SendImageMessageAsync(img, "by random-d.uk"); } [SlashCommand("lizard", "Get a random lizard image")] @@ -76,7 +76,7 @@ public static async Task LizardAsync(InteractionContext ctx) return; var img = new MemoryStream(await ctx.Client.RestClient.GetByteArrayAsync(nekosLifeImage.Url.ResizeLink())); - await ctx.SendActionMessageAsync(img, footer: "by nekos.life"); + await ctx.SendImageMessageAsync(img, "by nekos.life"); } } diff --git a/MikuSharp/Commands/NsfwCommands.cs b/MikuSharp/Commands/NsfwCommands.cs index f00fd30e..9cb0d413 100644 --- a/MikuSharp/Commands/NsfwCommands.cs +++ b/MikuSharp/Commands/NsfwCommands.cs @@ -9,7 +9,7 @@ public class NsfwCommands : BaseCommandModule [Command("4k"), Description("lewd")] public async Task FourK(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=4k"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=4k"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -18,15 +18,15 @@ public async Task FourK(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("anal"), Description("lewd")] public async Task Anal(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=anal"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=anal"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -35,15 +35,15 @@ public async Task Anal(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("ass"), Description("lewd")] public async Task Ass(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=ass"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=ass"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -52,15 +52,15 @@ public async Task Ass(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("gonewild"), Description("lewd")] public async Task Gonewild(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=gonewild"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -69,15 +69,15 @@ public async Task Gonewild(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("lewdkitsune"), Description("lewd")] public async Task LewdKitsune(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=lewdkitsune"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -86,15 +86,15 @@ public async Task LewdKitsune(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("lewdneko"), Description("lewd")] public async Task LewdNeko(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=lewdneko"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -103,15 +103,15 @@ public async Task LewdNeko(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("porngif"), Description("lewd")] public async Task PornGif(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=pgif"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -120,15 +120,15 @@ public async Task PornGif(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("pussy"), Description("lewd")] public async Task Pussy(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=pussy"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -137,15 +137,15 @@ public async Task Pussy(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } [Command("thighs"), Aliases("thigh"), Description("lewd")] public async Task Thighs(CommandContext ctx) { - var img = await ctx.Client.RestClient.GetNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); + var img = await ctx.Client.RestClient.GetNsfwNekobotAsync("https://nekobot.xyz/api/image?type=thigh"); if (img is null) { await ctx.RespondAsync("Failed to get image"); @@ -154,8 +154,8 @@ public async Task Thighs(CommandContext ctx) } DiscordMessageBuilder builder = new(); - builder.AddFile($"image.{img.Filetype}", img.Data); - builder.AddEmbed(img.Embed); + builder.AddFile($"image.{img.FileType}", img.Data); + //builder.AddEmbed(img.Embed); await ctx.RespondAsync(builder); } } diff --git a/MikuSharp/Entities/ImgData.cs b/MikuSharp/Entities/ImgData.cs index 30b7c615..56e7cf12 100644 --- a/MikuSharp/Entities/ImgData.cs +++ b/MikuSharp/Entities/ImgData.cs @@ -15,11 +15,5 @@ public class ImgData /// Gets the file type. /// [JsonIgnore] - public string Filetype { get; set; } - - /// - /// Gets the embed. - /// - [JsonIgnore] - public DiscordEmbed Embed { get; set; } + public string FileType { get; set; } } diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 580b6dfc..931dffc8 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -99,9 +99,9 @@ public static async Task SendWeebMessageAsync(this BaseContext context, MeekMoeI { DiscordWebhookBuilder builder = new(); builder.WithV2Components(); - builder.AddFile($"image.{image.Filetype}", image.Data); + builder.AddFile($"image.{image.FileType}", image.Data); DiscordContainerComponent container = new(); - container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{image.Filetype}")])); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{image.FileType}")])); if (footer is not null) container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); builder.AddComponents(container); @@ -109,6 +109,29 @@ public static async Task SendWeebMessageAsync(this BaseContext context, MeekMoeI await image.Data.DisposeAsync(); } + /// + /// Sends an image message. + /// + /// The context. + /// The image. + /// + /// The optional footer. Tho footer is not really the correct word for the new components. We use a + /// section instead. + /// + public static async Task SendImageMessageAsync(this BaseContext context, MemoryStream image, string? footer = null) + { + DiscordWebhookBuilder builder = new(); + builder.WithV2Components(); + builder.AddFile($"image.{MimeGuesser.GuessExtension(image)}", image); + DiscordContainerComponent container = new(); + container.AddComponent(new DiscordMediaGalleryComponent([new($"attachment://image.{MimeGuesser.GuessExtension(image)}")])); + if (footer is not null) + container.AddComponent(new DiscordTextDisplayComponent(footer.Subtext())); + builder.AddComponents(container); + await context.EditResponseAsync(builder); + await image.DisposeAsync(); + } + /// /// Tries to get an image from the Weeb.net API. /// diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index 60e1cc02..8da8efe3 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -31,34 +31,6 @@ public static class WebExtensionMethods }; dl.Data = str; dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekos.life"); - dl.Embed = em.Build(); - return dl; - } - - /// - /// Gets a random image from ksoft.si. - /// - /// The http client. - /// The tag. - /// Whether the search should include NSFW results. - /// The ksoft.si response. - public static async Task GetKsoftSiImgageAsync(this HttpClient client, string tag = "hentai_gif", bool nsfw = true) - { - client.DefaultRequestHeaders.Authorization = new("Bearer", HatsuneMikuBot.Config.KsoftSiToken); - var dl = JsonConvert.DeserializeObject(await client.GetStringAsync($"https://api.ksoft.si/images/random-image?tag={tag}&nsfw={nsfw.ToString().ToLowerInvariant()}")); - if (dl is null) - return null; - - MemoryStream img = new(await client.GetByteArrayAsync(dl.Url.ResizeLink())); - dl.Data = img; - dl.Filetype = MimeGuesser.GuessExtension(img); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by KSoft.si"); - dl.Embed = em.Build(); return dl; } @@ -96,7 +68,7 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string /// The http client. /// The url. /// The nekobot response. - public static async Task GetNekobotAsync(this HttpClient client, string url) + public static async Task GetNsfwNekobotAsync(this HttpClient client, string url) { var dl = JsonConvert.DeserializeObject(await client.GetStringAsync(url)); if (dl is null) @@ -108,10 +80,6 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string }; dl.Data = str; dl.Filetype = MimeGuesser.GuessExtension(str); - var em = new DiscordEmbedBuilder(); - em.WithImageUrl($"attachment://image.{dl.Filetype}"); - em.WithFooter("by nekobot.xyz"); - dl.Embed = em.Build(); return dl; } From 4b6e6d77b96e0ed90df1ca5a98f508dd39ffadca Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Mar 2025 16:14:29 +0100 Subject: [PATCH 108/113] nom --- MikuSharp/Entities/MeekMoeImage.cs | 8 +++- .../Utilities/DiscordExtensionMethods.cs | 36 --------------- MikuSharp/Utilities/WebExtensionMethods.cs | 45 +++++++++++++++++-- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/MikuSharp/Entities/MeekMoeImage.cs b/MikuSharp/Entities/MeekMoeImage.cs index f67a2355..29ed536e 100644 --- a/MikuSharp/Entities/MeekMoeImage.cs +++ b/MikuSharp/Entities/MeekMoeImage.cs @@ -5,9 +5,15 @@ namespace MikuSharp.Entities; /// public sealed class MeekMoeImage : ImgData { + /// + /// Gets the url. + /// [JsonProperty("url")] - public string Url { get; set; } + public required string Url { get; set; } + /// + /// Gets the creator. + /// [JsonProperty("creator", NullValueHandling = NullValueHandling.Ignore)] public string? Creator { get; set; } } diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 931dffc8..9edc2332 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -151,42 +151,6 @@ public static bool TryGetWeebNetImage(this BaseContext context, RandomData? data return true; } - /// - /// Tries to get an image from the Weeb.sh API. - /// - /// The data. - /// The stream. - /// Whether the image was successfully retrieved. - public static bool TryGetWeebShImage(this WeebSh? data, [NotNullWhen(true)] out MemoryStream? stream) - { - if (data is null) - { - stream = null; - return false; - } - - stream = data.ImgData; - return true; - } - - /// - /// Tries to get an image from the Weeb.sh API. - /// - /// The data. - /// The image. - /// Whether the image was successfully retrieved. - public static bool TryGetMeekMoeImage(this MeekMoeImage? data, [NotNullWhen(true)] out MeekMoeImage? image) - { - if (data is null) - { - image = null; - return false; - } - - image = data; - return true; - } - /// /// Responds with an error message. /// diff --git a/MikuSharp/Utilities/WebExtensionMethods.cs b/MikuSharp/Utilities/WebExtensionMethods.cs index 8da8efe3..54a7a69f 100644 --- a/MikuSharp/Utilities/WebExtensionMethods.cs +++ b/MikuSharp/Utilities/WebExtensionMethods.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Net.Http; using HeyRed.Mime; @@ -30,7 +31,7 @@ public static class WebExtensionMethods Position = 0 }; dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); + dl.FileType = MimeGuesser.GuessExtension(str); return dl; } @@ -79,12 +80,12 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string Position = 0 }; dl.Data = str; - dl.Filetype = MimeGuesser.GuessExtension(str); + dl.FileType = MimeGuesser.GuessExtension(str); return dl; } /// - /// Gets an image from meek.moe. + /// Gets an image from meek.moe. /// /// The http client. /// The url. @@ -101,7 +102,7 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string }; mm.Data = img; - mm.Filetype = MimeGuesser.GuessExtension(img); + mm.FileType = MimeGuesser.GuessExtension(img); return mm; } @@ -133,4 +134,40 @@ public static async Task GenerateNekobotImageAsync(this BaseContext ctx, string Embed = em }; } + + /// + /// Tries to get an image from the Weeb.sh API. + /// + /// The data. + /// The stream. + /// Whether the image was successfully retrieved. + public static bool TryGetWeebShImage(this WeebSh? data, [NotNullWhen(true)] out MemoryStream? stream) + { + if (data is null) + { + stream = null; + return false; + } + + stream = data.ImgData; + return true; + } + + /// + /// Tries to get an image from the Weeb.sh API. + /// + /// The data. + /// The image. + /// Whether the image was successfully retrieved. + public static bool TryGetMeekMoeImage(this MeekMoeImage? data, [NotNullWhen(true)] out MeekMoeImage? image) + { + if (data is null) + { + image = null; + return false; + } + + image = data; + return true; + } } From 05ca50b00ca1b756e1a0c3b9cd3cff529946a1ca Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Mar 2025 17:02:41 +0100 Subject: [PATCH 109/113] eval v2 --- MikuSharp/Commands/DeveloperCommands.cs | 75 +++++++++++++++++-------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index b2401f9a..2437a0ae 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -102,11 +102,20 @@ public static async Task EvalV2Async(ContextMenuContext ctx) } var cs = code[cs1..cs2]; - - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder() - .WithColor(new("#FF007F")) - .WithDescription("Evaluating...\n\nMeanwhile: https://eval-deez-nuts.xyz/") - .Build())).ConfigureAwait(false); + DiscordSectionComponent sourceSectionComponent = new(); + sourceSectionComponent.WithThumbnailComponent("https://eval-deez-nuts.xyz/static/opinion.png"); + DiscordTextDisplayComponent sourceTitleComponent = new("Code Evaluation".Header1()); + DiscordTextDisplayComponent sourceSubTitleComponent = new("Source Code".Header2()); + DiscordTextDisplayComponent sourceComponent = new(cs.BlockCode("cs")); + sourceSectionComponent.AddTextDisplayComponents([sourceTitleComponent, sourceSubTitleComponent, sourceComponent]); + + DiscordWebhookBuilder responseBuilder = new(); + responseBuilder.WithV2Components(); + DiscordContainerComponent containerComponent = new(accentColor: DiscordColor.White); + DiscordTextDisplayComponent statusTitleComponent = new("Status".Header2()); + DiscordTextDisplayComponent stateComponent = new("Evaluating..."); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder); try { @@ -117,33 +126,53 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.Blue); + stateComponent = new("Creating Script.."); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder); var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); + + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.Yellow); + stateComponent = new("Compiling Script.."); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder); script.Compile(HatsuneMikuBot.MikuCancellationTokenSource.Token); + + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.SpringGreen); + stateComponent = new("Running Script.."); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder); var result = await script.RunAsync(globals, HatsuneMikuBot.MikuCancellationTokenSource.Token).ConfigureAwait(false); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.Green); if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder - { - Title = "Evaluation Result", - Description = result.ReturnValue.ToString(), - Color = new DiscordColor("#007FFF") - }.Build())).ConfigureAwait(false); + { + stateComponent = new("Evaluation Successful"); + DiscordTextDisplayComponent returnValueTitleComponent = new("Return Value".Header2()); + DiscordTextDisplayComponent returnValue = new(result.ReturnValue.ToString()!.BlockCode()); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent, returnValueTitleComponent, returnValue]); + } else - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder - { - Title = "Evaluation Successful", - Description = "No result was returned.", - Color = new DiscordColor("#007FFF") - }.Build())).ConfigureAwait(false); + { + stateComponent = new("Evaluation Successful"); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); + } + + await ctx.EditResponseAsync(responseBuilder); } catch (Exception ex) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbedBuilder - { - Title = "Evaluation Failure", - Description = string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message), - Color = new DiscordColor("#FF0000") - }.Build())).ConfigureAwait(false); + responseBuilder.ClearComponents(); + containerComponent = new(accentColor: DiscordColor.DarkRed); + stateComponent = new(":warning: Evaluation Failure"); + DiscordTextDisplayComponent exceptionTitleComponent = new("Exception".Header2()); + DiscordTextDisplayComponent exceptionComponent = new(string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message).BlockCode()); + containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent, exceptionTitleComponent, exceptionComponent]); + await ctx.EditResponseAsync(responseBuilder); } } From 4be9f449a2318ebcd7497466b4743607743ab530 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Tue, 11 Mar 2025 17:22:28 +0100 Subject: [PATCH 110/113] Update DeveloperCommands.cs --- MikuSharp/Commands/DeveloperCommands.cs | 56 ++++++++++++------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/MikuSharp/Commands/DeveloperCommands.cs b/MikuSharp/Commands/DeveloperCommands.cs index 2437a0ae..1dad017f 100644 --- a/MikuSharp/Commands/DeveloperCommands.cs +++ b/MikuSharp/Commands/DeveloperCommands.cs @@ -89,6 +89,8 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(new DiscordEmbe [ContextMenu(ApplicationCommandType.Message, "Eval V2 - Miku Dev"), DeferResponseAsync] public static async Task EvalV2Async(ContextMenuContext ctx) { + DiscordWebhookBuilder responseBuilder = new(); + responseBuilder.WithV2Components(); var msg = ctx.TargetMessage; var code = msg.Content; var cs1 = code.IndexOf("```", StringComparison.Ordinal) + 3; @@ -97,25 +99,21 @@ public static async Task EvalV2Async(ContextMenuContext ctx) if (cs1 is -1 || cs2 is -1) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent("You need to wrap the code into a code block.")); + await ctx.EditResponseAsync(responseBuilder.AddComponents(new DiscordTextDisplayComponent("You need to wrap the code into a code block."))); return; } var cs = code[cs1..cs2]; - DiscordSectionComponent sourceSectionComponent = new(); - sourceSectionComponent.WithThumbnailComponent("https://eval-deez-nuts.xyz/static/opinion.png"); - DiscordTextDisplayComponent sourceTitleComponent = new("Code Evaluation".Header1()); - DiscordTextDisplayComponent sourceSubTitleComponent = new("Source Code".Header2()); + DiscordTextDisplayComponent titleComponent = new("Code Evaluation".Header1()); + DiscordMediaGalleryComponent imageComponent = new([new("https://eval-deez-nuts.xyz/static/opinion.png")]); + DiscordTextDisplayComponent sourceTitleComponent = new("Source Code".Header2()); DiscordTextDisplayComponent sourceComponent = new(cs.BlockCode("cs")); - sourceSectionComponent.AddTextDisplayComponents([sourceTitleComponent, sourceSubTitleComponent, sourceComponent]); - DiscordWebhookBuilder responseBuilder = new(); - responseBuilder.WithV2Components(); DiscordContainerComponent containerComponent = new(accentColor: DiscordColor.White); DiscordTextDisplayComponent statusTitleComponent = new("Status".Header2()); DiscordTextDisplayComponent stateComponent = new("Evaluating..."); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); - await ctx.EditResponseAsync(responseBuilder); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); try { @@ -126,53 +124,53 @@ public static async Task EvalV2Async(ContextMenuContext ctx) "DisCatSharp.Interactivity", "DisCatSharp.Interactivity.Extensions", "DisCatSharp.Enums", "Microsoft.Extensions.Logging", "MikuSharp.Entities"); sopts = sopts.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(xa => !xa.IsDynamic && !string.IsNullOrWhiteSpace(xa.Location))); + await Task.Delay(TimeSpan.FromSeconds(2)); responseBuilder.ClearComponents(); containerComponent = new(accentColor: DiscordColor.Blue); stateComponent = new("Creating Script.."); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); - await ctx.EditResponseAsync(responseBuilder); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); var script = CSharpScript.Create(cs, sopts, typeof(MikuDeveloperEvalVariables)); + await Task.Delay(TimeSpan.FromSeconds(2)); responseBuilder.ClearComponents(); containerComponent = new(accentColor: DiscordColor.Yellow); stateComponent = new("Compiling Script.."); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); - await ctx.EditResponseAsync(responseBuilder); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); script.Compile(HatsuneMikuBot.MikuCancellationTokenSource.Token); + await Task.Delay(TimeSpan.FromSeconds(2)); responseBuilder.ClearComponents(); containerComponent = new(accentColor: DiscordColor.SpringGreen); stateComponent = new("Running Script.."); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); - await ctx.EditResponseAsync(responseBuilder); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); var result = await script.RunAsync(globals, HatsuneMikuBot.MikuCancellationTokenSource.Token).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromSeconds(2)); responseBuilder.ClearComponents(); containerComponent = new(accentColor: DiscordColor.Green); + stateComponent = new("Evaluation Successful"); if (result is { ReturnValue: not null } && !string.IsNullOrWhiteSpace(result.ReturnValue.ToString())) { - stateComponent = new("Evaluation Successful"); - DiscordTextDisplayComponent returnValueTitleComponent = new("Return Value".Header2()); - DiscordTextDisplayComponent returnValue = new(result.ReturnValue.ToString()!.BlockCode()); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent, returnValueTitleComponent, returnValue]); + DiscordTextDisplayComponent returnValueComponent = new("Return Value".Header2() + "\n" + result.ReturnValue.ToString()!); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent, returnValueComponent]); } else - { - stateComponent = new("Evaluation Successful"); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent]); - } + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent]); - await ctx.EditResponseAsync(responseBuilder); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); } catch (Exception ex) { + await Task.Delay(TimeSpan.FromSeconds(2)); responseBuilder.ClearComponents(); containerComponent = new(accentColor: DiscordColor.DarkRed); stateComponent = new(":warning: Evaluation Failure"); - DiscordTextDisplayComponent exceptionTitleComponent = new("Exception".Header2()); - DiscordTextDisplayComponent exceptionComponent = new(string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message).BlockCode()); - containerComponent.AddComponents([sourceSectionComponent, statusTitleComponent, stateComponent, exceptionTitleComponent, exceptionComponent]); - await ctx.EditResponseAsync(responseBuilder); + DiscordTextDisplayComponent exceptionComponent = new("Exception".Header2() + "\n" + string.Concat("**", ex.GetType().ToString(), "**: ", ex.Message)); + containerComponent.AddComponents([titleComponent, imageComponent, sourceTitleComponent, sourceComponent, statusTitleComponent, stateComponent, exceptionComponent]); + await ctx.EditResponseAsync(responseBuilder.AddComponents(containerComponent)); } } From 3675e5029e3557ddafc2862a89e25cfc61c15edc Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 18 Apr 2025 22:07:17 +0200 Subject: [PATCH 111/113] nom --- MikuSharp.sln | 10 +++++++--- MikuSharp/MikuSharp.csproj | 23 +++++++++-------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/MikuSharp.sln b/MikuSharp.sln index 186ede68..7a4bdb9f 100644 --- a/MikuSharp.sln +++ b/MikuSharp.sln @@ -15,9 +15,6 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Lavalink", "..\DisCatSharp\DisCatSharp.Lavalink\DisCatSharp.Lavalink.csproj", "{3A9FE493-FC1B-9081-1E43-A3B2F37EBC76}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DisCatSharp", "DisCatSharp", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" - ProjectSection(SolutionItems) = preProject - ..\DisCatSharp\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj = ..\DisCatSharp\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj - EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp", "..\DisCatSharp\DisCatSharp\DisCatSharp.csproj", "{5E93A5B5-4FBD-8B4B-0C58-C94FBBEBD514}" EndProject @@ -29,6 +26,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.CommandsNext", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Interactivity", "..\DisCatSharp\DisCatSharp.Interactivity\DisCatSharp.Interactivity.csproj", "{D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DisCatSharp.Experimental", "..\DisCatSharp\DisCatSharp.Experimental\DisCatSharp.Experimental.csproj", "{71DC621E-D332-F94C-3F34-65A75DE657D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -67,6 +66,10 @@ Global {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Release|Any CPU.ActiveCfg = Release|Any CPU {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC}.Release|Any CPU.Build.0 = Release|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71DC621E-D332-F94C-3F34-65A75DE657D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -78,6 +81,7 @@ Global {C58808B1-4112-1650-4864-88B8A5BCE1E9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {9894841A-DFE9-21E9-8FE9-E3486D6E28BB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {D57EF042-AFE2-9B8D-E9DC-C4F3B8EE60CC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {71DC621E-D332-F94C-3F34-65A75DE657D3} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C76757C4-CC79-4C18-83BD-40F37AC98C1A} diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index db5c53ca..86a4dfe3 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -21,12 +21,7 @@ true false - - - - - - + 1701;1702;DV2001;CS8603;CS8604;CS8618;CS8601;CS8602;CS8600;CS8625 @@ -70,26 +65,26 @@ - - + + - + - + - + - + - + - + From a5aaa9e6309e78f0545b63f19448f0275cc82e78 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 18 Apr 2025 22:07:37 +0200 Subject: [PATCH 112/113] Update NicoNicoNii --- NicoNicoNii | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NicoNicoNii b/NicoNicoNii index 157d9ac8..bfe0f5a7 160000 --- a/NicoNicoNii +++ b/NicoNicoNii @@ -1 +1 @@ -Subproject commit 157d9ac86af03e34aa0c86756caf36086861d830 +Subproject commit bfe0f5a7a004b785b6af8fffa30cbe424351cb16 From 21168d2baefc9318834477eeda6dba9217229550 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 28 Dec 2025 02:53:33 +0100 Subject: [PATCH 113/113] a --- MikuSharp/Commands/AboutCommands.cs | 53 ++++++++++++------- MikuSharp/Commands/ActionCommands.cs | 4 +- MikuSharp/HatsuneMikuBot.cs | 4 +- MikuSharp/MikuSharp.csproj | 10 ++-- .../Utilities/DiscordExtensionMethods.cs | 11 ++-- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/MikuSharp/Commands/AboutCommands.cs b/MikuSharp/Commands/AboutCommands.cs index 111f5f97..b16e590d 100644 --- a/MikuSharp/Commands/AboutCommands.cs +++ b/MikuSharp/Commands/AboutCommands.cs @@ -9,30 +9,40 @@ internal class AboutCommands : ApplicationCommandsModule [SlashCommand("donate", "Financial support information")] public static async Task DonateAsync(InteractionContext ctx) { - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle("Donate Page!").WithAuthor("Miku MikuBot uwu").WithColor(new("#348573")) - .WithDescription("Thank you for your interest in supporting the bot's development!\n" + "Here are some links that may interest you").AddField(new("Patreon (Owner)", "[sekoree](https://patreon.com/sekoree)")) - .AddField(new("PayPal (Owner)", "[speyd3r](https://paypal.me/speyd3r)")).AddField(new("PayPal (Current Developer)", "[aitsys](https://paypal.me/aitsys)")).AddField(new("GitHub Sponsers (Current Developer)", "[Lulalaby](https://github.com/sponsors/Lulalaby)")); - await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AddEmbed(emb.Build()).AsEphemeral()); + DiscordContainerComponent container = new(accentColor: DiscordColor.Gold); + DiscordSectionComponent section = new(); + section.WithThumbnailComponent("https://i.imgur.com/HyqWCep.png"); + section.AddTextDisplayComponent(new("Donations".Header1())); + section.AddTextDisplayComponent(new("Thank you for your interest in supporting the bot's development ❤️\nHere are some links that may interest you:")); + section.AddTextDisplayComponent(new("Creator (@sekoree)".Header3() + "\n" + + "- " + "Patreon".MaskedUrl(new("https://patreon.com/sekoree")) + "\n" + + "- " + "PayPal".MaskedUrl(new("https://paypal.me/speyd3r")) + "\n" + + "Current Developer (@lulalaby)".Header3() + "\n" + + "- " + "PayPal".MaskedUrl(new("https://paypal.me/aitsys")) + "\n" + + "- " + "GitHub Sponsors".MaskedUrl(new("https://github.com/sponsors/Lulalaby")) + "\n\n" + + "Many thanks!".Subtext())); + container.AddComponent(section); + await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithV2Components().AddComponents(container)); } [SlashCommand("bot", "Information about the bot"), DeferResponseAsync(true)] public static async Task BotAsync(InteractionContext ctx) { - var emb = new DiscordEmbedBuilder(); - emb.WithThumbnail(ctx.Client.CurrentUser.AvatarUrl).WithTitle($"About {ctx.Client.CurrentApplication.Name}!").WithAuthor("Miku MikuBot uwu").WithColor(new("#348573")); + DiscordContainerComponent container = new(accentColor: new("#348573")); + DiscordSectionComponent section = new(); + section.WithThumbnailComponent("https://i.imgur.com/Uew8VFr.png"); + section.AddTextDisplayComponent(new($"About {ctx.Client.CurrentApplication.Name}".Header1())); if (ctx.Client.CurrentApplication.Description is not null) - emb.WithDescription(ctx.Client.CurrentApplication.Description); + section.AddTextDisplayComponent(new(ctx.Client.CurrentApplication.Description)); if (ctx.Client.CurrentApplication.Team is not null) - foreach (var member in ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username)) - emb.AddField(new(member.User.Id == ctx.Client.CurrentApplication.Team.Owner.Id - ? "Owner" - : "Developer", member.User.UsernameWithGlobalName)); + section.AddTextDisplayComponent(new("Team".Header3() + "\n" + string.Join("\n", ctx.Client.CurrentApplication.Team.Members.OrderByDescending(x => x.User.Username).Select(m => $"- {m.User.Username.Bold()} ({m.Role})")))); else - emb.AddField(new("Owner", ctx.Client.CurrentApplication.Owner.UsernameWithGlobalName)); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build())); + section.AddTextDisplayComponent(new("Owner".Header3() + "\n" + ctx.Client.CurrentApplication.Owner.UsernameWithGlobalName)); + container.AddComponent(section); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(container)); } + // TODO: CV2 [SlashCommand("news", "Get news about the bot in your server", allowedContexts: [InteractionContextType.Guild], integrationTypes: [ApplicationCommandIntegrationTypes.GuildInstall]), DeferResponseAsync(true)] public static async Task FollowNewsAsync( InteractionContext ctx, @@ -59,6 +69,7 @@ await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent( $"News setup complete {DiscordEmoji.FromGuildEmote(HatsuneMikuBot.ShardedClient.GetShard(483279257431441410), 623933340520546306)}\n\nYou'll get the newest news about the bot in your server in {channel.Mention}!")); } + // TODO: CV2 [SlashCommand("feedback", "Send feedback to the developers")] public static async Task FeedbackAsync(InteractionContext ctx) { @@ -75,7 +86,7 @@ public static async Task FeedbackAsync(InteractionContext ctx) await res.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral()); var title = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbacktitle").Value; var body = res.Result.Interaction.Data.Components.First(x => x.CustomId is "feedbackbody").Value; - var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); + //var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var emb = new DiscordEmbedBuilder(); emb.WithAuthor($"{ctx.User.UsernameWithGlobalName}", iconUrl: ctx.User.AvatarUrl).WithTitle(title).WithDescription(body); if (ctx.Guild is not null) @@ -99,12 +110,13 @@ ctx.Guild is not null [SlashCommand("ping", "Current ping to discord's services")] public static async Task PingAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Ping: {$"{ctx.Client.Ping}ms".InlineCode()}")); + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithV2Components().AddComponents(new DiscordTextDisplayComponent($"Ping: {$"{ctx.Client.Ping}ms".InlineCode()}"))); [SlashCommand("which_shard", "Gets the id of current shard you're using me on")] public static async Task GetExecutingShardAsync(InteractionContext ctx) - => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithContent($"Shard: {ctx.Client.ShardId.ToString().InlineCode()}")); + => await ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder().AsEphemeral().WithV2Components().AddComponents(new DiscordTextDisplayComponent($"Shard: {ctx.Client.ShardId.ToString().InlineCode()}"))); + // TODO: CV2 [SlashCommand("stats", "Statistics about the bot!"), DeferResponseAsync(true)] public static async Task StatsAsync(InteractionContext ctx) { @@ -140,7 +152,10 @@ public static async Task SupportAsybc(InteractionContext ctx) { var guild = await HatsuneMikuBot.ShardedClient.GetShard(483279257431441410).GetGuildAsync(483279257431441410); var widget = await guild.GetWidgetAsync(); - var emb = new DiscordEmbedBuilder().WithTitle("Support Server").WithDescription("Need help or is something broken?").WithThumbnail(ctx.Client.CurrentUser.AvatarUrl); - await ctx.EditResponseAsync(new DiscordWebhookBuilder().AddEmbed(emb.Build()).AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, "Support Server", false, new(704733597655105634)))); + DiscordContainerComponent container = new(accentColor: DiscordColor.IndianRed); + container.AddComponent(new DiscordTextDisplayComponent("Support".Header1())); + container.AddComponent(new DiscordTextDisplayComponent("Need help or is something broken?")); + container.AddComponent(new DiscordActionRowComponent([new DiscordLinkButtonComponent(widget.InstantInviteUrl.Replace("canary.discord.com/invite", "discord.gg"), "Support Server", false, new(704733597655105634))])); + await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithV2Components().AddComponents(container)); } } diff --git a/MikuSharp/Commands/ActionCommands.cs b/MikuSharp/Commands/ActionCommands.cs index 258e6f82..4f088ee3 100644 --- a/MikuSharp/Commands/ActionCommands.cs +++ b/MikuSharp/Commands/ActionCommands.cs @@ -7,10 +7,10 @@ namespace MikuSharp.Commands; internal class ActionCommands : ApplicationCommandsModule { [SlashCommand("hug", "Hug someone!")] - public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser user) + public static async Task HugAsync(InteractionContext ctx, [Option("user", "The user to execute the action with")] DiscordUser? user = null) { var title = "## A wild hug appears!"; - var content = $"{ctx.User.Mention} hugs {user.Mention} uwu"; + var content = $"{ctx.User.Mention} hugs {user?.Mention ?? "everyone"} uwu"; if (!(await ctx.Client.RestClient.GetWeebShAsync("hug")).TryGetWeebShImage(out var img)) { await ctx.ActionRespondWithErrorAsync(content, user); diff --git a/MikuSharp/HatsuneMikuBot.cs b/MikuSharp/HatsuneMikuBot.cs index 00660808..dc88f7d2 100644 --- a/MikuSharp/HatsuneMikuBot.cs +++ b/MikuSharp/HatsuneMikuBot.cs @@ -37,7 +37,7 @@ public sealed class HatsuneMikuBot : IDisposable /// /// Whether to disable Lavalink. /// - public const bool DISABLE_LAVALINK = true; + public const bool DISABLE_LAVALINK = false; /// /// Gets the Weeb client. @@ -95,7 +95,7 @@ public HatsuneMikuBot() AttachUserInfo = true, ReconnectIndefinitely = true, EnableLibraryDeveloperMode = true, - Proxy = ENABLE_PROXY ? new WebProxy("127.0.0.1", 8004) : null, + Proxy = ENABLE_PROXY ? new WebProxy("127.0.0.1", 8000) : null, GatewayCompressionLevel = DISABLE_GATEWAY_COMPRESSION ? GatewayCompressionLevel.None : GatewayCompressionLevel.Stream }); diff --git a/MikuSharp/MikuSharp.csproj b/MikuSharp/MikuSharp.csproj index 86a4dfe3..585e09f4 100644 --- a/MikuSharp/MikuSharp.csproj +++ b/MikuSharp/MikuSharp.csproj @@ -46,10 +46,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -64,7 +60,11 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/MikuSharp/Utilities/DiscordExtensionMethods.cs b/MikuSharp/Utilities/DiscordExtensionMethods.cs index 9edc2332..62de0e80 100644 --- a/MikuSharp/Utilities/DiscordExtensionMethods.cs +++ b/MikuSharp/Utilities/DiscordExtensionMethods.cs @@ -54,7 +54,7 @@ public static string GetGlobalOrUsername(this DiscordUser user) => user.GlobalName ?? user.Username; /// - /// Sends a Action message. + /// Sends an Action message. /// /// The context. /// The image. @@ -157,9 +157,14 @@ public static bool TryGetWeebNetImage(this BaseContext context, RandomData? data /// The context. /// The content. /// The user. - public static async Task ActionRespondWithErrorAsync(this BaseContext ctx, string content, DiscordUser user) + public static async Task ActionRespondWithErrorAsync(this BaseContext ctx, string content, DiscordUser? user = null) { - await ctx.EditResponseAsync(new DiscordWebhookBuilder().WithContent(content).WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)])); + var builder = new DiscordWebhookBuilder().WithContent(content); + if (user is not null) + builder.WithAllowedMentions([new UserMention(ctx.User), new UserMention(user)]); + else + builder.WithAllowedMentions([new UserMention(ctx.User)]); + await ctx.EditResponseAsync(builder); await ctx.FollowUpAsync(new DiscordFollowupMessageBuilder().AsEphemeral().WithContent("Failed to get image")); }