refactor: Initial release

This commit is contained in:
Mira 2025-01-27 17:17:53 +01:00
commit 9505750e29
Signed by untrusted user who does not match committer: Xorog
GPG key ID: 983798ED9C3E7C36
447 changed files with 41522 additions and 0 deletions

View file

@ -0,0 +1,92 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class AvatarCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var victim = (DiscordUser)arguments["user"];
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
victim ??= ctx.User;
victim = await victim.GetFromApiAsync();
var embed = new DiscordEmbedBuilder
{
ImageUrl = victim.AvatarUrl,
}.AsInfo(ctx, this.GetString(this.t.Commands.Utility.Avatar.Avatar, false, new TVar("User", victim.GetUsernameWithIdentifier())));
DiscordMember member = null;
try
{ member = await victim.ConvertToMember(ctx.Guild); }
catch { }
var ServerProfilePictureButton = new DiscordButtonComponent(ButtonStyle.Secondary, "ShowServer", this.GetString(this.t.Commands.Utility.Avatar.ShowServerProfile), (string.IsNullOrWhiteSpace(member?.GuildAvatarHash)), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🖥")));
var ProfilePictureButton = new DiscordButtonComponent(ButtonStyle.Secondary, "ShowProfile", this.GetString(this.t.Commands.Utility.Avatar.ShowUserProfile), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👤")));
var builder = new DiscordMessageBuilder().WithEmbed(embed).AddComponents(ServerProfilePictureButton);
var msg = await this.RespondOrEdit(builder);
CancellationTokenSource cancellationTokenSource = new();
ctx.Client.ComponentInteractionCreated += RunInteraction;
_ = Task.Delay(60000, cancellationTokenSource.Token).ContinueWith(x =>
{
if (x.IsCompletedSuccessfully)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
this.ModifyToTimedOut(true);
}
});
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
{
_ = Task.Run(async () =>
{
if (e.Message?.Id == msg.Id && e.User.Id == ctx.User.Id)
{
cancellationTokenSource.Cancel();
cancellationTokenSource = new();
_ = Task.Delay(60000, cancellationTokenSource.Token).ContinueWith(x =>
{
if (x.IsCompletedSuccessfully)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
this.ModifyToTimedOut(true);
}
});
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (e.GetCustomId() == ServerProfilePictureButton.CustomId)
{
embed.ImageUrl = member.GuildAvatarUrl;
_ = this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed).AddComponents(ProfilePictureButton));
}
else if (e.GetCustomId() == ProfilePictureButton.CustomId)
{
embed.ImageUrl = member.AvatarUrl;
_ = this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed).AddComponents(ServerProfilePictureButton));
}
}
}).Add(ctx.Bot, ctx);
}
});
}
}

View file

@ -0,0 +1,37 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class BannerCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var victim = (DiscordUser)arguments["user"];
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
victim ??= ctx.User;
victim = await victim.GetFromApiAsync();
var embed = new DiscordEmbedBuilder
{
ImageUrl = victim.BannerUrl,
Description = victim.BannerUrl.IsNullOrWhiteSpace() ? this.GetString(this.t.Commands.Utility.Banner.NoBanner, true) : ""
}.AsInfo(ctx, this.GetString(this.t.Commands.Utility.Banner.Banner, false, new TVar("User", victim.GetUsernameWithIdentifier())));
var builder = new DiscordMessageBuilder().WithEmbed(embed);
_ = await this.RespondOrEdit(builder);
});
}
}

View file

@ -0,0 +1,48 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class CreditsCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx, true))
return;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Credits.Fetching, true)
}.AsLoading(ctx));
var contributors = await ctx.Bot.GithubClient.Repository.GetAllContributors(ctx.Bot.status.LoadedConfig.Secrets.Github.Username, ctx.Bot.status.LoadedConfig.Secrets.Github.Repository);
var contributorsdcs = await ctx.Bot.GithubClient.Repository.GetAllContributors("Aiko-IT-Systems", "DisCatSharp");
List<DiscordUser> userlist = new();
foreach (var b in ctx.Bot.status.TeamMembers.Reverse<ulong>())
userlist.Add(await ctx.Client.GetUserAsync(b));
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Credits.Credits, false, false,
new TVar("BotName", ctx.CurrentUser.GetUsername(), false),
new TVar("Developer", "<@411950662662881290> ([`TheXorog`](https://github.com/TheXorog))", false),
new TVar("DiscordStaffList", string.Join(", ", userlist.Select(x => $"{x.Mention} [`{x.GetUsernameWithIdentifier()}`]({x.ProfileUrl})")), false),
new TVar("GitHubContList", string.Join("\n", contributors.Where(x => !x.Login.Contains("[bot]") && x.Login != "TheXorog").OrderByDescending(x => x.Contributions).Select(x => $"• [`{x.Login}`]({x.HtmlUrl})")), false),
new TVar("Library", "[`DisCatSharp`](https://github.com/Aiko-IT-Systems/DisCatSharp)", false),
new TVar("LibraryContList", string.Join(", ", contributorsdcs.Take(10).Where(x => !x.Login.Contains("[bot]")).OrderByDescending(x => x.Contributions).Select(x => $"[`{x.Login}`]({x.HtmlUrl})")), false),
new TVar("LibraryContCount", $"[{contributorsdcs.Count - 10}](https://github.com/Aiko-IT-Systems/DisCatSharp/graphs/contributors)", false),
new TVar("PhishingListRepos", $"[`nikolaischunk`](https://github.com/nikolaischunk), [`DevSpen`](https://github.com/DevSpen), [`PoorPocketsMcNewHold`](https://github.com/PoorPocketsMcNewHold), [`sk-cat`](https://github.com/sk-cat) & [`Junortiz`](https://github.com/Junortiz)", false))
}.AsInfo(ctx));
});
}
}

View file

@ -0,0 +1,166 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.Data;
internal sealed class DeleteCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx, true))
return;
var Yes = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Common.Yes), false, new DiscordComponentEmoji(true.ToEmote(ctx.Bot)));
var No = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), this.GetString(this.t.Common.No), false, new DiscordComponentEmoji(false.ToEmote(ctx.Bot)));
if (ctx.Bot.objectedUsers.Contains(ctx.User.Id))
{
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.ProfileAlreadyDeleted, true)
}.AsAwaitingInput(ctx)).AddComponents(new List<DiscordComponent> { Yes, No }));
var Menu1 = await ctx.WaitForButtonAsync();
if (Menu1.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = Menu1.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (Menu1.GetCustomId() == Yes.CustomId)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.EnablingDataProcessing, true)
}.AsLoading(ctx));
try
{
_ = ctx.Bot.objectedUsers.Remove(ctx.User.Id);
}
catch (Exception ex)
{
Log.Error(ex, "An exception occurred while trying to remove a user from the objection list");
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.EnablingDataProcessingError, true)
}.AsError(ctx));
return;
}
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.EnablingDataProcessingSuccess, true)
}.AsSuccess(ctx));
}
else
{
this.DeleteOrInvalidate();
}
return;
}
if (ctx.DbUser.Data.DeletionRequested)
{
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.DeletionAlreadyScheduled, true,
new TVar("RequestTimestamp", ctx.DbUser.Data.DeletionRequestDate.AddDays(-14).ToTimestamp()),
new TVar("ScheduleTimestamp", ctx.DbUser.Data.DeletionRequestDate.ToTimestamp()))
}.AsAwaitingInput(ctx)).AddComponents(new List<DiscordComponent> { Yes, No }));
var Menu1 = await ctx.WaitForButtonAsync();
if (Menu1.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = Menu1.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (Menu1.GetCustomId() == Yes.CustomId)
{
ctx.DbUser.Data.DeletionRequested = false;
ctx.DbUser.Data.DeletionRequestDate = DateTime.MinValue;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.DeletionScheduleReversed, true)
}.AsSuccess(ctx));
}
else
{
this.DeleteOrInvalidate();
}
return;
}
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.ObjectionDisclaimer, true, true)
}.AsAwaitingInput(ctx)).AddComponents(new List<DiscordComponent> { Yes, No }));
var Menu = await ctx.WaitForButtonAsync();
if (Menu.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (Menu.GetCustomId() == Yes.CustomId)
{
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = $"**{this.GetString(this.t.Commands.Utility.Data.Object.SecondaryConfirm, true)}**"
}.AsAwaitingInput(ctx)).AddComponents(new List<DiscordComponent> { No, Yes }));
Menu = await ctx.WaitForButtonAsync();
if (Menu.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (Menu.GetCustomId() == Yes.CustomId)
{
ctx.DbUser.Data.DeletionRequestDate = DateTime.UtcNow.AddDays(14);
ctx.DbUser.Data.DeletionRequested = true;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Object.ProfileDeletionScheduled, true)
}.AsSuccess(ctx));
}
else
{
this.DeleteOrInvalidate();
}
}
else
{
this.DeleteOrInvalidate();
}
});
}
}

View file

@ -0,0 +1,65 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.Data;
internal sealed class InfoCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx, true))
return;
if (ctx.Bot.RawFetchedPrivacyPolicy.IsNullOrWhiteSpace())
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Policy.NoPolicy, true, new TVar("Bot", ctx.CurrentUser.GetUsername())),
}.AsError(ctx));
return;
}
var RawPolicy = ctx.Bot.RawFetchedPrivacyPolicy.Replace("#", "");
var PolicyStrings = RawPolicy.ReplaceLineEndings("\n").Split("\n\n").ToList();
var Title = "";
List<DiscordEmbed> embeds = new();
for (var i = 0; i < PolicyStrings.Count; i++)
{
if (i == 0)
{
Title = PolicyStrings[i];
continue;
}
embeds.Add(new DiscordEmbedBuilder
{
Title = (i == 1 ? Title : ""),
Description = PolicyStrings[i]
});
}
try
{
foreach (var b in embeds)
_ = await ctx.User.SendMessageAsync(b);
this.SendDmRedirect();
}
catch (DisCatSharp.Exceptions.UnauthorizedException)
{
this.SendDmError();
}
});
}
}

View file

@ -0,0 +1,86 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.Data;
internal sealed class RequestCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx, true))
return;
if (ctx.DbUser.Data.LastDataRequest.GetTimespanSince() < TimeSpan.FromDays(14))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Request.TimeError, true,
new TVar("RequestTimestamp", ctx.DbUser.Data.LastDataRequest.ToTimestamp(TimestampFormat.ShortDateTime)),
new TVar("WaitTimestamp", ctx.DbUser.Data.LastDataRequest.AddDays(14).ToTimestamp(TimestampFormat.ShortDateTime)))
}.AsError(ctx));
return;
}
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Request.Fetching, true)
}.AsLoading(ctx));
RequestData requestData = new();
if (ctx.Bot.Users.ContainsKey(ctx.User.Id))
{
requestData.User = ctx.DbUser;
}
foreach (var guild in ctx.Bot.Guilds)
{
if (guild.Value.Members.TryGetValue(ctx.User.Id, out var member))
{
requestData.GuildData.Add(guild.Key, member);
}
}
Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(requestData, Formatting.Indented)));
switch (ctx.CommandType)
{
case Enums.CommandType.ApplicationCommand:
{
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Request.Confirm, true)
}.AsSuccess(ctx)).WithFile("userdata.json", stream));
ctx.DbUser.Data.LastDataRequest = DateTime.UtcNow;
break;
}
default:
{
try
{
_ = await ctx.User.SendMessageAsync(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Data.Request.Confirm, true)
}.AsSuccess(ctx)).WithFile("userdata.json", stream));
ctx.DbUser.Data.LastDataRequest = DateTime.UtcNow;
this.SendDmRedirect();
}
catch (DisCatSharp.Exceptions.UnauthorizedException)
{
this.SendDmError();
}
break;
}
}
});
}
}

View file

