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