diff --git a/MiniUtils/Commands/MakeRoomCommand.cs b/MiniUtils/Commands/MakeRoomCommand.cs
new file mode 100644
index 0000000..068bca5
--- /dev/null
+++ b/MiniUtils/Commands/MakeRoomCommand.cs
@@ -0,0 +1,235 @@
+using ArcaneLibs.Extensions;
+using LibMatrix.EventTypes.Common;
+using LibMatrix.EventTypes.Spec.State.RoomInfo;
+using LibMatrix.Helpers;
+using LibMatrix.Responses;
+using LibMatrix.RoomTypes;
+using LibMatrix.Utilities.Bot.Interfaces;
+
+namespace MiniUtils.Commands;
+
+public class MakeRoomCommand() : ICommand {
+ public string Name => "make room";
+
+ public string[]? Aliases => ["makeroom", "create room", "createroom"];
+
+ public string Description => "Make a new room";
+
+ public bool Unlisted => false;
+
+ public async Task Invoke(CommandContext ctx) {
+ if (ctx.Args.Length == 0) {
+ await ctx.Room.SendMessageEventAsync(
+ new MessageBuilder()
+ .WithTable(tb => {
+ tb.WithTitle("~create room", 3);
+ tb.WithRow(rb => {
+ rb.WithCell("Argument")
+ .WithCell("Alternatives")
+ .WithCell("Description");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--alias <localpart>")
+ .WithCell("")
+ .WithCell("Set the room alias");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--avatar-url <url>")
+ .WithCell("")
+ .WithCell("Set the room avatar URL");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--copy-avatar [room]")
+ .WithCell("")
+ .WithCell("Copy the avatar from another room (or current room if unspecified)");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--copy-powerlevels [room]")
+ .WithCell("")
+ .WithCell("Copy the power levels from another room (or current room if unspecified)");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--invite-admin <user>")
+ .WithCell("")
+ .WithCell("Invite a user as an admin");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--invite <user>")
+ .WithCell("")
+ .WithCell("Invite a user");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--name <name>")
+ .WithCell("")
+ .WithCell("Set the room name");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--topic <topic>")
+ .WithCell("")
+ .WithCell("Set the room topic");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--federate <true|false>")
+ .WithCell("")
+ .WithCell("Set whether the room is federatable");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--join-rule <rule>")
+ .WithCell("""
+ --public
+ --invite-only
+ --knock
+ --restricted
+ --knock_restricted
+ --private
+ """)
+ .WithCell("Set the room join rule to public, invite-only, knock, restricted, knock-restricted or private");
+ });
+ tb.WithRow(rb => {
+ rb.WithCell("--history-visibility <visibility>")
+ .WithCell("""
+ --shared
+ --invited
+ --joined
+ --world_readable
+ """)
+ .WithCell("Set the room history visibility to shared, invited, joined or world_readable");
+ });
+ })
+ .Build()
+ );
+ return;
+ }
+
+ var rb = new RoomBuilder() { };
+
+ for (int i = 0; i < ctx.Args.Length; i++) {
+ switch (ctx.Args[i]) {
+ case "--alias":
+ rb.AliasLocalPart = ctx.Args[++i];
+ break;
+ case "--avatar-url":
+ rb.Avatar!.Url = ctx.Args[++i];
+ break;
+ case "--copy-avatar": {
+ var room = await GetRoomByArgument(ctx, ctx.Args[i + 1]);
+ if (room != ctx.Room) i++;
+ rb.Avatar = await room.GetAvatarUrlAsync() ?? throw new ArgumentException($"Room {room.RoomId} does not have an avatar");
+ break;
+ }
+ case "--copy-powerlevels": {
+ var room = await GetRoomByArgument(ctx, ctx.Args[i + 1]);
+ if (room != ctx.Room) i++;
+ rb.PowerLevels = await room.GetPowerLevelsAsync() ?? throw new ArgumentException($"Room {room.RoomId} does not have power levels???");
+ break;
+ }
+ case "--invite-admin":
+ var inviteAdmin = ctx.Args[++i];
+ if (!inviteAdmin.StartsWith('@')) {
+ throw new ArgumentException("Invalid user reference: " + inviteAdmin);
+ }
+
+ rb.Invites.Add(inviteAdmin, "Marked explicitly as admin to be invited");
+ break;
+ case "--invite":
+ var inviteUser = ctx.Args[++i];
+ if (!inviteUser.StartsWith('@')) {
+ throw new ArgumentException("Invalid user reference: " + inviteUser);
+ }
+
+ rb.Invites.Add(inviteUser, "Marked explicitly to be invited");
+ break;
+ case "--name":
+ var nameEvt = rb.Name = new() { Name = "" };
+ while (i + 1 < ctx.Args.Length && !ctx.Args[i + 1].StartsWith("--")) {
+ nameEvt.Name += (nameEvt.Name.Length > 0 ? " " : "") + ctx.Args[++i];
+ }
+
+ break;
+ case "--topic":
+ var topicEvt = rb.Topic = new() { Topic = "" };
+ while (i + 1 < ctx.Args.Length && !ctx.Args[i + 1].StartsWith("--")) {
+ topicEvt.Topic += (topicEvt.Topic.Length > 0 ? " " : "") + ctx.Args[++i];
+ }
+
+ break;
+ case "--federate":
+ rb.IsFederatable = bool.Parse(ctx.Args[++i]);
+ break;
+ case "--public":
+ case "--invite-only":
+ case "--knock":
+ case "--restricted":
+ case "--knock_restricted":
+ case "--private":
+ rb.JoinRules.JoinRule = ctx.Args[i].Replace("--", "").ToLowerInvariant() switch {
+ "public" => RoomJoinRulesEventContent.JoinRules.Public,
+ "invite-only" => RoomJoinRulesEventContent.JoinRules.Invite,
+ "knock" => RoomJoinRulesEventContent.JoinRules.Knock,
+ "restricted" => RoomJoinRulesEventContent.JoinRules.Restricted,
+ "knock_restricted" => RoomJoinRulesEventContent.JoinRules.KnockRestricted,
+ "private" => RoomJoinRulesEventContent.JoinRules.Private,
+ _ => throw new ArgumentException("Unknown join rule: " + ctx.Args[i])
+ };
+ break;
+ case "--join-rule":
+ if (i + 1 >= ctx.Args.Length || !ctx.Args[i + 1].StartsWith("--")) {
+ throw new ArgumentException("Expected join rule after --join-rule");
+ }
+
+ rb.JoinRules.JoinRule = ctx.Args[++i].ToLowerInvariant() switch {
+ "public" => RoomJoinRulesEventContent.JoinRules.Public,
+ "invite" => RoomJoinRulesEventContent.JoinRules.Invite,
+ "knock" => RoomJoinRulesEventContent.JoinRules.Knock,
+ "restricted" => RoomJoinRulesEventContent.JoinRules.Restricted,
+ "knock_restricted" => RoomJoinRulesEventContent.JoinRules.KnockRestricted,
+ "private" => RoomJoinRulesEventContent.JoinRules.Private,
+ _ => throw new ArgumentException("Unknown join rule: " + ctx.Args[i])
+ };
+ break;
+ case "--history-visibility":
+ rb.HistoryVisibility = new RoomHistoryVisibilityEventContent {
+ HistoryVisibility = ctx.Args[++i].ToLowerInvariant() switch {
+ "shared" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Shared,
+ "invited" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Invited,
+ "joined" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.Joined,
+ "world_readable" => RoomHistoryVisibilityEventContent.HistoryVisibilityTypes.WorldReadable,
+ _ => throw new ArgumentException("Unknown history visibility: " + ctx.Args[i])
+ }
+ };
+ break;
+
+ default:
+ throw new ArgumentException("Unknown argument: " + ctx.Args[i]);
+ }
+ }
+
+ // await ctx.Room.SendMessageEventAsync(
+ // new MessageBuilder()
+ // .WithCodeBlock(rb.ToJson(), "json")
+ // .Build()
+ // );
+ // var result = await ctx.Homeserver.CreateRoom(creationContent);
+ var result = await rb.Create(ctx.Homeserver);
+ await ctx.Room.SendMessageEventAsync(new MessageBuilder()
+ .WithMention($"{result.RoomId}?via={ctx.Homeserver.ServerName}", rb.CanonicalAlias.Alias)
+ .Build());
+ }
+
+ private async Task<GenericRoom> GetRoomByArgument(CommandContext ctx, string roomReference, bool defaultToCurrent = true) {
+ if (roomReference.StartsWith("--")) {
+ return defaultToCurrent ? ctx.Room : throw new ArgumentException("Invalid room reference: " + roomReference);
+ }
+
+ if (roomReference.StartsWith('!')) {
+ return ctx.Homeserver.GetRoom(roomReference);
+ }
+
+ if (roomReference.StartsWith('#')) {
+ var resolvedAlias = await ctx.Homeserver.ResolveRoomAliasAsync(roomReference);
+ return ctx.Homeserver.GetRoom(resolvedAlias.RoomId);
+ }
+
+ throw new ArgumentException("Invalid room reference: " + roomReference);
+ }
+}
\ No newline at end of file
|