惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Stack Overflow Blog
Stack Overflow Blog
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
T
The Blog of Author Tim Ferriss
博客园 - 叶小钗
N
Netflix TechBlog - Medium
腾讯CDC
C
Check Point Blog
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
S
SegmentFault 最新的问题
F
Fortinet All Blogs
美团技术团队
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 司徒正美
F
Full Disclosure
Recorded Future
Recorded Future
D
DataBreaches.Net
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
J
Java Code Geeks
I
InfoQ
Y
Y Combinator Blog
A
About on SuperTechFans
AI
AI
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Forbes - Security
Forbes - Security
W
WeLiveSecurity
M
MIT News - Artificial intelligence
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
Latest news
Latest news
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Docker
Recent Commits to openclaw:main
Recent Commits to openclaw:main
量子位
V2EX - 技术
V2EX - 技术
Project Zero
Project Zero

WishMeLz - 猎奇

Namecrane/CraneMail 优化访问速度之 - Nginx stream 做 TCP 邮件代理 KeepAlive.cc 使用笔记,保备案尝鲜版 (方案很多,这只是一类,选择适合自己的即可) - WishMeLz namecrane CraneMail Lifetime 250G 使用记录 沉浸式翻译使用 DeepLX Api Key - WishMeLz ip信息查询 - WishMeLz Teamspeak 3 安装和汉化 - WishMeLz PTT签到脚本 - WishMeLz 地址生成器 - WishMeLz 自建Teamspeak3 - WishMeLz
Scriptable 小组件 - 搬瓦工 - WishMeLz
WishMeLz · 2026-03-25 · via WishMeLz - 猎奇

2026-03-25T07:18:22.png

/***********************
 * 固定配置(直接改这里)
 ***********************/
const baseUrl = "https://api.64clouds.com/v1";
const veid = "2012715";
const api_key = "private_X12fsdffeg345gdfg";
const vpsTitle = 'MEGABOX-PRO'

// ─────────────────────────────────────────────
// 工具函数
// ─────────────────────────────────────────────

