优化前:

优化后:



前言:
需要使用发信服务商的API我用的是Mailgun,一天免费100封够用了。用其他api复制代码找gemini去改就行了,报错了就复制给gemini。自建邮局需要用docker自建一个SMTP-to-API。
步骤
- 请在代码顶部的 C 对象中填入您的 Mailgun 信息:
-
key: Mailgun API Key
在哪里找:Mailgun 后台 -> Settings -> API Keys -> Private API Key。
格式通常是一长串字符(如xxxxxxxxxx-xxxxxxxx...)。 -
dom: 您的发送域名
在哪里找:Mailgun 后台 -> Sending -> Domains。
例如:mg.yourdomain.com 或 notify.example.com。 -
from: 发件人名称与地址
格式:"显示的名称 <邮箱地址>"。
例如:"Komari Monitor [email protected]"。 -
to: 接收通知的邮箱
您想让谁收到通知就填谁。
- Komari打开设置,通知,开启通知,通知渠道,JavaScript 代码填入保存就可以了。
Mailgun API版本
async function sendEvent(event) {
const C = {
key: "YOUR_MAILGUN_API_KEY",
dom: "YOUR_MAILGUN_DOMAIN",
from: "Komari Monitor <[email protected]>",
to: "[email protected]"
};
const b64 = s => {
try { if (typeof btoa != 'undefined') return btoa(s); } catch (e) {}
try { if (typeof Buffer != 'undefined') return Buffer.from(s).toString('base64'); } catch (e) {}
let o = "", c = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", i = 0, a, b, d;
while (i < s.length) {
a = s.charCodeAt(i++);
b = s.charCodeAt(i++);
d = s.charCodeAt(i++);
o += c.charAt(a >> 2) + c.charAt((a & 3) << 4 | b >> 4) + c.charAt((b & 15) << 2 | d >> 6) + c.charAt(d & 63).replace(64, '=');
}
return o;
};
const send = async (html, sub) => {
try {
const resp = await fetch(`https://api.mailgun.net/v3/${C.dom}/messages`, {
method: 'POST',
headers: {
'Authorization': `Basic ${b64('api:' + C.key)}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `from=${encodeURIComponent(C.from)}&to=${encodeURIComponent(C.to)}&subject=${encodeURIComponent(sub)}&html=${encodeURIComponent(html)}`
});
return resp.ok;
} catch (e) { return false; }
};
try {
const c = event.clients || [];
const map = {
'Offline': ['#e11d48', '离线警告'],
'Online': ['#059669', '恢复上线'],
'Alert': ['#d97706', '系统告警'],
'Renew': ['#2563eb', '自动续费'],
'Expire': ['#dc2626', '到期提醒'],
'Test': ['#7c3aed', '测试通知']
};
const [color, title] = map[event.event] || ['#4b5563', '系统通知'];
const d = new Date(new Date(event.time).getTime() + 28800000);
const date = `${d.getUTCFullYear()}/${d.getUTCMonth() + 1}/${d.getUTCDate()} ${d.getUTCHours().toString().padStart(2, '0')}:${d.getUTCMinutes().toString().padStart(2, '0')}`;
const rows = c.map(x =>
`<tr><td style="padding:10px 0;border-bottom:1px solid #f3f4f6"><div style="display:inline-block;background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px;padding:4px 8px"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:${color};margin-right:6px;margin-bottom:1px"></span><span style="font-weight:400;color:#1f2937;font-size:13px;letter-spacing:-0.2px">${x.name || '?'}</span></div></td><td style="text-align:right;border-bottom:1px solid #f3f4f6"><span style="font-size:10px;font-weight:500;color:#6b7280;background:#f9fafb;padding:3px 8px;border-radius:20px;font-family:'MiSans','MiLanPro',ui-monospace,SFMono-Regular,monospace;border:1px solid #e5e7eb">${x.region || ''}</span></td></tr>`
).join('');
const html = `<div style="font-family:'MiSans','MiLanPro','MiLan',ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;background:#ffffff;padding:40px 0"><div style="width:94%;max-width:480px;margin:0 auto;background:#fff;border-radius:12px;border:0;border-top:4px solid ${color};box-shadow:inset 0 3px 6px rgba(0,0,0,0.06),0 0 0 1px #e5e7eb,0 10px 25px -5px rgba(0,0,0,0.18),0 8px 10px -6px rgba(0,0,0,0.1);overflow:hidden"><div style="padding:28px 24px"><h1 style="font-size:20px;margin:0;color:#111827;font-weight:600;letter-spacing:-0.5px">${title}</h1><p style="color:#6b7280;font-size:12px;margin:8px 0 20px 0;font-weight:500">${date}</p>${event.message ? `<div style="margin:20px 0;background:#f9fafb;border-radius:6px;padding:12px 15px;color:#4b5563;font-size:13px;line-height:1.6;border:1px solid #f3f4f6">${event.message}</div>` : ''}${rows ? `<div style="margin-top:20px"><table style="width:100%;border-collapse:collapse">${rows}</table></div>` : ''}</div><div style="background:#fafafa;padding:12px;text-align:center;font-size:10px;color:#9ca3af;border-top:1px solid #f3f4f6;font-weight:500;letter-spacing:0.5px">Komari Monitoring</div></div></div>`;
let sub = `[${title}] ` + (!c.length ? 'System' : (c.length === 1 ? c[0].name : `${c.length} Servers: ${c.map(x => x.name).join(', ')}`));
if (sub.length > 80) sub = sub.slice(0, 77) + '...';
return await send(html, sub);
} catch (e) {
return await send(`Error: ${e.message}`, 'Script Error');
}
}
用ai改了一份Resend API版本,自测
async function sendEvent(event) {
const C = {
key: "re_12345678_xxxxxxxxxxxxxxxxxxxxxxxx",
from: "Komari Monitor <[email protected]>",
to: "[email protected]"
};
const send = async (html, sub) => {
try {
const resp = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${C.key}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
from: C.from,
to: C.to,
subject: sub,
html: html
})
});
return resp.ok;
} catch (e) { return false; }
};
try {
const c = event.clients || [];
const map = {
'Offline': ['#e11d48', '离线警告'],
'Online': ['#059669', '恢复上线'],
'Alert': ['#d97706', '系统告警'],
'Renew': ['#2563eb', '自动续费'],
'Expire': ['#dc2626', '到期提醒'],
'Test': ['#7c3aed', '测试通知']
};
const [color, title] = map[event.event] || ['#4b5563', '系统通知'];
const d = new Date(new Date(event.time).getTime() + 28800000);
const date = `${d.getUTCFullYear()}/${d.getUTCMonth() + 1}/${d.getUTCDate()} ${d.getUTCHours().toString().padStart(2, '0')}:${d.getUTCMinutes().toString().padStart(2, '0')}`;
const rows = c.map(x =>
`<tr><td style="padding:10px 0;border-bottom:1px solid #f3f4f6"><div style="display:inline-block;background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px;padding:4px 8px"><span style="display:inline-block;width:6px;height:6px;border-radius:50%;background:${color};margin-right:6px;margin-bottom:1px"></span><span style="font-weight:400;color:#1f2937;font-size:13px;letter-spacing:-0.2px">${x.name || '?'}</span></div></td><td style="text-align:right;border-bottom:1px solid #f3f4f6"><span style="font-size:10px;font-weight:500;color:#6b7280;background:#f9fafb;padding:3px 8px;border-radius:20px;font-family:'MiSans','MiLanPro',ui-monospace,SFMono-Regular,monospace;border:1px solid #e5e7eb">${x.region || ''}</span></td></tr>`
).join('');
const html = `<div style="font-family:'MiSans','MiLanPro','MiLan',ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;background:#ffffff;padding:40px 0"><div style="width:94%;max-width:480px;margin:0 auto;background:#fff;border-radius:12px;border:0;border-top:4px solid ${color};box-shadow:inset 0 3px 6px rgba(0,0,0,0.06),0 0 0 1px #e5e7eb,0 10px 25px -5px rgba(0,0,0,0.18),0 8px 10px -6px rgba(0,0,0,0.1);overflow:hidden"><div style="padding:28px 24px"><h1 style="font-size:20px;margin:0;color:#111827;font-weight:600;letter-spacing:-0.5px">${title}</h1><p style="color:#6b7280;font-size:12px;margin:8px 0 20px 0;font-weight:500">${date}</p>${event.message ? `<div style="margin:20px 0;background:#f9fafb;border-radius:6px;padding:12px 15px;color:#4b5563;font-size:13px;line-height:1.6;border:1px solid #f3f4f6">${event.message}</div>` : ''}${rows ? `<div style="margin-top:20px"><table style="width:100%;border-collapse:collapse">${rows}</table></div>` : ''}</div><div style="background:#fafafa;padding:12px;text-align:center;font-size:10px;color:#9ca3af;border-top:1px solid #f3f4f6;font-weight:500;letter-spacing:0.5px">Komari Monitoring</div></div></div>`;
let sub = `[${title}] ` + (!c.length ? 'System' : (c.length === 1 ? c[0].name : `${c.length} Servers: ${c.map(x => x.name).join(', ')}`));
if (sub.length > 80) sub = sub.slice(0, 77) + '...';
return await send(html, sub);
} catch (e) {
return await send(`Error: ${e.message}`, 'Script Error');
}
}
mark了
优化后这个截图是TG里面的效果?不错哦
@bbj #2 不是TG这个是邮件的效果

好东西,收藏加鸡腿
我也以为是tg呢
mark
顶
加了点阴影,更立体了。同时适配手机大小
成功复刻,感谢大佬教程。

@likeli #9 可以,我已经用了一段时间了,没啥bug