2025-12-14 10:39:18 +03:00
|
|
|
import socket
|
2025-12-14 10:49:32 +03:00
|
|
|
import json
|
2025-12-14 10:39:18 +03:00
|
|
|
import base64
|
|
|
|
|
import ipaddress
|
|
|
|
|
from urllib.parse import urlparse
|
|
|
|
|
|
2025-12-14 10:49:32 +03:00
|
|
|
|
2025-12-14 10:39:18 +03:00
|
|
|
def load_artwork_settings(settings_data):
|
2025-12-14 10:49:32 +03:00
|
|
|
if not settings_data:
|
|
|
|
|
return None
|
2025-12-14 10:39:18 +03:00
|
|
|
try:
|
2025-12-14 10:49:32 +03:00
|
|
|
# Пробуем base64 + JSON
|
|
|
|
|
try:
|
|
|
|
|
raw = base64.b64decode(settings_data)
|
|
|
|
|
return json.loads(raw)
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
# Просто JSON
|
2025-12-14 10:39:18 +03:00
|
|
|
try:
|
2025-12-14 10:49:32 +03:00
|
|
|
return json.loads(settings_data)
|
2025-12-14 10:39:18 +03:00
|
|
|
except:
|
|
|
|
|
pass
|
2025-12-14 10:49:32 +03:00
|
|
|
# Fallback — plain text description
|
|
|
|
|
return {'description': settings_data}
|
|
|
|
|
except:
|
2025-12-14 10:39:18 +03:00
|
|
|
return {'description': settings_data}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def save_artwork_description(description):
|
|
|
|
|
if not description:
|
|
|
|
|
return None
|
2025-12-14 10:49:32 +03:00
|
|
|
return json.dumps({'description': description})
|
2025-12-14 10:39:18 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ArtworkConfig:
|
|
|
|
|
def __init__(self, colors=None, animation=False, public=True):
|
|
|
|
|
self.colors = colors or ["#FF0000", "#00FF00", "#0000FF"]
|
|
|
|
|
self.animation = animation
|
|
|
|
|
self.public = public
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return f"ArtworkConfig(colors={self.colors}, animation={self.animation}, public={self.public})"
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
return self.__repr__()
|
|
|
|
|
|
|
|
|
|
def __reduce__(self):
|
|
|
|
|
return (self.__class__, (self.colors, self.animation, self.public))
|
|
|
|
|
|
|
|
|
|
def is_safe_url(url: str):
|
|
|
|
|
try:
|
|
|
|
|
parsed = urlparse(url)
|
|
|
|
|
|
|
|
|
|
if parsed.scheme not in ("http", "https"):
|
|
|
|
|
return False, "403"
|
|
|
|
|
|
|
|
|
|
hostname = parsed.hostname
|
|
|
|
|
if not hostname:
|
|
|
|
|
return False, "403"
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ip_str = socket.gethostbyname(hostname)
|
|
|
|
|
except socket.gaierror:
|
|
|
|
|
return False, "403"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
ip = ipaddress.ip_address(ip_str)
|
|
|
|
|
except ValueError:
|
|
|
|
|
return False, "403"
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
ip.is_loopback
|
|
|
|
|
or ip.is_private
|
|
|
|
|
or ip.is_link_local
|
|
|
|
|
or ip.is_unspecified
|
|
|
|
|
):
|
|
|
|
|
return False, "403"
|
|
|
|
|
return True, ip_str
|
|
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
return False, "403"
|
|
|
|
|
|