@ -0,0 +1,530 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class EmojiStealerCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
DiscordMessage bMessage;
if (arguments?.ContainsKey("message") ?? false)
{
bMessage = (DiscordMessage)arguments["message"];
}
else
{
switch (ctx.CommandType)
{
case Enums.CommandType.PrefixCommand:
{
if (ctx.OriginalCommandContext.Message.ReferencedMessage is not null)
{
bMessage = ctx.OriginalCommandContext.Message.ReferencedMessage;
}
else
{
this.SendSyntaxError();
return;
}
break;
}
default:
throw new ArgumentException("Message expected");
}
}
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
HttpClient client = new();
var embed = new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.EmojiStealer.DownloadingPre, true),
}.AsLoading(ctx);
_ = await this.RespondOrEdit(embed);
Dictionary<ulong, EmojiEntry> SanitizedEmoteList = new();
MemoryStream zipFileStream = new();
var FinishedInteraction = false;
var Emotes = bMessage.Content.GetEmotes();
foreach (var b in Emotes)
SanitizedEmoteList.Add(b.Item1, new EmojiEntry
{
Name = b.Item2,
Animated = b.Item3,
EntryType = EmojiType.EMOJI
});
if (Emotes.Count == 0 && (bMessage.Stickers is null || bMessage.Stickers.Count == 0))
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.NoEmojis, true);
_ = await this.RespondOrEdit(embed.AsError(ctx));
return;
}
var guid = Guid.NewGuid().ToString().MakeValidFileName();
try
{
if (SanitizedEmoteList.Count > 0)
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.DownloadingEmojis, true, new TVar("Count", SanitizedEmoteList.Count));
_ = await this.RespondOrEdit(embed);
foreach (var b in SanitizedEmoteList.ToList())
{
try
{
var EmoteStream = await client.GetStreamAsync($"https://cdn.discordapp.com/emojis/{b.Key}.{(b.Value.Animated ? "gif" : "png")}");
var NameExists = "";
var NameExistsInt = 1;
var Name = $"{b.Value.Name}{NameExists}.{(b.Value.Animated ? "gif" : "png")}".MakeValidFileName('_');
while (SanitizedEmoteList.Any(x => x.Value.Data.Name == Name))
{
NameExistsInt++;
NameExists = $" ({NameExistsInt})";
Name = $"{b.Value.Name}{NameExists}.{(b.Value.Animated ? "gif" : "png")}".MakeValidFileName('_');
}
b.Value.Data.Name = Name;
EmoteStream.CopyTo(b.Value.Data.Stream);
b.Value.Data.Stream.Position = 0;
}
catch (Exception ex)
{
Log.Error(ex, "Failed to download an emote");
_ = SanitizedEmoteList.Remove(b.Key);
}
}
}
if (bMessage.Stickers.Count > 0)
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.DownloadingStickers, true, new TVar("Count", bMessage.Stickers.GroupBy(x => x.Url).Select(x => x.First()).Count()));
_ = await this.RespondOrEdit(embed);
foreach (var b in bMessage.Stickers.GroupBy(x => x.Url).Select(x => x.First()))
{
var newEntry = new EmojiEntry
{
Animated = false,
Name = b.Name,
Description = b.Description,
Emoji = "🤖".UnicodeToEmoji(),
StickerFormat = b.FormatType,
EntryType = EmojiType.STICKER
};
try
{
var StickerStream = await client.GetStreamAsync(b.Url);
var NameExists = "";
var NameExistsInt = 1;
var Name = $"{b.Name}{NameExists}.png".MakeValidFileName('_');
while (SanitizedEmoteList.Any(x => x.Value.Data.Name == Name))
{
NameExistsInt++;
NameExists = $" ({NameExistsInt})";
Name = $"{newEntry.Name}{NameExists}.png".MakeValidFileName('_');
}
newEntry.Data.Name = Name;
StickerStream.CopyTo(newEntry.Data.Stream);
newEntry.Data.Stream.Position = 0;
}
catch (Exception ex)
{
Log.Error(ex, "Failed to download an emote");
_ = SanitizedEmoteList.Remove(b.Id);
}
SanitizedEmoteList.Add(b.Id, newEntry);
}
}
if (SanitizedEmoteList.Count == 0)
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.NoSuccessfulDownload, true);
_ = await this.RespondOrEdit(embed.AsError(ctx));
return;
}
var emojiText = "";
if (SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.EMOJI))
emojiText += this.GetString(this.t.Commands.Utility.EmojiStealer.Emoji);
if (SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.STICKER))
emojiText += $"{(emojiText.Length > 0 ? $" & {this.GetString(this.t.Commands.Utility.EmojiStealer.Sticker)}" : this.GetString(this.t.Commands.Utility.EmojiStealer.Sticker))}";
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.ReceivePrompt, true, new TVar("Type", emojiText));
_ = embed.AsAwaitingInput(ctx);
var IncludeStickers = false;
if (!SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.EMOJI))
IncludeStickers = true;
var IncludeStickersButton = new DiscordButtonComponent((IncludeStickers ? ButtonStyle.Success : ButtonStyle.Danger), "ToggleStickers", this.GetString(this.t.Commands.Utility.EmojiStealer.ToggleStickers), !SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.EMOJI), new DiscordComponentEmoji(DiscordEmoji.FromGuildEmote(ctx.Client, (ulong)(IncludeStickers ? 970278964755038248 : 970278964079767574))));
var AddToServerButton = new DiscordButtonComponent(ButtonStyle.Success, "AddToServer", this.GetString(this.t.Commands.Utility.EmojiStealer.AddEmojisToServer), !ctx.Member.Permissions.HasPermission(Permissions.ManageGuildExpressions), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("")));
var ZipPrivateMessageButton = new DiscordButtonComponent(ButtonStyle.Primary, "ZipPrivateMessage", this.GetString(this.t.Commands.Utility.EmojiStealer.DirectMessageZip), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🖥")));
var SinglePrivateMessageButton = new DiscordButtonComponent(ButtonStyle.Primary, "SinglePrivateMessage", this.GetString(this.t.Commands.Utility.EmojiStealer.DirectMessageSingle), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("📱")));
var SendHereButton = new DiscordButtonComponent(ButtonStyle.Secondary, "SendHere", this.GetString(this.t.Commands.Utility.EmojiStealer.CurrentChatZip), !(ctx.Member.Permissions.HasPermission(Permissions.AttachFiles)), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("💬")));
var builder = new DiscordMessageBuilder().WithEmbed(embed);
if (SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.STICKER))
_ = builder.AddComponents(IncludeStickersButton);
_ = builder.AddComponents(new List<DiscordComponent> { AddToServerButton, ZipPrivateMessageButton, SinglePrivateMessageButton, SendHereButton });
_ = await this.RespondOrEdit(builder);
CancellationTokenSource cancellationTokenSource = new();
ctx.Client.ComponentInteractionCreated += RunInteraction;
_ = Task.Delay(60000, cancellationTokenSource.Token).ContinueWith(x =>
{
if (x.IsCompletedSuccessfully)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
FinishedInteraction = true;
this.ModifyToTimedOut();
}
});
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
{
_ = Task.Run(async () =>
{
try
{
if (e.Message?.Id == ctx.ResponseMessage.Id && e.User.Id == ctx.User.Id)
{
cancellationTokenSource.Cancel();
cancellationTokenSource = new();
_ = Task.Delay(60000, cancellationTokenSource.Token).ContinueWith(x =>
{
if (x.IsCompletedSuccessfully)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
this.ModifyToTimedOut();
}
});
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (e.GetCustomId() == AddToServerButton.CustomId)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
cancellationTokenSource.Cancel();
if (!ctx.Member.Permissions.HasPermission(Permissions.ManageGuildExpressions))
{
this.SendPermissionError(Permissions.ManageGuildExpressions);
return;
}
if (!ctx.CurrentMember.Permissions.HasPermission(Permissions.ManageGuildExpressions))
{
this.SendOwnPermissionError(Permissions.ManageGuildExpressions);
return;
}
var DiscordWarning = false;
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.AddEmojisToServerLoading, true,
new TVar("Min", 0),
new TVar("Max", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count())));
_ = embed.AsLoading(ctx);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed));
for (var i = 0; i < SanitizedEmoteList.Count; i++)
{
try
{
Task task;
switch (SanitizedEmoteList.ElementAt(i).Value.EntryType)
{
case EmojiType.STICKER:
{
var sticker = SanitizedEmoteList.ElementAt(i).Value;
task = ctx.Guild.CreateStickerAsync(sticker.Name, sticker.Description ?? sticker.Name, sticker.Emoji, sticker.Data.Stream, sticker.StickerFormat);
var WaitSeconds = 0;
while (task.Status == TaskStatus.WaitingForActivation)
{
WaitSeconds++;
if (WaitSeconds > 10 && !DiscordWarning)
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.AddStickersToServerLoading, true,
new TVar("Min", 0),
new TVar("Max", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count()))) +
$"\n{this.GetString(this.t.Commands.Utility.EmojiStealer.AddToServerLoadingNotice)}";
_ = await this.RespondOrEdit(embed);
DiscordWarning = true;
}
await Task.Delay(1000);
}
if (task.IsFaulted)
throw task.Exception.InnerException;
break;
}
case EmojiType.EMOJI:
{
var emoji = SanitizedEmoteList.ElementAt(i);
task = ctx.Guild.CreateEmojiAsync(SanitizedEmoteList.ElementAt(i).Value.Name, emoji.Value.Data.Stream);
var WaitSeconds = 0;
while (task.Status == TaskStatus.WaitingForActivation)
{
WaitSeconds++;
if (WaitSeconds > 10 && !DiscordWarning)
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.AddEmojisToServerLoading, true,
new TVar("Min", 0),
new TVar("Max", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count()))) +
$"\n{this.GetString(this.t.Commands.Utility.EmojiStealer.AddToServerLoadingNotice)}";
_ = await this.RespondOrEdit(embed);
DiscordWarning = true;
}
await Task.Delay(1000);
}
if (task.IsFaulted)
throw task.Exception.InnerException;
break;
}
default:
throw new NotImplementedException();
}
}
catch (DisCatSharp.Exceptions.BadRequestException ex)
{
var regex = Regex.Match(ex.WebResponse.Response.Replace("\\", ""), "((\"code\": )(\\d*))");
if (regex.Groups[3].Value == "30008")
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.NoMoreRoom, true, new TVar("Count", i));
_ = embed.AsError(ctx);
_ = await this.RespondOrEdit(embed);
return;
}
else
throw;
}
}
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SuccessAdded, true,
new TVar("Count", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count())));
_ = embed.AsSuccess(ctx);
_ = await this.RespondOrEdit(embed);
return;
}
else if (e.GetCustomId() == SinglePrivateMessageButton.CustomId)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
cancellationTokenSource.Cancel();
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SendingDm, true, new TVar("Type", emojiText));
_ = embed.AsLoading(ctx);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed));
try
{
var totalCount = IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count();
for (var i = 0; i < SanitizedEmoteList.Count; i++)
{
if (!IncludeStickers)
if (SanitizedEmoteList.ElementAt(i).Value.EntryType != EmojiType.EMOJI)
continue;
var current = SanitizedEmoteList.ElementAt(i);
_ = current.Value.Data.Stream.Seek(0, SeekOrigin.Begin);
var currentFilename = $"{current.Value.Name}.{(current.Value.Animated == true ? "gif" : "png")}";
_ = await ctx.User.SendMessageAsync(new DiscordMessageBuilder()
.WithContent($"`{i + 1}/{totalCount}` `{currentFilename}`")
.WithFile($"{currentFilename}", current.Value.Data.Stream));
await Task.Delay(1000);
}
_ = await ctx.User.SendMessageAsync(new DiscordMessageBuilder().WithContent(this.GetString(this.t.Commands.Utility.EmojiStealer.SuccessDm, new TVar("Type", emojiText))));
}
catch (DisCatSharp.Exceptions.UnauthorizedException)
{
this.SendDmError();
return;
}
catch (Exception)
{
throw;
}
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SuccessDmMain, true,
new TVar("Count", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count())),
new TVar("Type", emojiText));
_ = await this.RespondOrEdit(embed.AsSuccess(ctx));
return;
}
else if (e.GetCustomId() == ZipPrivateMessageButton.CustomId || e.GetCustomId() == SendHereButton.CustomId)
{
ctx.Client.ComponentInteractionCreated -= RunInteraction;
cancellationTokenSource.Cancel();
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.PreparingZip, true);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed.AsLoading(ctx)));
using (var archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create, true))
{
for (var i = 0; i < SanitizedEmoteList.Count; i++)
{
if (!IncludeStickers)
if (SanitizedEmoteList.ElementAt(i).Value.EntryType != EmojiType.EMOJI)
continue;
var current = SanitizedEmoteList.ElementAt(i);
var newEntry = archive.CreateEntry(current.Value.Data.Name);
using (var entryStream = newEntry.Open())
await current.Value.Data.Stream.CopyToAsync(entryStream);
}
}
_ = zipFileStream.Seek(0, SeekOrigin.Begin);
if (e.GetCustomId() == ZipPrivateMessageButton.CustomId)
{
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SendingZipDm, true);
_ = await this.RespondOrEdit(embed);
try
{
_ = zipFileStream.Seek(0, SeekOrigin.Begin);
_ = await ctx.User.SendMessageAsync(new DiscordMessageBuilder().WithFile($"Emojis.zip", zipFileStream).WithContent(this.GetString(this.t.Commands.Utility.EmojiStealer.SuccessDm, new TVar("Type", emojiText))));
}
catch (DisCatSharp.Exceptions.UnauthorizedException)
{
this.SendDmError();
return;
}
catch (Exception)
{
throw;
}
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SuccessDmMain, true,
new TVar("Count", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count())),
new TVar("Type", emojiText));
_ = await this.RespondOrEdit(embed.AsSuccess(ctx));
}
else if (e.GetCustomId() == SendHereButton.CustomId)
{
if (!ctx.Member.Permissions.HasPermission(Permissions.AttachFiles))
{
this.SendPermissionError(Permissions.AttachFiles);
return;
}
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SendingZipChat, true);
_ = await this.RespondOrEdit(embed);
embed.Description = this.GetString(this.t.Commands.Utility.EmojiStealer.SuccessChat, true,
new TVar("Count", (IncludeStickers ? SanitizedEmoteList.Count : SanitizedEmoteList.Where(x => x.Value.EntryType == EmojiType.EMOJI).Count())),
new TVar("Type", emojiText));
_ = zipFileStream.Seek(0, SeekOrigin.Begin);
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithFile($"Emotes.zip", zipFileStream).WithEmbed(embed.AsSuccess(ctx)));
}
return;
}
else if (e.GetCustomId() == IncludeStickersButton.CustomId)
{
IncludeStickers = !IncludeStickers;
if (!IncludeStickers)
{
if (!SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.EMOJI))
IncludeStickers = true;
}
IncludeStickersButton = new DiscordButtonComponent((IncludeStickers ? ButtonStyle.Success : ButtonStyle.Danger), "ToggleStickers", this.GetString(this.t.Commands.Utility.EmojiStealer.ToggleStickers), !SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.EMOJI), new DiscordComponentEmoji(DiscordEmoji.FromGuildEmote(ctx.Client, (ulong)(IncludeStickers ? 970278964755038248 : 970278964079767574))));
AddToServerButton = new DiscordButtonComponent(ButtonStyle.Success, "AddToServer", (IncludeStickers ? this.GetString(this.t.Commands.Utility.EmojiStealer.AddEmojisAndStickerToServer) : this.GetString(this.t.Commands.Utility.EmojiStealer.AddEmojisToServer)), !ctx.Member.Permissions.HasPermission(Permissions.ManageGuildExpressions), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("")));
var builder = new DiscordMessageBuilder().WithEmbed(embed);
if (SanitizedEmoteList.Any(x => x.Value.EntryType == EmojiType.STICKER))
_ = builder.AddComponents(IncludeStickersButton);
_ = builder.AddComponents(new List<DiscordComponent> { AddToServerButton, ZipPrivateMessageButton, SinglePrivateMessageButton, SendHereButton });
_ = await this.RespondOrEdit(builder);
}
}
}
finally
{
if (e.GetCustomId() != IncludeStickersButton.CustomId)
FinishedInteraction = true;
}
}).Add(ctx.Bot, ctx);
}
}
finally
{
while (!FinishedInteraction)
await Task.Delay(1000);
try
{ await zipFileStream.DisposeAsync(); }
catch { }
foreach (var b in SanitizedEmoteList)
try
{ await b.Value.Data.Stream.DisposeAsync(); }
catch { }
}
});
}
}

