diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 8b243fcc3..2782b9d74 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -1,4 +1,7 @@ #nullable disable +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using NadekoBot.Modules.Utility.Services; using Newtonsoft.Json; using System.Diagnostics; using System.Text; @@ -32,6 +35,7 @@ public partial class Utility : NadekoModule private readonly IBotCredentials _creds; private readonly DownloadTracker _tracker; private readonly IHttpClientFactory _httpFactory; + private readonly VerboseErrorsService _veService; public Utility( DiscordSocketClient client, @@ -39,7 +43,8 @@ public partial class Utility : NadekoModule IStatsService stats, IBotCredentials creds, DownloadTracker tracker, - IHttpClientFactory httpFactory) + IHttpClientFactory httpFactory, + VerboseErrorsService veService) { _client = client; _coord = coord; @@ -47,6 +52,7 @@ public partial class Utility : NadekoModule _creds = creds; _tracker = tracker; _httpFactory = httpFactory; + _veService = veService; } [Cmd] @@ -482,44 +488,17 @@ public partial class Utility : NadekoModule sem.Release(); } } + + [Cmd] + [RequireContext(ContextType.Guild)] + [UserPerm(GuildPerm.ManageMessages)] + public async partial Task VerboseError(bool? newstate = null) + { + var state = _veService.ToggleVerboseErrors(ctx.Guild.Id, newstate); - - // [NadekoCommand, Usage, Description, Aliases] - // [RequireContext(ContextType.Guild)] - // public async Task CreateMyInvite(CreateInviteType type = CreateInviteType.Any) - // { - // if (type == CreateInviteType.Any) - // { - // if (_inviteService.TryGetInvite(type, out var code)) - // { - // await ReplyErrorLocalizedAsync(strs.your_invite($"https://discord.gg/{code}")); - // return; - // } - // } - // - // var invite = await ((ITextChannel) ctx.Channel).CreateInviteAsync(isUnique: true); - // } - // - // [NadekoCommand, Usage, Description, Aliases] - // [RequireContext(ContextType.Guild)] - // public async partial Task InviteLb(int page = 1) - // { - // if (--page < 0) - // return; - // - // var inviteUsers = await _inviteService.GetInviteUsersAsync(ctx.Guild.Id); - // - // var embed = _eb.Create() - // .WithOkColor(); - // - // await ctx.SendPaginatedConfirmAsync(page, (curPage) => - // { - // var items = inviteUsers.Skip(curPage * 9).Take(9); - // var i = 0; - // foreach (var item in items) - // embed.AddField($"#{curPage * 9 + ++i} {item.UserName} [{item.User.Id}]", item.InvitedUsers); - // - // return embed; - // }, inviteUsers.Count, 9); - // } + if (state) + await ReplyConfirmLocalizedAsync(strs.verbose_errors_enabled); + else + await ReplyConfirmLocalizedAsync(strs.verbose_errors_disabled); + } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/VerboseErrors/EvalCommands.cs b/src/NadekoBot/Modules/Utility/VerboseErrors/EvalCommands.cs new file mode 100644 index 000000000..03d29b9aa --- /dev/null +++ b/src/NadekoBot/Modules/Utility/VerboseErrors/EvalCommands.cs @@ -0,0 +1,76 @@ +#nullable disable +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; + +namespace NadekoBot.Modules.Utility; + +public partial class Utility +{ + [Group] + public partial class EvalCommands : NadekoModule + { + private readonly IServiceProvider _services; + + public EvalCommands(IServiceProvider services) + { + _services = services; + } + + [Cmd] + [NoPublicBot] + [OwnerOnly] + public async partial Task Eval([Leftover] string scriptText) + { + _ = ctx.Channel.TriggerTypingAsync(); + + if (scriptText.StartsWith("```cs")) + scriptText = scriptText[5..]; + else if (scriptText.StartsWith("```")) + scriptText = scriptText[3..]; + + if (scriptText.EndsWith("```")) + scriptText = scriptText[..^3]; + + var script = CSharpScript.Create(scriptText, + ScriptOptions.Default + .WithReferences(this.GetType().Assembly) + .WithImports( + "System", + "NadekoBot", + "NadekoBot.Extensions", + "Microsoft.Extensions.DependencyInjection", + "NadekoBot.Common", + "System.Text", + "System.Text.Json"), + globalsType: typeof(EvalGlobals)); + + try + { + var result = await script.RunAsync(new EvalGlobals() + { + ctx = this.ctx, + guild = this.ctx.Guild, + channel = this.ctx.Channel, + user = this.ctx.User, + self = this, + services = _services + }); + + var output = result.ReturnValue?.ToString(); + if (!string.IsNullOrWhiteSpace(output)) + { + var eb = _eb.Create(ctx) + .WithOkColor() + .AddField("Code", scriptText) + .AddField("Output", output.TrimTo(512)!); + + _ = ctx.Channel.EmbedAsync(eb); + } + } + catch (Exception ex) + { + await SendErrorAsync(ex.Message); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/VerboseErrors/EvalGlobals.cs b/src/NadekoBot/Modules/Utility/VerboseErrors/EvalGlobals.cs new file mode 100644 index 000000000..ef7a69ba1 --- /dev/null +++ b/src/NadekoBot/Modules/Utility/VerboseErrors/EvalGlobals.cs @@ -0,0 +1,13 @@ +// ReSharper disable InconsistentNaming +#nullable disable +namespace NadekoBot.Modules.Utility; + +public class EvalGlobals +{ + public ICommandContext ctx; + public Utility.EvalCommands self; + public IUser user; + public IMessageChannel channel; + public IGuild guild; + public IServiceProvider services; +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/VerboseErrors/VerboseErrorCommands.cs b/src/NadekoBot/Modules/Utility/VerboseErrors/VerboseErrorCommands.cs deleted file mode 100644 index 313a4f70e..000000000 --- a/src/NadekoBot/Modules/Utility/VerboseErrors/VerboseErrorCommands.cs +++ /dev/null @@ -1,24 +0,0 @@ -#nullable disable -using NadekoBot.Modules.Utility.Services; - -namespace NadekoBot.Modules.Utility; - -public partial class Utility -{ - [Group] - public partial class VerboseErrorCommands : NadekoModule - { - [Cmd] - [RequireContext(ContextType.Guild)] - [UserPerm(GuildPerm.ManageMessages)] - public async partial Task VerboseError(bool? newstate = null) - { - var state = _service.ToggleVerboseErrors(ctx.Guild.Id, newstate); - - if (state) - await ReplyConfirmLocalizedAsync(strs.verbose_errors_enabled); - else - await ReplyConfirmLocalizedAsync(strs.verbose_errors_disabled); - } - } -} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/VerboseErrors/VerboseErrorsService.cs b/src/NadekoBot/Modules/Utility/VerboseErrorsService.cs similarity index 100% rename from src/NadekoBot/Modules/Utility/VerboseErrors/VerboseErrorsService.cs rename to src/NadekoBot/Modules/Utility/VerboseErrorsService.cs diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index 0966d4396..1e302ea50 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -39,6 +39,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/NadekoBot/data/aliases.yml b/src/NadekoBot/data/aliases.yml index f8b929355..c66d7e977 100644 --- a/src/NadekoBot/data/aliases.yml +++ b/src/NadekoBot/data/aliases.yml @@ -1305,4 +1305,6 @@ patron: - patron patronmessage: - patronmessage - - patronmsg \ No newline at end of file + - patronmsg +eval: + - eval \ No newline at end of file diff --git a/src/NadekoBot/data/strings/commands/commands.en-US.yml b/src/NadekoBot/data/strings/commands/commands.en-US.yml index 3dd021fa1..0f5238ddf 100644 --- a/src/NadekoBot/data/strings/commands/commands.en-US.yml +++ b/src/NadekoBot/data/strings/commands/commands.en-US.yml @@ -2215,3 +2215,17 @@ patronmessage: desc: "Sends a message to all patrons of the specified tier and higher. Supports embeds." args: - "x hello" +eval: + desc: |- + Execute arbitrary C# code and (optionally) return a result. Several namespaces are included by default. + Special variables available: + `self` - Instance of the command group executing the command (this) + `guild` - Server in which the command is executed + `channel` - Channel in which the command is executed + `user` - User executing the command + `ctx` - Discord.Net command context + `services` - Nadeko's IServiceProvider + args: + - "123 / 4.5f" + - "await ctx.OkAsync();" + - 'await ctx.SendConfirmAsync("uwu");' \ No newline at end of file