function formatBytes(bytes, decimals = 1) {
  const n = Number(bytes || 0);
  if (n === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB", "TB"];
  const i = Math.floor(Math.log(n) / Math.log(k));
  return `${(n / Math.pow(k, i)).toFixed(decimals)} ${sizes[i]}`;
}

function clamp(v, min, max) {
  return Math.max(min, Math.min(max, v));
}

function timestampToDate(ts) {
  if (!ts) return null;
  const d = new Date(Number(ts) * 1000);
  return isNaN(d.getTime()) ? null : d;
}

function daysUntil(date) {
  if (!date) return null;
  return Math.ceil((date.getTime() - Date.now()) / 86400000);
}

// 根据使用率返回颜色
function ratioColor(ratio) {
  if (ratio >= 0.9) return new Color("#FF453A");
  if (ratio >= 0.75) return new Color("#FF9F0A");
  return new Color("#30D158");
}

// Light/Dark 动态颜色
function dc(light, dark) {
  return Color.dynamic(new Color(light), new Color(dark));
}

// ─────────────────────────────────────────────
// DrawContext 绘图工具
// ─────────────────────────────────────────────

function makeProgressBar(ratio, width, height, fillColor) {
  const ctx = new DrawContext();
  ctx.size = new Size(width, height);
  ctx.opaque = false;
  ctx.respectScreenScale = true;

  const r = height / 2;

  // 背景轨道
  const bgPath = new Path();
  bgPath.addRoundedRect(new Rect(0, 0, width, height), r, r);
  ctx.setFillColor(dc("#E5E5EA", "#3A3A3C"));
  ctx.addPath(bgPath);
  ctx.fillPath();

  // 填充
  const filled = Math.max(r * 2, width * clamp(ratio, 0, 1));
  const fgPath = new Path();
  fgPath.addRoundedRect(new Rect(0, 0, filled, height), r, r);
  ctx.setFillColor(fillColor || ratioColor(ratio));
  ctx.addPath(fgPath);
  ctx.fillPath();

  return ctx.getImage();
}

// ─────────────────────────────────────────────
// UI 组件辅助
// ─────────────────────────────────────────────

// 小徽章
function addBadge(stack, label, bgHex, fgHex = "#FFFFFF", alpha = 0.9) {
  const badge = stack.addStack();
  badge.backgroundColor = new Color(bgHex, alpha);
  badge.cornerRadius = 4;
  badge.setPadding(2, 5, 2, 5);
  const t = badge.addText(label);
  t.font = Font.boldSystemFont(8);
  t.textColor = new Color(fgHex);
}

// 分区标签
function addSectionTitle(parent, title) {
  const row = parent.addStack();
  row.centerAlignContent();
  const t = row.addText(title.toUpperCase());
  t.font = Font.boldSystemFont(9);
  t.textColor = dc("#9A9A9E", "#636366");
  row.addSpacer();
}

// 左色块 + 标签 + 右值
function addSpecRow(parent, label, value, dotColor) {
  const row = parent.addStack();
  row.centerAlignContent();

  const dot = row.addText("■");
  dot.font = Font.boldSystemFont(9);
  dot.textColor = dotColor;

  row.addSpacer(5);

  const lbl = row.addText(label);
  lbl.font = Font.systemFont(11);
  lbl.textColor = dc("#6C6C70", "#8E8E93");

  row.addSpacer();

  const val = row.addText(value);
  val.font = Font.boldSystemFont(11);
  val.textColor = dc("#1C1C1E", "#EBEBF5");
}

// ─────────────────────────────────────────────
// API 请求
// ─────────────────────────────────────────────

async function loadServiceInfo() {
  const url = `${baseUrl}/getServiceInfo?veid=${veid}&api_key=${api_key}`;
  const req = new Request(url);
  req.method = "GET";
  const json = await req.loadJSON();
  if (json.error && json.error !== 0) throw new Error(json.message || "API 错误");
  return json;
}

// ─────────────────────────────────────────────
// 渲染:小尺寸
// ─────────────────────────────────────────────

async function renderSmall(widget, data) {
  const multiplier = Number(data.monthly_data_multiplier || 1);
  const total = Number(data.plan_monthly_data || 0) * multiplier;
  const used = Number(data.data_counter || 0) * multiplier;
  const ratio = clamp(total > 0 ? used / total : 0, 0, 1);
  const resetDate = timestampToDate(data.data_next_reset);
  const daysLeft = daysUntil(resetDate);
  const ip = (data.ip_addresses || [])[0] || "N/A";
  const color = ratioColor(ratio);
  const isSuspended = !!data.suspended;

  const grad = new LinearGradient();
  grad.locations = [0, 1];
  grad.colors = [dc("#F2F2F7", "#1C1C1E"), dc("#FFFFFF", "#111113")];
  widget.backgroundGradient = grad;
  widget.setPadding(12, 14, 12, 14);

  // 顶部:状态点 + 主机名
  const topRow = widget.addStack();
  topRow.centerAlignContent();

  const statusDot = topRow.addText("●");
  statusDot.font = Font.boldSystemFont(10);
  statusDot.textColor = isSuspended ? new Color("#FF453A") : new Color("#30D158");

  topRow.addSpacer(5);

  const hostT = topRow.addText((vpsTitle || "VPS").split(".")[0]);
  hostT.font = Font.boldSystemFont(12);
  hostT.textColor = dc("#000000", "#FFFFFF");
  hostT.lineLimit = 1;
  hostT.minimumScaleFactor = 0.6;

  widget.addSpacer(6);

  // 大百分比
  const pctT = widget.addText(`${Math.round(ratio * 100)}%`);
  pctT.font = Font.boldSystemFont(38);
  pctT.textColor = color;

  widget.addSpacer(4);

  // 进度条
  widget.addImage(makeProgressBar(ratio, 130, 7, color));

  widget.addSpacer(5);

  // 流量数值
  const trafficT = widget.addText(`${formatBytes(used)} / ${formatBytes(total)}`);
  trafficT.font = Font.systemFont(9);
  trafficT.textColor = dc("#6C6C70", "#8E8E93");
  trafficT.minimumScaleFactor = 0.7;

  widget.addSpacer();

  // 底部:IP + 剩余天数
  const botRow = widget.addStack();
  botRow.centerAlignContent();

  const ipT = botRow.addText(ip);
  ipT.font = new Font("Menlo", 9);
  ipT.textColor = dc("#6C6C70", "#8E8E93");

  botRow.addSpacer();

  if (daysLeft !== null) {
    const dT = botRow.addText(`${daysLeft}d`);
    dT.font = Font.systemFont(9);
    dT.textColor = daysLeft <= 3 ? new Color("#FF9F0A") : dc("#6C6C70", "#8E8E93");
  }
}

// ─────────────────────────────────────────────
// 渲染:中号
// ─────────────────────────────────────────────

async function renderMedium(widget, data) {
  const multiplier = Number(data.monthly_data_multiplier || 1);
  const total = Number(data.plan_monthly_data || 0) * multiplier;
  const used = Number(data.data_counter || 0) * multiplier;
  const ratio = clamp(total > 0 ? used / total : 0, 0, 1);
  const resetDate = timestampToDate(data.data_next_reset);
  const daysLeft = daysUntil(resetDate);
  const ip = (data.ip_addresses || [])[0] || "N/A";
  const isSuspended = !!data.suspended;
  const color = ratioColor(ratio);
  const vmType = (data.vm_type || "KVM").toUpperCase();
  const osRaw = data.os || "";
  const osShort = osRaw.split("-").slice(0, 2).join(" ");

  const grad = new LinearGradient();
  grad.locations = [0, 1];
  grad.colors = [dc("#F7F7F7", "#1C1C1E"), dc("#FFFFFF", "#111113")];
  widget.backgroundGradient = grad;
  widget.setPadding(14, 16, 14, 16);

  // ── 标题行 ──
  const topRow = widget.addStack();
  topRow.centerAlignContent();

  const statusDot = topRow.addText("●");
  statusDot.font = Font.boldSystemFont(11);
  statusDot.textColor = isSuspended ? new Color("#FF453A") : new Color("#30D158");
  topRow.addSpacer(6);

  const hostT = topRow.addText(vpsTitle || "VPS");
  hostT.font = Font.boldSystemFont(15);
  hostT.textColor = dc("#000000", "#FFFFFF");
  hostT.lineLimit = 1;
  hostT.minimumScaleFactor = 0.65;
  topRow.addSpacer();

  addBadge(topRow, vmType, vmType === "KVM" ? "#0A84FF" : "#9B59B6");

  if (data.location_ipv6_ready) {
    topRow.addSpacer(4);
    addBadge(topRow, "IPv6", "#30D158");
  }
  if (isSuspended) {
    topRow.addSpacer(4);
    addBadge(topRow, "暂停", "#FF453A");
  }

  widget.addSpacer(3);

  // ── 副信息行 ──
  const subRow = widget.addStack();
  subRow.centerAlignContent();

  const locT = subRow.addText(`📍 ${data.node_location || "Unknown"}`);
  locT.font = Font.systemFont(10);
  locT.textColor = dc("#6C6C70", "#8E8E93");

  subRow.addSpacer(8);

  const planT = subRow.addText(data.plan || "");
  planT.font = Font.systemFont(10);
  planT.textColor = dc("#6C6C70", "#8E8E93");
  planT.lineLimit = 1;
  planT.minimumScaleFactor = 0.7;

  widget.addSpacer(10);

  // ── 流量主区域 ──
  const mainRow = widget.addStack();
  mainRow.centerAlignContent();

  // 左:大百分比
  const leftCol = mainRow.addStack();
  leftCol.layoutVertically();

  const pctT = leftCol.addText(`${Math.round(ratio * 100)}%`);
  pctT.font = Font.boldSystemFont(40);
  pctT.textColor = color;
  pctT.minimumScaleFactor = 0.7;

  const pctLabel = leftCol.addText("月流量使用");
  pctLabel.font = Font.systemFont(9);
  pctLabel.textColor = dc("#9A9A9E", "#636366");

  mainRow.addSpacer();

  // 右:详细数值
  const rightCol = mainRow.addStack();
  rightCol.layoutVertically();

  const usedT = rightCol.addText(formatBytes(used));
  usedT.font = Font.boldSystemFont(14);
  usedT.textColor = dc("#1C1C1E", "#EBEBF5");
  usedT.rightAlignText();

  const totalT = rightCol.addText(`共 ${formatBytes(total)}`);
  totalT.font = Font.systemFont(10);
  totalT.textColor = dc("#6C6C70", "#8E8E93");
  totalT.rightAlignText();

  rightCol.addSpacer(6);

  const resetT = rightCol.addText(
    daysLeft !== null ? `${daysLeft} 天后重置` : "—"
  );
  resetT.font = Font.systemFont(10);
  resetT.textColor =
    daysLeft !== null && daysLeft <= 3
      ? new Color("#FF9F0A")
      : dc("#6C6C70", "#8E8E93");
  resetT.rightAlignText();

  widget.addSpacer(8);

  // ── 进度条 ──
  widget.addImage(makeProgressBar(ratio, 290, 8, color));

  widget.addSpacer(10);

  // ── 底部行:IP + OS ──
  const botRow = widget.addStack();
  botRow.centerAlignContent();

  const ipT = botRow.addText(ip);
  ipT.font = new Font("Menlo", 10);
  ipT.textColor = dc("#3C3C43", "#EBEBF5");

  botRow.addSpacer();

  const osT = botRow.addText(osShort);
  osT.font = Font.systemFont(10);
  osT.textColor = dc("#6C6C70", "#8E8E93");
  osT.lineLimit = 1;
  osT.minimumScaleFactor = 0.7;
}

// ─────────────────────────────────────────────
// 渲染:大尺寸
// ─────────────────────────────────────────────

async function renderLarge(widget, data) {
  const multiplier = Number(data.monthly_data_multiplier || 1);
  const total = Number(data.plan_monthly_data || 0) * multiplier;
  const used = Number(data.data_counter || 0) * multiplier;
  const ratio = clamp(total > 0 ? used / total : 0, 0, 1);
  const resetDate = timestampToDate(data.data_next_reset);
  const daysLeft = daysUntil(resetDate);
  const ip = (data.ip_addresses || [])[0] || "N/A";
  const isSuspended = !!data.suspended;
  const color = ratioColor(ratio);
  const vmType = (data.vm_type || "KVM").toUpperCase();
  const osRaw = data.os || "";
  const osShort = osRaw.split("-").slice(0, 2).join(" ");

  const ram = Number(data.plan_ram || 0);
  const disk = Number(data.plan_disk || 0);
  const swap = Number(data.plan_swap || 0);
  const abusePoints = Number(data.total_abuse_points || 0);
  const maxAbuse = Number(data.max_abuse_points || 100);
  const abuseRatio = clamp(maxAbuse > 0 ? abusePoints / maxAbuse : 0, 0, 1);
  const suspCount = Number(data.suspension_count || 0);
  const policyViolation = !!data.policy_violation;

  const grad = new LinearGradient();
  grad.locations = [0, 1];
  grad.colors = [dc("#F7F7F7", "#1C1C1E"), dc("#FFFFFF", "#111113")];
  widget.backgroundGradient = grad;
  widget.setPadding(16, 16, 16, 16);

  // ── 标题行 ──
  const topRow = widget.addStack();
  topRow.centerAlignContent();

  const statusDot = topRow.addText("●");
  statusDot.font = Font.boldSystemFont(12);
  statusDot.textColor = isSuspended ? new Color("#FF453A") : new Color("#30D158");
  topRow.addSpacer(6);

  const hostT = topRow.addText(vpsTitle || "VPS");
  hostT.font = Font.boldSystemFont(16);
  hostT.textColor = dc("#000000", "#FFFFFF");
  hostT.lineLimit = 1;
  hostT.minimumScaleFactor = 0.65;
  topRow.addSpacer();

  addBadge(topRow, vmType, vmType === "KVM" ? "#0A84FF" : "#9B59B6");
  if (data.location_ipv6_ready) {
    topRow.addSpacer(4);
    addBadge(topRow, "IPv6", "#30D158");
  }
  if (isSuspended) {
    topRow.addSpacer(4);
    addBadge(topRow, "已暂停", "#FF453A");
  }
  if (policyViolation) {
    topRow.addSpacer(4);
    addBadge(topRow, "违规", "#FF9F0A", "#1C1C1E");
  }

  widget.addSpacer(3);

  // ── 副信息 ──
  const infoRow = widget.addStack();
  const infoT = infoRow.addText(
    `📍 ${data.node_location || "Unknown"}  ·  ${data.plan || ""}  ·  ${osShort}`
  );
  infoT.font = Font.systemFont(10);
  infoT.textColor = dc("#6C6C70", "#8E8E93");
  infoT.lineLimit = 1;
  infoT.minimumScaleFactor = 0.7;

  widget.addSpacer(12);

  // ══ 月流量 ══
  addSectionTitle(widget, "月流量");
  widget.addSpacer(5);

  const trafficRow = widget.addStack();
  trafficRow.centerAlignContent();

  const pctT = trafficRow.addText(`${Math.round(ratio * 100)}%`);
  pctT.font = Font.boldSystemFont(34);
  pctT.textColor = color;
  pctT.minimumScaleFactor = 0.7;

  trafficRow.addSpacer(14);

  const trafficDetails = trafficRow.addStack();
  trafficDetails.layoutVertically();

  const usedT = trafficDetails.addText(`已用  ${formatBytes(used)}`);
  usedT.font = Font.boldSystemFont(12);
  usedT.textColor = dc("#1C1C1E", "#EBEBF5");

  const totalT = trafficDetails.addText(`共    ${formatBytes(total)}`);
  totalT.font = Font.systemFont(11);
  totalT.textColor = dc("#6C6C70", "#8E8E93");

  trafficDetails.addSpacer(3);

  const resetT = trafficDetails.addText(
    daysLeft !== null ? `⏱ ${daysLeft} 天后重置` : "⏱ —"
  );
  resetT.font = Font.systemFont(10);
  resetT.textColor =
    daysLeft !== null && daysLeft <= 3
      ? new Color("#FF9F0A")
      : dc("#6C6C70", "#8E8E93");

  widget.addSpacer(6);
  widget.addImage(makeProgressBar(ratio, 292, 8, color));

  widget.addSpacer(14);

  // ══ 套餐规格 ══
  addSectionTitle(widget, "套餐规格");
  widget.addSpacer(6);
  addSpecRow(widget, "RAM", formatBytes(ram, 0), new Color("#0A84FF"));
  widget.addSpacer(5);
  addSpecRow(widget, "磁盘", formatBytes(disk, 0), new Color("#30D158"));
  widget.addSpacer(5);
  addSpecRow(widget, "SWAP", formatBytes(swap, 0), new Color("#FF9F0A"));

  widget.addSpacer(14);

  // ══ 安全状态 ══
  addSectionTitle(widget, "安全状态");
  widget.addSpacer(5);

  const abuseRow = widget.addStack();
  abuseRow.centerAlignContent();

  const abuseLabel = abuseRow.addText("滥用积分");
  abuseLabel.font = Font.systemFont(11);
  abuseLabel.textColor = dc("#6C6C70", "#8E8E93");
  abuseRow.addSpacer();

  const abuseVal = abuseRow.addText(`${abusePoints} / ${maxAbuse}`);
  abuseVal.font = Font.boldSystemFont(11);
  abuseVal.textColor =
    abuseRatio >= 0.75 ? new Color("#FF453A") : dc("#1C1C1E", "#EBEBF5");

  widget.addSpacer(4);
  widget.addImage(makeProgressBar(abuseRatio, 292, 5, ratioColor(abuseRatio)));

  widget.addSpacer(12);

  // ══ 底部 ══
  const botRow = widget.addStack();
  botRow.centerAlignContent();

  const ipT = botRow.addText(ip);
  ipT.font = new Font("Menlo", 10);
  ipT.textColor = dc("#3C3C43", "#EBEBF5");

  botRow.addSpacer();

  if (suspCount > 0) {
    const suspT = botRow.addText(`暂停 ${suspCount} 次`);
    suspT.font = Font.systemFont(10);
    suspT.textColor = new Color("#FF9F0A");
    botRow.addSpacer(8);
  }

  const ipCountText = botRow.addText(
    `${(data.ip_addresses || []).length} IPs`
  );
  ipCountText.font = Font.systemFont(10);
  ipCountText.textColor = dc("#6C6C70", "#8E8E93");
}

// ─────────────────────────────────────────────
// 主入口
// ─────────────────────────────────────────────

async function main() {
  const widget = new ListWidget();

  try {
    const data = await loadServiceInfo();
    const family = config.widgetFamily;

    if (family === "small") {
      await renderSmall(widget, data);
    } else if (family === "large") {
      await renderLarge(widget, data);
    } else {
      await renderMedium(widget, data);
    }
  } catch (e) {
    widget.backgroundColor = new Color("#1C1C1E");
    widget.setPadding(16, 16, 16, 16);
    const title = widget.addText("⚠️ 加载失败");
    title.font = Font.boldSystemFont(14);
    title.textColor = new Color("#FF453A");
    widget.addSpacer(6);
    const msg = widget.addText(e.message || String(e));
    msg.font = Font.systemFont(10);
    msg.textColor = new Color("#8E8E93");
    msg.lineLimit = 3;
  }

  Script.setWidget(widget);
  Script.complete();
}

await main();