View file

@ -0,0 +1,231 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class GuildInfoCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var CommandKey = this.t.Commands.Utility.GuildInfo;
var rawGuildId = (string?)arguments["guild"];
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
var guildId = rawGuildId?.ToUInt64() ?? ctx.Guild.Id;
if (guildId == 0)
guildId = ctx.Guild.Id;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(CommandKey.Fetching, true)).AsLoading(ctx));
_ = Directory.CreateDirectory("cache");
try
{
var guild = await ctx.Client.GetGuildAsync(guildId);
//var imageHash = guild.DiscoverySplashHash ?? guild.SplashHash ?? "";
//var imageUrl = guild.DiscoverySplashUrl ?? guild.SplashUrl ?? "";
//if (!File.Exists($"cache/{imageHash}") && !imageHash.IsNullOrWhiteSpace())
//{
// var fileExtension = imageUrl[..(imageUrl.LastIndexOf('?'))];
// fileExtension = fileExtension[(fileExtension.LastIndexOf(".") + 1)..];
// using (var outputStream = new MemoryStream())
// {
// var arguments = FFMpegArguments
// .FromPipeInput(new StreamPipeSource(await new HttpClient().GetStreamAsync(imageUrl)))
// .OutputToPipe(new StreamPipeSink(outputStream), x => x
// .ForceFormat("image2")
// .WithVideoCodec(fileExtension)
// .WithArgument(new CustomArgument("-vf scale=2048:256:force_original_aspect_ratio=decrease,pad=2048:256:-1:-1")));
// _ = await arguments.ProcessAsynchronously();
// using (var file = new FileStream($"cache/{imageHash}", FileMode.Create, FileAccess.Write))
// {
// outputStream.Position = 0;
// await outputStream.CopyToAsync(file);
// }
// }
//}
var embed = new DiscordEmbedBuilder
{
Title = guild.Name,
Thumbnail = new DiscordEmbedBuilder.EmbedThumbnail
{
Url = guild.IconUrl ?? AuditLogIcons.QuestionMark,
},
//ImageUrl = $"attachment://banner.png",
Description = $"{(guild.Description.IsNullOrWhiteSpace() ? "" : $"{guild.Description}\n\n")}",
}.AsInfo(ctx);
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.MemberTitle), $"👥 `{guild.Members.Count}` **{this.GetString(CommandKey.MemberTitle)}**\n" +
$"🟢 `{guild.Members.Where(x => (x.Value?.Presence?.Status ?? UserStatus.Offline) != UserStatus.Offline).Count()}` **{this.GetString(CommandKey.OnlineMembers)}**\n" +
$"🛑 `{guild.MaxMembers}` **{this.GetString(CommandKey.MaxMembers)}**\n"));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.GuildTitle), $"👤 **{this.GetString(CommandKey.Owner)}**: {guild.Owner.Mention} (`{guild.Owner.GetUsernameWithIdentifier()}`)\n" +
$"🕒 **{this.GetString(CommandKey.Creation)}**: {guild.CreationTimestamp.ToTimestamp(TimestampFormat.LongDateTime)} ({guild.CreationTimestamp.ToTimestamp()})\n" +
$"🗺 **{this.GetString(CommandKey.Locale)}**: `{guild.PreferredLocale}`\n" +
$"🔮 `{guild.PremiumSubscriptionCount}` **{this.GetString(CommandKey.Boosts)} (`{guild.PremiumTier switch { PremiumTier.None => this.GetString(CommandKey.BoostsNone), PremiumTier.TierOne => this.GetString(CommandKey.BoostsTierOne), PremiumTier.TierTwo => this.GetString(CommandKey.BoostsTierTwo), PremiumTier.TierThree => this.GetString(CommandKey.BoostsTierThree), PremiumTier.Unknown => "?", _ => "?", }}`)**\n\n" +
$"😀 `{guild.Emojis.Count}` **{this.GetString(this.t.Commands.Utility.EmojiStealer.Emoji)}**\n" +
$"🖼 `{guild.Stickers.Count}` **{this.GetString(this.t.Commands.Utility.EmojiStealer.Sticker)}**\n\n" +
$"{(guild.WidgetEnabled ?? false).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.Widget)}**\n" +
$"{(guild.IsCommunity).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.Community)}**", true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.Security), $"{(guild.MfaLevel == MfaLevel.Enabled).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.MultiFactor)}**\n" +
$"{(guild.Features.Features.Any(x => x == GuildFeaturesEnum.HasMembershipScreeningEnabled)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.Screening)}**\n" +
$"{(guild.Features.Features.Any(x => x == GuildFeaturesEnum.HasWelcomeScreenEnabled)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.WelcomeScreen)}**\n" +
$"🚪 **{this.GetString(CommandKey.Verification)}**: `{guild.VerificationLevel switch { VerificationLevel.None => this.GetString(CommandKey.VerificationNone), VerificationLevel.Low => this.GetString(CommandKey.VerificationLow), VerificationLevel.Medium => this.GetString(CommandKey.VerificationMedium), VerificationLevel.High => this.GetString(CommandKey.VerificationHigh), VerificationLevel.Highest => this.GetString(CommandKey.VerificationHighest), _ => "?", }}`\n" +
$"🔍 **{this.GetString(CommandKey.ExplicitContent)}**: `{guild.ExplicitContentFilter switch { ExplicitContentFilter.Disabled => this.GetString(CommandKey.ExplicitContentNone), ExplicitContentFilter.MembersWithoutRoles => this.GetString(CommandKey.ExplicitContentNoRoles), ExplicitContentFilter.AllMembers => this.GetString(CommandKey.ExplicitContentEveryone), _ => "?", }}`\n" +
$"⚠ **{this.GetString(CommandKey.Nsfw)}**: `{guild.NsfwLevel switch { NsfwLevel.Default => this.GetString(CommandKey.NsfwNoRating), NsfwLevel.Explicit => this.GetString(CommandKey.NsfwExplicit), NsfwLevel.Safe => this.GetString(CommandKey.NsfwSafe), NsfwLevel.Age_Restricted => this.GetString(CommandKey.NsfwQuestionable), _ => "?", }}`\n" +
$"💬 **{this.GetString(CommandKey.DefaultNotifications)}**: `{guild.DefaultMessageNotifications switch { DefaultMessageNotifications.AllMessages => this.GetString(CommandKey.DefaultNotificationsAll), DefaultMessageNotifications.MentionsOnly => this.GetString(CommandKey.DefaultNotificationsMentions), _ => "?", }}`\n", true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.SpecialChannels), $"📑 **{this.GetString(CommandKey.Rules)}**: {guild.RulesChannel?.Mention ?? this.GetString(this.t.Common.Off, true)}\n" +
$"📰 **{this.GetString(CommandKey.CommunityUpdates)}**: {guild.PublicUpdatesChannel?.Mention ?? this.GetString(this.t.Common.Off, true)}\n\n" +
$"⌨ **{this.GetString(CommandKey.InactiveChannel)}**: {guild.AfkChannel?.Mention ?? this.GetString(this.t.Common.Off, true)}\n" +
$"> **{this.GetString(CommandKey.InactiveTimeout)}**: `{((long)guild.AfkTimeout).GetHumanReadable()}`\n\n" +
$"🤖 **{this.GetString(CommandKey.SystemMessages)}**: {guild.SystemChannel?.Mention ?? this.GetString(this.t.Common.Off, true)}\n" +
$"> {(!guild.SystemChannelFlags.HasSystemChannelFlag(SystemChannelFlags.SuppressJoinNotifications)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.SystemMessagesWelcome)}**\n" +
$"> {(!guild.SystemChannelFlags.HasSystemChannelFlag(SystemChannelFlags.SuppressJoinNotificationReplies)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.SystemMessagesWelcomeStickers)}**\n" +
$"> {(!guild.SystemChannelFlags.HasSystemChannelFlag(SystemChannelFlags.SuppressPremiumSubscriptions)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.SystemMessagesBoost)}**\n" +
$"> {(!guild.SystemChannelFlags.HasSystemChannelFlag(SystemChannelFlags.SuppressRoleSubbscriptionPurchaseNotification)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.SystemMessagesRole)}**\n" +
$"> {(!guild.SystemChannelFlags.HasSystemChannelFlag(SystemChannelFlags.SuppressRoleSubbscriptionPurchaseNotificationReplies)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.SystemMessagesRoleSticker)}**\n" +
$"> {(!guild.SystemChannelFlags.HasSystemChannelFlag(SystemChannelFlags.SuppressGuildReminderNotifications)).ToPillEmote(ctx.Bot)} **{this.GetString(CommandKey.SystemMessagesSetupTips)}**\n"));
if (guild.RawFeatures.Count > 0)
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.GuildFeatures), $"{string.Join(", ", guild.RawFeatures.Select(x => $"`{string.Join(" ", x.Replace("_", " ").ToLower().Split(" ").Select(x => x.FirstLetterToUpper()))}`"))}"));
var builder = new DiscordMessageBuilder().WithEmbed(embed);
if (!guild.VanityUrlCode.IsNullOrWhiteSpace())
_ = builder.AddComponents(new DiscordLinkButtonComponent($"https://discord.gg/{guild.VanityUrlCode}", this.GetString(CommandKey.JoinServer), false, DiscordEmoji.FromUnicode("🔗").ToComponent()));
_ = await this.RespondOrEdit(new DiscordMessageBuilder()
.WithEmbed(embed)
.AddComponents(new DiscordLinkButtonComponent(guild.BannerUrl ?? "https://discord.gg", this.GetString(CommandKey.Banner), guild.BannerUrl is null),
new DiscordLinkButtonComponent(guild.SplashUrl ?? "https://discord.gg", this.GetString(CommandKey.Splash), guild.BannerUrl is null),
new DiscordLinkButtonComponent(guild.DiscoverySplashUrl ?? "https://discord.gg", this.GetString(CommandKey.DiscoverySplash), guild.BannerUrl is null),
new DiscordLinkButtonComponent(guild.HomeHeaderUrl ?? "https://discord.gg", this.GetString(CommandKey.HomeHeader), guild.HomeHeaderUrl is null)));
//if (imageHash.IsNullOrWhiteSpace())
// _ = await this.RespondOrEdit(embed);
//else
//{
// using (var file = new FileStream($"cache/{imageHash}", FileMode.Open, FileAccess.Read))
// {
// _ = await this.RespondOrEdit(new DiscordMessageBuilder()
// .WithEmbed(embed)
// .WithFile("banner.png", file));
// }
//}
}
catch (Exception ex1) when (ex1 is DisCatSharp.Exceptions.UnauthorizedException or
DisCatSharp.Exceptions.NotFoundException)
{
HttpClient client = new();
try
{
var preview = await ctx.Client.GetGuildPreviewAsync(guildId);
var embed = new DiscordEmbedBuilder
{
Title = preview.Name,
Thumbnail = new DiscordEmbedBuilder.EmbedThumbnail
{
Url = preview.IconUrl ?? AuditLogIcons.QuestionMark,
},
//ImageUrl = preview.SplashUrl ?? preview.DiscoverySplashUrl ?? "",
Description = preview.Description ?? "",
}.AsInfo(ctx, "", this.GetString(CommandKey.GuildPreviewNotice));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.MemberTitle), $"👥 `{preview.ApproximateMemberCount}` **{this.GetString(CommandKey.MemberTitle)}**\n" +
$"🟢 `{preview.ApproximatePresenceCount}` **{this.GetString(CommandKey.OnlineMembers)}**\n"));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.GuildTitle), $"🕒 **{this.GetString(CommandKey.Creation)}**: {preview.CreationTimestamp.ToTimestamp(TimestampFormat.LongDateTime)} ({preview.CreationTimestamp.ToTimestamp()})\n" +
$"😀 `{preview.Emojis.Count}` **{this.GetString(this.t.Commands.Utility.EmojiStealer.Emoji)}**\n" +
$"🖼 `{preview.Stickers.Count}` **{this.GetString(this.t.Commands.Utility.EmojiStealer.Sticker)}**\n", true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.GuildFeatures), $"{string.Join(", ", preview.Features.Select(x => $"`{string.Join(" ", x.Replace("_", " ").ToLower().Split(" ").Select(x => x.FirstLetterToUpper()))}`"))}"));
var builder = new DiscordMessageBuilder().WithEmbed(embed);
var invite = "";
try { invite = (await ctx.Client.GetGuildWidgetAsync(guildId)).InstantInviteUrl; } catch { }
if (!invite.IsNullOrWhiteSpace())
_ = builder.AddComponents(new DiscordLinkButtonComponent(invite, this.GetString(CommandKey.JoinServer), false, DiscordEmoji.FromUnicode("🔗").ToComponent()));
_ = await this.RespondOrEdit(builder);
}
catch (Exception ex2) when (ex2 is DisCatSharp.Exceptions.UnauthorizedException or
DisCatSharp.Exceptions.NotFoundException)
{
try
{
var widget = await ctx.Client.GetGuildWidgetAsync(guildId);
var embed = new DiscordEmbedBuilder
{
Title = widget.Name,
}.AsInfo(ctx, "", this.GetString(CommandKey.GuildWidgetNotice));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.MemberTitle), $"🟢 `{widget.PresenceCount}` **{this.GetString(CommandKey.OnlineMembers)}**\n"));
var builder = new DiscordMessageBuilder().WithEmbed(embed);
if (!widget.InstantInviteUrl.IsNullOrWhiteSpace())
_ = builder.AddComponents(new DiscordLinkButtonComponent(widget.InstantInviteUrl, this.GetString(CommandKey.JoinServer), false, DiscordEmoji.FromUnicode("🔗").ToComponent()));
_ = await this.RespondOrEdit(builder);
}
catch (Exception)
{
try
{
var mee6 = JsonConvert.DeserializeObject<Mee6Leaderboard>(await client.GetStringAsync($"https://mee6.xyz/api/plugins/levels/leaderboard/{guildId}"));
var embed = new DiscordEmbedBuilder
{
Title = mee6.guild.name,
Thumbnail = new DiscordEmbedBuilder.EmbedThumbnail
{
Url = $"https://cdn.discordapp.com/icons/{guildId}/{mee6.guild.icon}.webp?size=96",
},
//ImageUrl = mee6.banner_url ?? "",
}.AsInfo(ctx, "", this.GetString(CommandKey.Mee6Notice));
_ = embed.AddField(new DiscordEmbedField(this.GetString(CommandKey.MemberTitle), $"👥 `{mee6.players.Length}` **{this.GetString(CommandKey.MemberTitle)}**\n"));
_ = await this.RespondOrEdit(embed);
}
catch (Exception)
{
var embed = new DiscordEmbedBuilder
{
Description = this.GetString(CommandKey.NoGuildFound, true),
}.AsError(ctx);
_ = await this.RespondOrEdit(embed);
}
}
}
}
});
}
}

