-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
538 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
using System.Threading.Tasks; | ||
using Discord.Commands; | ||
using Discord.Interactions; | ||
using Zhongli.Services.Core.Preconditions.Interactions; | ||
using Zhongli.Services.Evaluation; | ||
using InteractionContext = Zhongli.Data.Models.Discord.InteractionContext; | ||
|
||
namespace Zhongli.Bot.Modules; | ||
|
||
public class InteractiveGeneralModule : InteractionModuleBase<SocketInteractionContext> | ||
{ | ||
private readonly EvaluationService _evaluation; | ||
|
||
public InteractiveGeneralModule(EvaluationService evaluation) { _evaluation = evaluation; } | ||
|
||
[SlashCommand("eval", "Evaluate C# code")] | ||
[RequireTeamMember] | ||
public async Task EvalAsync([Remainder] string code) | ||
{ | ||
await DeferAsync(true); | ||
var context = new InteractionContext(Context); | ||
var result = await _evaluation.EvaluateAsync(context, code); | ||
|
||
var embed = EvaluationService.BuildEmbed(context, result); | ||
await FollowupAsync(embed: embed.Build(), ephemeral: true); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
Zhongli.Services/Core/Preconditions/Commands/RequireTeamMemberAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Discord; | ||
using Discord.Commands; | ||
|
||
namespace Zhongli.Services.Core.Preconditions.Commands; | ||
|
||
public class RequireTeamMemberAttribute : PreconditionAttribute | ||
{ | ||
public override async Task<PreconditionResult> CheckPermissionsAsync( | ||
ICommandContext context, CommandInfo command, IServiceProvider services) | ||
{ | ||
if (context.Client.TokenType is not TokenType.Bot) | ||
{ | ||
return PreconditionResult.FromError( | ||
$"{nameof(RequireTeamMemberAttribute)} is not supported by this TokenType."); | ||
} | ||
|
||
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false); | ||
|
||
if (context.User.Id == application.Owner.Id | ||
|| application.Team.OwnerUserId == application.Owner.Id | ||
|| application.Team.TeamMembers.Any(t => context.User.Id == t.User.Id)) | ||
return PreconditionResult.FromSuccess(); | ||
|
||
return PreconditionResult.FromError("Command can only be run by team members of the bot."); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
Zhongli.Services/Core/Preconditions/Interactions/RequireTeamMemberAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Discord; | ||
using Discord.Interactions; | ||
|
||
namespace Zhongli.Services.Core.Preconditions.Interactions; | ||
|
||
public class RequireTeamMemberAttribute : PreconditionAttribute | ||
{ | ||
public override async Task<PreconditionResult> CheckRequirementsAsync( | ||
IInteractionContext context, ICommandInfo command, IServiceProvider services) | ||
{ | ||
if (context.Client.TokenType is not TokenType.Bot) | ||
{ | ||
return PreconditionResult.FromError( | ||
$"{nameof(RequireTeamMemberAttribute)} is not supported by this TokenType."); | ||
} | ||
|
||
var application = await context.Client.GetApplicationInfoAsync().ConfigureAwait(false); | ||
|
||
if (context.User.Id == application.Owner.Id | ||
|| application.Team.OwnerUserId == application.Owner.Id | ||
|| application.Team.TeamMembers.Any(t => context.User.Id == t.User.Id)) | ||
return PreconditionResult.FromSuccess(); | ||
|
||
return PreconditionResult.FromError("Command can only be run by team members of the bot."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.IO; | ||
using System.Text; | ||
|
||
namespace Zhongli.Services.Evaluation; | ||
|
||
[SuppressMessage("ReSharper", "UnusedMember.Global")] | ||
[SuppressMessage("ReSharper", "UnusedParameter.Global")] | ||
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] | ||
[SuppressMessage("Performance", "CA1822:Mark members as static")] | ||
public class ConsoleLikeStringWriter : StringWriter | ||
{ | ||
public ConsoleLikeStringWriter(StringBuilder builder) : base(builder) { } | ||
|
||
public ConsoleKeyInfo ReadKey() => new('z', ConsoleKey.Z, false, false, false); | ||
|
||
public ConsoleKeyInfo ReadKey(bool z) | ||
{ | ||
if (z) Write("z"); | ||
return ReadKey(); | ||
} | ||
|
||
public int Read() => 0; | ||
|
||
public Stream OpenStandardError() => new MemoryStream(); | ||
|
||
public Stream OpenStandardError(int a) => new MemoryStream(a); | ||
|
||
public Stream OpenStandardInput() => new MemoryStream(); | ||
|
||
public Stream OpenStandardInput(int a) => new MemoryStream(a); | ||
|
||
public Stream OpenStandardOutput() => new MemoryStream(); | ||
|
||
public Stream OpenStandardOutput(int a) => new MemoryStream(a); | ||
|
||
public string ReadLine() => $"{nameof(Zhongli)}{Environment.NewLine}"; | ||
|
||
public void Beep() { } | ||
|
||
public void Beep(int a, int b) { } | ||
|
||
public void Clear() { } | ||
|
||
public void MoveBufferArea(int a, int b, int c, int d, int e) { } | ||
|
||
public void MoveBufferArea(int a, int b, int c, int d, int e, char f, ConsoleColor g, ConsoleColor h) { } | ||
|
||
public void ResetColor() { } | ||
|
||
public void SetBufferSize(int a, int b) { } | ||
|
||
public void SetCursorPosition(int a, int b) { } | ||
|
||
public void SetError(TextWriter wr) { } | ||
|
||
public void SetIn(TextWriter wr) { } | ||
|
||
public void SetOut(TextWriter wr) { } | ||
|
||
public void SetWindowPosition(int a, int b) { } | ||
|
||
public void SetWindowSize(int a, int b) { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.Scripting; | ||
|
||
namespace Zhongli.Services.Evaluation; | ||
|
||
public class EvaluationResult | ||
{ | ||
public static readonly JsonSerializerOptions SerializerOptions = new() | ||
{ | ||
MaxDepth = 10240, | ||
IncludeFields = true, | ||
PropertyNameCaseInsensitive = true, | ||
WriteIndented = true, | ||
ReferenceHandler = ReferenceHandler.IgnoreCycles | ||
}; | ||
|
||
public EvaluationResult() { } | ||
|
||
public EvaluationResult(string code, ScriptState<object?> state, string consoleOut, TimeSpan executionTime, | ||
TimeSpan compileTime) | ||
{ | ||
state = state ?? throw new ArgumentNullException(nameof(state)); | ||
|
||
ReturnValue = state.ReturnValue; | ||
var type = state.ReturnValue?.GetType(); | ||
|
||
if (type?.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IEnumerator)) ?? false) | ||
{ | ||
var genericParams = type.GetGenericArguments(); | ||
|
||
if (genericParams.Length == 2) | ||
{ | ||
type = typeof(List<>).MakeGenericType(genericParams[1]); | ||
|
||
ReturnValue = Activator.CreateInstance(type, ReturnValue); | ||
} | ||
} | ||
|
||
ReturnTypeName = type?.ParseGenericArgs(); | ||
ExecutionTime = executionTime; | ||
CompileTime = compileTime; | ||
ConsoleOut = consoleOut; | ||
Code = code; | ||
Exception = state.Exception?.Message; | ||
ExceptionType = state.Exception?.GetType().Name; | ||
} | ||
|
||
public object? ReturnValue { get; set; } | ||
|
||
public string Code { get; set; } = null!; | ||
|
||
public string ConsoleOut { get; set; } = null!; | ||
|
||
public string? Exception { get; set; } | ||
|
||
public string? ExceptionType { get; set; } | ||
|
||
public string? ReturnTypeName { get; set; } | ||
|
||
public TimeSpan CompileTime { get; set; } | ||
|
||
public TimeSpan ExecutionTime { get; set; } | ||
|
||
public static EvaluationResult CreateErrorResult(string code, string consoleOut, TimeSpan compileTime, | ||
ImmutableArray<Diagnostic> compileErrors) | ||
{ | ||
var ex = new CompilationErrorException(string.Join("\n", compileErrors.Select(a => a.GetMessage())), | ||
compileErrors); | ||
var errorResult = new EvaluationResult | ||
{ | ||
Code = code, | ||
CompileTime = compileTime, | ||
ConsoleOut = consoleOut, | ||
Exception = ex.Message, | ||
ExceptionType = ex.GetType().Name, | ||
ExecutionTime = TimeSpan.FromMilliseconds(0), | ||
ReturnValue = null, | ||
ReturnTypeName = null | ||
}; | ||
return errorResult; | ||
} | ||
} |
Oops, something went wrong.