diff --git a/README.md b/README.md index 615db68..604bd78 100644 --- a/README.md +++ b/README.md @@ -207,11 +207,11 @@ Keyword responses support these template variables: ### Adding Newlines -To add newlines in keyword responses, use `\\n` (double backslash + n): +To add newlines in keyword responses, use `\n` (single backslash + n): ```ini [Keywords] -test = "Line 1\\nLine 2\\nLine 3" +test = "Line 1\nLine 2\nLine 3" ``` This will output: @@ -221,7 +221,8 @@ Line 2 Line 3 ``` -Other escape sequences: `\\t` (tab), `\\r` (carriage return), `\\\\` (literal backslash) +To use a literal backslash + n, use `\\n` (double backslash + n). +Other escape sequences: `\t` (tab), `\r` (carriage return), `\\` (literal backslash) Example: ```ini diff --git a/config.ini.example b/config.ini.example index 2c4ce72..3669188 100644 --- a/config.ini.example +++ b/config.ini.example @@ -230,14 +230,15 @@ long_jokes = false # {firstlast_distance} - Distance between first and last repeater in path (e.g., "45.6km" or empty if locations missing) # {elapsed} - Message elapsed time # -# To add newlines in responses, use \\n (double backslash + n): -# Example: test = "Line 1\\nLine 2\\nLine 3" +# To add newlines in responses, use \n (single backslash + n): +# Example: test = "Line 1\nLine 2\nLine 3" # This will output: # Line 1 # Line 2 # Line 3 # -# Other escape sequences: \\t (tab), \\r (carriage return), \\\\ (literal backslash) +# To use a literal backslash + n, use \\n (double backslash + n) +# Other escape sequences: \t (tab), \r (carriage return), \\ (literal backslash) # # Available placeholders (mesh network info - same as Scheduled_Messages): # Total counts (ever heard): diff --git a/modules/command_manager.py b/modules/command_manager.py index 53c49e8..2690773 100644 --- a/modules/command_manager.py +++ b/modules/command_manager.py @@ -181,7 +181,11 @@ class CommandManager: """Decode escape sequences in config strings. Processes common escape sequences like \\n (newline), \\t (tab), \\\\ (backslash). - This allows users to add newlines in keyword responses using \\n. + This allows users to add newlines in keyword responses using \n (single backslash). + + Behavior: + - \n in config file → newline character + - \\n in config file → literal backslash + n Args: text: The text string to process. @@ -191,6 +195,7 @@ class CommandManager: """ # Replace escape sequences # Order matters: \\ must be processed first to avoid double-processing + # This preserves literal backslashes (\\n becomes \n, not a newline) text = text.replace('\\\\', '\x00') # Temporary placeholder for backslash text = text.replace('\\n', '\n') # Newline text = text.replace('\\t', '\t') # Tab diff --git a/modules/commands/greeter_command.py b/modules/commands/greeter_command.py index b33e8ea..7e2b637 100644 --- a/modules/commands/greeter_command.py +++ b/modules/commands/greeter_command.py @@ -123,7 +123,11 @@ class GreeterCommand(BaseCommand): """Decode escape sequences in config strings. Processes common escape sequences like \\n (newline), \\t (tab), \\\\ (backslash). - This allows users to add newlines in greeting messages using \\n. + This allows users to add newlines in greeting messages using \n (single backslash). + + Behavior: + - \n in config file → newline character + - \\n in config file → literal backslash + n Args: text: The text string to process. @@ -133,6 +137,7 @@ class GreeterCommand(BaseCommand): """ # Replace escape sequences # Order matters: \\ must be processed first to avoid double-processing + # This preserves literal backslashes (\\n becomes \n, not a newline) text = text.replace('\\\\', '\x00') # Temporary placeholder for backslash text = text.replace('\\n', '\n') # Newline text = text.replace('\\t', '\t') # Tab @@ -153,9 +158,13 @@ class GreeterCommand(BaseCommand): fallback=True, value_type='bool') self.mesh_info_format = self.get_config_value('Greeter_Command', 'mesh_info_format', fallback='\n\nMesh Info: {total_contacts} contacts, {repeaters} repeaters') - # Decode escape sequences (e.g., \\n for newlines) + # Decode escape sequences (e.g., \n for newlines) self.mesh_info_format = self._decode_escape_sequences(self.mesh_info_format) + # Log configuration for debugging + self.logger.debug(f"Greeter config loaded: include_mesh_info={self.include_mesh_info}, " + f"mesh_info_format={repr(self.mesh_info_format)}") + self.per_channel_greetings = self.get_config_value('Greeter_Command', 'per_channel_greetings', fallback=False, value_type='bool') self.auto_backfill = self.get_config_value('Greeter_Command', 'auto_backfill', @@ -1021,17 +1030,28 @@ class GreeterCommand(BaseCommand): # Add mesh info to the last part if enabled if self.include_mesh_info: - mesh_info_text = self.mesh_info_format.format( - total_contacts=mesh_info.get('total_contacts', 0), - repeaters=mesh_info.get('repeaters', 0), - companions=mesh_info.get('companions', 0), - recent_activity_24h=mesh_info.get('recent_activity_24h', 0) - ) - # Append mesh info to the last greeting part - if formatted_parts: - formatted_parts[-1] += mesh_info_text - else: - formatted_parts.append(mesh_info_text) + self.logger.debug(f"Including mesh info. Format: {repr(self.mesh_info_format)}, Mesh info: {mesh_info}") + try: + mesh_info_text = self.mesh_info_format.format( + total_contacts=mesh_info.get('total_contacts', 0), + repeaters=mesh_info.get('repeaters', 0), + companions=mesh_info.get('companions', 0), + recent_activity_24h=mesh_info.get('recent_activity_24h', 0) + ) + self.logger.debug(f"Formatted mesh info text: {repr(mesh_info_text)}") + # Append mesh info to the last greeting part + if formatted_parts: + formatted_parts[-1] += mesh_info_text + else: + formatted_parts.append(mesh_info_text) + except (KeyError, ValueError) as e: + self.logger.warning(f"Error formatting mesh info: {e}. Format string: {repr(self.mesh_info_format)}, Mesh info keys: {list(mesh_info.keys())}") + # Continue without mesh info rather than failing the entire greeting + except Exception as e: + self.logger.error(f"Unexpected error formatting mesh info: {e}", exc_info=True) + # Continue without mesh info rather than failing the entire greeting + else: + self.logger.debug("Mesh info not included (include_mesh_info is False)") return formatted_parts