View file

@ -0,0 +1,220 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class HelpCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var command_filter = (string)arguments["command"];
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
List<KeyValuePair<string, string>> Commands = new();
var PrefixCommandsList = ctx.Client.GetCommandsNext().RegisteredCommands.GroupBy(x => x.Value.Name).Select(x => x.First()).ToList();
var ApplicationCommandsList = ctx.Client.GetApplicationCommands().RegisteredCommands.First(x => x.Value?.Count > 0).Value.Where(x => x.Version != 0);
foreach (var appCommand in ApplicationCommandsList
.OrderByDescending(x => x.ContainingType?.GetCustomAttribute<ModulePriorityAttribute>()?.Priority ?? 0))
{
var nspace = appCommand?.ContainingType?.Namespace ?? "";
var module = appCommand?.ContainingType?.Name?.Replace("Commands", "")?.ToLower() ?? "";
if (!nspace.Equals("ProjectMakoto.ApplicationCommands", StringComparison.InvariantCultureIgnoreCase))
module = ctx.Bot.CommandModules.FirstOrDefault(m => m.Commands.Any(cmd => cmd.Name == appCommand.Name))?.Name ?? ctx.Bot.PluginCommandModules
.FirstOrDefault(pl => pl.Value.Any(m => m.Commands.Any(cmd => cmd.Name == appCommand.Name)), default).Value?
.FirstOrDefault(m => m.Commands.Any(cmd => cmd.Name == appCommand.Name))?.Name;
if (module.IsNullOrEmpty())
continue;
switch (module)
{
case "configuration":
if (!ctx.Member.IsAdmin(ctx.Bot.status))
continue;
break;
case "debug":
if (!ctx.User.IsMaintenance(ctx.Bot.status))
continue;
break;
case "hidden":
continue;
default:
break;
}
var cmdPerm = appCommand.DefaultMemberPermissions ?? null;
if (cmdPerm is not null && ctx.Member.Permissions.HasPermission(cmdPerm.Value))
continue;
try
{
var commandKey = this.t.CommandList.FirstOrDefault(localized => localized.Names.Any(x => x.Value == appCommand.Name), null);
string commandName;
string commandDescription;
string commandUsage;
if (commandKey is not null)
{
commandName = this.GetString(commandKey.Names);
commandDescription = this.GetString(commandKey.Descriptions);
commandUsage = string.Join(" ", commandKey.Options?.Select(x => $"<{this.GetString(x.Names).FirstLetterToUpper()}>") ?? new List<string>());
}
else
{
commandName = appCommand.Name;
commandDescription = appCommand.Description;
commandUsage = string.Join(" ", appCommand.Options?.Select(x => $"<{x.Name.FirstLetterToUpper()}>") ?? new List<string>());
}
if (command_filter is not null)
if (!(commandKey?.Names.Any(x => x.Value.Contains(command_filter, StringComparison.InvariantCultureIgnoreCase)) ?? false) && !commandName.Contains(command_filter, StringComparison.InvariantCultureIgnoreCase))
continue;
string commandMention;
if (appCommand.Options?.Any(x => x.Type == ApplicationCommandOptionType.SubCommand) ?? false)
commandMention = $"`/{commandName}`";
else commandMention = appCommand.Type != ApplicationCommandType.ChatInput ? $"`{commandName}`" : appCommand.Mention;
Command? prefixCommand;
if (PrefixCommandsList.Any(x => x.Value.Name.Equals(appCommand.Name, StringComparison.CurrentCultureIgnoreCase)))
prefixCommand = PrefixCommandsList.First(x => x.Value.Name.Equals(appCommand.Name, StringComparison.CurrentCultureIgnoreCase)).Value;
else prefixCommand = appCommand.CustomAttributes.Any(x => x is PrefixCommandAlternativeAttribute)
? PrefixCommandsList
.First(x => x.Value.Name.ToLower() == ((PrefixCommandAlternativeAttribute)appCommand.CustomAttributes
.First(x => x is PrefixCommandAlternativeAttribute)).PrefixCommand.ToLower().TruncateAt(' ')).Value
: null;
var commandModuleName = module.ToLower() switch
{
"utility" => this.GetString(this.t.Commands.ModuleNames.Utility),
"social" => this.GetString(this.t.Commands.ModuleNames.Social),
"music" => this.GetString(this.t.Commands.ModuleNames.Music),
"moderation" => this.GetString(this.t.Commands.ModuleNames.Moderation),
"configuration" => this.GetString(this.t.Commands.ModuleNames.Configuration),
_ => module.FirstLetterToUpper(),
};
var TypeEmoji = appCommand.Type switch
{
ApplicationCommandType.ChatInput => EmojiTemplates.GetSlashCommand(ctx.Bot),
ApplicationCommandType.Message => EmojiTemplates.GetMessageCommand(ctx.Bot),
ApplicationCommandType.User => EmojiTemplates.GetUserCommand(ctx.Bot),
_ => throw new NotImplementedException(),
};
Commands.Add(new KeyValuePair<string, string>($"{commandModuleName}",
$"{TypeEmoji}{((prefixCommand is null) ? EmojiTemplates.GetPrefixCommandDisabled(ctx.Bot) : EmojiTemplates.GetPrefixCommandEnabled(ctx.Bot))} {commandMention}{(commandUsage.IsNullOrWhiteSpace() ? "" : $"`{commandUsage}`")}{(commandDescription.IsNullOrWhiteSpace() ? "" : $" - _{commandDescription}_")}"));
foreach (var subCmd in appCommand.Options?.Where(x => x.Type == ApplicationCommandOptionType.SubCommand) ?? new List<DiscordApplicationCommandOption>())
{
var subKey = commandKey?.Commands.FirstOrDefault(localized => localized.Names.Any(x => x.Value == subCmd.Name), null);
string subName;
string subDescription;
string subUsage;
if (subKey is not null)
{
subName = $"{commandName} {this.GetString(subKey.Names)}";
subDescription = this.GetString(subKey.Descriptions);
subUsage = string.Join(" ", subKey.Options?.Select(x => $"<{this.GetString(x.Names).FirstLetterToUpper()}>") ?? new List<string>());
}
else
{
subName = $"{commandName} {subCmd.Name}";
subDescription = subCmd.Description;
subUsage = string.Join(" ", subCmd.Options?.Select(x => $"<{x.Name.FirstLetterToUpper()}>") ?? new List<string>());
}
Command? subPrefixCommand = null;
if (prefixCommand is CommandGroup group)
subPrefixCommand = group.Children.FirstOrDefault(x => x.Name == subCmd.Name);
Commands.Add(new KeyValuePair<string, string>($"{commandModuleName}",
$"{EmojiTemplates.GetInVisible(ctx.Bot)}{TypeEmoji}{(subPrefixCommand is null ? EmojiTemplates.GetPrefixCommandDisabled(ctx.Bot) : EmojiTemplates.GetPrefixCommandEnabled(ctx.Bot))} `/{subName}`{(subUsage.IsNullOrWhiteSpace() ? "" : $"`{subUsage}`")}{(subDescription.IsNullOrWhiteSpace() ? "" : $" - _{subDescription}_")}"));
}
}
catch (Exception ex)
{
Log.Error(ex.AddData("Command", appCommand), "Failed to generate help");
}
}
if (Commands.Count == 0)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder()
.WithDescription(this.GetString(this.t.Commands.Utility.Help.MissingCommand, true))
.AsError(ctx));
return;
}
var Fields = Commands.PrepareEmbedFields();
var discordEmbeds = Fields.PrepareEmbeds(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.Help.Disclaimer)).AsInfo(ctx), true);
var Page = 0;
while (true)
{
var PreviousButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Common.PreviousPage), (Page <= 0), DiscordEmoji.FromUnicode("◀").ToComponent());
var NextButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Common.NextPage), (Page >= discordEmbeds.Count - 1), DiscordEmoji.FromUnicode("▶").ToComponent());
var builder = new DiscordMessageBuilder().WithEmbed(discordEmbeds.ElementAt(Page));
if (!PreviousButton.Disabled || !NextButton.Disabled)
_ = builder.AddComponents(PreviousButton, NextButton);
_ = builder.AddComponents(MessageComponents.GetCancelButton(ctx.DbUser, ctx.Bot));
_ = await this.RespondOrEdit(builder);
if (PreviousButton.Disabled && NextButton.Disabled)
return;
var Menu = await ctx.WaitForButtonAsync();
if (Menu.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (Menu.GetCustomId() == PreviousButton.CustomId)
{
Page--;
continue;
}
else if (Menu.GetCustomId() == NextButton.CustomId)
{
Page++;
continue;
}
else
{
this.DeleteOrInvalidate();
return;
}
}
});
}
}

View file

