Fixes to encode.py, requirements.txt and updated README.txt with encode commnds help
This commit is contained in:
@@ -390,7 +390,14 @@ A fully offline, CyberChef-like data manipulation plugin with dozens of operatio
|
|||||||
|
|
||||||
**Compression**
|
**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**
|
**Data Processing**
|
||||||
|
|
||||||
@@ -430,6 +437,27 @@ A fully offline, CyberChef-like data manipulation plugin with dozens of operatio
|
|||||||
```
|
```
|
||||||
!encode recipe list
|
!encode recipe list
|
||||||
!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"
|
||||||
|
!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 '<json>' <data>` - Execute a JSON recipe on input data
|
||||||
|
- `!encode recipe run '<op> arg | <op> arg :: <data>'` - 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
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+124
-12
@@ -55,6 +55,12 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
HAS_CRYPTOGRAPHY = False
|
HAS_CRYPTOGRAPHY = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
import zstandard
|
||||||
|
HAS_ZSTD = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_ZSTD = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import bcrypt
|
import bcrypt
|
||||||
HAS_BCRYPT = True
|
HAS_BCRYPT = True
|
||||||
@@ -855,6 +861,46 @@ async def op_dns(text: str) -> str:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"DNS error: {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 ----------
|
# ---------- Recipe system ----------
|
||||||
class Recipe:
|
class Recipe:
|
||||||
"""A sequence of operations to apply."""
|
"""A sequence of operations to apply."""
|
||||||
@@ -896,9 +942,9 @@ class Recipe:
|
|||||||
break
|
break
|
||||||
return data
|
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"])
|
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()
|
sub = subcmd.lower()
|
||||||
if sub == "list":
|
if sub == "list":
|
||||||
cats = collections.defaultdict(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):
|
for cat in sorted(cats):
|
||||||
out.append(f"{cat}: {', '.join(cats[cat])}")
|
out.append(f"{cat}: {', '.join(cats[cat])}")
|
||||||
return "\n".join(out)
|
return "\n".join(out)
|
||||||
|
|
||||||
elif sub == "run":
|
elif sub == "run":
|
||||||
# Usage: !encode recipe run '<json>' <data>
|
s = json_or_data.strip()
|
||||||
# Here json_or_data is the JSON string, extra[0] is the data
|
|
||||||
if not extra:
|
# Strip a single pair of matching outer quotes if present
|
||||||
raise ValueError("Provide data after JSON recipe")
|
if (s.startswith("'") and s.endswith("'")) or (s.startswith('"') and s.endswith('"')):
|
||||||
recipe_json = json_or_data
|
s = s[1:-1].strip()
|
||||||
data = " ".join(extra) # extra is tuple
|
|
||||||
|
# ---- 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 <op> [args] | <op> [args] :: <data>\n"
|
||||||
|
"Or JSON: !encode recipe run '{...}' <data>"
|
||||||
|
)
|
||||||
|
|
||||||
|
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:
|
try:
|
||||||
recipe = Recipe.from_json(recipe_json)
|
recipe = Recipe(steps)
|
||||||
return await recipe.run(data, OPERATIONS)
|
return await recipe.run(data_part, OPERATIONS)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Recipe error: {e}"
|
return f"Recipe error: {e}"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("Use 'list' or 'run <json> <data>'")
|
raise ValueError("Use 'list' or 'run'")
|
||||||
|
|
||||||
# ---------- Main handler (interface to the bot) ----------
|
# ---------- Main handler (interface to the bot) ----------
|
||||||
async def handle_command(room, message, bot, prefix, config):
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
@@ -1137,6 +1242,10 @@ compression, data processing, forensics, and networking. Fully offline.
|
|||||||
<code>!encode bzip2 compress data</code></li>
|
<code>!encode bzip2 compress data</code></li>
|
||||||
<li><b>lzma</b> – LZMA compress/decompress<br>
|
<li><b>lzma</b> – LZMA compress/decompress<br>
|
||||||
<code>!encode lzma compress data</code></li>
|
<code>!encode lzma compress data</code></li>
|
||||||
|
<li><b>deflate</b> – Raw DEFLATE compress/decompress<br>
|
||||||
|
<code>!encode deflate compress data</code></li>
|
||||||
|
<li><b>zstd</b> – Zstandard compress/decompress (requires zstandard)<br>
|
||||||
|
<code>!encode zstd compress data</code></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Data Processing</h3>
|
<h3>Data Processing</h3>
|
||||||
@@ -1195,7 +1304,10 @@ compression, data processing, forensics, and networking. Fully offline.
|
|||||||
<li><b>recipe list</b> – List all available operations<br>
|
<li><b>recipe list</b> – List all available operations<br>
|
||||||
<code>!encode recipe list</code></li>
|
<code>!encode recipe list</code></li>
|
||||||
<li><b>recipe run</b> – Execute a JSON recipe on data<br>
|
<li><b>recipe run</b> – Execute a JSON recipe on data<br>
|
||||||
<code>!encode recipe run '{"steps":[{"op":"base64","args":["encode"]},{"op":"hex","args":["encode"]}]}' "hello world"</code></li>
|
<code>!encode recipe run '{"steps":[{"op":"base64", args ["encode"]}, {"op":"hex", args ["encode"]}]}' "hello world"</code></li>
|
||||||
|
<li><b>recipe run (pipe syntax)</b> – Chain operations with <code>|</code> and separate data with <code>::</code><br>
|
||||||
|
<code>!encode recipe run base64 encode | hex encode :: hello world</code><br>
|
||||||
|
<code>!encode recipe run base64 encode | hex encode | gzip compress :: my secret data</code></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Type <code>!encode help <op></code> for detailed argument info on any operation.</p>
|
<p>Type <code>!encode help <op></code> for detailed argument info on any operation.</p>
|
||||||
|
|||||||
@@ -23,3 +23,4 @@ PyYAML
|
|||||||
wcwidth
|
wcwidth
|
||||||
markdown
|
markdown
|
||||||
python-cryptography-fernet-wrapper
|
python-cryptography-fernet-wrapper
|
||||||
|
zstandard
|
||||||
Reference in New Issue
Block a user