83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
"""
|
||
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
|
||
|
||
async def handle_command(room, message, bot, prefix, config):
|
||
"""No-op handler so the bot doesn't crash when loading this module as a plugin."""
|
||
pass
|
||
|
||
async def send_html_message(bot, room_id, html_body, markdown_fallback):
|
||
"""Send an HTML-formatted message with a Markdown fallback.
|
||
|
||
Args:
|
||
bot: simplematrixbotlib.Bot instance
|
||
room_id: Matrix room ID
|
||
html_body: HTML string (table, etc.)
|
||
markdown_fallback: Markdown/plain text for clients that don't render HTML
|
||
"""
|
||
content = {
|
||
"msgtype": "m.text",
|
||
"body": markdown_fallback,
|
||
"format": "org.matrix.custom.html",
|
||
"formatted_body": html_body
|
||
}
|
||
await bot.async_client.room_send(
|
||
room_id=room_id,
|
||
message_type="m.room.message",
|
||
content=content
|
||
)
|