@ -0,0 +1,108 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class LanguageCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder()
{
Description = $"{this.GetString(this.t.Commands.Utility.Language.Disclaimer, true)}\n" +
$"{this.GetString(this.t.Commands.Utility.Language.Response, true)}: `{(ctx.DbUser.OverrideLocale.IsNullOrWhiteSpace() ? (ctx.DbUser.CurrentLocale.IsNullOrWhiteSpace() ? "en (Default)" : $"{ctx.DbUser.CurrentLocale} (Discord)") : $"{ctx.DbUser.OverrideLocale} (Override)")}`"
});
List<DiscordStringSelectComponentOption> options = new();
List<DiscordStringSelectComponentOption> newOptions = new();
newOptions.Add(new DiscordStringSelectComponentOption("Disable Override", "_", this.GetString(this.t.Commands.Utility.Language.DisableOverride), false, DiscordEmoji.FromUnicode("❌").ToComponent()));
options.Add(new DiscordStringSelectComponentOption("English", "en", "English"));
options.Add(new DiscordStringSelectComponentOption("German", "de", "Deutsch"));
options.Add(new DiscordStringSelectComponentOption("Indonesian", "id", "Bahasa Indonesia"));
options.Add(new DiscordStringSelectComponentOption("Danish", "da", "Dansk"));
options.Add(new DiscordStringSelectComponentOption("Spanish", "es-ES", "Español"));
options.Add(new DiscordStringSelectComponentOption("French", "fr", "Français"));
options.Add(new DiscordStringSelectComponentOption("Croatian", "hr", "Hrvatski"));
options.Add(new DiscordStringSelectComponentOption("Italian", "it", "Italiano"));
options.Add(new DiscordStringSelectComponentOption("Lithuanian", "lt", "Lietuviškai"));
options.Add(new DiscordStringSelectComponentOption("Hungarian", "hu", "Magyar"));
options.Add(new DiscordStringSelectComponentOption("Dutch", "nl", "Nederlands"));
options.Add(new DiscordStringSelectComponentOption("Norwegian", "no", "Norsk"));
options.Add(new DiscordStringSelectComponentOption("Polish", "pl", "Polski"));
options.Add(new DiscordStringSelectComponentOption("Portuguese, Brazilian", "pt-BR", "Português do Brasil"));
options.Add(new DiscordStringSelectComponentOption("Romanian, Romania", "ro", "Română"));
options.Add(new DiscordStringSelectComponentOption("Finnish", "fi", "Suomi"));
options.Add(new DiscordStringSelectComponentOption("Swedish", "sv-SE", "Svenska"));
options.Add(new DiscordStringSelectComponentOption("Vietnamese", "vi", "Tiếng Việt"));
options.Add(new DiscordStringSelectComponentOption("Turkish", "tr", "Türkçe"));
options.Add(new DiscordStringSelectComponentOption("Czech", "cs", "Čeština"));
options.Add(new DiscordStringSelectComponentOption("Greek", "el", "Ελληνικά"));
options.Add(new DiscordStringSelectComponentOption("Bulgarian", "bg", "български"));
options.Add(new DiscordStringSelectComponentOption("Russian", "ru", "Pусский"));
options.Add(new DiscordStringSelectComponentOption("Ukrainian", "uk", "Українська"));
options.Add(new DiscordStringSelectComponentOption("Hindi", "hi", "हिन्दी"));
options.Add(new DiscordStringSelectComponentOption("Thai", "th", "ไทย"));
options.Add(new DiscordStringSelectComponentOption("Chinese, China", "zh-CN", "中文"));
options.Add(new DiscordStringSelectComponentOption("Japanese", "ja", "日本語"));
options.Add(new DiscordStringSelectComponentOption("Chinese, Taiwan", "zh-TW", "繁體中文"));
options.Add(new DiscordStringSelectComponentOption("Korean", "ko", "한국어"));
foreach (var b in options)
if (this.t.Progress.TryGetValue(b.Value, out var value))
{
var perc = (value / (decimal)this.t.Progress["en"] * 100);
DiscordComponentEmoji emoji = null;
if (perc >= 100)
emoji = DiscordEmoji.FromUnicode("🟢").ToComponent();
else emoji = perc >= 85 ? DiscordEmoji.FromUnicode("🟡").ToComponent() : DiscordEmoji.FromUnicode("🔴").ToComponent();
newOptions.Add(new DiscordStringSelectComponentOption(b.Label, b.Value, b.Description.Insert(0, $"{perc.ToString("N1", CultureInfo.CreateSpecificCulture("en-US"))}% | "), false, emoji));
}
var SelectionResult = await this.PromptCustomSelection(newOptions, this.GetString(this.t.Commands.Utility.Language.Selector));
if (SelectionResult.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
else if (SelectionResult.Cancelled)
{
this.DeleteOrInvalidate();
return;
}
else if (SelectionResult.Errored)
{
throw SelectionResult.Exception;
}
switch (SelectionResult.Result)
{
case "_":
{
ctx.DbUser.OverrideLocale = null;
break;
}
default:
{
ctx.DbUser.OverrideLocale = SelectionResult.Result;
break;
}
}
await this.ExecuteCommand(ctx, arguments);
return;
});
}
}

View file

@ -0,0 +1,106 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class LeaderboardCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var ShowAmount = (int)arguments["amount"];
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
if (!ctx.DbGuild.Experience.UseExperience)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Leaderboard.Disabled, true,
new TVar("Command", $"{ctx.Prefix}experiencesettings config"))
}.AsError(ctx, this.GetString(this.t.Commands.Utility.Leaderboard.Title)));
return;
}
if (ShowAmount is > 50 or < 3)
{
this.SendSyntaxError();
return;
}
var embed = new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Leaderboard.Fetching, true),
}.AsLoading(ctx, this.GetString(this.t.Commands.Utility.Leaderboard.Title));
_ = await this.RespondOrEdit(embed: embed);
var count = 0;
var currentuserplacement = 0;
foreach (var b in ctx.DbGuild.Members.Fetch().OrderByDescending(x => x.Value.Experience.Points))
{
currentuserplacement++;
if (b.Key == ctx.User.Id)
break;
}
var members = await ctx.Guild.GetAllMembersAsync();
List<KeyValuePair<string, string>> Board = new();
foreach (var b in ctx.DbGuild.Members.Fetch().OrderByDescending(x => x.Value.Experience.Points))
{
try
{
if (!members.Any(x => x.Id == b.Key))
continue;
var bMember = members.First(x => x.Id == b.Key);
if (bMember is null)
continue;
if (bMember.IsBot)
continue;
if (b.Value.Experience.Points <= 1)
break;
count++;
Board.Add(new KeyValuePair<string, string>("󠂪 󠂪 ", $"**{count.ToEmotes()}**. <@{b.Key}> `{bMember.GetUsernameWithIdentifier()}` ({this.GetString(this.t.Commands.Utility.Leaderboard.Level, true, new TVar("Level", b.Value.Experience.Level), new TVar("Points", b.Value.Experience.Points))}"));
if (count >= ShowAmount)
break;
}
catch { }
}
var fields = Board.PrepareEmbedFields();
foreach (var field in fields)
_ = embed.AddField(new DiscordEmbedField(field.Key, field.Value));
if (count != 0)
{
embed.Author.IconUrl = ctx.Guild.IconUrl;
embed.Description = this.GetString(this.t.Commands.Utility.Leaderboard.Placement, new TVar("Placement", currentuserplacement));
_ = await this.RespondOrEdit(embed.AsInfo(ctx, this.GetString(this.t.Commands.Utility.Leaderboard.Title)));
}
else
{
embed.Description = $":no_entry_sign: {this.GetString(this.t.Commands.Utility.Leaderboard.NoPoints, true)}";
_ = await this.RespondOrEdit(embed.AsInfo(ctx, this.GetString(this.t.Commands.Utility.Leaderboard.Title)));
}
});
}
}

View file

@ -0,0 +1,49 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class RankCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var victim = (DiscordUser)arguments["user"];
if (await ctx.DbUser.Cooldown.WaitForLight(ctx))
return;
if (!ctx.DbGuild.Experience.UseExperience)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Leaderboard.Disabled, true,
new TVar("Command", $"{ctx.Prefix}experiencesettings config"))
}.AsError(ctx, this.GetString(this.t.Commands.Utility.Rank.Title)));
return;
}
victim ??= ctx.User;
victim = await victim.GetFromApiAsync();
var current = (long)Math.Floor((decimal)(ctx.DbGuild.Members[victim.Id].Experience.Points - ctx.Bot.ExperienceHandler.CalculateLevelRequirement(ctx.DbGuild.Members[victim.Id].Experience.Level - 1)));
var max = (long)Math.Floor((decimal)(ctx.Bot.ExperienceHandler.CalculateLevelRequirement(ctx.DbGuild.Members[victim.Id].Experience.Level) - ctx.Bot.ExperienceHandler.CalculateLevelRequirement(ctx.DbGuild.Members[victim.Id].Experience.Level - 1)));
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = $"{(victim.Id == ctx.User.Id ? this.GetString(this.t.Commands.Utility.Rank.Self, new TVar("Level", ctx.DbGuild.Members[victim.Id].Experience.Level.ToEmotes()), new TVar("Points", ctx.DbGuild.Members[victim.Id].Experience.Points.ToString("N0", CultureInfo.GetCultureInfo("en-US")))) : this.GetString(this.t.Commands.Utility.Rank.Other, new TVar("User", victim.Mention), new TVar("Level", ctx.DbGuild.Members[victim.Id].Experience.Level.ToEmotes()), new TVar("Points", ctx.DbGuild.Members[victim.Id].Experience.Points.ToString("N0", CultureInfo.GetCultureInfo("en-US")))))}\n\n" +
$"**{this.GetString(this.t.Commands.Utility.Rank.Progress, new TVar("Level", (ctx.DbGuild.Members[victim.Id].Experience.Level + 1).ToEmotes()))}**\n" +
$"`{Math.Floor((decimal)((decimal)((decimal)current / (decimal)max) * 100)).ToString().Replace(",", ".")}%` " +
$"`{StringTools.GenerateASCIIProgressbar(current, max, 44)}` " +
$"`{current}/{max} XP`",
}.AsInfo(ctx, this.GetString(this.t.Commands.Utility.Rank.Title)));
});
}
}

View file

@ -0,0 +1,217 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using ProjectMakoto.Entities.Users;
namespace ProjectMakoto.Commands;
internal sealed class RemindersCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
string? snoozeDescription = null;
if ((arguments?.Count ?? 0) > 0)
snoozeDescription = arguments["description"]?.ToString();
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
return;
var rem = ctx.DbUser.Reminders;
var AddButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Utility.Reminders.NewReminder), (rem.ScheduledReminders.Length >= 10), DiscordEmoji.FromUnicode("").ToComponent());
var RemoveButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Utility.Reminders.DeleteReminder), (rem.ScheduledReminders.Length <= 0), DiscordEmoji.FromUnicode("").ToComponent());
var SelectedCustomId = (snoozeDescription is null ? "" : AddButton.CustomId);
if (snoozeDescription is null)
{
_ = await this.RespondOrEdit(new DiscordMessageBuilder()
.WithEmbed(new DiscordEmbedBuilder()
.WithDescription($"{this.GetString(this.t.Commands.Utility.Reminders.Count, true, new TVar("Count", rem.ScheduledReminders.Length))}\n\n" +
$"{string.Join("\n\n", rem.ScheduledReminders.Select(x => $"> {x.Description.FullSanitize()}\n{this.GetString(this.t.Commands.Utility.Reminders.CreatedOn, new TVar("Guild", $"**{x.CreationPlace}**"))}\n{this.GetString(this.t.Commands.Utility.Reminders.DueTime, new TVar("Relative", x.DueTime.ToTimestamp()), new TVar("DateTime", x.DueTime.ToTimestamp(TimestampFormat.LongDateTime)))}").ToList())}\n\n" +
$"**⚠ {this.GetString(this.t.Commands.Utility.Reminders.Notice)}**")
.AsInfo(ctx, this.GetString(this.t.Commands.Utility.Reminders.Title)))
.AddComponents(new List<DiscordComponent> { AddButton, RemoveButton })
.AddComponents(MessageComponents.GetCancelButton(ctx.DbUser, ctx.Bot)));
var Button = await ctx.WaitForButtonAsync(TimeSpan.FromMinutes(2));
if (Button.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
_ = Button.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
SelectedCustomId = Button.GetCustomId();
}
if (SelectedCustomId == AddButton.CustomId)
{
var selectedDescription = snoozeDescription.IsNullOrWhiteSpace() ? "" : snoozeDescription;
DateTime? selectedDueDate = null;
while (true)
{
if (selectedDueDate.HasValue && (selectedDueDate.Value.Ticks < DateTime.UtcNow.Ticks || selectedDueDate.Value.GetTimespanUntil() > TimeSpan.FromDays(30 * 6)))
{
selectedDueDate = null;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.Reminders.InvalidDateTime, true)).AsError(ctx));
await Task.Delay(5000);
}
var SelectDescriptionButton = new DiscordButtonComponent((selectedDescription.IsNullOrWhiteSpace() ? ButtonStyle.Primary : ButtonStyle.Secondary), Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Utility.Reminders.SetDescription), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✏")));
var SelectDueDateButton = new DiscordButtonComponent((selectedDueDate is null ? ButtonStyle.Primary : ButtonStyle.Secondary), Guid.NewGuid().ToString(), this.GetString(this.t.Commands.Utility.Reminders.SetDateTime), (selectedDescription is null), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("🕒")));
var Finish = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Common.Submit), (selectedDescription.IsNullOrWhiteSpace() || selectedDueDate is null), new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));
var padding = TranslationUtil.CalculatePadding(ctx.DbUser, this.t.Commands.Utility.Reminders.Description, this.t.Commands.Utility.Reminders.DateTime);
var action_embed = new DiscordEmbedBuilder
{
Description = $"`{this.GetString(this.t.Commands.Utility.Reminders.Description).PadRight(padding)}`: {(selectedDescription.IsNullOrWhiteSpace() ? $"`{this.GetString(this.t.Common.NotSelected)}`" : $"`{selectedDescription.FullSanitize()}`")}\n" +
$"`{this.GetString(this.t.Commands.Utility.Reminders.DateTime).PadRight(padding)}`: {(selectedDueDate is null ? $"`{this.GetString(this.t.Common.NotSelected)}`" : $"{selectedDueDate.Value.ToTimestamp(TimestampFormat.LongDateTime)} ({selectedDueDate.Value.ToTimestamp()})")}"
}.AsAwaitingInput(ctx, this.GetString(this.t.Commands.Utility.Reminders.Title));
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(action_embed)
.AddComponents(new List<DiscordComponent> { SelectDescriptionButton, SelectDueDateButton, Finish })
.AddComponents(MessageComponents.GetBackButton(ctx.DbUser, ctx.Bot)));
var Menu = await ctx.WaitForButtonAsync();
if (Menu.TimedOut)
{
this.ModifyToTimedOut();
return;
}
if (Menu.GetCustomId() == SelectDescriptionButton.CustomId)
{
var maxLength = 100 - JsonConvert.SerializeObject(new ReminderSnoozeButton(), new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Include }).Length;
var modal = new DiscordInteractionModalBuilder(this.GetString(this.t.Commands.Utility.Reminders.NewReminder), Guid.NewGuid().ToString())
.AddTextComponent(new DiscordTextComponent(TextComponentStyle.Small, "desc", this.GetString(this.t.Commands.Utility.Reminders.Description), this.GetString(this.t.Commands.Utility.Reminders.SetDescription), 1, maxLength, true));
var ModalResult = await this.PromptModalWithRetry(Menu.Result.Interaction, modal, false);
if (ModalResult.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
else if (ModalResult.Cancelled)
{
continue;
}
else if (ModalResult.Errored)
{
throw ModalResult.Exception;
}
selectedDescription = ModalResult.Result.Interaction.GetModalValueByCustomId("desc").TruncateWithIndication(maxLength);
}
else if (Menu.GetCustomId() == SelectDueDateButton.CustomId)
{
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
var ModalResult = await this.PromptModalForDateTime(selectedDueDate ?? DateTime.UtcNow.AddMinutes(5), false);
if (ModalResult.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
else if (ModalResult.Cancelled)
{
continue;
}
else if (ModalResult.Errored)
{
if (ModalResult.Exception.GetType() == typeof(ArgumentException) || ModalResult.Exception.GetType() == typeof(ArgumentOutOfRangeException))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.Reminders.InvalidDateTime, true)).AsError(ctx));
await Task.Delay(5000);
continue;
}
throw ModalResult.Exception;
}
selectedDueDate = ModalResult.Result;
}
else if (Menu.GetCustomId() == Finish.CustomId)
{
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (selectedDueDate < DateTime.UtcNow)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.Reminders.InvalidDateTime, true)).AsError(ctx, this.GetString(this.t.Commands.Utility.Reminders.Title)));
await Task.Delay(2000);
continue;
}
rem.ScheduledReminders = rem.ScheduledReminders.Add(new()
{
Description = selectedDescription,
DueTime = selectedDueDate.Value.ToUniversalTime(),
CreationPlace = ctx.Channel.IsPrivate ? $"[`@{ctx.CurrentUser.GetUsername()}`](https://discord.com/channels/@me/{ctx.Channel.Id})" : $"[`{ctx.Guild.Name}`](https://discord.com/channels/{ctx.Guild.Id}/{ctx.Channel.Id})"
});
await this.ExecuteCommand(ctx, null);
return;
}
else if (Menu.GetCustomId() == MessageComponents.BackButtonId)
{
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
await this.ExecuteCommand(ctx, null);
return;
}
}
}
else if (SelectedCustomId == RemoveButton.CustomId)
{
if (rem.ScheduledReminders.Length == 0)
{
await this.ExecuteCommand(ctx, null);
return;
}
var UuidResult = await this.PromptCustomSelection(rem.ScheduledReminders
.Select(x => new DiscordStringSelectComponentOption($"{x.Description}".TruncateWithIndication(100), x.UUID, $"in {x.DueTime.GetTotalSecondsUntil().GetHumanReadable()}")).ToList());
if (UuidResult.TimedOut)
{
this.ModifyToTimedOut();
return;
}
else if (UuidResult.Cancelled)
{
await this.ExecuteCommand(ctx, null);
return;
}
else if (UuidResult.Errored)
{
throw UuidResult.Exception;
}
rem.ScheduledReminders = rem.ScheduledReminders.Remove(x => x.UUID, rem.ScheduledReminders.First(x => x.UUID == UuidResult.Result));
await this.ExecuteCommand(ctx, null);
return;
}
else if (SelectedCustomId == MessageComponents.CancelButtonId)
{
this.DeleteOrInvalidate();
return;
}
});
}
}

