refactor: Initial release
This commit is contained in:
commit
9505750e29
447 changed files with 41522 additions and 0 deletions
21
ProjectMakoto/Commands/Maintainers/DevDebug/ThrowCommand.cs
Normal file
21
ProjectMakoto/Commands/Maintainers/DevDebug/ThrowCommand.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// 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.Debug;
|
||||
|
||||
internal sealed class ThrowCommand : BaseCommand
|
||||
{
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
throw new InvalidCastException();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class BanGuildCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var guild = (ulong)arguments["guild"];
|
||||
var reason = (string)arguments["reason"];
|
||||
|
||||
if (reason.IsNullOrWhiteSpace())
|
||||
reason = "No reason provided.";
|
||||
|
||||
if (ctx.Bot.bannedGuilds.ContainsKey(guild))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Guild '{guild}' is already banned from using the bot.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Bot.bannedGuilds.Add(guild, new(ctx.Bot, "banned_guilds", guild) { Reason = reason, Moderator = ctx.User.Id });
|
||||
|
||||
foreach (var b in ctx.Client.Guilds.Where(x => x.Key == guild))
|
||||
{
|
||||
Log.Information("Leaving guild '{guild}'..", b.Key);
|
||||
await b.Value.LeaveAsync();
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Guild '{guild}' was banned from using the bot.`").AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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.DevTools;
|
||||
|
||||
internal sealed class BanUserCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var victim = (DiscordUser)arguments["victim"];
|
||||
var reason = (string)arguments["reason"];
|
||||
|
||||
if (reason.IsNullOrWhiteSpace())
|
||||
reason = "No reason provided.";
|
||||
|
||||
if (ctx.Bot.status.TeamMembers.Contains(victim.Id))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`'{victim.GetUsernameWithIdentifier()}' is registered in the staff team.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.Bot.bannedUsers.ContainsKey(victim.Id))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`'{victim.GetUsernameWithIdentifier()}' is already banned from using the bot.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Bot.bannedUsers.Add(victim.Id, new(ctx.Bot, "banned_users", victim.Id) { Reason = reason, Moderator = ctx.User.Id });
|
||||
|
||||
foreach (var b in ctx.Client.Guilds.Where(x => x.Value.OwnerId == victim.Id))
|
||||
{
|
||||
Log.Information("Leaving guild '{guild}'..", b.Key);
|
||||
await b.Value.LeaveAsync();
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`'{victim.GetUsernameWithIdentifier()}' was banned from using the bot.`").AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class BatchLookupCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var IDs = ((string)arguments["IDs"]).Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(x => x.ToUInt64()).ToList();
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Looking up {IDs.Count} users..`\n`{StringTools.GenerateASCIIProgressbar(0d, IDs.Count)}`").AsLoading(ctx));
|
||||
|
||||
Dictionary<ulong, DiscordUser> fetched = new();
|
||||
|
||||
for (var i = 0; i < IDs.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
fetched.Add(IDs[i], await ctx.Client.GetUserAsync(IDs[i]));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
fetched.Add(IDs[i], null);
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Looking up {IDs.Count} users..`\n`{StringTools.GenerateASCIIProgressbar(i, IDs.Count)}`").AsLoading(ctx));
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription(string.Join("\n", fetched.Select(x => $"{(x.Value is null ? $"❌ `Failed to fetch '{x.Key}'`" : $"✅ {x.Value.Mention} `{x.Value.GetUsernameWithIdentifier()}` (`{x.Value.Id}`)")}"))).AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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.DevTools;
|
||||
|
||||
internal sealed class BotnickCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var newNickname = (string)arguments["newNickname"];
|
||||
|
||||
try
|
||||
{
|
||||
await ctx.Guild.CurrentMember.ModifyAsync(x => x.Nickname = newNickname);
|
||||
|
||||
if (newNickname.IsNullOrWhiteSpace())
|
||||
_ = await this.RespondOrEdit($"My nickname on this server has been reset.");
|
||||
else
|
||||
_ = await this.RespondOrEdit($"My nickname on this server has been changed to **{newNickname}**.");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_ = await this.RespondOrEdit($"My nickname could not be changed.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class CommandManageCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var EnableCommandButton = new DiscordButtonComponent(ButtonStyle.Success, Guid.NewGuid().ToString(), "Enable Command", ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Count == 0, "➕".UnicodeToEmoji().ToComponent());
|
||||
var DisableCommandButton = new DiscordButtonComponent(ButtonStyle.Danger, Guid.NewGuid().ToString(), "Disable Command", false, "➖".UnicodeToEmoji().ToComponent());
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder()
|
||||
.AddEmbed(new DiscordEmbedBuilder()
|
||||
.WithTitle("Disabled Commands")
|
||||
.WithDescription($"{(ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Count != 0 ? string.Join(", ", ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Select(x => $"`{x}`")) : "`No commands disabled.`")}")
|
||||
.AsAwaitingInput(ctx))
|
||||
.AddComponents(EnableCommandButton, DisableCommandButton)
|
||||
.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);
|
||||
|
||||
if (Button.GetCustomId() == EnableCommandButton.CustomId)
|
||||
{
|
||||
var SelectionResult = await this.PromptCustomSelection(ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Select(x => new DiscordStringSelectComponentOption(x, x)).ToList());
|
||||
|
||||
if (SelectionResult.TimedOut)
|
||||
{
|
||||
this.ModifyToTimedOut(true);
|
||||
return;
|
||||
}
|
||||
else if (SelectionResult.Cancelled)
|
||||
{
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (SelectionResult.Errored)
|
||||
{
|
||||
throw SelectionResult.Exception;
|
||||
}
|
||||
|
||||
if (!ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Contains(SelectionResult.Result))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder()
|
||||
.WithDescription("`That command is already enabled.`")
|
||||
.AsError(ctx));
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Remove(SelectionResult.Result);
|
||||
ctx.Bot.status.LoadedConfig.Save();
|
||||
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (Button.GetCustomId() == DisableCommandButton.CustomId)
|
||||
{
|
||||
List<string> CommandList = new();
|
||||
|
||||
foreach (var cmd in ctx.Client.GetCommandList(ctx.Bot))
|
||||
{
|
||||
if (ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Contains(cmd.Name.ToLower()))
|
||||
continue;
|
||||
|
||||
CommandList.Add(cmd.Name.ToLower());
|
||||
|
||||
foreach (var sub in cmd.Options?.Where(x => x.Type == ApplicationCommandOptionType.SubCommand) ?? new List<DiscordApplicationCommandOption>())
|
||||
{
|
||||
if (ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Contains($"{cmd.Name} {sub.Name}".ToLower()))
|
||||
continue;
|
||||
|
||||
CommandList.Add($"{cmd.Name} {sub.Name}".ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
if (CommandList.Count == 0)
|
||||
{
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
var SelectionResult = await this.PromptCustomSelection(CommandList.Select(x =>
|
||||
new DiscordStringSelectComponentOption(x.FirstLetterToUpper(), x,
|
||||
(x.Contains(' ') ? "Sub Command" : (CommandList.Where(y => y.StartsWith(x)).Count() >= 2 ? "Command Group" : "Single Command")))).ToList(), "Select a command to disable..");
|
||||
|
||||
if (SelectionResult.TimedOut)
|
||||
{
|
||||
this.ModifyToTimedOut(true);
|
||||
return;
|
||||
}
|
||||
else if (SelectionResult.Cancelled)
|
||||
{
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (SelectionResult.Errored)
|
||||
{
|
||||
throw SelectionResult.Exception;
|
||||
}
|
||||
|
||||
if (ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Contains(SelectionResult.Result))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder()
|
||||
.WithDescription("`That command is already disabled.`")
|
||||
.AsError(ctx));
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Bot.status.LoadedConfig.Discord.DisabledCommands.Add(SelectionResult.Result);
|
||||
ctx.Bot.status.LoadedConfig.Save();
|
||||
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (Button.GetCustomId() == MessageComponents.CancelButtonId)
|
||||
{
|
||||
this.DeleteOrInvalidate();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
using Octokit;
|
||||
|
||||
namespace ProjectMakoto.Commands.DevTools;
|
||||
|
||||
internal sealed class CreateIssueCommand : BaseCommand
|
||||
{
|
||||
public override async Task<bool> BeforeExecution(SharedCommandContext ctx) => (await this.CheckMaintenance() && await this.CheckSource(Enums.CommandType.ApplicationCommand));
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var UseOldTagsSelector = (bool)arguments["UseOldTagsSelector"];
|
||||
|
||||
if (ctx.Bot.status.LoadedConfig.Secrets.Github.TokenExperiation.GetTotalSecondsUntil() <= 0)
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithContent($"❌ `The GitHub Token expired, please update.`"));
|
||||
return;
|
||||
}
|
||||
|
||||
var labels = await ctx.Bot.GithubClient.Issue.Labels.GetAllForRepository(ctx.Bot.status.LoadedConfig.Secrets.Github.Username, ctx.Bot.status.LoadedConfig.Secrets.Github.Repository);
|
||||
|
||||
var modal = new DiscordInteractionModalBuilder().WithCustomId(Guid.NewGuid().ToString()).WithTitle("Create new Issue on Github")
|
||||
.AddModalComponents(new DiscordTextComponent(TextComponentStyle.Small, "title", "Title", "New issue", 4, 250, true))
|
||||
.AddModalComponents(new DiscordTextComponent(TextComponentStyle.Paragraph, "description", "Description", required: false));
|
||||
|
||||
if (!UseOldTagsSelector)
|
||||
_ = modal.AddModalComponents(new DiscordStringSelectComponent("Select tags", labels.Select(x => new DiscordStringSelectComponentOption(x.Name, x.Name.ToLower().MakeValidFileName(), "", false, new DiscordComponentEmoji(new DiscordColor(x.Color).GetClosestColorEmoji(ctx.Client)))), "labels", 1, labels.Count));
|
||||
else
|
||||
_ = modal.AddModalComponents(new DiscordTextComponent(TextComponentStyle.Paragraph, "labels", "Labels", "", null, null, false, $"Put a # in front of every label you want to add.\n\n{string.Join("\n", labels.Select(x => x.Name))}"));
|
||||
|
||||
await ctx.OriginalInteractionContext.CreateModalResponseAsync(modal);
|
||||
|
||||
CancellationTokenSource cancellationTokenSource = new();
|
||||
|
||||
ctx.Client.ComponentInteractionCreated += RunInteraction;
|
||||
|
||||
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (e.GetCustomId() == modal.CustomId)
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||||
|
||||
_ = e.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||||
var followup = await e.Interaction.CreateFollowupMessageAsync(new DiscordFollowupMessageBuilder { IsEphemeral = true }.WithContent(":arrows_counterclockwise: `Submitting your issue..`"));
|
||||
|
||||
var labelComp = e.Interaction.Data.Components.Where(x => x.CustomId == "labels").First();
|
||||
|
||||
var title = e.Interaction.Data.Components.Where(x => x.CustomId == "title").First().Value;
|
||||
var description = e.Interaction.Data.Components.Where(x => x.CustomId == "description").First().Value;
|
||||
var labels = labelComp.Type == ComponentType.StringSelect
|
||||
? labelComp.Values.ToList()
|
||||
: labelComp.Value.Split("\n", StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).Where(x => x.StartsWith('#')).Select(x => x.Replace("#", "")).ToList();
|
||||
|
||||
if (ctx.Bot.status.LoadedConfig.Secrets.Github.TokenExperiation.GetTotalSecondsUntil() <= 0)
|
||||
{
|
||||
_ = e.Interaction.EditFollowupMessageAsync(followup.Id, new DiscordWebhookBuilder().WithContent($"❌ `The GitHub Token expired, please update.`"));
|
||||
return;
|
||||
}
|
||||
|
||||
var issue = await ctx.Bot.GithubClient.Issue.Create(ctx.Bot.status.LoadedConfig.Secrets.Github.Username, ctx.Bot.status.LoadedConfig.Secrets.Github.Repository, new NewIssue(title) { Body = $"{(description.IsNullOrWhiteSpace() ? "_No description provided_" : description)}\n\n<b/>\n\n##### <img align=\"left\" style=\"align:center;\" width=\"32\" height=\"32\" src=\"{ctx.User.AvatarUrl}\">_Submitted by [`{ctx.User.GetUsernameWithIdentifier()}`]({ctx.User.ProfileUrl}) (`{ctx.User.Id}`) via Discord._" });
|
||||
|
||||
if (labels.Count > 0)
|
||||
_ = await ctx.Bot.GithubClient.Issue.Labels.ReplaceAllForIssue(ctx.Bot.status.LoadedConfig.Secrets.Github.Username, ctx.Bot.status.LoadedConfig.Secrets.Github.Repository, issue.Number, labels.ToArray());
|
||||
|
||||
_ = e.Interaction.EditFollowupMessageAsync(followup.Id, new DiscordWebhookBuilder().WithContent($"✅ `Issue submitted:` {issue.HtmlUrl}"));
|
||||
}
|
||||
}).Add(ctx.Bot, ctx);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMinutes(15), cancellationTokenSource.Token);
|
||||
|
||||
ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class Disenroll2FAUserCommand : BaseCommand
|
||||
{
|
||||
public override async Task<bool> BeforeExecution(SharedCommandContext ctx)
|
||||
=> await this.CheckMaintenance() && await this.CheckBotOwner();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var victim = (DiscordUser)arguments["victim"];
|
||||
|
||||
if (!ctx.Client.CheckTwoFactorEnrollmentFor(victim.Id))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`{victim.GetUsernameWithIdentifier()} is not enrolled in Two Factor Authentication.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.Client.DisenrollTwoFactor(victim.Id);
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Two Factor Authentication removed for {victim.GetUsername()}.`").AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// 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 DisCatSharp.Extensions.TwoFactorCommands.Enums;
|
||||
|
||||
namespace ProjectMakoto.Commands.DevTools;
|
||||
|
||||
internal sealed class EnrollTwoFactorCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx)
|
||||
=> this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
if (ctx.Client.CheckTwoFactorEnrollmentFor(ctx.User.Id))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`You're already enrolled in Two Factor Authentication.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
var Confirmed = false;
|
||||
|
||||
var ConfirmButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), "Confirm Two Factor Authentication", false, DiscordEmoji.FromUnicode("✅").ToComponent());
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Enrolling you into Two Factor Authentication..`").AsLoading(ctx));
|
||||
var (Secret, QrCode) = ctx.Client.EnrollTwoFactor(ctx.User);
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithContent($"Please scan this QR Code or use the Secret below to register the Two Factor in an App of your choosing." +
|
||||
$"\n\n`{Secret}`\n\n" +
|
||||
$"When you're done, please press the button below to confirm the success of the registration.")
|
||||
.WithFile("2fa.png", QrCode, false, "This is a QR Code for an Authenticator App.")
|
||||
.AddComponents(ConfirmButton, MessageComponents.GetCancelButton(ctx.DbUser, ctx.Bot)));
|
||||
|
||||
_ = Task.Delay(120000).ContinueWith((_) =>
|
||||
{
|
||||
ctx.Client.ComponentInteractionCreated -= RunInteraction;
|
||||
|
||||
if (!Confirmed)
|
||||
{
|
||||
ctx.Client.DisenrollTwoFactor(ctx.User.Id);
|
||||
_ = this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Failed to authenticate. Enrollment reverted.`").AsError(ctx));
|
||||
}
|
||||
});
|
||||
|
||||
async Task RunInteraction(DiscordClient s, ComponentInteractionCreateEventArgs e)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (e.Message?.Id == ctx.ResponseMessage.Id && e.User.Id == ctx.User.Id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.GetCustomId() == ConfirmButton.CustomId)
|
||||
{
|
||||
var tfa_result = await e.RequestTwoFactorAsync(s);
|
||||
|
||||
if (tfa_result.Result is TwoFactorResult.ValidCode or TwoFactorResult.InvalidCode)
|
||||
_ = tfa_result.ComponentInteraction.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||||
|
||||
if (tfa_result.Result == TwoFactorResult.ValidCode)
|
||||
{
|
||||
Confirmed = true;
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Enrolled successfully.`").AsSuccess(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("Invalid Code");
|
||||
}
|
||||
else if (e.GetCustomId() == MessageComponents.CancelButtonId)
|
||||
{
|
||||
throw new Exception("Cancelled");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ctx.Client.DisenrollTwoFactor(ctx.User.Id);
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Failed to authenticate. Enrollment reverted.`").AsError(ctx));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ctx.Client.ComponentInteractionCreated += RunInteraction;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
// 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 Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
|
||||
namespace ProjectMakoto.Commands.DevTools;
|
||||
|
||||
internal sealed class EvaluationCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckBotOwner();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
if (ctx.CommandType is not Enums.CommandType.ApplicationCommand and not Enums.CommandType.ContextMenu)
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithEmbed(new DiscordEmbedBuilder().WithDescription("Evaluating CScript has the potential of leaking confidential information. Are you sure you want to run this command as Prefix Command?").AsWarning(ctx))
|
||||
.AddComponents(new List<DiscordComponent> { new DiscordButtonComponent(ButtonStyle.Success, "yes", "Yes"),
|
||||
new DiscordButtonComponent(ButtonStyle.Danger, "no", "No")}));
|
||||
|
||||
var result = await ctx.ResponseMessage.WaitForButtonAsync(ctx.User);
|
||||
|
||||
if (result.TimedOut || result.GetCustomId() != "yes")
|
||||
{
|
||||
this.DeleteOrInvalidate();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var rawCode = (string)arguments["code"];
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Evaluating..`").AsLoading(ctx));
|
||||
|
||||
var code = RegexTemplates.Code.Match(rawCode).Groups[1]?.Value?.Trim() ?? "";
|
||||
|
||||
if (code.IsNullOrWhiteSpace())
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`No code block was found.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var options = ScriptOptions.Default;
|
||||
options = options.WithImports(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"System.Linq",
|
||||
"System.Text",
|
||||
"System.Threading.Tasks",
|
||||
"DisCatSharp",
|
||||
"DisCatSharp.Entities",
|
||||
"DisCatSharp.Interactivity",
|
||||
"DisCatSharp.Interactivity.Extensions",
|
||||
"DisCatSharp.Interactivity.Enums",
|
||||
"DisCatSharp.Enums",
|
||||
"Newtonsoft.Json"
|
||||
);
|
||||
options = options.WithReferences(AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && !x.Location.IsNullOrWhiteSpace()));
|
||||
|
||||
var script = CSharpScript.Create(code, options, typeof(SharedCommandContext));
|
||||
_ = script.Compile();
|
||||
var result = await script.RunAsync(ctx).ConfigureAwait(false);
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithTitle("Successful Evaluation")
|
||||
.WithDescription($"{(result.ReturnValue?.ToString().IsNullOrWhiteSpace() ?? true ? "`The evaluation did not return any result.`" : $"{result.ReturnValue}")}").AsSuccess(ctx));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithTitle("Failed Evaluation").WithDescription($"```{ex.Message.SanitizeForCode()}```").AsError(ctx));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
183
ProjectMakoto/Commands/Maintainers/DevTools/GlobalBanCommand.cs
Normal file
183
ProjectMakoto/Commands/Maintainers/DevTools/GlobalBanCommand.cs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// 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.DevTools;
|
||||
internal sealed class GlobalBanCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var victims = await DiscordExtensions.ParseStringAsUserArray((string)arguments["victims"], ctx.Client);
|
||||
var reason = (string)arguments["reason"];
|
||||
|
||||
if (victims?.Length <= 0)
|
||||
{
|
||||
_ = this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Please provide user(s).`").AsError(ctx, "Global Ban"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (reason.IsNullOrWhiteSpace())
|
||||
{
|
||||
_ = this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Please provide a reason for the global ban.`").AsError(ctx, "Global Ban"));
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStatus = new Dictionary<DiscordUser, CurrentStatus>([
|
||||
..victims.Select(x => new KeyValuePair<DiscordUser, CurrentStatus>(x, CurrentStatus.InQueue)).ToList()
|
||||
]);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var desc = string.Empty;
|
||||
|
||||
lock (currentStatus)
|
||||
{
|
||||
desc = $"{string.Join("\n\n", currentStatus
|
||||
.Select(x =>
|
||||
{
|
||||
var emoji = x.Value switch
|
||||
{
|
||||
CurrentStatus.Invalid => DiscordEmoji.FromUnicode("❌"),
|
||||
CurrentStatus.Added => DiscordEmoji.FromUnicode("✅"),
|
||||
CurrentStatus.Changed => DiscordEmoji.FromUnicode("🔄"),
|
||||
CurrentStatus.InQueue => DiscordEmoji.FromUnicode("🕒"),
|
||||
CurrentStatus.InProgress => EmojiTemplates.GetLoading(ctx.Bot),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
var text = x.Value switch
|
||||
{
|
||||
CurrentStatus.Invalid => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `This user cannot be global banned.`",
|
||||
CurrentStatus.Added => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `User was added to global ban list.`",
|
||||
CurrentStatus.Changed => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `User was already global banned, updated entry.`",
|
||||
CurrentStatus.InQueue => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `In queue..`",
|
||||
CurrentStatus.InProgress => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `Processing..`",
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return $"{emoji} {text}";
|
||||
}))}";
|
||||
}
|
||||
|
||||
var embed = new DiscordEmbedBuilder();
|
||||
|
||||
var done = true;
|
||||
|
||||
if (currentStatus.All(x => x.Value is CurrentStatus.Changed or CurrentStatus.Added))
|
||||
_ = embed.AsSuccess(ctx, "Global Ban").WithDescription(desc.TruncateWithIndication(2000));
|
||||
else if (currentStatus.All(x => x.Value is CurrentStatus.Changed or CurrentStatus.Added or CurrentStatus.Invalid))
|
||||
_ = embed.AsWarning(ctx, "Global Ban").WithDescription(desc.TruncateWithIndication(2000));
|
||||
else
|
||||
{
|
||||
_ = embed.AsLoading(ctx, "Global Ban").WithDescription($"`Global banning {currentStatus.Count} users..`\n\n{desc}".TruncateWithIndication(2000));
|
||||
done = false;
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit(embed);
|
||||
|
||||
if (done)
|
||||
return;
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}).Add(ctx.Bot, ctx);
|
||||
|
||||
foreach (var victim in currentStatus)
|
||||
{
|
||||
currentStatus[victim.Key] = CurrentStatus.InProgress;
|
||||
await Task.Delay(2000);
|
||||
|
||||
if (ctx.Bot.globalBans.ContainsKey(victim.Key.Id))
|
||||
{
|
||||
ctx.Bot.globalBans[victim.Key.Id].Reason = reason;
|
||||
ctx.Bot.globalBans[victim.Key.Id].Moderator = ctx.User.Id;
|
||||
currentStatus[victim.Key] = CurrentStatus.Changed;
|
||||
|
||||
var announceChannel1 = await ctx.Client.GetChannelAsync(ctx.Bot.status.LoadedConfig.Channels.GlobalBanAnnouncements);
|
||||
_ = await announceChannel1.SendMessageAsync(new DiscordEmbedBuilder
|
||||
{
|
||||
Author = new DiscordEmbedBuilder.EmbedAuthor
|
||||
{
|
||||
Name = ctx.CurrentUser.GetUsername(),
|
||||
IconUrl = AuditLogIcons.UserUpdated
|
||||
},
|
||||
Description = $"The global ban entry of {victim.Key.Mention} `{victim.Key.GetUsernameWithIdentifier()}` (`{victim.Key.Id}`) was updated.\n\n" +
|
||||
$"Reason: `{reason.SanitizeForCode()}`\n" +
|
||||
$"Moderator: {ctx.User.Mention} `{ctx.User.GetUsernameWithIdentifier()}` (`{ctx.User.Id}`)",
|
||||
Color = EmbedColors.Warning,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctx.Bot.status.TeamMembers.Contains(victim.Key.Id))
|
||||
{
|
||||
currentStatus[victim.Key] = CurrentStatus.Invalid;
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx.Bot.globalBans.Add(victim.Key.Id, new(ctx.Bot, "globalbans", victim.Key.Id) { Reason = reason, Moderator = ctx.User.Id });
|
||||
|
||||
var Success = 0;
|
||||
var Failed = 0;
|
||||
|
||||
foreach (var b in ctx.Client.Guilds.OrderByDescending(x => x.Key == ctx.Guild.Id))
|
||||
{
|
||||
if (!ctx.Bot.Guilds.ContainsKey(b.Key))
|
||||
ctx.Bot.Guilds.Add(b.Key, new Guild(ctx.Bot, b.Key));
|
||||
|
||||
if (ctx.Bot.Guilds[b.Key].Join.AutoBanGlobalBans)
|
||||
{
|
||||
try
|
||||
{
|
||||
await b.Value.BanMemberAsync(victim.Key.Id, 7, $"Globalban: {reason}");
|
||||
Success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception occurred while trying to ban user from {guild}", b.Key);
|
||||
Failed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentStatus[victim.Key] = CurrentStatus.Added;
|
||||
|
||||
var announceChannel = await ctx.Client.GetChannelAsync(ctx.Bot.status.LoadedConfig.Channels.GlobalBanAnnouncements);
|
||||
_ = await announceChannel.SendMessageAsync(new DiscordEmbedBuilder
|
||||
{
|
||||
Author = new DiscordEmbedBuilder.EmbedAuthor
|
||||
{
|
||||
Name = ctx.CurrentUser.GetUsername(),
|
||||
IconUrl = AuditLogIcons.UserBanned
|
||||
},
|
||||
Description = $"{victim.Key.Mention} `{victim.Key.GetUsernameWithIdentifier()}` (`{victim.Key.Id}`) was added to the global ban list.\n\n" +
|
||||
$"Reason: `{reason.SanitizeForCode()}`\n" +
|
||||
$"Moderator: {ctx.User.Mention} `{ctx.User.GetUsernameWithIdentifier()}` (`{ctx.User.Id}`)",
|
||||
Color = EmbedColors.Error,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private enum CurrentStatus
|
||||
{
|
||||
InQueue,
|
||||
InProgress,
|
||||
Changed,
|
||||
Added,
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class GlobalNotesCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx, true))
|
||||
return;
|
||||
|
||||
var victim = (DiscordUser)arguments["victim"];
|
||||
|
||||
var ModeratorCache = new Dictionary<ulong, DiscordUser>();
|
||||
|
||||
if (ctx.Bot.globalNotes.TryGetValue(victim.Id, out var globalNotes))
|
||||
foreach (var b in globalNotes.Notes)
|
||||
{
|
||||
if (ModeratorCache.ContainsKey(b.Moderator))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
ModeratorCache.Add(b.Moderator, await ctx.Client.GetUserAsync(b.Moderator));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
ModeratorCache.Add(b.Moderator, null);
|
||||
}
|
||||
}
|
||||
|
||||
var AddButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), "Add Notes", false, DiscordEmoji.FromUnicode("➕").ToComponent());
|
||||
var RemoveButton = new DiscordButtonComponent(ButtonStyle.Primary, Guid.NewGuid().ToString(), "Remove Notes", (!ctx.Bot.globalNotes.ContainsKey(victim.Id)), DiscordEmoji.FromUnicode("➖").ToComponent());
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder()
|
||||
.WithEmbed(new DiscordEmbedBuilder()
|
||||
.WithDescription($"{victim.Mention} `has {(ctx.Bot.globalNotes.TryGetValue(victim.Id, out var noteObj) ? noteObj.Notes.Length : 0)} global notes.`")
|
||||
.AddFields((noteObj is not null ? noteObj.Notes.Take(20).Select(x => new DiscordEmbedField(" ", $"{x.Reason.FullSanitize()} - `{(ModeratorCache[x.Moderator] is null ? "Unknown#0000" : ModeratorCache[x.Moderator].GetUsernameWithIdentifier())}` {x.Timestamp.ToTimestamp()}")) : new List<DiscordEmbedField>())))
|
||||
.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;
|
||||
}
|
||||
|
||||
if (Button.GetCustomId() == AddButton.CustomId)
|
||||
{
|
||||
var ModalResult = await this.PromptModalWithRetry(Button.Result.Interaction,
|
||||
new DiscordInteractionModalBuilder().AddTextComponent(new DiscordTextComponent(TextComponentStyle.Paragraph, "Note", "New Note", "", 1, 256, true)), false);
|
||||
|
||||
if (ModalResult.TimedOut)
|
||||
{
|
||||
this.ModifyToTimedOut(true);
|
||||
return;
|
||||
}
|
||||
else if (ModalResult.Cancelled)
|
||||
{
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (ModalResult.Errored)
|
||||
{
|
||||
throw ModalResult.Exception;
|
||||
}
|
||||
|
||||
var note = ModalResult.Result.Interaction.GetModalValueByCustomId("Note");
|
||||
|
||||
ctx.Bot.globalNotes[victim.Id].Notes = ctx.Bot.globalNotes[victim.Id].Notes.Add(new GlobalNote.Note() { Moderator = ctx.User.Id, Reason = note });
|
||||
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (Button.GetCustomId() == RemoveButton.CustomId)
|
||||
{
|
||||
_ = Button.Result.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate);
|
||||
|
||||
var SelectionResult = await this.PromptCustomSelection(ctx.Bot.globalNotes[victim.Id].Notes
|
||||
.Select(x => new DiscordStringSelectComponentOption(x.Reason.TruncateWithIndication(100), x.Timestamp.Ticks.ToString(), $"Added by {(ModeratorCache[x.Moderator] is null ? "Unknown#0000" : ModeratorCache[x.Moderator].GetUsernameWithIdentifier())} {x.Timestamp.GetTimespanSince().GetHumanReadable()} ago")).ToList());
|
||||
|
||||
if (SelectionResult.TimedOut)
|
||||
{
|
||||
this.ModifyToTimedOut(true);
|
||||
return;
|
||||
}
|
||||
else if (SelectionResult.Cancelled)
|
||||
{
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (SelectionResult.Errored)
|
||||
{
|
||||
throw SelectionResult.Exception;
|
||||
}
|
||||
|
||||
ctx.Bot.globalNotes[victim.Id].Notes = ctx.Bot.globalNotes[victim.Id].Notes.Remove(x => x.UUID, ctx.Bot.globalNotes[victim.Id].Notes.First(x => x.Timestamp.Ticks.ToString() == SelectionResult.Result));
|
||||
await this.ExecuteCommand(ctx, arguments);
|
||||
return;
|
||||
}
|
||||
else if (Button.GetCustomId() == MessageComponents.CancelButtonId)
|
||||
{
|
||||
this.DeleteOrInvalidate();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
namespace ProjectMakoto.Commands.DevTools;
|
||||
|
||||
internal sealed class GlobalUnbanCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var victims = await DiscordExtensions.ParseStringAsUserArray((string)arguments["victims"], ctx.Client);
|
||||
var UnbanFromGuilds = (bool)arguments["UnbanFromGuilds"];
|
||||
|
||||
if (victims?.Length <= 0)
|
||||
{
|
||||
_ = this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Please provide user(s).`").AsError(ctx, "Global Ban"));
|
||||
return;
|
||||
}
|
||||
|
||||
var currentStatus = new Dictionary<DiscordUser, CurrentStatus>([
|
||||
..victims.Select(x => new KeyValuePair<DiscordUser, CurrentStatus>(x, CurrentStatus.InQueue)).ToList()
|
||||
]);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var desc = string.Empty;
|
||||
|
||||
lock (currentStatus)
|
||||
{
|
||||
desc = $"{string.Join("\n\n", currentStatus
|
||||
.Select(x =>
|
||||
{
|
||||
var emoji = x.Value switch
|
||||
{
|
||||
CurrentStatus.Invalid => DiscordEmoji.FromUnicode("❌"),
|
||||
CurrentStatus.Removed => DiscordEmoji.FromUnicode("✅"),
|
||||
CurrentStatus.InQueue => DiscordEmoji.FromUnicode("🕒"),
|
||||
CurrentStatus.InProgress => EmojiTemplates.GetLoading(ctx.Bot),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
var text = x.Value switch
|
||||
{
|
||||
CurrentStatus.Invalid => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `This user is not on the global ban list.`",
|
||||
CurrentStatus.Removed => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `User was removed from the global ban list.`",
|
||||
CurrentStatus.InQueue => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `In queue..`",
|
||||
CurrentStatus.InProgress => $"**`{x.Key.GetUsernameWithIdentifier()} ({x.Key.Id})`**\n{EmojiTemplates.GetInVisible(ctx.Bot)} `Processing..`",
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return $"{emoji} {text}";
|
||||
}))}";
|
||||
}
|
||||
|
||||
var embed = new DiscordEmbedBuilder();
|
||||
|
||||
var done = true;
|
||||
|
||||
if (currentStatus.All(x => x.Value is CurrentStatus.Removed))
|
||||
_ = embed.AsSuccess(ctx, "Global Ban").WithDescription(desc.TruncateWithIndication(2000));
|
||||
else if (currentStatus.All(x => x.Value is CurrentStatus.Removed or CurrentStatus.Invalid))
|
||||
_ = embed.AsWarning(ctx, "Global Ban").WithDescription(desc.TruncateWithIndication(2000));
|
||||
else
|
||||
{
|
||||
_ = embed.AsLoading(ctx, "Global Ban").WithDescription($"`Removing Global ban for {currentStatus.Count} users..`\n\n{desc}".TruncateWithIndication(2000));
|
||||
done = false;
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit(embed);
|
||||
|
||||
if (done)
|
||||
return;
|
||||
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}).Add(ctx.Bot, ctx);
|
||||
|
||||
foreach (var victim in currentStatus)
|
||||
{
|
||||
currentStatus[victim.Key] = CurrentStatus.InProgress;
|
||||
await Task.Delay(2000);
|
||||
|
||||
if (!ctx.Bot.globalBans.ContainsKey(victim.Key.Id))
|
||||
{
|
||||
currentStatus[victim.Key] = CurrentStatus.Invalid;
|
||||
continue;
|
||||
}
|
||||
|
||||
_ = ctx.Bot.globalBans.Remove(victim.Key.Id);
|
||||
currentStatus[victim.Key] = CurrentStatus.Removed;
|
||||
|
||||
var Success = 0;
|
||||
var Failed = 0;
|
||||
|
||||
if (UnbanFromGuilds)
|
||||
foreach (var b in ctx.Client.Guilds.OrderByDescending(x => x.Key == ctx.Guild.Id))
|
||||
{
|
||||
if (!ctx.Bot.Guilds.ContainsKey(b.Key))
|
||||
ctx.Bot.Guilds.Add(b.Key, new Guild(ctx.Bot, b.Key));
|
||||
|
||||
if (ctx.Bot.Guilds[b.Key].Join.AutoBanGlobalBans)
|
||||
{
|
||||
try
|
||||
{
|
||||
var Ban = await b.Value.GetBanAsync(victim.Key);
|
||||
|
||||
if (Ban.Reason.StartsWith("Globalban: "))
|
||||
await b.Value.UnbanMemberAsync(victim.Key, $"Globalban removed.");
|
||||
|
||||
Success++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception occurred while trying to unban user from {guild}", b.Key);
|
||||
Failed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var announceChannel = await ctx.Client.GetChannelAsync(ctx.Bot.status.LoadedConfig.Channels.GlobalBanAnnouncements);
|
||||
_ = await announceChannel.SendMessageAsync(new DiscordEmbedBuilder
|
||||
{
|
||||
Author = new DiscordEmbedBuilder.EmbedAuthor
|
||||
{
|
||||
Name = ctx.CurrentUser.GetUsername(),
|
||||
IconUrl = AuditLogIcons.UserBanRemoved
|
||||
},
|
||||
Description = $"{victim.Key.Mention} `{victim.Key.GetUsernameWithIdentifier()}` (`{victim.Key.Id}`) was removed from the global ban list.\n\n" +
|
||||
$"Moderator: {ctx.User.Mention} `{ctx.User.GetUsernameWithIdentifier()}` (`{ctx.User.Id}`)",
|
||||
Color = EmbedColors.Success,
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private enum CurrentStatus
|
||||
{
|
||||
InQueue,
|
||||
InProgress,
|
||||
Removed,
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
211
ProjectMakoto/Commands/Maintainers/DevTools/InfoCommand.cs
Normal file
211
ProjectMakoto/Commands/Maintainers/DevTools/InfoCommand.cs
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class InfoCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
if (await ctx.DbUser.Cooldown.WaitForModerate(ctx))
|
||||
return;
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Fetching system details..`").AsLoading(ctx));
|
||||
|
||||
Dictionary<DateTime, Entities.SystemMonitor.SystemInfo> history = new();
|
||||
|
||||
try
|
||||
{
|
||||
var rawHistory = ctx.Bot.MonitorClient.GetHistory().GroupBy(x => $"{x.Key.Hour}-{(int)Math.Floor(x.Key.Minute / 6d)}");
|
||||
foreach (var entry in rawHistory)
|
||||
{
|
||||
history.Add(entry.Last().Key, new()
|
||||
{
|
||||
Cpu = new()
|
||||
{
|
||||
Load = entry.Average(x => x.Value.Cpu.Load),
|
||||
Temperature = entry.Average(x => x.Value.Cpu.Temperature)
|
||||
},
|
||||
Memory = new()
|
||||
{
|
||||
Available = entry.Average(x => x.Value.Memory.Available),
|
||||
Used = entry.Average(x => x.Value.Memory.Used),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch {}
|
||||
|
||||
history.Add(DateTime.UtcNow, await ctx.Bot.MonitorClient.GetCurrent());
|
||||
history = history.OrderBy(x => x.Key.Ticks).ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
var ServerUptime = "";
|
||||
if (Environment.OSVersion.Platform == PlatformID.Unix)
|
||||
{
|
||||
ProcessStartInfo info = new()
|
||||
{
|
||||
FileName = "bash",
|
||||
Arguments = $"-c uptime",
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false
|
||||
};
|
||||
|
||||
var b = Process.Start(info);
|
||||
|
||||
b.WaitForExit();
|
||||
|
||||
var Output = b.StandardOutput.ReadToEnd();
|
||||
ServerUptime = Output.Remove(Output.IndexOf(','), Output.Length - Output.IndexOf(',')).TrimStart();
|
||||
}
|
||||
|
||||
IEnumerable<string> bFile;
|
||||
|
||||
try
|
||||
{
|
||||
bFile = File.ReadLines("LatestGitPush.cfg");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
bFile = new List<string>
|
||||
{
|
||||
"Developer Version",
|
||||
"dev",
|
||||
$"{DateTime.UtcNow:dd.MM.yy}",
|
||||
$"{DateTime.UtcNow:HH:mm:ss},00"
|
||||
};
|
||||
}
|
||||
|
||||
var Version = bFile.First().Trim();
|
||||
var Branch = bFile.Skip(1).First().Trim();
|
||||
var Date = bFile.Skip(2).First().Trim().Replace("/", ".");
|
||||
|
||||
var Time = bFile.Skip(3).First().Trim();
|
||||
Time = Time[..Time.IndexOf(',')];
|
||||
|
||||
var miscEmbed = new DiscordEmbedBuilder().WithTitle($"{ctx.CurrentUser.GetUsername()} Details")
|
||||
.AddField(new DiscordEmbedField("Currently running as", $"`{ctx.CurrentUser.GetUsernameWithIdentifier()}`", true))
|
||||
.AddField(new DiscordEmbedField("Process PID", $"`{Environment.ProcessId}`", true))
|
||||
.AddField(new DiscordEmbedField(" ", $" ", true))
|
||||
.AddField(new DiscordEmbedField("Bot uptime", $"`{Math.Round((DateTime.UtcNow - ctx.Bot.status.startupTime).TotalHours, 2)} hours`", true))
|
||||
.AddField(new DiscordEmbedField("Discord API Latency", $"`{ctx.Client.Ping}ms`", true))
|
||||
.AddField(new DiscordEmbedField("Server uptime", $"`{(ServerUptime.IsNullOrWhiteSpace() ? "Currently unavailable" : ServerUptime)}`", true))
|
||||
.AddField(new DiscordEmbedField("Currently running software", $"`Project Makoto by {(await ctx.Client.GetUserAsync(411950662662881290)).GetUsernameWithIdentifier()} ({Version} ({Branch}) built on the {Date} at {Time})`"))
|
||||
.AddField(new DiscordEmbedField("Current bot library and version", $"[`{ctx.Client.BotLibrary} {ctx.Client.VersionString}`](https://github.com/Aiko-IT-Systems/DisCatSharp)"))
|
||||
.AddField(new DiscordEmbedField("Plugin Status",
|
||||
$"{(ctx.Bot.status.LoadedConfig.EnablePlugins && ctx.Bot.Plugins.Count > 0 ?
|
||||
$"`{ctx.Bot.Plugins.Count}/{new DirectoryInfo("Plugins")?.GetFiles()?.Where(x => !x.Name.StartsWith('.') && x.Extension == ".pmpl")?.Count() ?? 0} loaded`\n\n" +
|
||||
$"{string.Join("\n", ctx.Bot.Plugins.Select(x => $"- {x.Value.OfficialPlugin.ToEmote(ctx.Bot)} `{x.Value.Name}` `v{x.Value.Version}` by {x.Value.AuthorUser?.Mention ?? "`N/A`"} (`{x.Value.Author}`)"))}" :
|
||||
"`Disabled`")}"))
|
||||
.AsInfo(ctx).WithFooter().WithTimestamp(null);
|
||||
|
||||
var cpuEmbed1 = new DiscordEmbedBuilder()
|
||||
.WithTitle("CPU")
|
||||
.AddField(new DiscordEmbedField("Load", $"`{history.MaxBy(x => x.Key).Value.Cpu.Load.ToString("N0", CultureInfo.CreateSpecificCulture("en-US")),3}%`", true))
|
||||
.AddField(new DiscordEmbedField("Temperature", $"`{history.MaxBy(x => x.Key).Value.Cpu.Temperature.ToString("N0", CultureInfo.CreateSpecificCulture("en-US")),2}°C`", true))
|
||||
.AsLoading(ctx).WithFooter().WithTimestamp(null).WithAuthor();
|
||||
|
||||
var memoryEmbed = new DiscordEmbedBuilder()
|
||||
.WithTitle("Memory")
|
||||
.AddField(new DiscordEmbedField("Usage", $"`{history.MaxBy(x => x.Key).Value.Memory.Used.ToString("N0", CultureInfo.CreateSpecificCulture("en-US"))}/{history.MaxBy(x => x.Key).Value.Memory.Total.ToString("N0", CultureInfo.CreateSpecificCulture("en-US"))} MB`", true))
|
||||
.AsLoading(ctx);
|
||||
|
||||
var embeds = new List<DiscordEmbed>() { miscEmbed };
|
||||
|
||||
if (ctx.Bot.status.LoadedConfig.MonitorSystem.Enabled)
|
||||
embeds.AddRange(cpuEmbed1, memoryEmbed);
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbeds(embeds));
|
||||
|
||||
if (!ctx.Bot.status.LoadedConfig.MonitorSystem.Enabled)
|
||||
return;
|
||||
|
||||
Dictionary<string, byte[]> charts = new();
|
||||
|
||||
try
|
||||
{
|
||||
var prev = "";
|
||||
var qc = ctx.Bot.ChartsClient.GetChart(800, 600, history.Select(x =>
|
||||
{
|
||||
var value = x.Key.ToString("HH:mm");
|
||||
if (prev == value)
|
||||
return " ";
|
||||
prev = value;
|
||||
return $"{value}";
|
||||
}), new ChartGeneration.Dataset[]
|
||||
{
|
||||
new("Usage (%)", history.Select(x => $"{(int)x.Value.Cpu.Load}"), "getGradientFillHelper('vertical', ['#ff0000', '#00ff00'])"),
|
||||
new("Temperature (°C)", history.Select(x => $"{(int)x.Value.Cpu.Temperature}"), "getGradientFillHelper('vertical', ['#4287f5', '#ff0000'])"),
|
||||
}, 0, 100);
|
||||
|
||||
charts.Add("cpu.png", qc.ToByteArray());
|
||||
cpuEmbed1.ImageUrl = "attachment://cpu.png";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to generate cpu usage graph");
|
||||
cpuEmbed1.ImageUrl = "attachment://1.png";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = cpuEmbed1.AsInfo(ctx).WithFooter().WithTimestamp(null).WithAuthor();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var prev = "";
|
||||
var qc = ctx.Bot.ChartsClient.GetChart(800, 600, history.Select(x =>
|
||||
{
|
||||
var value = x.Key.ToString("HH:mm");
|
||||
if (prev == value)
|
||||
return " ";
|
||||
prev = value;
|
||||
return $"{value}";
|
||||
}),
|
||||
new ChartGeneration.Dataset[]
|
||||
{
|
||||
new("Usage (MB)", history.Select(x => $"{(int)x.Value.Memory.Used}"))
|
||||
}, 0, (int)history.First().Value.Memory.Total);
|
||||
|
||||
charts.Add("mem.png", qc.ToByteArray());
|
||||
memoryEmbed.ImageUrl = "attachment://mem.png";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Failed to generate memory graph");
|
||||
memoryEmbed.ImageUrl = "attachment://1.png";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = memoryEmbed.AsInfo(ctx).WithAuthor();
|
||||
}
|
||||
|
||||
var list = new List<DiscordEmbed>();
|
||||
list.Add(miscEmbed.WithImageUrl("attachment://1.png"));
|
||||
list.Add(cpuEmbed1);
|
||||
list.Add(memoryEmbed);
|
||||
|
||||
var files = charts.ToDictionary(x => x.Key, y => (Stream)new MemoryStream(y.Value));
|
||||
try
|
||||
{
|
||||
files.Add("1.png", new FileStream("Assets/1.png", FileMode.Open));
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().AddEmbeds(new DiscordEmbed[] { miscEmbed.Build(), cpuEmbed1.Build(), memoryEmbed.Build() }).WithFiles(files));
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (var file in files)
|
||||
file.Value.Dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
29
ProjectMakoto/Commands/Maintainers/DevTools/LogCommand.cs
Normal file
29
ProjectMakoto/Commands/Maintainers/DevTools/LogCommand.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class LogCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var Level = (int)arguments["Level"];
|
||||
|
||||
if (Level is > ((int)LogEventLevel.Fatal) or < ((int)LogEventLevel.Verbose))
|
||||
throw new Exception("Invalid Log Level");
|
||||
|
||||
ctx.Bot.loggingLevel.MinimumLevel = (LogEventLevel)Level;
|
||||
_ = await this.RespondOrEdit($"`Changed LogLevel to '{Enum.GetName((LogEventLevel)Level)}'`");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class Quit2FASessionCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx)
|
||||
=> this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
ctx.DbUser.LastSuccessful2FA = DateTime.MinValue;
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription("`Your active 2FA Session, if present, has been quit.`").AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class RawGuildCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var guild = (ulong?)arguments["guild"];
|
||||
guild ??= ctx.Guild.Id;
|
||||
|
||||
_ = await this.RespondOrEdit(new DiscordMessageBuilder().WithFile("guild.json", JsonConvert.SerializeObject(ctx.Bot.Guilds[guild.Value], Formatting.Indented, new JsonSerializerSettings
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||
}).ToStream()));
|
||||
});
|
||||
}
|
||||
}
|
||||
35
ProjectMakoto/Commands/Maintainers/DevTools/StopCommand.cs
Normal file
35
ProjectMakoto/Commands/Maintainers/DevTools/StopCommand.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class StopCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var msg = await this.RespondOrEdit(new DiscordMessageBuilder().WithContent("Confirm?").AddComponents(new DiscordButtonComponent(ButtonStyle.Danger, "Shutdown", "Confirm shutdown", false, new DiscordComponentEmoji(DiscordEmoji.FromUnicode("⛔")))));
|
||||
|
||||
var x = await ctx.WaitForButtonAsync(TimeSpan.FromMinutes(1));
|
||||
|
||||
if (x.TimedOut)
|
||||
{
|
||||
_ = await this.RespondOrEdit("_Interaction timed out._");
|
||||
return;
|
||||
}
|
||||
|
||||
_ = await this.RespondOrEdit("Shutting down!");
|
||||
|
||||
await ctx.Bot.ExitApplication(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class UnbanGuildCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var guild = (ulong)arguments["guild"];
|
||||
|
||||
if (!ctx.Bot.bannedGuilds.ContainsKey(guild))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Guild '{guild}' is not banned from using the bot.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
_ = ctx.Bot.bannedGuilds.Remove(guild);
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`Guild '{guild}' was unbanned from using the bot.`").AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.DevTools;
|
||||
|
||||
internal sealed class UnbanUserCommand : BaseCommand
|
||||
{
|
||||
public override Task<bool> BeforeExecution(SharedCommandContext ctx) => this.CheckMaintenance();
|
||||
|
||||
public override Task ExecuteCommand(SharedCommandContext ctx, Dictionary<string, object> arguments)
|
||||
{
|
||||
return Task.Run(async () =>
|
||||
{
|
||||
var victim = (DiscordUser)arguments["victim"];
|
||||
|
||||
if (!ctx.Bot.bannedUsers.ContainsKey(victim.Id))
|
||||
{
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`'{victim.GetUsernameWithIdentifier()}' is not banned from using the bot.`").AsError(ctx));
|
||||
return;
|
||||
}
|
||||
|
||||
_ = ctx.Bot.bannedUsers.Remove(victim.Id);
|
||||
_ = await this.RespondOrEdit(new DiscordEmbedBuilder().WithDescription($"`'{victim.GetUsernameWithIdentifier()}' was unbanned from using the bot.`").AsSuccess(ctx));
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue