subnet and encoder plugins added. stable diffusion fix. updated requirements.txt
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
Shared utilities for FunguyBot plugins.
|
||||
"""
|
||||
import html
|
||||
import ipaddress
|
||||
import socket
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Networks considered unsafe for outbound connections
|
||||
_PRIVATE_RANGES = [
|
||||
ipaddress.ip_network('10.0.0.0/8'),
|
||||
ipaddress.ip_network('172.16.0.0/12'),
|
||||
ipaddress.ip_network('192.168.0.0/16'),
|
||||
ipaddress.ip_network('127.0.0.0/8'),
|
||||
ipaddress.ip_network('169.254.0.0/16'),
|
||||
ipaddress.ip_network('0.0.0.0/8'),
|
||||
ipaddress.ip_network('::1/128'),
|
||||
ipaddress.ip_network('fc00::/7'),
|
||||
ipaddress.ip_network('fe80::/10'),
|
||||
ipaddress.ip_network('::/128'),
|
||||
]
|
||||
|
||||
def html_escape(text: str) -> str:
|
||||
"""Escape HTML special characters for safe embedding in messages."""
|
||||
return html.escape(str(text), quote=False)
|
||||
|
||||
def collapsible_summary(title: str, body: str, expanded: bool = False) -> str:
|
||||
"""Wrap content in a collapsible HTML details block."""
|
||||
open_attr = ' open' if expanded else ''
|
||||
return f"<details{open_attr}>\n<summary><strong>{title}</strong></summary>\n{body}\n</details>"
|
||||
|
||||
def is_public_destination(target: str) -> bool:
|
||||
"""
|
||||
Returns True if `target` (hostname or IP) does NOT resolve to any
|
||||
private, loopback, or link‑local address.
|
||||
"""
|
||||
try:
|
||||
addr = ipaddress.ip_address(target)
|
||||
if any(addr in net for net in _PRIVATE_RANGES):
|
||||
return False
|
||||
return True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
try:
|
||||
addrinfo = socket.getaddrinfo(target, None)
|
||||
for _, _, _, _, sockaddr in addrinfo:
|
||||
ip = sockaddr[0]
|
||||
addr = ipaddress.ip_address(ip)
|
||||
if any(addr in net for net in _PRIVATE_RANGES):
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"Cannot resolve {target}: {e}")
|
||||
return False
|
||||
+1203
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Plugin for generating images using self-hosted Stable Diffusion and sending them to a Matrix chat room.
|
||||
|
||||
Now supports a `--seed` parameter to control deterministic generation.
|
||||
"""
|
||||
|
||||
import requests
|
||||
@@ -90,8 +93,13 @@ async def handle_command(room, message, bot, prefix, config):
|
||||
parser.add_argument('--cfg', type=int, default=2, help='CFG scale, default=2')
|
||||
parser.add_argument('--h', type=int, default=512, help='Height of the image, default=512')
|
||||
parser.add_argument('--w', type=int, default=512, help='Width of the image, default=512')
|
||||
parser.add_argument('--neg', type=str, nargs='+', default=['((((ugly)))), (((duplicate))), ((morbid)), ((mutilated)), out of frame, extra fingers, mutated hands, ((poorly drawn hands)), ((poorly drawn face)), (((mutation))), (((deformed))), ((ugly)), blurry, ((bad anatomy)), (((bad proportions))), ((extra limbs)), cloned face, (((disfigured))), out of frame, ugly, extra limbs, (bad anatomy), gross proportions, (malformed limbs), ((missing arms)), ((missing legs)), (((extra arms))), (((extra legs))), mutated hands, (fused fingers), (too many fingers), (((long neck)))'], help='Negative prompt')
|
||||
parser.add_argument('--sampler', type=str, nargs='*', default=['DPM++', 'SDE'], help='Sampler name, default=DPM++ SDE')
|
||||
parser.add_argument('--neg', type=str, nargs='+',
|
||||
default=['((((ugly)))), (((duplicate))), ((morbid)), ((mutilated)), out of frame, extra fingers, mutated hands, ((poorly drawn hands)), ((poorly drawn face)), (((mutation))), (((deformed))), ((ugly)), blurry, ((bad anatomy)), (((bad proportions))), ((extra limbs)), cloned face, (((disfigured))), out of frame, ugly, extra limbs, (bad anatomy), gross proportions, (malformed limbs), ((missing arms)), ((missing legs)), (((extra arms))), (((extra legs))), mutated hands, (fused fingers), (too many fingers), (((long neck)))'],
|
||||
help='Negative prompt')
|
||||
parser.add_argument('--sampler', type=str, nargs='*', default=['DPM++', 'SDE Karras'],
|
||||
help='Sampler name, default=DPM++ SDE')
|
||||
parser.add_argument('--seed', type=int, default=None,
|
||||
help='Seed for deterministic generation (omit for random)')
|
||||
parser.add_argument('prompt', type=str, nargs='*', help='Prompt for the image')
|
||||
|
||||
args = parser.parse_args(message.body.split()[1:]) # skip command prefix
|
||||
@@ -112,6 +120,9 @@ async def handle_command(room, message, bot, prefix, config):
|
||||
"width": args.w,
|
||||
"height": args.h,
|
||||
}
|
||||
# Add seed only if explicitly provided
|
||||
if args.seed is not None:
|
||||
payload["seed"] = args.seed
|
||||
|
||||
url = "http://127.0.0.1:7860/sdapi/v1/txt2img"
|
||||
response = requests.post(url=url, json=payload, timeout=600)
|
||||
@@ -127,7 +138,14 @@ async def handle_command(room, message, bot, prefix, config):
|
||||
|
||||
# Optional: send info about generated image
|
||||
neg_prompt_clean = neg_prompt.replace(" ", "")
|
||||
info_msg = f"""<details><summary>🔍 Image Info</summary><strong>Prompt:</strong> {prompt[:100]}<br><strong>Steps:</strong> {args.steps}<br><strong>Dimensions:</strong> {args.h}x{args.w}<br><strong>Sampler:</strong> {sampler_name}<br><strong>CFG Scale:</strong> {args.cfg}<br><strong>Negative Prompt:</strong> {neg_prompt_clean}</details>"""
|
||||
seed_info = f"<br><strong>Seed:</strong> {args.seed}" if args.seed is not None else ""
|
||||
info_msg = f"""<details><summary>🔍 Image Info</summary>
|
||||
<strong>Prompt:</strong> {prompt[:100]}<br>
|
||||
<strong>Steps:</strong> {args.steps}<br>
|
||||
<strong>Dimensions:</strong> {args.h}x{args.w}<br>
|
||||
<strong>Sampler:</strong> {sampler_name}<br>
|
||||
<strong>CFG Scale:</strong> {args.cfg}{seed_info}<br>
|
||||
<strong>Negative Prompt:</strong> {neg_prompt_clean}</details>"""
|
||||
# await bot.api.send_markdown_message(room.room_id, info_msg)
|
||||
|
||||
# Clean up temp file
|
||||
@@ -167,6 +185,7 @@ def print_help():
|
||||
<li>--w W - Width of the image, default=512</li>
|
||||
<li>--neg NEG - Negative prompt, default=((((ugly)))), (((duplicate))), ((morbid)), ((mutilated)), out of frame, extra fingers, mutated hands, ((poorly drawn hands)), ((poorly drawn face)), (((mutation))), (((deformed))), ((ugly)), blurry, ((bad anatomy)), (((bad proportions))), ((extra limbs)), cloned face, (((disfigured))), out of frame, ugly, extra limbs, (bad anatomy), gross proportions, (malformed limbs), ((missing arms)), ((missing legs)), (((extra arms))), (((extra legs))), mutated hands, (fused fingers), (too many fingers), (((long neck)))</li>
|
||||
<li>--sampler SAMPLER - Sampler name, default=DPM++ SDE</li>
|
||||
<li>--seed SEED - Seed for deterministic generation (omit for random)</li>
|
||||
</ul>
|
||||
|
||||
<p>LORA List:</p>
|
||||
@@ -191,9 +210,9 @@ def print_help():
|
||||
# Plugin Metadata
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "1.1.0"
|
||||
__author__ = "Funguy Bot"
|
||||
__description__ = "Stable Diffusion image generation"
|
||||
__description__ = "Stable Diffusion image generation (supports --seed)"
|
||||
__help__ = """
|
||||
<details>
|
||||
<summary><strong>!sd</strong> – Generate images via Stable Diffusion</summary>
|
||||
@@ -204,6 +223,7 @@ __help__ = """
|
||||
<li><code>--h H --w W</code> – Image dimensions (default 512)</li>
|
||||
<li><code>--neg <negative prompt></code></li>
|
||||
<li><code>--sampler SAMPLER</code> – Sampler name (default DPM++ SDE)</li>
|
||||
<li><code>--seed SEED</code> – Deterministic seed (optional)</li>
|
||||
</ul>
|
||||
<p>Requires a locally running Stable Diffusion API.</p>
|
||||
</details>
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
plugins/subnet.py – Subnet calculator and network splitting plugin for Funguy Bot.
|
||||
|
||||
Provides the following commands:
|
||||
!subnet info <CIDR> – Show detailed info about a network
|
||||
!subnet split <CIDR> --prefix <N> – Split network into smaller subnets (new prefix length)
|
||||
!subnet split <CIDR> --diff <N> – Split network into equal subnets (prefixlen delta)
|
||||
!subnet adjacent <CIDR> <count> – Show given network and next <count> adjacent ones
|
||||
!subnet help – Display this help
|
||||
|
||||
Examples:
|
||||
!subnet info 192.168.4.0/26
|
||||
!subnet split 192.168.4.0/24 --prefix 26
|
||||
!subnet split 10.0.0.0/16 --diff 2
|
||||
!subnet adjacent 192.168.4.0/26 3
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
# ------------------------------- helper functions --------------------------------
|
||||
|
||||
def _fmt_subnet_info(net: Union[ipaddress.IPv4Network, ipaddress.IPv6Network]) -> str:
|
||||
"""Return a human‑readable string with all relevant subnet details."""
|
||||
nw = net.network_address
|
||||
bc = net.broadcast_address if hasattr(net, "broadcast_address") else None
|
||||
total = net.num_addresses
|
||||
|
||||
if net.version == 4:
|
||||
if net.prefixlen == 32:
|
||||
usable_count = 1
|
||||
first = last = nw
|
||||
elif net.prefixlen == 31:
|
||||
usable_count = 2
|
||||
first = nw
|
||||
last = bc
|
||||
else:
|
||||
usable_count = max(0, total - 2)
|
||||
first = nw + 1 if usable_count > 0 else None
|
||||
last = bc - 1 if usable_count > 0 else None
|
||||
else:
|
||||
hosts_iter = net.hosts()
|
||||
try:
|
||||
first = next(hosts_iter)
|
||||
last = net.network_address + (total - 1)
|
||||
usable_count = total
|
||||
except StopIteration:
|
||||
first = last = None
|
||||
usable_count = 0
|
||||
|
||||
lines = [
|
||||
f"CIDR: {net.with_prefixlen}",
|
||||
f"Network: {nw}",
|
||||
f"Broadcast: {bc if bc is not None else 'N/A'}",
|
||||
f"Netmask: {net.netmask if hasattr(net, 'netmask') else 'N/A'}",
|
||||
f"Wildcard Mask: {net.hostmask if hasattr(net, 'hostmask') else 'N/A'}",
|
||||
f"Total IPs: {total}",
|
||||
f"Usable Hosts: {usable_count}",
|
||||
]
|
||||
if first is not None and last is not None:
|
||||
lines.append(f"First Usable: {first}")
|
||||
lines.append(f"Last Usable: {last}")
|
||||
lines.append(f"Usable Range: {first} - {last}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _split_by_prefix(net, new_prefix: int) -> str:
|
||||
if new_prefix < net.prefixlen:
|
||||
return f"[!] New prefix /{new_prefix} is smaller than current prefix /{net.prefixlen}. Cannot split."
|
||||
out = [f"# Splitting {net.with_prefixlen} into /{new_prefix} subnets:"]
|
||||
for i, sub in enumerate(net.subnets(new_prefix=new_prefix)):
|
||||
out.append(f"\n-- Subnet #{i+1} --")
|
||||
out.append(_fmt_subnet_info(sub))
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def _split_by_diff(net, diff: int) -> str:
|
||||
new_prefix = net.prefixlen + diff
|
||||
return _split_by_prefix(net, new_prefix)
|
||||
|
||||
|
||||
def _adjacent_networks(net, count: int) -> str:
|
||||
out = [f"# Adjacent networks of size /{net.prefixlen} (starting at {net.with_prefixlen}):"]
|
||||
current = net
|
||||
for i in range(count + 1):
|
||||
out.append(f"\n-- Adjacent #{i} --")
|
||||
out.append(_fmt_subnet_info(current))
|
||||
try:
|
||||
next_net_addr = current.network_address + current.num_addresses
|
||||
current = ipaddress.ip_network(f"{next_net_addr}/{current.prefixlen}", strict=True)
|
||||
except ValueError:
|
||||
out.append("[!] Reached address space limit.")
|
||||
break
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
# ------------------------------- bot plugin entry -------------------------------
|
||||
|
||||
async def handle_command(room, message, bot, prefix, config):
|
||||
import simplematrixbotlib as botlib
|
||||
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||
|
||||
if not (match.is_not_from_this_bot() and match.prefix() and match.command("subnet")):
|
||||
return
|
||||
|
||||
args = match.args()
|
||||
if not args:
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"Usage: !subnet <info|split|adjacent> ...\n"
|
||||
" !subnet help – show full help"
|
||||
)
|
||||
return
|
||||
|
||||
subcmd = args[0].lower()
|
||||
|
||||
# --- help ---
|
||||
if subcmd in ("help", "-h", "--help"):
|
||||
# Send nicely formatted HTML in a details tag via markdown
|
||||
html = "<details><summary><strong>!subnet</strong> – Subnet calculator and exploration</summary>\n"
|
||||
html += "<p>Calculate subnet details, split networks, or enumerate adjacent subnets.</p>\n"
|
||||
html += "<h4>Commands</h4>\n"
|
||||
html += "<ul>\n"
|
||||
html += "<li><b>info</b> – Show detailed info for a network<br>\n"
|
||||
html += "<code>!subnet info <CIDR></code><br>\n"
|
||||
html += "Example: <code>!subnet info 192.168.1.0/24</code></li>\n"
|
||||
html += "<li><b>split</b> – Split a network into smaller subnets<br>\n"
|
||||
html += "<code>!subnet split <CIDR> --prefix <new_prefix></code><br>\n"
|
||||
html += "Example: <code>!subnet split 192.168.1.0/24 --prefix 26</code><br>\n"
|
||||
html += "<i>Alternatively, use --diff to split by prefix delta:</i><br>\n"
|
||||
html += "<code>!subnet split <CIDR> --diff <delta></code><br>\n"
|
||||
html += "Example: <code>!subnet split 10.0.0.0/16 --diff 2</code> (creates 4 subnets)</li>\n"
|
||||
html += "<li><b>adjacent</b> – Show the current network and adjacent ones<br>\n"
|
||||
html += "<code>!subnet adjacent <CIDR> <count></code><br>\n"
|
||||
html += "Example: <code>!subnet adjacent 192.168.4.0/26 3</code></li>\n"
|
||||
html += "</ul>\n"
|
||||
html += "<h4>Notes</h4>\n"
|
||||
html += "<ul>\n"
|
||||
html += "<li>IPv4 /31 and /32 networks show both addresses as usable (RFC 3021).</li>\n"
|
||||
html += "<li>IPv6 networks list all addresses as hosts (no broadcast).</li>\n"
|
||||
html += "</ul>\n"
|
||||
html += "</details>"
|
||||
await bot.api.send_markdown_message(room.room_id, html)
|
||||
return
|
||||
|
||||
# --- info (or a CIDR passed directly) ---
|
||||
if subcmd == "info" or "/" in subcmd:
|
||||
cidr = args[1] if subcmd == "info" else subcmd
|
||||
try:
|
||||
net = ipaddress.ip_network(cidr, strict=False)
|
||||
except ValueError as e:
|
||||
await bot.api.send_text_message(room.room_id, f"[!] Invalid CIDR: {e}")
|
||||
return
|
||||
await bot.api.send_text_message(room.room_id, _fmt_subnet_info(net))
|
||||
return
|
||||
|
||||
# --- split ---
|
||||
if subcmd == "split":
|
||||
if len(args) < 2:
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"Usage: !subnet split <CIDR> --prefix <new_prefix> OR --diff <delta>"
|
||||
)
|
||||
return
|
||||
cidr = args[1]
|
||||
try:
|
||||
net = ipaddress.ip_network(cidr, strict=False)
|
||||
except ValueError as e:
|
||||
await bot.api.send_text_message(room.room_id, f"[!] Invalid CIDR: {e}")
|
||||
return
|
||||
|
||||
if "--prefix" in args:
|
||||
try:
|
||||
idx = args.index("--prefix")
|
||||
new_prefix = int(args[idx + 1])
|
||||
except (ValueError, IndexError):
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"Usage: !subnet split <CIDR> --prefix <number>"
|
||||
)
|
||||
return
|
||||
result = _split_by_prefix(net, new_prefix)
|
||||
elif "--diff" in args:
|
||||
try:
|
||||
idx = args.index("--diff")
|
||||
diff = int(args[idx + 1])
|
||||
except (ValueError, IndexError):
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"Usage: !subnet split <CIDR> --diff <delta>"
|
||||
)
|
||||
return
|
||||
result = _split_by_diff(net, diff)
|
||||
else:
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"You must provide either --prefix <N> or --diff <N> for split."
|
||||
)
|
||||
return
|
||||
await bot.api.send_text_message(room.room_id, result)
|
||||
return
|
||||
|
||||
# --- adjacent ---
|
||||
if subcmd == "adjacent":
|
||||
if len(args) < 3:
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"Usage: !subnet adjacent <CIDR> <count>"
|
||||
)
|
||||
return
|
||||
cidr = args[1]
|
||||
try:
|
||||
net = ipaddress.ip_network(cidr, strict=False)
|
||||
except ValueError as e:
|
||||
await bot.api.send_text_message(room.room_id, f"[!] Invalid CIDR: {e}")
|
||||
return
|
||||
try:
|
||||
count = int(args[2])
|
||||
except ValueError:
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
"Count must be an integer."
|
||||
)
|
||||
return
|
||||
result = _adjacent_networks(net, count)
|
||||
await bot.api.send_text_message(room.room_id, result)
|
||||
return
|
||||
|
||||
# Unknown subcommand
|
||||
await bot.api.send_text_message(
|
||||
room.room_id,
|
||||
f"Unknown subcommand '{subcmd}'. Use !subnet help to see available commands."
|
||||
)
|
||||
|
||||
|
||||
# Plugin metadata
|
||||
__version__ = "1.0.1"
|
||||
__author__ = "Funguy Bot"
|
||||
__description__ = "Subnet calculator, splitter, and adjacent network enumerator"
|
||||
__help__ = """
|
||||
<details>
|
||||
<summary><strong>!subnet</strong> – Subnet calculator and exploration</summary>
|
||||
<p>Calculate subnet details, split networks, or enumerate adjacent subnets.</p>
|
||||
<ul>
|
||||
<li><code>!subnet info <CIDR></code> – Show detailed info for a network<br>
|
||||
Example: <code>!subnet info 192.168.1.0/24</code></li>
|
||||
<li><code>!subnet split <CIDR> --prefix <new_prefix></code> – Split into smaller subnets<br>
|
||||
Example: <code>!subnet split 192.168.1.0/24 --prefix 26</code></li>
|
||||
<li><code>!subnet split <CIDR> --diff <delta></code> – Split by prefix delta<br>
|
||||
Example: <code>!subnet split 10.0.0.0/16 --diff 2</code></li>
|
||||
<li><code>!subnet adjacent <CIDR> <count></code> – Show adjacent networks<br>
|
||||
Example: <code>!subnet adjacent 192.168.4.0/26 3</code></li>
|
||||
</ul>
|
||||
</details>
|
||||
"""
|
||||
@@ -24,3 +24,10 @@ ddgs
|
||||
playwright
|
||||
lxml
|
||||
beautifulsoup4
|
||||
cryptography
|
||||
bcrypt
|
||||
argon2-cffi
|
||||
yara-python
|
||||
asn1crypto
|
||||
PyYAML
|
||||
lxml
|
||||
|
||||
Reference in New Issue
Block a user