diff --git a/Boolean/Boolean.csproj b/Boolean/Boolean.csproj index 7b2302e..836bba5 100644 --- a/Boolean/Boolean.csproj +++ b/Boolean/Boolean.csproj @@ -1,36 +1,38 @@ - - - - Exe - net8.0 - enable - enable - 4043f2b4-1120-4946-b098-003bba4cbbe4 - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - Always - - - - + + + + Exe + net8.0 + enable + enable + 4043f2b4-1120-4946-b098-003bba4cbbe4 + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + Always + + + + \ No newline at end of file diff --git a/Boolean/Modules/BotInfo.cs b/Boolean/Modules/BotInfo.cs index fdef5d8..c76b185 100644 --- a/Boolean/Modules/BotInfo.cs +++ b/Boolean/Modules/BotInfo.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Net.NetworkInformation; using Boolean.Util; using Discord; using Discord.Interactions; @@ -70,6 +71,16 @@ public async Task Status() var startTime = DateTime.UtcNow; var startCpuUsage = botProcess.TotalProcessorTime; + NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); + + float startBytesSent = 0; + float startBytesReceived = 0; + + foreach (NetworkInterface networkInterface in networkInterfaces) { + startBytesSent += networkInterface.GetIPv4Statistics().BytesSent; + startBytesReceived += networkInterface.GetIPv4Statistics().BytesReceived; + } + await Task.Delay(500); var endTime = DateTime.UtcNow; @@ -78,6 +89,20 @@ public async Task Status() var cpuUsage = (float) (endCpuUsage - startCpuUsage).TotalMilliseconds / (float) (Environment.ProcessorCount * (endTime - startTime).TotalMilliseconds); + float endBytesSent = 0; + float endBytesReceived = 0; + + foreach (NetworkInterface networkInterface in networkInterfaces) { + endBytesSent += networkInterface.GetIPv4Statistics().BytesSent; + endBytesReceived += networkInterface.GetIPv4Statistics().BytesReceived; + } + + float megabytesSentPerSecond = ((endBytesSent - startBytesSent) / (float) Math.Pow(1024, 2)) + / (float) (endTime - startTime).TotalSeconds; + + float megabytesReceivedPerSecond = ((endBytesReceived - startBytesReceived) / (float) Math.Pow(1024, 2)) + / (float) (endTime - startTime).TotalSeconds; + var embed = new EmbedBuilder { Title = "Bot Status", @@ -89,6 +114,9 @@ public async Task Status() embed .AddField("RAM", $"`{Math.Round(ramUsageGb, 2)} GB`", true) .AddField("CPU Usage", $"`{Math.Round(cpuUsage * 100, 2)}%`", true) + .AddField("** **", "** **") // Empty line to display the following fields in a separate row + .AddField("MB/S Sent", $"`{Math.Round(megabytesSentPerSecond, 2)} MB/S`", true) + .AddField("MB/S Received", $"`{Math.Round(megabytesReceivedPerSecond, 2)} MB/S`", true) .AddField("Up Time", $"{uptime.Days} days, {uptime.Hours} hours, {uptime.Minutes} minutes, {uptime.Seconds} seconds", false); await RespondAsync(embed: embed.Build(), ephemeral: true); diff --git a/Boolean/Modules/FunUtils.cs b/Boolean/Modules/FunUtils.cs index 92530b0..474b723 100644 --- a/Boolean/Modules/FunUtils.cs +++ b/Boolean/Modules/FunUtils.cs @@ -1,19 +1,57 @@ -using Boolean.Util; -using Discord; -using Discord.Interactions; - -namespace Boolean; - -public class FunUtils : InteractionModuleBase -{ - [SlashCommand("execute", "Executes a fiendish individual.")] - public async Task Execute(IUser user, string? reason = null) - { - await RespondAsync(embed: new EmbedBuilder - { - Description = $"{user.Mention} has been executed by {Context.User.Mention}" - + (reason != null ? $"\nReason: `{reason}`" : ""), - Color = EmbedColors.Normal, - }.Build()); - } +using Boolean.Util; +using Discord; +using Discord.Interactions; +using SkiaSharp; + +namespace Boolean; + +public class FunUtils : InteractionModuleBase +{ + [SlashCommand("execute", "Executes a fiendish individual.")] + public async Task Execute(IUser user, string? reason = null) + { + await RespondAsync(embed: new EmbedBuilder + { + Description = $"{user.Mention} has been executed by {Context.User.Mention}" + + (reason != null ? $"\nReason: `{reason}`" : ""), + Color = EmbedColors.Normal, + }.Build()); + } + + [SlashCommand("conatidrake", "Creates the drake preference meme BUT conaticus")] + public async Task Conatidrake(string top, string bottom) + { + try + { + var templateImage = SKImage.FromEncodedData("./images/Conatidrake.jpg"); + using (var surface = SKSurface.Create(templateImage.Info)) + { + surface.Canvas.DrawImage(templateImage, 0, 0); + + var font = new SKPaint + { + TextSize = 50, + IsAntialias = true, + Color = new SKColor(0, 0, 0), + Style = SKPaintStyle.Fill, + }; + + var width = templateImage.Width; + var height = templateImage.Height; + + var topRect = new SKRect(width / 2, 0, width, height / 2); + var bottomRect = new SKRect(width / 2, height / 2, width, height); + + Text.DrawTextBox(surface.Canvas, top, topRect, font); + Text.DrawTextBox(surface.Canvas, bottom, bottomRect, font); + + var stream = surface.Snapshot().Encode().AsStream(); + await Context.Channel.SendFileAsync(stream, "conatidrake.png"); + } + } + catch (Exception e) + { + Console.WriteLine(e); + } + } } \ No newline at end of file diff --git a/Boolean/Program.cs b/Boolean/Program.cs index 4105562..416d49c 100644 --- a/Boolean/Program.cs +++ b/Boolean/Program.cs @@ -1,73 +1,74 @@ -using System.Reflection; -using Discord; -using Discord.Interactions; -using Discord.WebSocket; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Boolean; - -class Program -{ - private static ServiceProvider _serviceProvider; - private static DiscordSocketClient _client; - - public static async Task Main() - { - HostApplicationBuilder builder = Host.CreateApplicationBuilder(); - Config config = builder.Configuration.Get() - ?? throw new Exception("Failed to load valid config from appsettings.json, please refer to the README.md for instructions."); - - builder.Services - .AddDbContext(options => options.UseNpgsql(config.GetConnectionString())); - - // Only start the bot outside of design time (avoids app running during dotnet ef commands) - if (EF.IsDesignTime) { - _serviceProvider = builder.Services.BuildServiceProvider(); - goto buildApp; - } - - _client = new DiscordSocketClient(new DiscordSocketConfig - { - GatewayIntents = GatewayIntents.All, - UseInteractionSnowflakeDate = false, // Prevents a funny from happening when your OS clock is out of sync - MessageCacheSize = 100, - }); - - var interactionService = new InteractionService(_client.Rest); - - builder.Services - .AddSingleton(interactionService) - .AddSingleton(_client) - .AddSingleton(config) - .AddSingleton(); - - _serviceProvider = builder.Services.BuildServiceProvider(); - - await _client.LoginAsync(TokenType.Bot, config.DiscordToken); - await _client.StartAsync(); - - await interactionService.AddModulesAsync(Assembly.GetEntryAssembly(), _serviceProvider); - AttachEventHandlers(); - - buildApp: - IHost app = builder.Build(); - await app.RunAsync(); - } - - private static void AttachEventHandlers() - { - var eventHandlers = _serviceProvider.GetRequiredService(); - - _client.Log += eventHandlers.LogMessage; - _client.Ready += eventHandlers.Ready; - _client.InteractionCreated += eventHandlers.InteractionCreated; - _client.JoinedGuild += eventHandlers.GuildCreate; - _client.ButtonExecuted += eventHandlers.ButtonExecuted; - _client.ReactionAdded += eventHandlers.ReactionAdded; - _client.ReactionRemoved += eventHandlers.ReactionRemoved; - _client.UserJoined += eventHandlers.UserJoined; - } +using System.Reflection; +using Discord; +using Discord.Interactions; +using Discord.WebSocket; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Boolean; + +class Program +{ + private static ServiceProvider _serviceProvider; + private static DiscordSocketClient _client; + + public static async Task Main() + { + HostApplicationBuilder builder = Host.CreateApplicationBuilder(); + Config config = builder.Configuration.Get() + ?? throw new Exception("Failed to load valid config from appsettings.json, please refer to the README.md for instructions."); + + builder.Services + .AddDbContext(options => options.UseNpgsql(config.GetConnectionString())); + + // Only start the bot outside of design time (avoids app running during dotnet ef commands) + if (EF.IsDesignTime) + { + _serviceProvider = builder.Services.BuildServiceProvider(); + goto buildApp; + } + + _client = new DiscordSocketClient(new DiscordSocketConfig + { + GatewayIntents = GatewayIntents.All, + UseInteractionSnowflakeDate = false, // Prevents a funny from happening when your OS clock is out of sync + MessageCacheSize = 100, + }); + + var interactionService = new InteractionService(_client.Rest); + + builder.Services + .AddSingleton(interactionService) + .AddSingleton(_client) + .AddSingleton(config) + .AddSingleton(); + + _serviceProvider = builder.Services.BuildServiceProvider(); + + await _client.LoginAsync(TokenType.Bot, config.DiscordToken); + await _client.StartAsync(); + + await interactionService.AddModulesAsync(Assembly.GetEntryAssembly(), _serviceProvider); + AttachEventHandlers(); + + buildApp: + IHost app = builder.Build(); + await app.RunAsync(); + } + + private static void AttachEventHandlers() + { + var eventHandlers = _serviceProvider.GetRequiredService(); + + _client.Log += eventHandlers.LogMessage; + _client.Ready += eventHandlers.Ready; + _client.InteractionCreated += eventHandlers.InteractionCreated; + _client.JoinedGuild += eventHandlers.GuildCreate; + _client.ButtonExecuted += eventHandlers.ButtonExecuted; + _client.ReactionAdded += eventHandlers.ReactionAdded; + _client.ReactionRemoved += eventHandlers.ReactionRemoved; + _client.UserJoined += eventHandlers.UserJoined; + } } \ No newline at end of file diff --git a/Boolean/Utils/Drawing/Text.cs b/Boolean/Utils/Drawing/Text.cs new file mode 100644 index 0000000..5a522a7 --- /dev/null +++ b/Boolean/Utils/Drawing/Text.cs @@ -0,0 +1,25 @@ +using SkiaSharp; + +public static class Text +{ + public static void DrawTextBox(SKCanvas canvas, string text, SKRect rect, SKPaint paint) + { + var spaceWidth = paint.MeasureText(" "); + var wordX = rect.Left; + var wordY = rect.Top + paint.TextSize; + + foreach (var word in text.Split(' ')) + { + var wordWidth = paint.MeasureText(word); + + if (wordWidth > rect.Right - wordX) + { + wordY += paint.FontSpacing; + wordX = rect.Left; + } + + canvas.DrawText(word, wordX, wordY, paint); + wordX += wordWidth + spaceWidth; + } + } +} \ No newline at end of file diff --git a/Boolean/Utils/UI/Paginator.cs b/Boolean/Utils/UI/Paginator.cs index 8676cb4..95d11f1 100644 --- a/Boolean/Utils/UI/Paginator.cs +++ b/Boolean/Utils/UI/Paginator.cs @@ -32,10 +32,21 @@ public class Paginator( public async Task SendAsync() { var embed = pageChangeFunc(SlicePage(), CreateDefaultEmbed()); - await interaction.RespondAsync(embed: embed.Build(), components: CreateButtons(true), ephemeral: true); + await interaction.RespondAsync(embed: embed.Build(), components: CreateButtonsForCurrentPage(), ephemeral: true); await PaginatorCache.Add(_id, this); } + + private MessageComponent CreateButtonsForCurrentPage() + { + bool previousButtonDisabled = _currentPage == 0; + + int numberOfPages = (int) MathF.Ceiling(data.Count / (float) itemsPerPage); + bool nextButtonDisabled = numberOfPages - 1 <= _currentPage; + + return CreateButtons(previousButtonDisabled, nextButtonDisabled); + } + private MessageComponent CreateButtons(bool isPreviousDisabled = false, bool isNextDisabled = false) { return new ComponentBuilder() @@ -72,13 +83,7 @@ public async Task HandleChange(bool isNext, SocketMessageComponent component) await interaction.ModifyOriginalResponseAsync(m => { m.Embed = embed.Build(); - - if (_currentPage + itemsPerPage + 1 > data.Count) - m.Components = CreateButtons(false, true); - else if (_currentPage == 0) - m.Components = CreateButtons(true); - else - m.Components = CreateButtons(); + m.Components = CreateButtonsForCurrentPage(); }); await component.DeferAsync(); diff --git a/Boolean/images/Conatidrake.jpg b/Boolean/images/Conatidrake.jpg new file mode 100644 index 0000000..6bb37c1 Binary files /dev/null and b/Boolean/images/Conatidrake.jpg differ