135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
String utility module: compression, formatting, and analysis helpers.
|
|
Designed for quick text manipulation tasks in the Hermes Agent workflow.
|
|
"""
|
|
|
|
import re
|
|
import base64
|
|
import zlib
|
|
from typing import Dict, List, Tuple, Optional
|
|
from collections import Counter
|
|
|
|
|
|
def compress_string(s: str) -> str:
|
|
"""Compress a string using zlib and return as base64."""
|
|
if not s:
|
|
return ""
|
|
compressed = zlib.compress(s.encode('utf-8'))
|
|
return base64.b64encode(compressed).decode('ascii')
|
|
|
|
|
|
def decompress_string(b64: str) -> str:
|
|
"""Decompress a base64-encoded zlib string."""
|
|
if not b64:
|
|
return ""
|
|
compressed = base64.b64decode(b64.encode('ascii'))
|
|
return zlib.decompress(compressed).decode('utf-8')
|
|
|
|
|
|
def count_words(s: str) -> Dict[str, int]:
|
|
"""Count word frequencies in a string."""
|
|
words = re.findall(r'\b[a-zA-Z]+\b', s.lower())
|
|
return dict(Counter(words))
|
|
|
|
|
|
def extract_emails(s: str) -> List[str]:
|
|
"""Extract all email addresses from a string."""
|
|
pattern = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
|
|
return list(set(re.findall(pattern, s)))
|
|
|
|
|
|
def mask_email(email: str) -> str:
|
|
"""Mask an email address for privacy (user@x.com -> u**@*.com)."""
|
|
if '@' not in email:
|
|
return email
|
|
local, domain = email.split('@', 1)
|
|
if len(local) <= 1:
|
|
masked_local = '*'
|
|
else:
|
|
masked_local = local[0] + '*' * (len(local) - 1)
|
|
return f"{masked_local}**@*.{domain}"
|
|
|
|
|
|
def camel_to_snake(name: str) -> str:
|
|
"""Convert camelCase to snake_case."""
|
|
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
|
|
|
|
|
def snake_to_camel(name: str) -> str:
|
|
"""Convert snake_case to camelCase."""
|
|
components = name.split('_')
|
|
return components[0] + ''.join(x.title() for x in components[1:])
|
|
|
|
|
|
def abbreviate(s: str, max_len: int = 50) -> str:
|
|
"""Abbreviate a string to max_len with '...' suffix if longer."""
|
|
if len(s) <= max_len:
|
|
return s
|
|
return s[:max_len - 3] + '...'
|
|
|
|
|
|
def word_wrap(text: str, width: int = 70) -> str:
|
|
"""Wrap text to specified width, preserving existing line breaks."""
|
|
paragraphs = text.split('\n\n')
|
|
wrapped = []
|
|
for p in paragraphs:
|
|
words = p.split()
|
|
lines = []
|
|
current = []
|
|
current_len = 0
|
|
for w in words:
|
|
if current_len + len(w) + (1 if current else 0) <= width:
|
|
current.append(w)
|
|
current_len += len(w) + (1 if current else 0)
|
|
else:
|
|
if current:
|
|
lines.append(' '.join(current))
|
|
current = [w]
|
|
current_len = len(w)
|
|
if current:
|
|
lines.append(' '.join(current))
|
|
wrapped.append('\n'.join(lines))
|
|
return '\n\n'.join(wrapped)
|
|
|
|
|
|
def extract_commands(text: str) -> List[str]:
|
|
"""Extract shell commands from markdown code blocks."""
|
|
pattern = r'```(?:bash|shell)?\n(.*?)\n```'
|
|
matches = re.findall(pattern, text, re.DOTALL)
|
|
commands = []
|
|
for m in matches:
|
|
for line in m.split('\n'):
|
|
line = line.strip()
|
|
if line and not line.startswith('#'):
|
|
commands.append(line)
|
|
return commands
|
|
|
|
|
|
def analyze_text(text: str) -> Dict:
|
|
"""Return text analysis: lines, words, chars, readability."""
|
|
lines = text.split('\n')
|
|
words = text.split()
|
|
chars = len(text)
|
|
avg_word_len = sum(len(w) for w in words) / len(words) if words else 0
|
|
avg_line_len = sum(len(l) for l in lines) / len(lines) if lines else 0
|
|
return {
|
|
'lines': len(lines),
|
|
'words': len(words),
|
|
'characters': chars,
|
|
'avg_word_length': round(avg_word_len, 2),
|
|
'avg_line_length': round(avg_line_len, 2),
|
|
'is_empty': chars == 0,
|
|
}
|
|
|
|
|
|
if __name__ == '__main__':
|
|
# Debug: print mask_email output
|
|
mask_test = mask_email("admin@company.co.uk")
|
|
print(f"DEBUG mask_test = {repr(mask_test)}")
|
|
print(f"DEBUG '@company.co.uk' in mask_test = {'@company.co.uk' in mask_test}")
|
|
print(f"DEBUG mask starts with 'a' = {mask_test.startswith('a')}")
|
|
print(f"DEBUG asterisks = {mask_test.count('*')}")
|
|
|
|
exit(0) |