View file

@ -0,0 +1,197 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class ReportHostCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var url = (string)arguments["url"];
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var tos_version = 3;
if (ctx.DbUser.UrlSubmissions.AcceptedTOS != tos_version)
{
var button = new DiscordButtonComponent(ButtonStyle.Primary, "accepted-tos", this.GetString(this.t.Commands.Utility.ReportHost.AcceptTos), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👍")));
var tos_embed = new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.ReportHost.Tos,
new TVar("1", 1.ToEmotes()),
new TVar("2", 2.ToEmotes()),
new TVar("3", 3.ToEmotes()),
new TVar("4", 4.ToEmotes()))
}.AsAwaitingInput(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
if (ctx.DbUser.UrlSubmissions.AcceptedTOS != 0 && ctx.DbUser.UrlSubmissions.AcceptedTOS < tos_version)
{
tos_embed.Description = tos_embed.Description.Insert(0, $"**{this.GetString(this.t.Commands.Utility.ReportHost.TosChangedNotice)}**\n\n");
}
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(tos_embed).AddComponents(button));
var TosAccept = await ctx.WaitForButtonAsync(TimeSpan.FromMinutes(2));
if (TosAccept.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
await TosAccept.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
ctx.DbUser.UrlSubmissions.AcceptedTOS = tos_version;
}
var embed = new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.ReportHost.Processing, true)
}.AsLoading(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
_ = await this.RespondOrEdit(embed);
if (ctx.DbUser.UrlSubmissions.LastTime.AddMinutes(45) > DateTime.UtcNow && !ctx.User.IsMaintenance(ctx.Bot.status))
{
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.CooldownError, true,
new TVar("Timestamp", ctx.DbUser.UrlSubmissions.LastTime.AddMinutes(45).ToTimestamp()));
_ = this.RespondOrEdit(embed.AsError(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title)));
return;
}
if (ctx.Bot.SubmittedHosts.Fetch().Any(x => x.Value.Submitter == ctx.User.Id) && !ctx.User.IsMaintenance(ctx.Bot.status))
{
if (ctx.Bot.SubmittedHosts.Fetch().Where(x => x.Value.Submitter == ctx.User.Id).Count() >= 5)
{
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.LimitError, true);
_ = this.RespondOrEdit(embed.AsError(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title)));
return;
}
}
string host;
try
{
host = new UriBuilder(url).Host;
}
catch (Exception)
{
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.InvalidHost, true,
new TVar("Host", url, true));
_ = this.RespondOrEdit(embed.AsError(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title)));
return;
}
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.ConfirmHost, true,
new TVar("Host", host, true));
_ = embed.AsAwaitingInput(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
var ContinueButton = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Common.Confirm), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(embed).AddComponents(new List<DiscordComponent>
{
{ ContinueButton },
{ MessageComponents.GetCancelButton(ctx.DbUser, ctx.Bot) }
}));
var e = await ctx.WaitForButtonAsync(TimeSpan.FromMinutes(2));
if (e.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
await e.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (e.GetCustomId() == ContinueButton.CustomId)
{
_ = embed.AsLoading(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.DatabaseCheck, true);
_ = await this.RespondOrEdit(embed);
foreach (var b in ctx.Bot.PhishingHosts)
{
if (host.Contains(b.Key))
{
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.DatabaseError, true, new TVar("Host", host, true));
_ = embed.AsError(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
_ = this.RespondOrEdit(embed.Build());
return;
}
}
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.SubmissionCheck, true);
_ = await this.RespondOrEdit(embed);
foreach (var b in ctx.Bot.SubmittedHosts)
{
if (b.Value.Url == host)
{
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.SubmissionError, true, new TVar("Host", host, true));
_ = embed.AsError(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
_ = this.RespondOrEdit(embed.Build());
return;
}
}
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.CreatingSubmission, true);
_ = await this.RespondOrEdit(embed);
var channel = await ctx.Client.GetChannelAsync(ctx.Bot.status.LoadedConfig.Channels.UrlSubmissions);
var AcceptSubmission = new DiscordButtonComponent(ButtonStyle.Success, "accept_submission", "Accept submission", false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("✅")));
var DenySubmission = new DiscordButtonComponent(ButtonStyle.Danger, "deny_submission", "Deny submission", false, new DiscordComponentEmoji(DiscordEmoji.FromGuildEmote(ctx.Client, 1005430134070841395)));
var BanUserButton = new DiscordButtonComponent(ButtonStyle.Danger, "ban_user", "Deny submission & ban submitter", false, new DiscordComponentEmoji(DiscordEmoji.FromGuildEmote(ctx.Client, 1005430134070841395)));
var BanGuildButton = new DiscordButtonComponent(ButtonStyle.Danger, "ban_guild", "Deny submission & ban guild", false, new DiscordComponentEmoji(DiscordEmoji.FromGuildEmote(ctx.Client, 1005430134070841395)));
var submittedMsg = await channel.SendMessageAsync(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Author = new DiscordEmbedBuilder.EmbedAuthor { IconUrl = StatusIndicatorIcons.Success, Name = this.GetString(this.t.Commands.Utility.ReportHost.Title) },
Color = EmbedColors.Success,
Timestamp = DateTime.UtcNow,
Description = $"`Submitted host`: `{host.SanitizeForCode()}`\n" +
$"`Submission by `: `{ctx.User.GetUsernameWithIdentifier()} ({ctx.User.Id})`\n" +
$"`Submitted on `: `{ctx.Guild.Name} ({ctx.Guild.Id})`"
})
.AddComponents(new List<DiscordComponent>
{
{ AcceptSubmission },
{ DenySubmission },
{ BanUserButton },
{ BanGuildButton },
}));
ctx.Bot.SubmittedHosts.Add(submittedMsg.Id, new SubmittedUrlEntry(ctx.Bot, submittedMsg.Id)
{
Url = host,
Submitter = ctx.User.Id,
GuildOrigin = ctx.Guild.Id
});
ctx.DbUser.UrlSubmissions.LastTime = DateTime.UtcNow;
embed.Description = this.GetString(this.t.Commands.Utility.ReportHost.SubmissionCreated, true);
_ = embed.AsSuccess(ctx, this.GetString(this.t.Commands.Utility.ReportHost.Title));
_ = await this.RespondOrEdit(embed);
}
else if (e.GetCustomId() == MessageComponents.CancelButtonId)
{
this.DeleteOrInvalidate();
}
});
}
}

View file

@ -0,0 +1,155 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using Octokit;
namespace ProjectMakoto.Commands;
internal sealed class ReportTranslationCommand : BaseCommand
{
internal static readonly string[] labels = new string[] { "Translations", "Low Priority" };
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var CommandKey = this.t.Commands.Utility.ReportTranslation;
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var affectedType = (ReportTranslationType)arguments["affected_type"];
var reasonType = (ReportTranslationReason)arguments["report_type"];
var component = (string)arguments["component"];
var additionalInformation = (string?)arguments["additional_information"];
var tos_version = 1;
if (ctx.DbUser.TranslationReports.AcceptedTOS != tos_version)
{
var button = new DiscordButtonComponent(ButtonStyle.Primary, "accepted-tos", this.GetString(CommandKey.AcceptTos), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👍")));
var tos_embed = new DiscordEmbedBuilder
{
Description = this.GetString(CommandKey.Tos,
new TVar("1", 1.ToEmotes()),
new TVar("2", 2.ToEmotes()),
new TVar("3", 3.ToEmotes()),
new TVar("4", 4.ToEmotes()))
}.AsAwaitingInput(ctx, this.GetString(CommandKey.Title));
if (ctx.DbUser.TranslationReports.AcceptedTOS != 0 && ctx.DbUser.TranslationReports.AcceptedTOS < tos_version)
{
tos_embed.Description = tos_embed.Description.Insert(0, $"**{this.GetString(CommandKey.TosChangedNotice)}**\n\n");
}
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(tos_embed).AddComponents(button));
var TosAccept = await ctx.WaitForButtonAsync(TimeSpan.FromMinutes(2));
if (TosAccept.TimedOut)
{
this.ModifyToTimedOut(true);
return;
}
await TosAccept.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
ctx.DbUser.TranslationReports.AcceptedTOS = tos_version;
}
if (ctx.Bot.status.LoadedConfig.Secrets.Github.TokenExperiation.GetTotalSecondsUntil() <= 0)
throw new Exception("Required login data for report outdated.");
if (ctx.DbUser.TranslationReports.FirstRequestTime.GetTimespanSince() > TimeSpan.FromHours(24))
{
ctx.DbUser.TranslationReports.RequestCount = 0;
ctx.DbUser.TranslationReports.FirstRequestTime = DateTime.UtcNow;
}
if (ctx.DbUser.TranslationReports.RequestCount >= 3)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder()
.WithDescription(this.GetString(CommandKey.RatelimitReached, true, new TVar("Timestamp", ctx.DbUser.TranslationReports.FirstRequestTime.AddHours(24).ToTimestamp())))
.AsError(ctx, this.GetString(CommandKey.Title)));
return;
}
var YesButton = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Common.Yes), false, "✅".UnicodeToEmoji().ToComponent());
var NoButton = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), this.GetString(this.t.Common.No), false, "❌".UnicodeToEmoji().ToComponent());
_ = await this.RespondOrEdit(new DiscordMessageBuilder()
.AddEmbed(new DiscordEmbedBuilder()
.WithDescription($"{this.GetString(CommandKey.ConfirmationPrompt, true)}")
.AsAwaitingInput(ctx, this.GetString(CommandKey.Title)))
.AddComponents(YesButton, NoButton));
var result = await ctx.ResponseMessage.WaitForButtonAsync(ctx.User);
if (result.TimedOut)
{
this.ModifyToTimedOut();
return;
}
if (result.Result.GetCustomId() != YesButton.CustomId)
{
this.DeleteOrInvalidate();
return;
}
string GetReason(ReportTranslationReason reason)
{
return reason switch
{
ReportTranslationReason.MissingTranslation => "Missing Translation",
ReportTranslationReason.IncorrectTranslation => "Incorrect Translation",
ReportTranslationReason.ValuesNotFilledIntoString => "Values Missing in Strings",
ReportTranslationReason.Other => "Other",
_ => throw new NotImplementedException(),
};
}
string GetType(ReportTranslationType type)
{
return Enum.GetName(typeof(ReportTranslationType), type);
}
var issue = await ctx.Bot.GithubClient.Issue.Create(ctx.Bot.status.LoadedConfig.Secrets.Github.Username,
ctx.Bot.status.LoadedConfig.Secrets.Github.Repository,
new NewIssue($"{GetReason(reasonType)}: {component.FullSanitize()}")
{
Body =
$"### Component Type: `{GetType(affectedType)}`\n" +
$"### Affected Component: `{component.SanitizeForCode().Replace("@", "")}`\n" +
$"```\n" +
$"{additionalInformation?.Replace("@", "") ?? "No additional information supplied."}\n" +
$"```\n" +
$"</br></br></br>\n" +
$"**Submission Details**\n" +
$"</br>\n" +
$"<img align=\"left\" style=\"align:center;\" width=\"32\" height=\"32\" src=\"{ctx.User.AvatarUrl}\"> [`{ctx.User.GetUsernameWithIdentifier().SanitizeForCode()}`]({ctx.User.AvatarUrl}) (`{ctx.User.Id}`)\n\n" +
$"<img align=\"left\" style=\"align:center;\" width=\"32\" height=\"32\" src=\"{ctx.Guild.IconUrl}\"> [`{ctx.Guild.Name.SanitizeForCode()}`]({ctx.Guild.IconUrl}) (`{ctx.Guild.Id}`)\n"
});
try
{
_ = await ctx.Bot.GithubClient.Issue.Labels.ReplaceAllForIssue(ctx.Bot.status.LoadedConfig.Secrets.Github.Username, ctx.Bot.status.LoadedConfig.Secrets.Github.Repository, issue.Number, labels);
}
catch (Exception ex)
{
Log.Warning(ex, "Failed to update labels on reported issue");
}
_ = await this.RespondOrEdit(new DiscordEmbedBuilder()
.WithDescription(this.GetString(CommandKey.ReportSubmitted, true))
.AsSuccess(ctx, this.GetString(CommandKey.Title)));
});
}
}

