- 特征检测(规则/签名)与行为检测(24h 统计)单进程运行 - 融合名单按首次发现时间倒序,展示位置/归属(GeoIP + ASN) - 钉钉融合推送(加签、24h 节流、去重) - 页面状态灯、CSV 导出、Docker 镜像构建文件
78 lines
2.3 KiB
Python
78 lines
2.3 KiB
Python
"""Terminal rendering helpers (CJK-aware column widths)."""
|
|
|
|
import unicodedata
|
|
|
|
|
|
def display_width(text):
|
|
width = 0
|
|
for char in str(text):
|
|
if unicodedata.east_asian_width(char) in ("W", "F"):
|
|
width += 2
|
|
else:
|
|
width += 1
|
|
return width
|
|
|
|
|
|
def clip(text, limit=60):
|
|
text = "" if text is None else str(text)
|
|
text = text.replace("\n", " ").replace("\r", " ").replace("\t", " ")
|
|
if len(text) <= limit:
|
|
return text
|
|
return text[: max(0, limit - 1)] + "…"
|
|
|
|
|
|
def table(headers, rows, align=None):
|
|
"""Render a plain-text table. *align* is a list of ``l``/``r`` per column."""
|
|
rows = [["" if cell is None else str(cell) for cell in row] for row in rows]
|
|
if align is None:
|
|
align = ["l"] * len(headers)
|
|
widths = [display_width(header) for header in headers]
|
|
for row in rows:
|
|
for index, cell in enumerate(row[: len(widths)]):
|
|
widths[index] = max(widths[index], display_width(cell))
|
|
|
|
def render(cells):
|
|
parts = []
|
|
for index, cell in enumerate(cells):
|
|
padding = widths[index] - display_width(cell)
|
|
if align[index] == "r":
|
|
parts.append(" " * padding + cell)
|
|
else:
|
|
parts.append(cell + " " * padding)
|
|
return " ".join(parts).rstrip()
|
|
|
|
lines = [render(headers), " ".join("-" * width for width in widths)]
|
|
lines.extend(render(row) for row in rows)
|
|
return "\n".join(lines)
|
|
|
|
|
|
def human_bytes(size):
|
|
if not isinstance(size, (int, float)):
|
|
return str(size)
|
|
value = float(size)
|
|
for unit in ("B", "KiB", "MiB", "GiB", "TiB"):
|
|
if value < 1024 or unit == "TiB":
|
|
if unit == "B":
|
|
return "%d B" % value
|
|
return "%.1f %s" % (value, unit)
|
|
value /= 1024.0
|
|
return str(size)
|
|
|
|
|
|
def percent(part, total):
|
|
if not total:
|
|
return "-"
|
|
return "%.1f%%" % (100.0 * part / total)
|
|
|
|
|
|
def kv_block(pairs):
|
|
"""Render aligned ``key: value`` lines."""
|
|
if not pairs:
|
|
return ""
|
|
width = max(display_width(key) for key, _ in pairs)
|
|
lines = []
|
|
for key, value in pairs:
|
|
padding = " " * (width - display_width(key))
|
|
lines.append("%s%s : %s" % (key, padding, "" if value is None else value))
|
|
return "\n".join(lines)
|