diff --git a/Jenny/Commands/ConfigureCommand.cs b/Jenny/Commands/ConfigureCommand.cs
new file mode 100644
index 0000000..efd3417
--- /dev/null
+++ b/Jenny/Commands/ConfigureCommand.cs
@@ -0,0 +1,38 @@
+using System.Text;
+using LibMatrix.EventTypes.Spec;
+using LibMatrix.Utilities.Bot.Commands;
+using LibMatrix.Utilities.Bot.Interfaces;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Jenny.Commands;
+
+public class ConfigureCommand(IServiceProvider svcs) : ICommandGroup {
+ public string Name { get; } = "configure";
+ public string[]? Aliases { get; } = ["config", "cfg"];
+ public string Description { get; }
+ public bool Unlisted { get; } = true;
+
+ public async Task Invoke(CommandContext ctx) {
+ var commands = svcs.GetServices<ICommand>().Where(x => x.GetType().IsAssignableTo(typeof(ICommand<>).MakeGenericType(GetType()))).ToList();
+
+ if (ctx.Args.Length == 0) {
+ await ctx.Room.SendMessageEventAsync(HelpCommand.GenerateCommandList(commands).Build());
+ }
+ else {
+ var subcommand = ctx.Args[0];
+ var command = commands.FirstOrDefault(x => x.Name == subcommand || x.Aliases?.Contains(subcommand) == true);
+ if (command == null) {
+ await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent("m.notice", "Unknown subcommand"));
+ return;
+ }
+
+ await command.Invoke(new CommandContext {
+ Room = ctx.Room,
+ MessageEvent = ctx.MessageEvent,
+ CommandName = ctx.CommandName,
+ Args = ctx.Args.Skip(1).ToArray(),
+ Homeserver = ctx.Homeserver
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Jenny/Commands/ConfigureSubCommands/ControlRoomConfigureSubcommand.cs b/Jenny/Commands/ConfigureSubCommands/ControlRoomConfigureSubcommand.cs
new file mode 100644
index 0000000..94294fb
--- /dev/null
+++ b/Jenny/Commands/ConfigureSubCommands/ControlRoomConfigureSubcommand.cs
@@ -0,0 +1,18 @@
+using LibMatrix.Helpers;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace Jenny.Commands.ConfigureSubCommands;
+
+public class ControlRoomConfigureSubCommand : ICommand<ConfigureCommand> {
+ public string Name { get; } = "controlroom";
+ public string[]? Aliases { get; }
+ public string Description { get; } = "Configure the control room";
+ public bool Unlisted { get; }
+
+ public async Task Invoke(CommandContext ctx) {
+ if (ctx.Args.Length == 0) {
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder("m.notice").WithBody("meow").Build());
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Jenny/Commands/ImgCommand.cs b/Jenny/Commands/ImgCommand.cs
new file mode 100644
index 0000000..9a4ed0c
--- /dev/null
+++ b/Jenny/Commands/ImgCommand.cs
@@ -0,0 +1,134 @@
+using System.Diagnostics;
+using System.Numerics;
+using ArcaneLibs;
+using ArcaneLibs.Extensions;
+using LibMatrix.EventTypes.Spec;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace Jenny.Commands;
+
+public class ImgCommand : ICommand {
+ public string Name { get; } = "img";
+ public string[]? Aliases { get; } = [];
+ public string Description { get; }
+ public bool Unlisted { get; } = true;
+
+ public async Task Invoke(CommandContext ctx) {
+ int count = 1;
+ if (ctx.Args is { Length: 1 })
+ int.TryParse(ctx.Args[0], out count);
+
+ for (var i = 0; i < count; i++) {
+ new Thread(async () => {
+ var bigNoise = GenerateHeightMap(5000, 2000);
+ await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent("m.image", "src_noise.png") {
+ Url = await ctx.Homeserver.UploadFile("data.png", await Float2DArrayToPng(bigNoise), "image/png"),
+ FileInfo = new() {
+ Width = bigNoise.GetWidth(),
+ Height = bigNoise.GetHeight()
+ }
+ });
+ }).Start();
+ }
+
+ }
+
+ public async Task<byte[]> Float2DArrayToPng(float[,] data) {
+ //dump heightmap as PPM
+ Console.WriteLine($"{DateTime.Now} Converting to PNG");
+ var width = data.GetLength(1);
+ var height = data.GetLength(0);
+
+ //convert ppm to png with ffmpeg
+ var process = new Process {
+ StartInfo = new ProcessStartInfo {
+ // FileName = "/nix/store/4hz763c5w2hnzm55ll5vgfgmrr6i9kgg-imagemagick-7.1.1-28/bin/convert",
+ FileName = "convert",
+ Arguments = $"ppm:- png:-",
+ UseShellExecute = false,
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true
+ }
+ };
+ process.Start();
+ await process.StandardInput.WriteLineAsync($"P2\n{width} {height}\n255");
+ for (var i = 0; i < height; i++) {
+ for (var j = 0; j < width; j++) {
+ await process.StandardInput.WriteAsync($"{(int)(data[i, j] * 255)} ");
+ }
+
+ // ppm.AppendLine();
+ }
+ await process.StandardInput.FlushAsync();
+ process.StandardInput.Close();
+ await using var ms = new MemoryStream();
+ await process.StandardOutput.BaseStream.CopyToAsync(ms);
+ ms.Position = 0;
+ Console.WriteLine($"{DateTime.Now} Converted to PNG");
+ return ms.ToArray();
+ }
+
+ public float[,] GenerateHeightMap(int width, int height) {
+ var rnd = new Random();
+
+ var bigNoiseVector3 = new Vector3[height, width];
+ for (var y = 0; y < bigNoiseVector3.GetLength(0); y++) {
+ for (var x = 0; x < bigNoiseVector3.GetLength(1); x++) {
+ bigNoiseVector3[y, x] = new Vector3(0, 0, 0);
+ }
+ }
+
+ bigNoiseVector3[0, 0] = new Vector3(rnd.NextSingle(), rnd.NextSingle(), rnd.NextSingle());
+ var last = bigNoiseVector3[0, 0];
+ for (var y = 0; y < bigNoiseVector3.GetLength(0); y++) {
+ if (y > 0) break;
+ for (var x = 0; x < bigNoiseVector3.GetLength(1); x++) {
+ float currentX = x;
+ float currentY = y;
+ int steps = 0;
+ int maxSteps = 1000;
+ while (steps++ < maxSteps) {
+ if (currentX < 0) break;
+ if (currentY < 0) break;
+ if (currentX > bigNoiseVector3.GetWidth() - 1) break;
+ if (currentY > bigNoiseVector3.GetHeight() - 1) break;
+
+ var current = bigNoiseVector3[(int)currentY, (int)currentX];
+ // if (current is {X: 0f, Y: 0f, Z: 0f}) {
+ // bigNoiseVector3[currentY, currentX] = current = new Vector3(rnd.NextSingle(), rnd.NextSingle(), rnd.NextSingle());
+ // }
+
+ current = new(last.X, last.Y, last.Z);
+ var diff = new Vector3(
+ MathUtil.Map(rnd.NextSingle(), 0f, 1f, -0.2f, 0.2f),
+ MathUtil.Map(rnd.NextSingle(), 0f, 1f, -0.2f, 0.2f),
+ -0.1f
+ );
+ current += diff;
+ bigNoiseVector3[(int)currentY, (int)currentX] = current;
+
+ // Console.WriteLine("{0}/{1}={2} (+{3})", currentX, currentY, current, diff);
+ currentX += current.X;
+ currentY += current.Y;
+
+ // if (current.X > 0.666f) currentX++;
+ // else if (current.X < 0.333f) currentX--;
+ // if (current.Y > 0.666f) currentY++;
+ // else if (current.Y < 0.333f) currentY--;
+
+ last = current;
+ }
+ }
+ }
+
+ var bigNoise = new float[height, width];
+ for (var i = 0; i < bigNoise.GetLength(0); i++) {
+ for (var j = 0; j < bigNoise.GetLength(1); j++) {
+ // bigNoise[i, j] = (float) (bigNoiseVector[i, j].Length() / Math.Sqrt(2));
+ bigNoise[i, j] = (float)(bigNoiseVector3[i, j].Z);
+ }
+ }
+
+ return bigNoise;
+ }
+}
\ No newline at end of file
diff --git a/Jenny/Commands/PatCommand.cs b/Jenny/Commands/PatCommand.cs
new file mode 100644
index 0000000..efceaa4
--- /dev/null
+++ b/Jenny/Commands/PatCommand.cs
@@ -0,0 +1,164 @@
+using System.Text;
+using LibMatrix.EventTypes.Spec.State;
+using LibMatrix.Helpers;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace Jenny.Commands;
+
+public class PatCommand : ICommand {
+ public string Name { get; } = "pat";
+ public string[]? Aliases { get; } = [ "patpat", "patpatpat" ];
+ public string Description { get; }
+ public bool Unlisted { get; } = true;
+
+ public async Task Invoke(CommandContext ctx) {
+ int count = 1;
+ if (ctx.Args is { Length: 1 })
+ int.TryParse(ctx.Args[0], out count);
+
+ var selfName =
+ (await ctx.Room.GetStateAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, ctx.Homeserver.UserId))?.DisplayName
+ ?? (await ctx.Homeserver.GetProfileAsync(ctx.Homeserver.UserId)).DisplayName
+ ?? ctx.Homeserver.WhoAmI.UserId;
+
+ var remoteName =
+ ctx.MessageEvent.Sender == null
+ ? null
+ : (await ctx.Room.GetStateAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, ctx.MessageEvent.Sender))?.DisplayName
+ ?? (await ctx.Homeserver.GetProfileAsync(ctx.MessageEvent.Sender)).DisplayName
+ ?? ctx.MessageEvent.Sender;
+
+ var msb = new MessageBuilder("m.emote");
+ var pat = new StringBuilder();
+ var msg = $"snuggles {remoteName}";
+
+ Console.WriteLine(pat.ToString());
+ // msb.WithHtmlTag("code", await GenerateSkyboxAroundString(msg, ctx));
+ msb.WithBody(msg);
+
+ await ctx.Room.SendMessageEventAsync(msb.Build());
+ Console.WriteLine(msb.Build().FormattedBody);
+
+ }
+
+#region old stuff
+ // TODO: implement:
+ // var selfName =
+ // (await ctx.Room.GetStateAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, ctx.Homeserver.UserId))?.DisplayName
+ // ?? (await ctx.Homeserver.GetProfileAsync(ctx.Homeserver.UserId)).DisplayName
+ // ?? ctx.Homeserver.WhoAmI.UserId;
+ //
+ // var remoteName =
+ // ctx.MessageEvent.Sender == null
+ // ? null
+ // : (await ctx.Room.GetStateAsync<RoomMemberEventContent>(RoomMemberEventContent.EventId, ctx.MessageEvent.Sender))?.DisplayName
+ // ?? (await ctx.Homeserver.GetProfileAsync(ctx.MessageEvent.Sender)).DisplayName
+ // ?? ctx.MessageEvent.Sender;
+
+ // var msb = new MessageBuilder();
+ // var pat = new StringBuilder();
+ // var msg = $"{selfName} snuggles {remoteName}";
+
+ // Console.WriteLine(pat.ToString());
+ // msb.WithHtmlTag("code", await GenerateSkyboxAroundString(msg, ctx));
+ // Console.WriteLine(msb.Build().FormattedBody);
+//}
+
+ //
+ //
+ // private class CharacterWeight {
+ // public float Width { get; set; }
+ // public float Density { get; set; }
+ // }
+ //
+ // private Dictionary<char, CharacterWeight> starrySkyCharacters = new Dictionary<char, CharacterWeight> {
+ // { ' ', new CharacterWeight { Width = 0.5f, Density = 0.1f } }, // Space has the lowest density and width
+ // { '.', new CharacterWeight { Width = 0.2f, Density = 0.2f } }, // Dot has low density but small width
+ // { '+', new CharacterWeight { Width = 0.6f, Density = 0.3f } }, // Plus sign has medium density and width
+ // { '*', new CharacterWeight { Width = 0.7f, Density = 0.7f } }, // Asterisk has high density but medium width
+ //
+ // { '¨', new CharacterWeight { Width = 0.3f, Density = 0.2f } },
+ // { '˜', new CharacterWeight { Width = 0.4f, Density = 0.3f } },
+ // { 'ˆ', new CharacterWeight { Width = 0.5f, Density = 0.4f } },
+ // { '”', new CharacterWeight { Width = 0.6f, Density = 0.5f } },
+ // { '⍣', new CharacterWeight { Width = 0.8f, Density = 0.7f } },
+ // { '~', new CharacterWeight { Width = 0.9f, Density = 0.8f } },
+ // { '⊹', new CharacterWeight { Width = 1.2f, Density = 1.1f } },
+ // { '٭', new CharacterWeight { Width = 1.3f, Density = 1.2f } },
+ // { '„', new CharacterWeight { Width = 1.4f, Density = 1.3f } },
+ // { '¸', new CharacterWeight { Width = 1.5f, Density = 1.4f } },
+ // { '¤', new CharacterWeight { Width = 1.9f, Density = 1.8f } },
+ // { '✬', new CharacterWeight { Width = 2.1f, Density = 2.0f } },
+ // { '°', new CharacterWeight { Width = 0.6f, Density = 0.35f } },
+ // { '•', new CharacterWeight { Width = 0.6f, Density = 0.4f } },
+ // { '✡', new CharacterWeight { Width = 2.0f, Density = 4.0f } },
+ // { '#', new CharacterWeight { Width = 1.0f, Density = 1.0f } },
+ // };
+ //
+ // private Dictionary<char, CharacterWeight> characterWeights = new Dictionary<char, CharacterWeight> {
+ // { 'a', new() { Density = 1, Width = 1 } }
+ // };
+ //
+ // private async Task<string> GenerateSkyboxAroundString(string str, CommandContext ctx) {
+ // var sb = new StringBuilder();
+ // int scale = 32;
+ // var outerTopBottomBorder = 4;
+ // var outerLeftRightBorder = 2;
+ // var innerTopBottomBorder = 1;
+ // var innerLeftRightBorder = 2;
+ //
+ // var innerBorder = 2;
+ //
+ // var width = str.Length + outerLeftRightBorder * 2 + innerLeftRightBorder * 2;
+ // var height = outerTopBottomBorder * 2 + innerTopBottomBorder * 2 + 1;
+ // var skybox = new char[height, width];
+ // var bigNoise = GenerateHeightMap(2000, 1000);
+ //
+ // // await ctx.Room.SendMessageEventAsync(new RoomMessageEventContent("m.image", "heightmap.png") {
+ // // Url = await Float2DArrayToMxc(noise, ctx),
+ // // FileInfo = new() {
+ // // Width = noise.GetLength(1),
+ // // Height = noise.GetLength(0)
+ // // }
+ // // });
+ // //
+ // // //fill skybox with characters according to gradient noise
+ // // for (var i = 0; i < height; i++) {
+ // // for (var j = 0; j < width; j++) {
+ // // var c = ' ';
+ // // var shuffled = Random.Shared.GetItems(starrySkyCharacters.ToArray(), starrySkyCharacters.Count);
+ // // var cellWeight = noise[i, j];
+ // // var item = starrySkyCharacters.OrderByDescending(x => x.Value.Density).FirstOrDefault(x => x.Value.Density <= cellWeight);
+ // // if (item.Value != null) {
+ // // c = item.Key;
+ // // }
+ // //
+ // // // foreach (var (key, value) in starrySkyCharacters) {
+ // // // var diff = Math.Abs(noise[i, j] - value.Density);
+ // // // if (diff < min) {
+ // // // min = diff;
+ // // // c = key;
+ // // // }
+ // // // }
+ // //
+ // // skybox[i, j] = c;
+ // // }
+ // // }
+ // //
+ // // for (var i = 0; i < str.Length; i++) {
+ // // skybox[outerTopBottomBorder + innerTopBottomBorder, i + outerLeftRightBorder + innerLeftRightBorder] = str[i];
+ // // }
+ // //
+ // // for (var i = 0; i < height; i++) {
+ // // for (var j = 0; j < width; j++) {
+ // // sb.Append(skybox[i, j]);
+ // // }
+ // //
+ // // sb.AppendLine();
+ // // }
+ //
+ // return sb.ToString();
+ // }
+
+#endregion
+}
\ No newline at end of file
|