logo NodeSeekbeta

Komari TG通知美化模版 纳米缪斯 修改版

1234
  • @miuler #20 谢谢,我看帖子日期,这个代码不是最新的吗?修改版帖子比这还久

  • @tonymoses #21 发布于2026/4/29 08:48:01
    @miuler #20 谢谢,我看帖子日期,这个代码不是最新的吗?修改版帖子比这还久

    是最新的,我记错了,我想去掉还没去掉详情,我以前是匹配的我自己的主题页面,后面改成官方默认页面了。你可以试试,用了主题的详情页面是不是和默认主题不一样。

  • @miuler #22 详情页面应该是显示的啥?我发送了一个测试消息,打开详情页面是空白的啥也没显示,是不因为是测试消息的原因,上面说的404的原因找到了复制网址多了一个/详情页变//🤣,现在没404了但是空白啥也不显示

  • @tonymoses #23 发布于2026/4/29 08:57:35,编辑于2026/4/29 08:58:03
    @miuler #22 详情页面应该是显示的啥?我发送了一个测试消息,打开详情页面是空白的啥也没显示,是不因为是测试消息的原因,上面说的404的原因找到了复制网址多了一个/详情页变//🤣,现在没404了但是空白啥也不显示

    详情页面就是你单机器的详细情况页面,比如有 延时 , 负载那类的

  • 👍 学到了,收藏备用!

  • 厉害

  • 不错不错

  • NanoMuse · Discord Webhook 通知配置脚本(点击展开完整代码)
    /* ═══════════════════════════════
       NanoMuse · Discord Webhook 通知配置
       ═══════════════════════════════ */
    const DISCORD_WEBHOOK_URL = "你的 Discord Webhook URL"; // Discord 频道设置 → 集成 → Webhook → 复制 Webhook URL
    const PANEL_URL = "你的域名"; // 面板地址(用于按钮跳转和 Footer 域名提取)
    const PHOTO_URL = "https://img.uppic.to/2026/05/05/og-image.png"; // 通知头图地址
    const SITE_NAME = "你的站点名称"; // 站点名称(显示在 Footer)
    const WEBHOOK_USERNAME = "NanoMuse"; // Discord Webhook 显示名称,可留空
    const WEBHOOK_AVATAR_URL = ""; // Discord Webhook 头像,可留空
    /* ═══════════════════════════════ */
    function escapeMd(s) {
      return String(s ?? "")
        .replace(/\\/g, "\\\\")
        .replace(/\*/g, "\\*")
        .replace(/_/g, "\\_")
        .replace(/~/g, "\\~")
        .replace(/`/g, "\\`")
        .replace(/\|/g, "\\|")
        .replace(/>/g, "\\>");
    }
    function code(s) {
      return "`" + String(s ?? "—").replace(/`/g, "ˋ") + "`";
    }
    function blockquote(lines) {
      return String(lines)
        .split("\n")
        .map(line => `> ${line}`)
        .join("\n");
    }
    function discordColorByEvent(eventName) {
      const colors = {
        Offline: 0xff3b30,
        Online: 0x3ba55d,
        Alert: 0xffcc00,
        Renew: 0x9b59b6,
        Expire: 0xff9500,
        Test: 0x3498db
      };
      return colors[eventName] || 0x5865f2;
    }
    async function sendMessage(message, title, instanceId = null, eventName = null) {
      const webhookUrl = DISCORD_WEBHOOK_URL;
      const panelUrl = PANEL_URL;
      const photoUrl = PHOTO_URL;
      if (!webhookUrl) return false;
      const components = [
        {
          type: 1,
          components: [
            {
              type: 2,
              style: 5,
              label: "◈ 控制台",
              url: panelUrl
            },
            ...(instanceId && instanceId !== "未知"
              ? [{
                  type: 2,
                  style: 5,
                  label: "◈ 详情",
                  url: `${panelUrl.replace(/\/+$/, "")}/instance/${instanceId}`
                }]
              : [])
          ]
        }
      ];
      const embed = {
        title: title || "NanoMuse 通知",
        description: message.slice(0, 4096),
        color: discordColorByEvent(eventName),
        image: photoUrl ? { url: photoUrl } : undefined,
        footer: {
          text: `${SITE_NAME} · ${panelUrl.replace(/^https?:\/\//, "").replace(/\/+$/, "")}`
        },
        timestamp: new Date().toISOString()
      };
      const payload = {
        username: WEBHOOK_USERNAME || undefined,
        avatar_url: WEBHOOK_AVATAR_URL || undefined,
        embeds: [embed],
        components,
        allowed_mentions: { parse: [] }
      };
      try {
        const resp = await fetch(webhookUrl + "?wait=true&with_components=true", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(payload)
        });
        if (resp.ok) return true;
        const text = await resp.text().catch(() => "");
        console.error("Discord webhook failed:", resp.status, text);
        // 兜底:如果按钮组件被环境或 Discord 限制拒绝,则只发送 Embed
        const fallbackResp = await fetch(webhookUrl + "?wait=true", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            username: WEBHOOK_USERNAME || undefined,
            avatar_url: WEBHOOK_AVATAR_URL || undefined,
            embeds: [embed],
            allowed_mentions: { parse: [] }
          })
        });
        return fallbackResp.ok;
      } catch (e) {
        console.error("Discord webhook error:", e);
        return false;
      }
    }
    async function sendEvent(event) {
      try {
        const toUTC8 = (t) => {
          if (!t || String(t).startsWith("0001")) return null;
          const d = new Date(String(t).replace(/\.\d+Z$/, "Z"));
          if (isNaN(d.getTime())) return null;
          const u = new Date(d.getTime() + 8 * 36e5);
          const p = (n) => String(n).padStart(2, "0");
          return `${u.getUTCFullYear()}.${p(u.getUTCMonth() + 1)}.${p(u.getUTCDate())} ${p(u.getUTCHours())}:${p(u.getUTCMinutes())}:${p(u.getUTCSeconds())}`;
        };
        const nowUTC8 = () => {
          const u = new Date(Date.now() + 8 * 36e5);
          const p = (n) => String(n).padStart(2, "0");
          return `${u.getUTCFullYear()}.${p(u.getUTCMonth() + 1)}.${p(u.getUTCDate())} ${p(u.getUTCHours())}:${p(u.getUTCMinutes())}:${p(u.getUTCSeconds())}`;
        };
        const fmtSize = (b) => {
          if (!b || b === 0) return null;
          const g = b / 1073741824;
          return g >= 1024 ? `${(g / 1024).toFixed(1)}T` : g >= 1 ? `${g.toFixed(1)}G` : `${Math.round(b / 1048576)}M`;
        };
        const fmtCPU = (c) => (!c ? null : parseFloat(Number(c).toFixed(1)) + "C");
        const fmtTraffic = (b) => {
          if (!b || b === 0) return "∞";
          const g = b / 1073741824;
          return g >= 1024 ? `${(g / 1024).toFixed(1)}T` : `${g.toFixed(0)}G`;
        };
        const maskV4 = (ip) => {
          if (!ip) return null;
          const p = ip.split(".");
          return p.length === 4 ? `${p[0]}.${p[1]}.*.*` : null;
        };
        const maskV6 = (ip) => {
          if (!ip) return null;
          const p = ip.split(":");
          return p.length > 2 ? `${p[0]}:${p[1]}:*:*` : null;
        };
        const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
        const domain = PANEL_URL.replace(/^https?:\/\//, "").replace(/\/+$/, "");
        /* ═══════════════════════════════
           台词库 · voice / advice
           Discord 使用 Markdown,不再使用 Telegram HTML 标签
           ═══════════════════════════════ */
        const voiceLines = {
          Offline: {
            tag: "LINK LOST", cn: "链路中断", icon: "🔻",
            lines: (n) => [
              `与 **${escapeMd(n)}** 的信号已归零。...不是波动,是彻底消失了。观测者。`,
              `**${escapeMd(n)}** 从我的感知范围消失了。链路中断,等待重新捕获。`,
              `检测到异常——**${escapeMd(n)}** 的心跳信号归零了。`,
              `...信号断了。**${escapeMd(n)}** 不在了,观测者。`
            ],
            advice: (n) => [
              `建议检查该节点的物理链路与上游连通性。如短时间内未恢复,可尝试触发系统重启。...需要我持续追踪它的信号吗,观测者?`,
              `上游链路可能存在中断。建议优先确认机房侧状态,必要时触发系统重启。`,
              `建议排查网络路由与物理连接。若长时间无响应,可能需要联系机房确认。...我会继续等它的。`,
              `链路丢失原因未知。建议先尝试触发系统重启,若仍无法恢复,可能需要执行镜像重构。`
            ]
          },
          Online: {
            tag: "LINK RESTORED", cn: "信号恢复", icon: "🔹",
            lines: (n) => [
              `**${escapeMd(n)}** 重新出现在感知范围内。链路已重建。...它回来了。`,
              `观测者,好消息——**${escapeMd(n)}** 的信号恢复了。`,
              `感知到 **${escapeMd(n)}** 的心跳恢复。一切稳定。`,
              `**${escapeMd(n)}** 已归位。...松了口气。`
            ],
            advice: (n) => [
              `建议观察 30 分钟确认链路稳定性。中断期间的数据缺口已标记,如需回溯请告知。`,
              `链路已恢复,建议关注后续运行状态是否平稳。如有异常波动我会立即通知。`,
              `信号稳定中。建议短期内留意该节点的响应延时,确认无残留问题。`
            ]
          },
          Alert: {
            tag: "ANOMALY", cn: "信号异常", icon: "⚡",
            lines: (n) => [
              `**${escapeMd(n)}** 的运行参数偏离了安全区间。还没到临界,但我在持续观测。`,
              `**${escapeMd(n)}** 触发了告警阈值。指标异常,请注意。`,
              `检测到波动——**${escapeMd(n)}** 的状态不太稳定。`,
              `...有些不对劲。**${escapeMd(n)}** 需要你看一下,观测者。`
            ],
            advice: (n) => [
              `建议排查近期负载变化与进程占用。可先尝试触发系统重启,若重启后指标仍未回归安全区间,可能需要执行镜像重构。`,
              `运行参数偏离可能与资源争抢有关。建议触发系统重启释放占用,持续异常则考虑镜像重构。`,
              `建议检查是否有异常进程导致资源溢出。必要时触发系统重启。...我会继续盯着的。`,
              `指标偏离中。建议优先触发系统重启恢复基线,若问题反复出现,镜像重构可能是更彻底的方案。`
            ]
          },
          Renew: {
            tag: "ENERGY CHARGED", cn: "能源补充", icon: "✨",
            lines: (n) => [
              `**${escapeMd(n)}** 的能源已补充完毕。运行周期已延伸。`,
              `收到了,观测者。**${escapeMd(n)}** 又可以陪我们更久了。`,
              `能源写入完成——**${escapeMd(n)}** 的生命周期已刷新。`,
              `**${escapeMd(n)}** 的倒计时重置了。...很好。`
            ],
            advice: (n) => [
              `能源充足,当前无需额外操作。下一个周期终止点我会提前提醒你,观测者。`,
              `一切正常运转中。周期终止前我会再次通知,不用担心。`,
              `能源已到位,节点运行稳定。...放心交给我观测就好。`
            ]
          },
          Expire: {
            tag: "ENERGY LOW", cn: "能源临界", icon: "⏰",
            lines: (n) => [
              `**${escapeMd(n)}** 的运行周期即将结束。剩余时间不多了。...观测者,需要你做决定。`,
              `**${escapeMd(n)}** 的能源储备即将耗尽。维持时间有限。`,
              `提醒一下——**${escapeMd(n)}** 的周期终止点在逼近了。`,
              `**${escapeMd(n)}** 的时间快用完了。...别忘了它,观测者。`
            ],
            advice: (n) => [
              `如不补充能源,该节点将在周期终止后停止响应。...观测者,要补充吗?`,
              `能源即将耗尽。建议尽快确认是否继续维持该节点的运行。`,
              `周期终止后节点将进入休眠。如需保持在线,请及时补充能源。...我在等你的指令。`
            ]
          },
          Test: {
            tag: "DIAGNOSTIC", cn: "系统诊断", icon: "🔧",
            lines: (n) => [
              `通信回路测试完毕。全链路信号正常。...随时待命,观测者。`,
              `诊断模式启动。所有管道畅通,系统运行正常。`,
              `系统自检通过。...有什么需要我做的吗,观测者?`,
              `诊断完成。一切就绪,信号清晰。`
            ],
            advice: (n) => [
              `全链路畅通,无异常。...一切尽在掌握中,观测者。`,
              `系统状态良好,随时可以执行任务。`,
              `诊断回路正常。如有新指令,我随时响应。`
            ]
          }
        };
        const evData = voiceLines[event.event] || {
          tag: event.event,
          cn: event.event,
          icon: "◆",
          lines: (n) => [`收到事件通知:**${escapeMd(n || "未知")}**`],
          advice: () => []
        };
        const mockClient = {
          uuid: "diag-0000-nano-muse",
          name: "NanoMuse-Diag",
          region: "Tokyo",
          cpu_cores: 4,
          mem_total: 8589934592,
          disk_total: 85899345920,
          tags: "1Gbps<green>;3T<blue>",
          ipv4: "103.56.168.1",
          ipv6: "2001:db8:cafe:1::1",
          traffic_limit: 3221225472000,
          traffic_limit_type: "max",
          price: 29.99,
          currency: "$",
          billing_cycle: 30,
          expired_at: "2026-12-31T00:00:00Z"
        };
        const hasClients = event.clients && event.clients.length > 0;
        const isMock = !hasClients && event.event === "Test";
        const c = hasClients ? event.clients[0] : (isMock ? mockClient : null);
        const targetId = c ? c.uuid : null;
        const nodeName = c ? (c.name || "—") : "—";
        let L = [];
        /* ── Header ── */
        const title = `${evData.icon} ${evData.tag} │ ${evData.cn}`;
        L.push(`## ${title}`);
        if (isMock) L.push(`_〔 模拟数据 〕_`);
        /* ── 台词 ── */
        L.push("");
        L.push(`_${pick(evData.lines(nodeName))}_`);
        if (c) {
          const region = c.region || "";
          /* ── 节点信息 ── */
          let b1 = [];
          b1.push(`**◈ ${escapeMd(nodeName)}**${region ? ` ${code(`[ ${region} ]`)}` : ""}`);
          const specs = [fmtCPU(c.cpu_cores), fmtSize(c.mem_total), fmtSize(c.disk_total)].filter(Boolean);
          if (specs.length) {
            b1.push(`┊ 核心规格 ${code(specs.join(" | "))}`);
          }
          if (c.tags) {
            const labels = c.tags
              .split(";")
              .map(t => t.replace(/<[^>]+>/g, "").trim())
              .filter(Boolean);
            if (labels.length) b1.push(`┊ 标识 ${code(labels.join(" · "))}`);
          }
          L.push("");
          L.push(blockquote(b1.join("\n")));
          /* ── 链路状态 ── */
          let b2 = [];
          const v4 = maskV4(c.ipv4);
          const v6 = maskV6(c.ipv6);
          if (v4 || v6) {
            b2.push(`**◈ 链路状态**`);
            if (v4) b2.push(`┊ 链路地址 v4 ${code(v4)}`);
            if (v6) b2.push(`┊ 链路地址 v6 ${code(v6)}`);
          }
          if (b2.length) {
            L.push("");
            L.push(blockquote(b2.join("\n")));
          }
          /* ── 资源状态 ── */
          let b3 = [];
          const cur = c.currency || "$";
          const price = c.price || 0;
          const hasTraffic = c.traffic_limit && c.traffic_limit > 0;
          const hasPrice = price > 0;
          const hasExpiry = toUTC8(c.expired_at);
          if (hasTraffic || hasPrice || hasExpiry) {
            b3.push(`**◈ 资源状态**`);
          }
          if (hasTraffic) {
            const limitType = c.traffic_limit_type ? ` (${c.traffic_limit_type})` : "";
            b3.push(`┊ 传输额度 ${code(`$$   {fmtTraffic(c.traffic_limit)}   $${limitType}`)}`);
          }
          if (hasPrice) {
            b3.push(`┊ 能量消耗 ${code(`$$   {cur}   $${price} 单位 / 周期`)}`);
          }
          if (hasExpiry) {
            let expLine = `┊ 能源周期 `;
            try {
              const ed = new Date(String(c.expired_at).replace(/\.\d+Z$/, "Z"));
              const days = Math.ceil((ed - new Date()) / 86400000);
              if (days >= 0) expLine += code(`剩余 ${days} 天`);
              else expLine += code(`已耗尽 ${Math.abs(days)} 天`);
            } catch (e) {
              expLine += code(hasExpiry.substring(0, 10));
            }
            b3.push(expLine);
          }
          if (b3.length) {
            L.push("");
            L.push(blockquote(b3.join("\n")));
          }
        }
        /* ── 处理建议 ── */
        const adviceList = evData.advice(nodeName);
        if (adviceList.length) {
          L.push("");
          L.push(blockquote(`💡 _${pick(adviceList)}_`));
        }
        /* ── 时间戳 ── */
        L.push("");
        L.push(`⏱ ${code(toUTC8(event.time) || nowUTC8())} _UTC+8_`);
        /* ── 自定义消息 ── */
        if (event.message && event.message.trim()) {
          L.push("");
          L.push(blockquote(`💬 _${escapeMd(event.message.trim())}_`));
        }
        /* ── Footer 文本也保留在正文里,Embed footer 里也会显示 */
        L.push("");
        L.push(`**${escapeMd(SITE_NAME)}** · _${escapeMd(domain)}_`);
        return await sendMessage(L.join("\n"), title, targetId, event.event);
      } catch (e) {
        return await sendMessage(`Error: ${escapeMd(e.message)}`, "⚠️ Error", null, "Alert");
      }
    }
    ···
    
  • 改了一版 discord webhook 的

1234

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有72289位seeker

🎉欢迎新用户🎉