View file

@ -0,0 +1,64 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class UploadCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var attachment = (DiscordAttachment)arguments["file"];
var stream = await new HttpClient().GetStreamAsync(attachment.Url);
var filesize = attachment.FileSize ?? 0;
if (ctx.DbUser.PendingUserUpload is null)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Upload.NoInteraction, true)
}.AsError(ctx));
return;
}
if (ctx.DbUser.PendingUserUpload.InteractionHandled)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Upload.AlreadyUploaded, true)
}.AsError(ctx));
return;
}
if (ctx.DbUser.PendingUserUpload.TimeOut.GetTotalSecondsUntil() < 0)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Upload.TimedOut, true,
new TVar("Timestamp", ctx.DbUser.PendingUserUpload.TimeOut.ToTimestamp()))
}.AsError(ctx));
ctx.DbUser.PendingUserUpload = null;
return;
}
ctx.DbUser.PendingUserUpload.UploadedData = stream;
ctx.DbUser.PendingUserUpload.FileSize = filesize;
ctx.DbUser.PendingUserUpload.InteractionHandled = true;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.Upload.Uploaded, true)
}.AsSuccess(ctx));
await Task.Delay(500);
this.DeleteOrInvalidate();
});
}
}

View file

@ -0,0 +1,133 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class UrbanDictionaryCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx, true))
return;
var term = (string)arguments["term"];
if (!ctx.Channel.IsNsfw && ctx.CommandType != Enums.CommandType.ApplicationCommand)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.UrbanDictionary.AdultContentError, true)
}.AsError(ctx));
return;
}
var Yes = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), this.GetString(this.t.Common.Yes), false, new DiscordComponentEmoji(true.ToEmote(ctx.Bot)));
var No = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), this.GetString(this.t.Common.No), false, new DiscordComponentEmoji(false.ToEmote(ctx.Bot)));
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.UrbanDictionary.AdultContentWarning, true)
}.AsAwaitingInput(ctx)).AddComponents(new List<DiscordComponent> { Yes, No }));
var Menu = await ctx.WaitForButtonAsync();
if (Menu.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = Menu.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
if (Menu.GetCustomId() == Yes.CustomId)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.UrbanDictionary.LookingUp, true,
new TVar("Term", term))
}.AsLoading(ctx));
if (term.IsNullOrWhiteSpace())
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.UrbanDictionary.LookupFail, true,
new TVar("Term", term))
}.AsError(ctx));
return;
}
HttpClient client = new();
string query;
using (var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "term", term },
}))
{
query = await content.ReadAsStringAsync();
}
var Response = await client.GetAsync($"https://api.urbandictionary.com/v0/define?{query}");
if (!Response.IsSuccessStatusCode)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.UrbanDictionary.LookupFail, true,
new TVar("Term", term))
}.AsError(ctx));
return;
}
List<UrbanDictionary.List> Definitions = null;
try
{
var rawDefinitions = JsonConvert.DeserializeObject<UrbanDictionary>(await Response.Content.ReadAsStringAsync());
Definitions = rawDefinitions.list.ToList();
Definitions.Sort((a, b) => b.RatingRatio.CompareTo(a.RatingRatio));
}
catch (Exception ex)
{
Log.Error(ex, string.Empty);
}
if (!Definitions.IsNotNullAndNotEmpty())
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder
{
Description = this.GetString(this.t.Commands.Utility.UrbanDictionary.NotExist, true, new TVar("Term", term))
}.AsError(ctx));
return;
}
var embeds = Definitions.Take(3).Select(x => new DiscordEmbedBuilder
{
Title = $"**{x.word.Replace("**", "")}** - {this.GetString(this.t.Commands.Utility.UrbanDictionary.WrittenBy, new TVar("Author", x.author))}",
Description = $"**{this.GetString(this.t.Commands.Utility.UrbanDictionary.Definition)}**\n\n" +
$"{x.definition.Replace("[", "").Replace("]", "")}\n\n" +
$"**{this.GetString(this.t.Commands.Utility.UrbanDictionary.Example)}**\n\n" +
$"{x.example.Replace("[", "").Replace("]", "")}\n\n" +
$"👍 `{x.thumbs_up}` | 👎 `{x.thumbs_down}` | 🕒 {Formatter.Timestamp(x.written_on, TimestampFormat.LongDateTime)}",
Url = x.permalink
}.AsInfo(ctx).Build()).ToList();
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbeds(embeds));
}
else
{
this.DeleteOrInvalidate();
}
});
}
}

View file

