#!/usr/bin/env python3 """ 三省六部 · 公共工具函数 避免 read_json / now_iso 等基础函数在多个脚本中重复定义 """ import os import sys import json, pathlib, datetime, shutil def read_json(path, default=None): """安全读取 JSON 文件,失败返回 default""" try: return json.loads(pathlib.Path(path).read_text(encoding='utf-8')) except Exception: return default if default is not None else {} def get_openclaw_home() -> pathlib.Path: """Return OpenClaw home directory, respecting OPENCLAW_HOME env var.""" env = os.environ.get('OPENCLAW_HOME') if env: return pathlib.Path(env).expanduser() return pathlib.Path.home() / '.openclaw' def now_iso(): """返回 UTC ISO 8601 时间字符串(末尾 Z)""" return datetime.datetime.now(datetime.timezone.utc).isoformat().replace('+00:00', 'Z') def today_str(fmt='%Y%m%d'): """返回今天日期字符串,默认 YYYYMMDD""" return datetime.date.today().strftime(fmt) def safe_name(s: str) -> bool: """检查名称是否只含安全字符(字母、数字、下划线、连字符、中文)""" import re return bool(re.match(r'^[a-zA-Z0-9_\-\u4e00-\u9fff]+$', s)) def python_bin() -> str: """返回当前 Python 解释器路径,兼容 Windows(无 python3 命令)""" return sys.executable or shutil.which('python3') or shutil.which('python') or 'python3' def validate_url(url: str, allowed_schemes=('https',), allowed_domains=None) -> bool: """校验 URL 合法性,防 SSRF""" from urllib.parse import urlparse try: parsed = urlparse(url) if parsed.scheme not in allowed_schemes: return False if allowed_domains and parsed.hostname not in allowed_domains: return False if not parsed.hostname: return False # 禁止内网地址 import ipaddress try: ip = ipaddress.ip_address(parsed.hostname) if ip.is_private or ip.is_loopback or ip.is_reserved: return False except ValueError: pass # hostname 不是 IP,放行 return True except Exception: return False