diff --git a/README.md b/README.md index c21cc5c..615db68 100644 --- a/README.md +++ b/README.md @@ -205,6 +205,24 @@ Keyword responses support these template variables: - `{timestamp}` - Message timestamp - `{path}` - Message routing path +### Adding Newlines + +To add newlines in keyword responses, use `\\n` (double backslash + n): + +```ini +[Keywords] +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) + Example: ```ini [Keywords] diff --git a/config.ini.example b/config.ini.example index f5f5f1d..33471cb 100644 --- a/config.ini.example +++ b/config.ini.example @@ -229,6 +229,15 @@ long_jokes = false # {path_distance} - Total distance between all hops in path with locations (e.g., "123.4km (3 segs, 1 no-loc)") # {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" +# This will output: +# Line 1 +# Line 2 +# Line 3 +# +# 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 b1453b0..53c49e8 100644 --- a/modules/command_manager.py +++ b/modules/command_manager.py @@ -177,6 +177,27 @@ class CommandManager: self.bot.bot_tx_rate_limiter.record_tx() return True + def _decode_escape_sequences(self, text: str) -> str: + """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. + + Args: + text: The text string to process. + + Returns: + str: The text with escape sequences decoded. + """ + # Replace escape sequences + # Order matters: \\ must be processed first to avoid double-processing + text = text.replace('\\\\', '\x00') # Temporary placeholder for backslash + text = text.replace('\\n', '\n') # Newline + text = text.replace('\\t', '\t') # Tab + text = text.replace('\\r', '\r') # Carriage return + text = text.replace('\x00', '\\') # Restore backslash + return text + def load_keywords(self) -> Dict[str, str]: """Load keywords from config. @@ -189,6 +210,8 @@ class CommandManager: # Strip quotes from the response if present if response.startswith('"') and response.endswith('"'): response = response[1:-1] + # Decode escape sequences (e.g., \\n for newlines) + response = self._decode_escape_sequences(response) keywords[keyword.lower()] = response return keywords @@ -200,6 +223,8 @@ class CommandManager: # Strip quotes from the response format if present if response_format.startswith('"') and response_format.endswith('"'): response_format = response_format[1:-1] + # Decode escape sequences (e.g., \\n for newlines) + response_format = self._decode_escape_sequences(response_format) syntax_patterns[pattern] = response_format return syntax_patterns