Skip to content

Commit c1e8a79

Browse files
committed
Add godbolt command
1 parent 5bbde45 commit c1e8a79

File tree

6 files changed

+161
-0
lines changed

6 files changed

+161
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#nullable enable
2+
using System;
3+
using System.Threading.Tasks;
4+
using Discord;
5+
using Discord.Interactions;
6+
using Modix.Services.AutoRemoveMessage;
7+
using Modix.Services.CommandHelp;
8+
using Modix.Services.Godbolt;
9+
10+
namespace Modix.Bot.Modules
11+
{
12+
[ModuleHelp("Godbolt", "Commands for working with Godbolt.")]
13+
public class GodboltModule : InteractionModuleBase
14+
{
15+
private readonly IAutoRemoveMessageService _autoRemoveMessageService;
16+
private readonly GodboltService _godboltService;
17+
18+
public GodboltModule(IAutoRemoveMessageService autoRemoveMessageService, GodboltService godboltService)
19+
{
20+
_autoRemoveMessageService = autoRemoveMessageService;
21+
_godboltService = godboltService;
22+
}
23+
24+
[SlashCommand("godbolt", "Compile and disassemble the JIT code.")]
25+
public async Task DisasmAsync(
26+
[Summary(description: "The code to compiler.")] string code,
27+
[Summary(description: "The language of code.")] Language language = Language.CSharp,
28+
[Summary(description: "Arguments to pass to the compiler.")] string arguments = "",
29+
[Summary(description: "Execute the code.")] bool execute = false)
30+
{
31+
var langId = language switch
32+
{
33+
Language.CSharp => "csharp",
34+
Language.FSharp => "fsharp",
35+
Language.VisualBasic => "vb",
36+
Language.IL => "il",
37+
_ => throw new ArgumentOutOfRangeException(nameof(language))
38+
};
39+
40+
try
41+
{
42+
var response = $"""
43+
```asm
44+
{await _godboltService.CompileAsync(code, langId, arguments, execute)}
45+
```
46+
""";
47+
48+
if (response.Length > DiscordConfig.MaxMessageSize)
49+
{
50+
await FollowupAsync("Error: The disassembly code is too long to be converted to a message.");
51+
return;
52+
}
53+
54+
await FollowupAsync(text: response);
55+
}
56+
catch (Exception ex)
57+
{
58+
await FollowupAsync($"Error: {ex}");
59+
return;
60+
}
61+
}
62+
63+
public enum Language
64+
{
65+
[ChoiceDisplay("C#")]
66+
CSharp,
67+
[ChoiceDisplay("F#")]
68+
FSharp,
69+
[ChoiceDisplay("VB.NET")]
70+
VisualBasic,
71+
[ChoiceDisplay("IL")]
72+
IL
73+
}
74+
}
75+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#nullable enable
2+
namespace Modix.Services.Godbolt
3+
{
4+
internal record class AssemblyFilters
5+
{
6+
public bool Binary { get; set; } = true;
7+
public bool CommentOnly { get; set; } = true;
8+
public bool Directives { get; set; } = true;
9+
public bool Labels { get; set; } = true;
10+
public bool Trim { get; set; } = true;
11+
public bool Demangle { get; set; } = true;
12+
public bool Intel { get; set; } = true;
13+
public bool Execute { get; set; } = false;
14+
}
15+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#nullable enable
2+
namespace Modix.Services.Godbolt
3+
{
4+
internal record class CompileRequest(string Lang, string Source)
5+
{
6+
public string Compiler { get; } = $"dotnettrunk{Lang}coreclr";
7+
public CompilerOptions Options { get; } = new();
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#nullable enable
2+
namespace Modix.Services.Godbolt
3+
{
4+
internal record class CompilerOptions
5+
{
6+
public string UserArguments { get; set; } = "";
7+
public AssemblyFilters Filters { get; } = new();
8+
}
9+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#nullable enable
2+
using System.Net.Http;
3+
using System.Text;
4+
using System.Text.Json;
5+
using System.Threading.Tasks;
6+
7+
namespace Modix.Services.Godbolt
8+
{
9+
public class GodboltService
10+
{
11+
private static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
12+
private const string GodboltApiScheme = "https://godbolt.org/api/compiler/dotnettrunk{0}coreclr/compile";
13+
14+
public GodboltService(IHttpClientFactory httpClientFactory)
15+
{
16+
HttpClientFactory = httpClientFactory;
17+
}
18+
19+
public async Task<string> CompileAsync(string code, string lang, string arguments, bool execute)
20+
{
21+
var request = new CompileRequest(lang, code);
22+
23+
if (!string.IsNullOrWhiteSpace(arguments))
24+
{
25+
request.Options.UserArguments = arguments;
26+
}
27+
28+
if (execute)
29+
{
30+
request.Options.Filters.Execute = true;
31+
}
32+
33+
var requestJson = JsonSerializer.Serialize(request, _jsonOptions);
34+
35+
var client = HttpClientFactory.CreateClient();
36+
client.DefaultRequestHeaders.Add("Accept", "text/plain");
37+
38+
var response = await client.PostAsync(string.Format(GodboltApiScheme, lang), new StringContent(requestJson, Encoding.UTF8, "application/json"));
39+
40+
if (!response.IsSuccessStatusCode)
41+
{
42+
throw new HttpRequestException($"Godbolt returns '{response.StatusCode}' for the request.");
43+
}
44+
45+
return await response.Content.ReadAsStringAsync();
46+
}
47+
48+
protected IHttpClientFactory HttpClientFactory { get; }
49+
}
50+
51+
}

src/Modix/Extensions/ServiceCollectionExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
using Modix.Services.Core;
2828
using Modix.Services.Csharp;
2929
using Modix.Services.EmojiStats;
30+
using Modix.Services.Godbolt;
3031
using Modix.Services.GuildStats;
3132
using Modix.Services.Images;
3233
using Modix.Services.Moderation;
@@ -174,6 +175,7 @@ public static IServiceCollection AddModix(
174175
services.AddScoped<WikipediaService>();
175176
services.AddScoped<StackExchangeService>();
176177
services.AddScoped<DocumentationService>();
178+
services.AddScoped<GodboltService>();
177179

178180
services.AddScoped<IModerationActionEventHandler, ModerationLoggingBehavior>();
179181
services.AddScoped<INotificationHandler<PromotionActionCreatedNotification>, PromotionLoggingHandler>();

0 commit comments

Comments
 (0)