From 1e653c60e3e8772701c7b3de85e9854c94afa88f Mon Sep 17 00:00:00 2001 From: ReK42 Date: Wed, 18 Feb 2026 18:34:07 -0800 Subject: [PATCH] Add `use_community_slug` option to support disabling the slug suffix --- base-config.yaml | 4 ++ community/bot.py | 18 +++++-- community/helpers/config_manager.py | 16 +++++- community/helpers/room_creation_utils.py | 7 ++- community/helpers/room_utils.py | 14 +++-- example-standalone-config.yaml | 3 ++ tests/test_room_utils.py | 65 ++++++++++++++++++------ 7 files changed, 100 insertions(+), 27 deletions(-) diff --git a/base-config.yaml b/base-config.yaml index 70b5c30..375abb9 100644 --- a/base-config.yaml +++ b/base-config.yaml @@ -10,6 +10,10 @@ parent_room: '' # leave blank to generate an acronym of your community name during initialization community_slug: '' +# use_community_slug +# whether to use the community slug as a suffix for room aliases +use_community_slug: true + # sleep time between actions. you can drop this to 0 if your bot has no # ratelimits imposed on its homeserver, otherwise you may want to increase this # to avoid errors. diff --git a/community/bot.py b/community/bot.py index 475e463..a3d204a 100644 --- a/community/bot.py +++ b/community/bot.py @@ -70,6 +70,7 @@ class Config(BaseProxyConfig): helper.copy("welcome_sleep") helper.copy("parent_room") helper.copy("community_slug") + helper.copy("use_community_slug") helper.copy("track_users") helper.copy("warn_threshold_days") helper.copy("kick_threshold_days") @@ -174,7 +175,9 @@ class CommunityBot(Plugin): Returns: tuple: (is_valid, list_of_conflicting_aliases) """ - if not self.config.get("community_slug", ""): + if self.config.get("use_community_slug", True) and not self.config.get( + "community_slug", "" + ): if evt: await evt.respond( "Error: No community slug configured. Please run initialize command first." @@ -183,7 +186,11 @@ class CommunityBot(Plugin): server = self.client.parse_user_id(self.client.mxid)[1] return await room_utils.validate_room_aliases( - self.client, room_names, self.config.get("community_slug", ""), server + self.client, + room_names, + self.config.get("community_slug", ""), + self.config.get("use_community_slug", True), + server, ) async def get_moderators_and_above(self) -> list[str]: @@ -1849,7 +1856,7 @@ class CommunityBot(Plugin): return # Check if community slug is configured - if not self.config["community_slug"]: + if self.config["use_community_slug"] and not self.config["community_slug"]: await evt.reply( "No community slug configured. Please run initialize command first." ) @@ -2055,7 +2062,7 @@ class CommunityBot(Plugin): aliases_to_transfer = await self.remove_room_aliases(room_id, evt) # Check if community slug is configured - if not self.config["community_slug"]: + if self.config["use_community_slug"] and not self.config["community_slug"]: await evt.respond( "No community slug configured. Please run initialize command first." ) @@ -2962,7 +2969,7 @@ class CommunityBot(Plugin): try: # Generate community slug if not already set - if not self.config["community_slug"]: + if self.config["use_community_slug"] and not self.config["community_slug"]: community_slug = self.generate_community_slug(community_name) self.config["community_slug"] = community_slug self.log.info(f"Generated community slug: {community_slug}") @@ -3145,6 +3152,7 @@ class CommunityBot(Plugin): await evt.respond( f"Community space initialized successfully!