@ -0,0 +1,234 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands;
internal sealed class UserInfoCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
var victim = (DiscordUser)arguments["user"];
if (await ctx.DbUser.Cooldown.WaitForLight(ctx))
return;
victim ??= ctx.User;
victim = await victim.GetFromApiAsync();
DiscordMember? bMember = null;
try
{
bMember = await ctx.Guild.GetMemberAsync(victim.Id);
}
catch { }
static string GetStatusIcon(UserStatus? status)
{
return status switch
{
UserStatus.Online => "🟢",
UserStatus.DoNotDisturb => "🔴",
UserStatus.Idle => "🟡",
UserStatus.Streaming => "🟣",
_ => "⚪",
};
}
var GenerateRoles = "";
if (bMember is not null)
{
GenerateRoles = bMember.Roles.Any() ? string.Join(", ", bMember.Roles.Select(x => x.Mention)) : this.GetString(this.t.Commands.Utility.UserInfo.NoRoles, true);
}
else
{
GenerateRoles = ctx.DbGuild.Members[victim.Id].MemberRoles.Length > 0
? string.Join(", ", ctx.DbGuild.Members[victim.Id].MemberRoles.Where(x => ctx.Guild.Roles.ContainsKey(x.Id)).Select(x => $"{ctx.Guild.GetRole(x.Id).Mention}"))
: this.GetString(this.t.Commands.Utility.UserInfo.NoStoredRoles, true);
}
var banList = await ctx.Guild.GetBansAsync();
var isBanned = banList.Any(x => x.User.Id == victim.Id);
var banDetails = (isBanned ? banList.First(x => x.User.Id == victim.Id) : null);
var builder = new DiscordMessageBuilder();
var embed = new DiscordEmbedBuilder()
{
Author = new DiscordEmbedBuilder.EmbedAuthor
{
Name = $"{(victim.IsBot ? $"[{(victim.IsSystem ?? false ? this.GetString(this.t.Commands.Utility.UserInfo.System) : $"{this.GetString(this.t.Commands.Utility.UserInfo.Bot)}{(victim.IsVerifiedBot ? "" : "")}")}] " : "")}{victim.GetUsernameWithIdentifier()}",
Url = victim.ProfileUrl
},
Thumbnail = new DiscordEmbedBuilder.EmbedThumbnail
{
Url = (string.IsNullOrWhiteSpace(victim.AvatarUrl) ? "https://cdn.discordapp.com/attachments/712761268393738301/899051918037504040/QuestionMark.png" : victim.AvatarUrl)
},
Color = victim.BannerColor ?? new("2f3136"),
ImageUrl = victim.BannerUrl,
Footer = new DiscordEmbedBuilder.EmbedFooter
{
Text = $"User-Id: {victim.Id}"
},
Description = $"{(bMember is null ? $"{(ctx.DbGuild.Members[victim.Id].FirstJoinDate == DateTime.MinValue ? this.GetString(this.t.Commands.Utility.UserInfo.NeverJoined, true) : $"{(isBanned ? this.GetString(this.t.Commands.Utility.UserInfo.IsBanned, true) : this.GetString(this.t.Commands.Utility.UserInfo.JoinedBefore, true))}")}\n\n" : "")}" +
$"{(ctx.Bot.globalBans.ContainsKey(victim.Id) ? $"💀 **{this.GetString(this.t.Commands.Utility.UserInfo.GlobalBanned, true)}**\n" : "")}" +
$"{(ctx.Bot.status.TeamOwner == victim.Id ? $"👑 **{this.GetString(this.t.Commands.Utility.UserInfo.BotOwner, true)}**\n" : "")}" +
$"{(ctx.Bot.status.TeamMembers.Contains(victim.Id) ? $"🔏 **{this.GetString(this.t.Commands.Utility.UserInfo.BotStaff, true)}**\n\n" : "")}" +
$"{(bMember is not null && bMember.IsOwner ? $" {this.GetString(this.t.Commands.Utility.UserInfo.Owner, true)}\n" : "")}" +
$"{(victim.IsStaff ? $"📘 **{this.GetString(this.t.Commands.Utility.UserInfo.DiscordStaff, true)}**\n" : "")}" +
$"{(victim.IsMod ? $" {this.GetString(this.t.Commands.Utility.UserInfo.CertifiedMod, true)}\n" : "")}" +
$"{(victim.IsBotDev ? $" {this.GetString(this.t.Commands.Utility.UserInfo.VerifiedBotDeveloper, true)}\n" : "")}" +
$"{(victim.IsPartner ? $"👥 {this.GetString(this.t.Commands.Utility.UserInfo.DiscordPartner, true)}\n" : "")}" +
$"{(bMember is not null && bMember.IsPending.HasValue && bMember.IsPending.Value ? $" {this.GetString(this.t.Commands.Utility.UserInfo.PendingMembership, true)}\n" : "")}" +
$"\n**{(bMember is null ? $"{this.GetString(this.t.Commands.Utility.UserInfo.Roles)} ({this.GetString(this.t.Commands.Utility.UserInfo.Backup)})" : this.GetString(this.t.Commands.Utility.UserInfo.Roles))}**\n{GenerateRoles}"
};
if (ctx.Bot.globalNotes.TryGetValue(victim.Id, out var globalNotes) && globalNotes.Notes.Length != 0)
{
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.BotNotes), $"{string.Join("\n\n", ctx.Bot.globalNotes[victim.Id].Notes.Select(x => $"{x.Reason.FullSanitize()} - <@{x.Moderator}> {x.Timestamp.ToTimestamp()}"))}".TruncateWithIndication(512)));
}
if (ctx.Bot.globalBans.TryGetValue(victim.Id, out var globalBanDetails))
{
var gBanMod = await ctx.Client.GetUserAsync(ctx.Bot.globalBans[victim.Id].Moderator);
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.GlobalBanReason), $"`{((string.IsNullOrWhiteSpace(globalBanDetails.Reason) || globalBanDetails.Reason == "-") ? this.GetString(this.t.Commands.Utility.UserInfo.NoReason) : globalBanDetails.Reason).SanitizeForCode()}`", true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.GlobalBanMod), $"`{gBanMod.GetUsernameWithIdentifier()}`", true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.GlobalBanDate), $"{Formatter.Timestamp(globalBanDetails.Timestamp)} ({Formatter.Timestamp(globalBanDetails.Timestamp, TimestampFormat.LongDateTime)})", true));
}
if (isBanned)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.BanDetails), $"`{(string.IsNullOrWhiteSpace(banDetails?.Reason) ? this.GetString(this.t.Commands.Utility.UserInfo.NoReason) : $"{banDetails.Reason}")}`", false));
var InviterButtonAdded = false;
if (ctx.DbGuild.InviteTracker.Enabled)
{
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.InvitedBy), $"{(ctx.DbGuild.Members[victim.Id].InviteTracker.Code.IsNullOrWhiteSpace() ? this.GetString(this.t.Commands.Utility.UserInfo.NoInviter, true) : $"<@{ctx.DbGuild.Members[victim.Id].InviteTracker.UserId}> (`{ctx.DbGuild.Members[victim.Id].InviteTracker.UserId}`)")}", true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.UsersInvited), $"`{(ctx.DbGuild.Members.Fetch().Where(b => b.Value.InviteTracker.UserId == victim.Id)).Count()}`", true));
if (!ctx.DbGuild.Members[victim.Id].InviteTracker.Code.IsNullOrWhiteSpace())
{
InviterButtonAdded = true;
_ = builder.AddComponents(new DiscordButtonComponent(ButtonStyle.Secondary, $"userinfo-inviter", this.GetString(this.t.Commands.Utility.UserInfo.ShowProfileInviter), false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("👤"))));
}
}
if (bMember is not null)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.ServerJoinDate), $"{Formatter.Timestamp(bMember.JoinedAt, TimestampFormat.LongDateTime)}", true));
else
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.ServerLeaveDate), (ctx.DbGuild.Members[victim.Id].LastLeaveDate != DateTime.MinValue ? $"{Formatter.Timestamp(ctx.DbGuild.Members[victim.Id].LastLeaveDate, TimestampFormat.LongDateTime)} ({Formatter.Timestamp(ctx.DbGuild.Members[victim.Id].LastLeaveDate)})" : this.GetString(this.t.Commands.Utility.UserInfo.NeverJoined, true)), true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.FirstJoinDate), (ctx.DbGuild.Members[victim.Id].FirstJoinDate != DateTime.MinValue ? $"{Formatter.Timestamp(ctx.DbGuild.Members[victim.Id].FirstJoinDate, TimestampFormat.LongDateTime)} ({Formatter.Timestamp(ctx.DbGuild.Members[victim.Id].FirstJoinDate)})" : this.GetString(this.t.Commands.Utility.UserInfo.NeverJoined, true)), true));
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.AccountCreationDate), $"{Formatter.Timestamp(victim.CreationTimestamp, TimestampFormat.LongDateTime)}", true));
if (bMember is not null && bMember.PremiumSince.HasValue)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.ServerBoosterSince), $"{Formatter.Timestamp(bMember.PremiumSince.Value, TimestampFormat.LongDateTime)}", true));
if (!string.IsNullOrWhiteSpace(victim.Pronouns))
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.Pronouns), $"`{victim.Pronouns}`", true));
if (victim.BannerColor is not null)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.BannerColor), $"`{victim.BannerColor.Value}`", true));
string TranslatePresence(UserStatus status)
{
return status switch
{
UserStatus.Online => this.GetString(this.t.Commands.Utility.UserInfo.Online),
UserStatus.Idle => this.GetString(this.t.Commands.Utility.UserInfo.Idle),
UserStatus.DoNotDisturb => this.GetString(this.t.Commands.Utility.UserInfo.DoNotDisturb),
UserStatus.Streaming => this.GetString(this.t.Commands.Utility.UserInfo.Streaming),
UserStatus.Offline => this.GetString(this.t.Commands.Utility.UserInfo.Offline),
UserStatus.Invisible => this.GetString(this.t.Commands.Utility.UserInfo.Offline),
_ => status.ToString(),
};
}
try
{
if (victim.Presence is not null)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.Presence), $"{GetStatusIcon(victim.Presence.Status)} `{TranslatePresence(victim.Presence.Status)}`\n" +
$"󠂪 󠂪 󠂪 󠂪{GetStatusIcon(victim.Presence.ClientStatus.Desktop.HasValue ? victim.Presence.ClientStatus.Desktop.Value : UserStatus.Offline)} {this.GetString(this.t.Commands.Utility.UserInfo.Desktop, true)}\n" +
$"󠂪 󠂪 󠂪 󠂪{GetStatusIcon(victim.Presence.ClientStatus.Mobile.HasValue ? victim.Presence.ClientStatus.Mobile.Value : UserStatus.Offline)} {this.GetString(this.t.Commands.Utility.UserInfo.Mobile, true)}\n" +
$"󠂪 󠂪 󠂪 󠂪{GetStatusIcon(victim.Presence.ClientStatus.Web.HasValue ? victim.Presence.ClientStatus.Web.Value : UserStatus.Offline)} {this.GetString(this.t.Commands.Utility.UserInfo.Web, true)}\n\n", true));
}
catch { }
string TranslateActivity(ActivityType type)
{
return type switch
{
ActivityType.Playing => this.GetString(this.t.Commands.Utility.UserInfo.Playing),
ActivityType.Streaming => this.GetString(this.t.Commands.Utility.UserInfo.Streaming),
ActivityType.ListeningTo => this.GetString(this.t.Commands.Utility.UserInfo.ListeningTo),
ActivityType.Watching => this.GetString(this.t.Commands.Utility.UserInfo.Watching),
ActivityType.Competing => this.GetString(this.t.Commands.Utility.UserInfo.Competing),
_ => type.ToString(),
};
}
try
{
if (victim.Presence is not null && victim.Presence.Activities is not null && victim.Presence.Activities?.Count > 0)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.Activities), string.Join("\n", victim.Presence.Activities.Select(x => $"{(x.ActivityType == ActivityType.Custom ? $" {this.GetString(this.t.Commands.Utility.UserInfo.Status)}: `{x.CustomStatus.Emoji?.Name ?? "None"}`{(string.IsNullOrWhiteSpace(x.CustomStatus.Name) ? "" : $" {x.CustomStatus.Name}")}\n" : $" {TranslateActivity(x.ActivityType)} {x.Name}")}")), true));
}
catch { }
if (bMember is not null && bMember.CommunicationDisabledUntil.HasValue && bMember.CommunicationDisabledUntil.Value.GetTotalSecondsUntil() > 0)
_ = embed.AddField(new DiscordEmbedField(this.GetString(this.t.Commands.Utility.UserInfo.TimedOutUntil), $"{Formatter.Timestamp(bMember.CommunicationDisabledUntil.Value, TimestampFormat.LongDateTime)}", true));
_ = await this.RespondOrEdit(builder.WithEmbed(embed));
if (InviterButtonAdded)
{
_ = ctx.ResponseMessage.WaitForButtonAsync(ctx.User, TimeSpan.FromMinutes(15)).ContinueWith(async x =>
{
if (x.IsFaulted)
return;
var e = x.Result;
if (e.TimedOut)
{
this.ModifyToTimedOut();
return;
}
_ = e.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
DiscordUser newVictim;
try
{
newVictim = await ctx.Client.GetUserAsync(ctx.DbGuild.Members[victim.Id].InviteTracker.UserId);
}
catch (Exception)
{
_ = e.Result.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder()
.AddEmbed(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.UserInfo.FetchUserError, true, new TVar("User", ctx.DbGuild.Members[victim.Id].InviteTracker.UserId))).AsError(ctx)));
return;
}
await this.ExecuteCommand(ctx, new Dictionary<string, object>
{
{ "victim", newVictim }
});
return;
}).Add(ctx.Bot, ctx);
}
});
}
}

View file

@ -0,0 +1,62 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class BanCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var victim = (DiscordMember)arguments["user"];
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (!channel.Users.Any(x => x.Id == victim.Id))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.VictimNotPresent, true,
new TVar("User", victim.Mention))).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId == victim.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Ban.CannotBanSelf, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].BannedUsers.Contains(victim.Id))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Ban.VictimAlreadyBanned, true,
new TVar("User", victim.Mention))).AsError(ctx));
return;
}
ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].BannedUsers = ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].BannedUsers.Add(victim.Id);
await channel.AddOverwriteAsync(victim, deny: Permissions.UseVoice);
await victim.DisconnectFromVoiceAsync();
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Ban.VictimBanned, true, new TVar("User", victim.Mention))).AsError(ctx));
});
}
}

View file

@ -0,0 +1,65 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class ChangeOwnerCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var victim = (DiscordMember)arguments["user"];
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (victim.IsBot)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.VictimIsBot, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId == victim.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.ChangeOwner.AlreadyOwner, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
if (ctx.Member.Permissions.HasPermission(Permissions.ManageChannels))
{
ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId = victim.Id;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.ChangeOwner.ForceAssign, true, new TVar("User", victim.Mention))).AsSuccess(ctx));
return;
}
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId = victim.Id;
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.ChangeOwner.Success, true, new TVar("User", victim.Mention))).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,39 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class CloseCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
await channel.ModifyAsync(x => x.PermissionOverwrites = channel.PermissionOverwrites.Merge(ctx.Guild.EveryoneRole, Permissions.None, Permissions.UseVoice));
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Close.Success, true)).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,69 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class InviteCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var victim = (DiscordMember)arguments["user"];
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId == victim.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Invite.CannotInviteSelf, true)).AsError(ctx));
return;
}
if (channel.Users.Any(x => x.Id == victim.Id))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Invite.AlreadyPresent, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
if (victim.IsBot)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.VictimIsBot, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
await channel.AddOverwriteAsync(victim, Permissions.UseVoice);
try
{
_ = await victim.SendMessageAsync(this.t.Commands.Utility.VoiceChannelCreator.Invite.VictimMessage.Get(ctx.Bot.Users[victim.Id]).Build(new TVar("Channel", channel.Mention)));
}
catch (DisCatSharp.Exceptions.UnauthorizedException)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Invite.PartialSuccess, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Invite.Success, true, new TVar("User", victim.Mention))).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,52 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class KickCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var victim = (DiscordMember)arguments["user"];
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId == victim.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Kick.CannotKickSelf, true)).AsError(ctx));
return;
}
if (!channel.Users.Any(x => x.Id == victim.Id))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.VictimNotPresent, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
await victim.DisconnectFromVoiceAsync();
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Kick.Success, true, new TVar("User", victim.Mention))).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,46 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class LimitCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var newLimit = (uint)arguments["limit"];
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (newLimit > 99)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Limit.OutsideRange, true)).AsError(ctx));
return;
}
await channel.ModifyAsync(x => x.UserLimit = newLimit.ToInt32());
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Limit.Success, true, new TVar("Count", newLimit))).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,54 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class NameCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var newName = (string)arguments["name"];
var channel = ctx.Member.VoiceState?.Channel;
newName = (newName.IsNullOrWhiteSpace() ? this.GetGuildString(this.t.Commands.Utility.VoiceChannelCreator.Events.DefaultChannelName, new TVar("User", ctx.Member.DisplayName)) : newName);
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].LastRename.GetTimespanSince() < TimeSpan.FromMinutes(5))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Name.Cooldown, true,
new TVar("Timestamp", ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].LastRename.AddMinutes(5).ToTimestamp()))).AsError(ctx));
return;
}
foreach (var b in ctx.Bot.ProfanityList)
newName = newName.Replace(b, new String('*', b.Length));
ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].LastRename = DateTime.UtcNow;
await channel.ModifyAsync(x => x.Name = newName.TruncateWithIndication(25));
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Name.Success, true,
new TVar("Name", newName, true))).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,39 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class OpenCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
await channel.ModifyAsync(x => x.PermissionOverwrites = channel.PermissionOverwrites.Merge(ctx.Guild.EveryoneRole, Permissions.None, Permissions.None, Permissions.UseVoice));
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Open.Success, true)).AsSuccess(ctx));
});
}
}

View file

@ -0,0 +1,47 @@
// Project Makoto
// Copyright (C) 2024 Fortunevale
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
namespace ProjectMakoto.Commands.VcCreator;
internal sealed class UnbanCommand : BaseCommand
{
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
{
return Task.Run(async () =>
{
if (await ctx.DbUser.Cooldown.WaitForHeavy(ctx))
return;
var victim = (DiscordMember)arguments["user"];
var channel = ctx.Member.VoiceState?.Channel;
if (!ctx.DbGuild.VcCreator.CreatedChannels.Any(x => x.ChannelId == (channel?.Id ?? 0)))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannel, true)).AsError(ctx));
return;
}
if (ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].OwnerId != ctx.User.Id)
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.NotAVccChannelOwner, true)).AsError(ctx));
return;
}
if (!ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].BannedUsers.Contains(victim.Id))
{
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Unban.VictimNotBanned, true, new TVar("User", victim.Mention))).AsError(ctx));
return;
}
ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].BannedUsers = ctx.DbGuild.VcCreator.CreatedChannels[channel.Id].BannedUsers.Remove(x => x.ToString(), victim.Id);
await channel.AddOverwriteAsync(victim, deny: Permissions.None);
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(this.GetString(this.t.Commands.Utility.VoiceChannelCreator.Unban.VictimUnbanned, true, new TVar("User", victim.Mention))).AsSuccess(ctx));
});
}
}