From 15cf9e72bb654d42a90cfe0213b238dff7291043 Mon Sep 17 00:00:00 2001 From: Hash Borgir Date: Sun, 10 May 2026 11:02:35 -0500 Subject: [PATCH] Fixes to encode.py, requirements.txt and updated README.txt with encode commnds help --- README.md | 30 +++++++++- plugins/encode.py | 136 ++++++++++++++++++++++++++++++++++++++++++---- requirements.txt | 1 + 3 files changed, 154 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 567964f..45d5346 100644 --- a/README.md +++ b/README.md @@ -390,7 +390,14 @@ A fully offline, CyberChef-like data manipulation plugin with dozens of operatio **Compression** -`gzip`, `zlib`, `bzip2`, `lzma` — each supports `compress` and `decompress` subcommands. +| Operation | Example | +|-----------|---------| +| `gzip compress\|decompress` | `!encode gzip compress "Hello World"` | +| `zlib compress\|decompress` | `!encode zlib compress "Hello World"` | +| `bzip2 compress\|decompress` | `!encode bzip2 compress "Hello World"` | +| `lzma compress\|decompress` | `!encode lzma compress "Hello World"` | +| `deflate compress\|decompress` | `!encode deflate compress "Hello World"` | +| `zstd compress\|decompress` | `!encode zstd compress "Hello World"` | **Data Processing** @@ -430,6 +437,27 @@ A fully offline, CyberChef-like data manipulation plugin with dozens of operatio ``` !encode recipe list !encode recipe run '{"steps":[{"op":"base64","args":["encode"]},{"op":"hex","args":["encode"]}]}' "hello world" +!encode recipe run 'base64 encode | hex encode :: hello world' +``` + +**Recipe Commands Details:** +- `!encode recipe list` - Lists all available operations that can be chained together +- `!encode recipe run '' ` - Execute a JSON recipe on input data +- `!encode recipe run ' arg | arg :: '` - Execute a pipe-style recipe with data + +JSON recipes allow complex operation chaining with the format: +```json +{ + "steps": [ + {"op": "base64", "args": ["encode"]}, + {"op": "hex", "args": ["encode"]} + ] +} +``` + +Pipe-style recipes provide a simpler syntax: +``` +base64 encode | hex encode :: hello world ``` --- diff --git a/plugins/encode.py b/plugins/encode.py index d951dad..a90cbfa 100644 --- a/plugins/encode.py +++ b/plugins/encode.py @@ -55,6 +55,12 @@ try: except ImportError: HAS_CRYPTOGRAPHY = False +try: + import zstandard + HAS_ZSTD = True +except ImportError: + HAS_ZSTD = False + try: import bcrypt HAS_BCRYPT = True @@ -855,6 +861,46 @@ async def op_dns(text: str) -> str: except Exception as e: return f"DNS error: {e}" +@register_op("deflate", "Raw DEFLATE compress / decompress", "Compression", arg_names=["subcmd"]) +async def op_deflate(subcmd: str, text: str) -> str: + sub = subcmd.lower() + raw = validate_input(text) + if sub in ("compress", "comp", "c"): + # Use zlib in raw‑deflate mode (wbits=-15) + compressor = zlib.compressobj(level=6, wbits=-15) + compressed = compressor.compress(raw) + compressor.flush() + return base64.b64encode(compressed).decode() + elif sub in ("decompress", "decomp", "d"): + try: + decompressed = zlib.decompress(base64.b64decode(raw), wbits=-15) + return decompressed.decode("utf-8", errors="replace") + except Exception as e: + return f"DEFLATE decompress error: {e}" + else: + raise ValueError("Use 'compress' or 'decompress'") + + +@register_op("zstd", "Zstandard compress / decompress", "Compression", arg_names=["subcmd"]) +async def op_zstd(subcmd: str, text: str) -> str: + if not HAS_ZSTD: + return "Error: zstandard library not installed" + import zstandard as zstd + sub = subcmd.lower() + raw = validate_input(text) + if sub in ("compress", "comp", "c"): + cctx = zstd.ZstdCompressor() + compressed = cctx.compress(raw) + return base64.b64encode(compressed).decode() + elif sub in ("decompress", "decomp", "d"): + try: + dctx = zstd.ZstdDecompressor() + decompressed = dctx.decompress(base64.b64decode(raw)) + return decompressed.decode("utf-8", errors="replace") + except Exception as e: + return f"Zstd decompress error: {e}" + else: + raise ValueError("Use 'compress' or 'decompress'") + # ---------- Recipe system ---------- class Recipe: """A sequence of operations to apply.""" @@ -896,9 +942,9 @@ class Recipe: break return data -@register_op("recipe", "Run a recipe (provide JSON and data)", "Recipes", +@register_op("recipe", "Run a recipe (JSON or pipe syntax)", "Recipes", arg_names=["subcmd"]) -async def op_recipe(subcmd: str, json_or_data: str, *extra: str) -> str: +async def op_recipe(subcmd: str, json_or_data: str) -> str: sub = subcmd.lower() if sub == "list": cats = collections.defaultdict(list) @@ -908,20 +954,79 @@ async def op_recipe(subcmd: str, json_or_data: str, *extra: str) -> str: for cat in sorted(cats): out.append(f"{cat}: {', '.join(cats[cat])}") return "\n".join(out) + elif sub == "run": - # Usage: !encode recipe run '' - # Here json_or_data is the JSON string, extra[0] is the data - if not extra: - raise ValueError("Provide data after JSON recipe") - recipe_json = json_or_data - data = " ".join(extra) # extra is tuple + s = json_or_data.strip() + + # Strip a single pair of matching outer quotes if present + if (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')): + s = s[1:-1].strip() + + # ---- try JSON first ---- + if s.startswith("{"): + # original JSON parsing + depth = 0 + end = 0 + for i, ch in enumerate(s): + if ch == "{": + depth += 1 + elif ch == "}": + depth -= 1 + if depth == 0: + end = i + break + if depth != 0: + raise ValueError("Unbalanced braces in JSON recipe") + + recipe_json = s[:end+1] + data_part = s[end+1:].strip() + if not data_part: + raise ValueError("No data provided after JSON recipe") + + try: + recipe = Recipe.from_json(recipe_json) + return await recipe.run(data_part, OPERATIONS) + except Exception as e: + return f"Recipe error: {e}" + + # ---- try pipe syntax: op args ... | op args ... :: data ---- + # Split into operations and data at the last occurrence of " :: " + if " :: " in s: + ops_section, data_part = s.rsplit(" :: ", 1) + data_part = data_part.strip() + else: + raise ValueError( + "Pipe syntax: !encode recipe run [args] | [args] :: \n" + "Or JSON: !encode recipe run '{...}' " + ) + + ops_section = ops_section.strip() + if not ops_section: + raise ValueError("At least one operation is required before ::") + if not data_part: + raise ValueError("No data provided after ::") + + # Split operations by " | " + ops_parts = [p.strip() for p in ops_section.split(" | ")] + steps = [] + for part in ops_parts: + tokens = part.split() + if not tokens: + continue + op_name = tokens[0].lower() + if op_name not in OPERATIONS: + raise ValueError(f"Unknown operation '{op_name}' in pipe recipe") + args = tokens[1:] # remaining tokens are args + steps.append({"op": op_name, "args": args}) + try: - recipe = Recipe.from_json(recipe_json) - return await recipe.run(data, OPERATIONS) + recipe = Recipe(steps) + return await recipe.run(data_part, OPERATIONS) except Exception as e: return f"Recipe error: {e}" + else: - raise ValueError("Use 'list' or 'run '") + raise ValueError("Use 'list' or 'run'") # ---------- Main handler (interface to the bot) ---------- async def handle_command(room, message, bot, prefix, config): @@ -1137,6 +1242,10 @@ compression, data processing, forensics, and networking. Fully offline. !encode bzip2 compress data
  • lzma – LZMA compress/decompress
    !encode lzma compress data
  • +
  • deflate – Raw DEFLATE compress/decompress
    +!encode deflate compress data
  • +
  • zstd – Zstandard compress/decompress (requires zstandard)
    +!encode zstd compress data
  • Data Processing

    @@ -1195,7 +1304,10 @@ compression, data processing, forensics, and networking. Fully offline.
  • recipe list – List all available operations
    !encode recipe list
  • recipe run – Execute a JSON recipe on data
    -!encode recipe run '{"steps":[{"op":"base64","args":["encode"]},{"op":"hex","args":["encode"]}]}' "hello world"
  • +!encode recipe run '{"steps":[{"op":"base64", args ["encode"]}, {"op":"hex", args ["encode"]}]}' "hello world" +
  • recipe run (pipe syntax) – Chain operations with | and separate data with ::
    +!encode recipe run base64 encode | hex encode :: hello world
    +!encode recipe run base64 encode | hex encode | gzip compress :: my secret data
  • Type !encode help <op> for detailed argument info on any operation.

    diff --git a/requirements.txt b/requirements.txt index a0731ba..872749f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,3 +23,4 @@ PyYAML wcwidth markdown python-cryptography-fernet-wrapper +zstandard \ No newline at end of file