" f"Community Slug: {self.config['community_slug']}
" + f"Use Community Slug: {self.config['use_community_slug']}" f"Room Version: {self.config['room_version']}
" f"Space: {space_alias}
" f"Moderators Room: {mod_room_alias}
" diff --git a/community/helpers/config_manager.py b/community/helpers/config_manager.py index c72f82a..71c87df 100644 --- a/community/helpers/config_manager.py +++ b/community/helpers/config_manager.py @@ -99,6 +99,14 @@ class ConfigManager: """ return self.config.get("community_slug") + def get_use_community_slug(self) -> Optional[str]: + """Get the community slug suffix setting. + + Returns: + bool: Whether to use the community slug as a room suffix + """ + return self.config.get("use_community_slug") + def get_parent_room(self) -> Optional[str]: """Get the parent room ID. @@ -201,7 +209,12 @@ class ConfigManager: Returns: List[str]: List of missing required configuration keys """ - required_configs = ["parent_room", "room_version", "community_slug"] + required_configs = [ + "parent_room", + "room_version", + "community_slug", + "use_community_slug", + ] missing = [] for config_key in required_configs: @@ -231,6 +244,7 @@ class ConfigManager: return { "room_version": self.get_room_version(), "community_slug": self.get_community_slug(), + "use_community_slug": self.get_use_community_slug(), "invitees": self.get_invitees(), "invite_power_level": self.get_invite_power_level(), "encrypt": self.is_encryption_enabled(), diff --git a/community/helpers/room_creation_utils.py b/community/helpers/room_creation_utils.py index 2001a5f..431fd7c 100644 --- a/community/helpers/room_creation_utils.py +++ b/community/helpers/room_creation_utils.py @@ -38,7 +38,7 @@ async def validate_room_creation_params( sanitized_name = re.sub(r"[^a-zA-Z0-9]", "", roomname).lower() # Check if community slug is configured - if not config.get("community_slug", ""): + if config.get("use_community_slug", True) and not config.get("community_slug", ""): error_msg = "No community slug configured. Please run initialize command first." return sanitized_name, force_encryption, force_unencryption, error_msg, roomname @@ -63,7 +63,10 @@ async def prepare_room_creation_data( Tuple of (alias_localpart, server, room_invitees, parent_room) """ # Create alias with community slug - alias_localpart = f"{sanitized_name}-{config.get('community_slug', '')}" + if config.get("use_community_slug", True): + alias_localpart = f"{sanitized_name}-{config.get('community_slug', '')}" + else: + alias_localpart = sanitized_name # Get server and invitees server = client.parse_user_id(client.mxid)[1] diff --git a/community/helpers/room_utils.py b/community/helpers/room_utils.py index 35a8315..e2b435d 100644 --- a/community/helpers/room_utils.py +++ b/community/helpers/room_utils.py @@ -31,7 +31,11 @@ async def validate_room_alias(client, alias_localpart: str, server: str) -> bool async def validate_room_aliases( - client, room_names: list[str], community_slug: str, server: str + client, + room_names: list[str], + community_slug: str, + use_community_slug: bool, + server: str, ) -> Tuple[bool, List[str]]: """Validate that all room aliases are available. @@ -39,12 +43,13 @@ async def validate_room_aliases( client: Matrix client instance room_names: List of room names to validate community_slug: The community slug to append + use_community_slug: Whether to append a community slug server: The server domain Returns: tuple: (is_valid, list_of_conflicting_aliases) """ - if not community_slug: + if use_community_slug and not community_slug: return False, [] conflicting_aliases = [] @@ -54,7 +59,10 @@ async def validate_room_aliases( from .message_utils import sanitize_room_name sanitized_name = sanitize_room_name(room_name) - alias_localpart = f"{sanitized_name}-{community_slug}" + if use_community_slug: + alias_localpart = f"{sanitized_name}-{community_slug}" + else: + alias_localpart = sanitized_name # Check if alias is available is_available = await validate_room_alias(client, alias_localpart, server) diff --git a/example-standalone-config.yaml b/example-standalone-config.yaml index 8a4222e..7151991 100644 --- a/example-standalone-config.yaml +++ b/example-standalone-config.yaml @@ -67,6 +67,9 @@ plugin_config: # leave blank to generate an acronym of your community name during initialization community_slug: '' + # use_community_slug + # whether to use the community slug as a suffix for room aliases + use_community_slug: true # sleep time between actions. you can drop this to 0 if your bot has no # ratelimits imposed on its homeserver, otherwise you may want to increase this diff --git a/tests/test_room_utils.py b/tests/test_room_utils.py index 33edf06..a3bc003 100644 --- a/tests/test_room_utils.py +++ b/tests/test_room_utils.py @@ -46,36 +46,69 @@ class TestRoomUtils: assert result == True @pytest.mark.asyncio - async def test_validate_room_aliases_no_slug(self): + async def test_validate_room_aliases_slug_not_required_with_no_slug(self): """Test alias validation without community slug.""" client = Mock() - - result = await validate_room_aliases(client, ["room1", "room2"], "", "example.com") - assert result == (False, []) - @pytest.mark.asyncio - async def test_validate_room_aliases_success(self): - """Test successful alias validation.""" - client = Mock() - client.resolve_room_alias = AsyncMock(side_effect=MNotFound("Room not found", 404)) - - result = await validate_room_aliases(client, ["room1", "room2"], "test", "example.com") + result = await validate_room_aliases(client, ["room1", "room2"], "", False, "example.com") assert result == (True, []) @pytest.mark.asyncio - async def test_validate_room_aliases_conflicts(self): + async def test_validate_room_aliases_slug_not_required_with_slug(self): + """Test successful alias validation.""" + client = Mock() + client.resolve_room_alias = AsyncMock(side_effect=MNotFound("Room not found", 404)) + + result = await validate_room_aliases(client, ["room1", "room2"], "test", False, "example.com") + assert result == (True, []) + + @pytest.mark.asyncio + async def test_validate_room_aliases_slug_required_with_no_slug(self): + """Test alias validation without community slug.""" + client = Mock() + + result = await validate_room_aliases(client, ["room1", "room2"], "", True, "example.com") + assert result == (False, []) + + @pytest.mark.asyncio + async def test_validate_room_aliases_slug_required_with_slug(self): + """Test successful alias validation.""" + client = Mock() + client.resolve_room_alias = AsyncMock(side_effect=MNotFound("Room not found", 404)) + + result = await validate_room_aliases(client, ["room1", "room2"], "test", True, "example.com") + assert result == (True, []) + + @pytest.mark.asyncio + async def test_validate_room_aliases_conflicts_slug_not_required(self): """Test alias validation with conflicts.""" client = Mock() - + def resolve_side_effect(alias): if "room1" in alias: return {"room_id": "!room1:example.com"} # Exists else: raise MNotFound() # Doesn't exist - + client.resolve_room_alias = AsyncMock(side_effect=resolve_side_effect) - - result = await validate_room_aliases(client, ["room1", "room2"], "test", "example.com") + + result = await validate_room_aliases(client, ["room1", "room2"], "", False, "example.com") + assert result == (False, ["#room1:example.com"]) + + @pytest.mark.asyncio + async def test_validate_room_aliases_conflicts_slug_required(self): + """Test alias validation with conflicts.""" + client = Mock() + + def resolve_side_effect(alias): + if "room1" in alias: + return {"room_id": "!room1:example.com"} # Exists + else: + raise MNotFound() # Doesn't exist + + client.resolve_room_alias = AsyncMock(side_effect=resolve_side_effect) + + result = await validate_room_aliases(client, ["room1", "room2"], "test", True, "example.com") assert result == (False, ["#room1-test:example.com"]) @pytest.mark.asyncio