diff --git a/.editorconfig b/.editorconfig index 0833e25b4..7ac5d9dcf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -76,7 +76,7 @@ dotnet_naming_style.interface_style.capitalization = pascal_case dotnet_naming_style.interface_style.required_prefix = I # Async methods are suffixed with Async -dotnet_naming_rule.async_methods_should_be_suffixed.severity = warning +dotnet_naming_rule.async_methods_should_be_suffixed.severity = none dotnet_naming_rule.async_methods_should_be_suffixed.symbols = async_methods dotnet_naming_rule.async_methods_should_be_suffixed.style = async_method_style diff --git a/src/Modix.Bot/Modules/IlModule.cs b/src/Modix.Bot/Modules/IlModule.cs index 7841e0246..4ea1d7c56 100644 --- a/src/Modix.Bot/Modules/IlModule.cs +++ b/src/Modix.Bot/Modules/IlModule.cs @@ -7,8 +7,8 @@ using Discord.Commands; using Microsoft.Extensions.Options; using Modix.Data.Models.Core; +using Modix.Services; using Modix.Services.AutoRemoveMessage; -using Modix.Services.CodePaste; using Modix.Services.CommandHelp; using Modix.Services.Utilities; using Serilog; @@ -22,12 +22,12 @@ public class IlModule : ModuleBase { private const string DefaultIlRemoteUrl = "http://csdiscord-repl-service:31337/Il"; private readonly string _ilUrl; - private readonly CodePasteService _pasteService; + private readonly PasteService _pasteService; private readonly IAutoRemoveMessageService _autoRemoveMessageService; private readonly IHttpClientFactory _httpClientFactory; public IlModule( - CodePasteService pasteService, + PasteService pasteService, IAutoRemoveMessageService autoRemoveMessageService, IHttpClientFactory httpClientFactory, IOptions modixConfig) @@ -118,10 +118,12 @@ private async Task BuildEmbedAsync(IGuildUser guildUser, string co embed.AddField(a => a.WithName("Code").WithValue(Format.Code(code, "cs"))); + const int MAX_LENGTH = 990; + embed.AddField(a => a.WithName($"Result:") - .WithValue(Format.Code(result.TruncateTo(990), "asm"))); + .WithValue(Format.Code(result.TruncateTo(MAX_LENGTH), "asm"))); - await embed.UploadToServiceIfBiggerThan(result, 990, _pasteService); + await embed.UploadToServiceIfBiggerThan(result, MAX_LENGTH, _pasteService); return embed; } diff --git a/src/Modix.Bot/Modules/ReplModule.cs b/src/Modix.Bot/Modules/ReplModule.cs index 60338f59a..a1922e05c 100644 --- a/src/Modix.Bot/Modules/ReplModule.cs +++ b/src/Modix.Bot/Modules/ReplModule.cs @@ -8,8 +8,8 @@ using Discord.Commands; using Microsoft.Extensions.Options; using Modix.Data.Models.Core; +using Modix.Services; using Modix.Services.AutoRemoveMessage; -using Modix.Services.CodePaste; using Modix.Services.CommandHelp; using Modix.Services.Utilities; using Newtonsoft.Json; @@ -36,12 +36,12 @@ public class ReplModule : ModuleBase private const int MaxFormattedFieldSize = 1000; private const string DefaultReplRemoteUrl = "http://csdiscord-repl-service:31337/Eval"; private readonly string _replUrl; - private readonly CodePasteService _pasteService; + private readonly PasteService _pasteService; private readonly IAutoRemoveMessageService _autoRemoveMessageService; private readonly IHttpClientFactory _httpClientFactory; public ReplModule( - CodePasteService pasteService, + PasteService pasteService, IAutoRemoveMessageService autoRemoveMessageService, IHttpClientFactory httpClientFactory, IOptions modixConfig) diff --git a/src/Modix.Services/CodePaste/CodePasteService.cs b/src/Modix.Services/CodePaste/CodePasteService.cs deleted file mode 100644 index ab907ca22..000000000 --- a/src/Modix.Services/CodePaste/CodePasteService.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Discord; -using Modix.Data.Utilities; -using Modix.Services.Utilities; -using Newtonsoft.Json.Linq; - -namespace Modix.Services.CodePaste -{ - public class CodePasteService - { - private const string Header = @" -/* - Written By: {0} in #{1} - Posted on {2} - Message ID: {3} -*/ - -{4}"; - - private const string ApiReferenceUrl = "https://paste.mod.gg/"; - private readonly IHttpClientFactory _httpClientFactory; - - public CodePasteService(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - /// - /// Uploads a given piece of code to the service, and returns the URL to the post. - /// - /// The code to post - /// The URL to the newly created post - public async Task UploadCodeAsync(string code) - { - var content = FormatUtilities.BuildContent(code); - - var client = _httpClientFactory.CreateClient(HttpClientNames.TimeoutFiveSeconds); - - var response = await client.PostAsync($"{ApiReferenceUrl}documents", content); - - if (!response.IsSuccessStatusCode) - { - var body = await response.Content?.ReadAsStringAsync(); - throw new Exception($"{response.StatusCode} returned when calling {response.RequestMessage?.RequestUri}. Response body: {body}"); - } - - var urlResponse = await response.Content.ReadAsStringAsync(); - var pasteKey = JObject.Parse(urlResponse)["key"]?.Value(); - - return $"{ApiReferenceUrl}{pasteKey}"; - } - - /// - /// Uploads the code in the given message to the service, and returns the URL to the post. - /// - /// The Discord message to upload - /// The string to upload instead of message content - /// The URL to the newly created post - public async Task UploadCodeAsync(IMessage msg, string code = null) - { - var formatted = string.Format(Header, - $"{msg.Author.Username}#{msg.Author.DiscriminatorValue}", msg.Channel.Name, - DateTimeOffset.UtcNow.ToString("dddd, MMMM d yyyy @ H:mm:ss"), msg.Id, - code ?? msg.Content); - - return await UploadCodeAsync(formatted); - } - - public static Embed BuildEmbed(IUser user, string content, string url) => new EmbedBuilder() - .WithTitle("Your message was re-uploaded") - .WithUserAsAuthor(user) - .WithDescription(content.Trim().Truncate(200, 6)) - .AddField("Auto-Paste", url, true) - .WithColor(new Color(95, 186, 125)) - .Build(); - } -} diff --git a/src/Modix.Services/CodePaste/CodePasteSetup.cs b/src/Modix.Services/CodePaste/CodePasteSetup.cs deleted file mode 100644 index 91516556e..000000000 --- a/src/Modix.Services/CodePaste/CodePasteSetup.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace Modix.Services.CodePaste -{ - public static class CodePasteSetup - { - public static IServiceCollection AddCodePaste(this IServiceCollection services) - => services - .AddSingleton(); - } -} diff --git a/src/Modix.Services/PasteService.cs b/src/Modix.Services/PasteService.cs new file mode 100644 index 000000000..92a1cb98a --- /dev/null +++ b/src/Modix.Services/PasteService.cs @@ -0,0 +1,39 @@ +#nullable enable +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Modix.Services.Utilities; +using Newtonsoft.Json.Linq; + +namespace Modix.Services; + +public class PasteService(IHttpClientFactory httpClientFactory, ILogger logger) +{ + private const string PASTE_URL = "https://paste.mod.gg/"; + + public async Task UploadPaste(string textToUpload) + { + var content = FormatUtilities.BuildContent(textToUpload); + + var client = httpClientFactory.CreateClient(HttpClientNames.TimeoutFiveSeconds); + + var response = await client.PostAsync($"{PASTE_URL}documents", content); + + if (!response.IsSuccessStatusCode) + { + var body = await response.Content.ReadAsStringAsync(); + + logger.LogError("Failed uploading paste to {Url}, failed with response {ResponseCode}, with body: {Body}", + PASTE_URL, + response.StatusCode, + body); + + return null; + } + + var urlResponse = await response.Content.ReadAsStringAsync(); + var pasteKey = JObject.Parse(urlResponse)["key"]?.Value(); + + return $"{PASTE_URL}{pasteKey}"; + } +} diff --git a/src/Modix.Services/Utilities/DiscordWebhookSink.cs b/src/Modix.Services/Utilities/DiscordWebhookSink.cs index f283e05e2..0e09c9e3a 100644 --- a/src/Modix.Services/Utilities/DiscordWebhookSink.cs +++ b/src/Modix.Services/Utilities/DiscordWebhookSink.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Discord; using Discord.Webhook; -using Modix.Services.CodePaste; using Newtonsoft.Json; using Serilog; using Serilog.Configuration; @@ -15,7 +14,7 @@ namespace Modix.Services.Utilities; public sealed class DiscordWebhookSink : ILogEventSink, IAsyncDisposable { - private readonly Lazy _codePasteService; + private readonly Lazy _codePasteService; private readonly DiscordWebhookClient _discordWebhookClient; private readonly IFormatProvider _formatProvider; private readonly JsonSerializerSettings _jsonSerializerSettings; @@ -27,7 +26,7 @@ public DiscordWebhookSink( ulong webhookId, string webhookToken, IFormatProvider formatProvider, - Lazy codePasteService) + Lazy codePasteService) { _codePasteService = codePasteService; _discordWebhookClient = new DiscordWebhookClient(webhookId, webhookToken); @@ -75,12 +74,15 @@ public async Task ProcessLogEventItemsAsync() var eventAsJson = JsonConvert.SerializeObject(logEvent, _jsonSerializerSettings); - var url = await _codePasteService.Value.UploadCodeAsync(eventAsJson); + var url = await _codePasteService.Value.UploadPaste(eventAsJson); - message.AddField(new EmbedFieldBuilder() - .WithIsInline(false) - .WithName("Full Log Event") - .WithValue($"[view on paste.mod.gg]({url})")); + if (!string.IsNullOrWhiteSpace(url)) + { + message.AddField(new EmbedFieldBuilder() + .WithIsInline(false) + .WithName("Full Log Event") + .WithValue($"[view on paste.mod.gg]({url})")); + } } catch (Exception ex) { @@ -121,7 +123,7 @@ public async ValueTask DisposeAsync() public static class DiscordWebhookSinkExtensions { - public static LoggerConfiguration DiscordWebhookSink(this LoggerSinkConfiguration config, ulong id, string token, LogEventLevel minLevel, Lazy codePasteService) + public static LoggerConfiguration DiscordWebhookSink(this LoggerSinkConfiguration config, ulong id, string token, LogEventLevel minLevel, Lazy codePasteService) => config.Sink(new DiscordWebhookSink(id, token, null, codePasteService), minLevel); } diff --git a/src/Modix.Services/Utilities/FormatUtilities.cs b/src/Modix.Services/Utilities/FormatUtilities.cs index 9f01f9e66..a2fbe2783 100644 --- a/src/Modix.Services/Utilities/FormatUtilities.cs +++ b/src/Modix.Services/Utilities/FormatUtilities.cs @@ -8,14 +8,10 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; - using Discord; - using Humanizer; using Humanizer.Localisation; - using Modix.Data.Models.Moderation; -using Modix.Services.CodePaste; namespace Modix.Services.Utilities { @@ -38,18 +34,16 @@ public static string StripFormatting(string code) => //strip out the ` characters and code block markers _buildContentRegex.Replace(code.Trim(), string.Empty); - public static async Task UploadToServiceIfBiggerThan(this EmbedBuilder embed, string content, uint size, CodePasteService service) + public static async Task UploadToServiceIfBiggerThan(this EmbedBuilder embed, string content, uint size, + PasteService service) { if (content.Length > size) { - try - { - var resultLink = await service.UploadCodeAsync(content); - embed.AddField(a => a.WithName("More...").WithValue($"[View on Hastebin]({resultLink})")); - } - catch (WebException we) + var resultLink = await service.UploadPaste(content); + + if (!string.IsNullOrWhiteSpace(resultLink)) { - embed.AddField(a => a.WithName("More...").WithValue(we.Message)); + embed.AddField(a => a.WithName("More...").WithValue($"[View on paste.mod.gg]({resultLink})")); } } } @@ -87,7 +81,8 @@ public static IReadOnlyCollection CollapsePlurals(IReadOnlyCollection x.Singular, x => x.Value, new SequenceEqualityComparer()); + var groupedBySingulars = + withSingulars.GroupBy(x => x.Singular, x => x.Value, new SequenceEqualityComparer()); var withDistinctParts = new HashSet[groupedBySingulars.Count()][]; @@ -129,7 +124,8 @@ public static IReadOnlyCollection CollapsePlurals(IReadOnlyCollection 1 // 1, because the current line is included in the count - ? GetRemainingLineCountComment(remainingCount).Length - : 0; + var possibleRemainingLineCommentLength = + remainingCount > 1 // 1, because the current line is included in the count + ? GetRemainingLineCountComment(remainingCount).Length + : 0; - if (line.Length + currentLength + possibleRemainingLineCommentLength + 1 > maxLength) // +1 because of the newline that will be added later + if (line.Length + currentLength + possibleRemainingLineCommentLength + 1 > + maxLength) // +1 because of the newline that will be added later return false; processedLines.Add(line); diff --git a/src/Modix/Extensions/ServiceCollectionExtensions.cs b/src/Modix/Extensions/ServiceCollectionExtensions.cs index 6f107f37e..a472469ad 100644 --- a/src/Modix/Extensions/ServiceCollectionExtensions.cs +++ b/src/Modix/Extensions/ServiceCollectionExtensions.cs @@ -22,7 +22,6 @@ using Modix.Data.Repositories; using Modix.Services; using Modix.Services.AutoRemoveMessage; -using Modix.Services.CodePaste; using Modix.Services.CommandHelp; using Modix.Services.Core; using Modix.Services.Csharp; @@ -157,7 +156,6 @@ public static IServiceCollection AddModix( .AddModixCore() .AddModixModeration() .AddModixPromotions() - .AddCodePaste() .AddCommandHelp() .AddGuildStats() .AddModixTags() @@ -166,6 +164,8 @@ public static IServiceCollection AddModix( .AddEmojiStats() .AddImages(); + services.AddScoped(); + services.AddScoped(); services.AddSingleton(); services.AddMemoryCache(); diff --git a/src/Modix/Program.cs b/src/Modix/Program.cs index 61013ce95..57a3e600e 100644 --- a/src/Modix/Program.cs +++ b/src/Modix/Program.cs @@ -17,7 +17,7 @@ using Modix.Configuration; using Modix.Data; using Modix.Data.Models.Core; -using Modix.Services.CodePaste; +using Modix.Services; using Modix.Services.Utilities; using Modix.Web; using Newtonsoft.Json.Converters; @@ -106,7 +106,7 @@ private static void ConfigureServices(WebApplicationBuilder builder, IConfigurat if (webhookId.HasValue && webhookToken != null) { lc - .WriteTo.DiscordWebhookSink(webhookId.Value, webhookToken, LogEventLevel.Error, new Lazy(sp.GetRequiredService)); + .WriteTo.DiscordWebhookSink(webhookId.Value, webhookToken, LogEventLevel.Error, new Lazy(sp.GetRequiredService)); } });