【死了么】自制白嫖版
这两天死了么很火,作为小白的我也想试试,但是收费了,花钱不存在的。
纯小白,代码 ai 写的,我不会。简简单单的实现了功能,里面还可以加很多功能,注册用户,多用户通知,我就不为难 ai 了,交给你们这群程序员了~嘿嘿~
原理就是签到证明自己活着。
实现:每天都要充电,iPhone 用户插电运行快捷指令,访问固定域名,记录下来,然后到期没有再运行这个快捷指令就证明死了。发 bark 给接收人。
需要用到 Cloudflare 的 worker 和 kv,新建 kv 名称写 CLOCKIN_KV,里面的值是账号密码,照着抄就行。
worker 绑定要写对变量,如图:
worker 中设置运行时间,我设置的是一天两次 0 9,21 * * * 用的白嫖的域名。
iPhone 快捷指令就是一个简单的请求,我的用户名是 mengnimen 密码是 123456,到时候根据你自己在 kv 里设置的值自己改。
https://你的域名/clockin?user=mengnimen&pwd=123456
快捷新建一个 获取 URL 内容 即可。
在自动化中添加一个充电运行的,记得把立即运行打开
以后就是每次充电运行,记录你还活着,一天不记录,就会发通知给 bark 接收人
纯小白,代码 ai 写的,我不会。代码中需要添加 bark 地址,多长时间认为活着也可以改,自己看注释就行了。简简单单的实现了功能,里面还可以加很多功能,注册用户,多用户通知,我就不为难 ai 了,交给你们这群程序员了~嘿嘿
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const path = url.pathname;
try {
if (path === "/" || path === "/health") return json({ ok: true });
if (path === "/clockin") return await handleClockin(url, env);
if (path === "/check") return await handleCheck(url, env);
return json({ ok: false, error: "Not Found" }, 404);
} catch (e) {
return json({ ok: false, error: e?.message || String(e) }, 500);
}
},
async scheduled(event, env, ctx) {
ctx.waitUntil(runTimeoutCheck(env));
},
};
const CONFIG = {
ALERT_AFTER_DAYS: 1,
MIN_ALERT_INTERVAL_HOURS: 6,
DEFAULT_NOTIFY: { type: "bark", url: "" }, // 这里填默认 bark 或钉钉
TITLE: "死了",
NOTIFY_ON_CLOCKIN: false,
};
async function handleClockin(url, env) {
const user = (url.searchParams.get("user") || "").trim();
const pwd = (url.searchParams.get("pwd") || "").trim();
if (!user || !pwd) return json({ ok: false, error: "Missing user or pwd" }, 400);
const ok = await verifyUserPlain(user, pwd, env);
if (!ok) return json({ ok: false, error: "Auth failed" }, 401);
const now = Date.now();
await env.CLOCKIN_KV.put(kLastTime(user), String(now));
if (CONFIG.NOTIFY_ON_CLOCKIN) {
await sendNotify(env, user, { title: "还活着", body: `已记录:${formatTime(now)}` });
}
return json({ ok: true, user, last_time: now, last_time_text: formatTime(now) });
}
async function handleCheck(url, env) {
const user = (url.searchParams.get("user") || "").trim();
const pwd = (url.searchParams.get("pwd") || "").trim();
if (!user || !pwd) return json({ ok: false, error: "Missing user or pwd" }, 400);
const ok = await verifyUserPlain(user, pwd, env);
if (!ok) return json({ ok: false, error: "Auth failed" }, 401);
const now = Date.now();
const lastStr = await env.CLOCKIN_KV.get(kLastTime(user));
const lastMs = lastStr ? Number(lastStr) : null;
const thresholdMs = CONFIG.ALERT_AFTER_DAYS * 24 * 60 * 60 * 1000;
return json({
ok: true,
user,
now,
now_text: formatTime(now),
last_time: lastMs,
last_time_text: lastMs ? formatTime(lastMs) : null,
since_hours: lastMs ? round2((now - lastMs) / 3600000) : null,
threshold_days: CONFIG.ALERT_AFTER_DAYS,
is_timeout: lastMs ? (now - lastMs > thresholdMs) : true,
});
}
async function runTimeoutCheck(env) {
const now = Date.now();
const thresholdMs = CONFIG.ALERT_AFTER_DAYS * 24 * 60 * 60 * 1000;
const minIntervalMs = CONFIG.MIN_ALERT_INTERVAL_HOURS * 60 * 60 * 1000;
let cursor = undefined;
let checked = 0;
let alerted = 0;
while (true) {
const listRes = await env.CLOCKIN_KV.list({ prefix: "user_pwd:", cursor });
cursor = listRes.cursor;
for (const k of listRes.keys) {
const user = k.name.slice("user_pwd:".length);
checked++;
const lastStr = await env.CLOCKIN_KV.get(kLastTime(user));
const lastMs = lastStr ? Number(lastStr) : 0;
const timeout = !lastMs || (now - lastMs > thresholdMs);
if (!timeout) continue;
const lastAlertStr = await env.CLOCKIN_KV.get(kLastAlert(user));
const lastAlertMs = lastAlertStr ? Number(lastAlertStr) : 0;
if (lastAlertMs && (now - lastAlertMs < minIntervalMs)) continue;
const hours = lastMs ? round2((now - lastMs) / 3600000) : null;
const body = lastMs
? `你已超过 ${hours} 小时未进行“活着打卡”。\n上次记录:${formatTime(lastMs)}`
: `未找到打卡记录,请连接充电器触发一次打卡。\n当前时间:${formatTime(now)}`;
const ok = await sendNotify(env, user, { title: CONFIG.TITLE, body });
if (ok) {
alerted++;
await env.CLOCKIN_KV.put(kLastAlert(user), String(now));
}
}
if (listRes.list_complete) break;
}
await env.CLOCKIN_KV.put("meta:last_run", String(now));
await env.CLOCKIN_KV.put("meta:last_run_summary", JSON.stringify({ now, checked, alerted }));
return { checked, alerted, now };
}
/** 明文校验:KV 中 user_pwd:<user> 直接存明文 */
async function verifyUserPlain(user, pwd, env) {
const stored = await env.CLOCKIN_KV.get(kUserPwd(user));
if (!stored) return false;
return stored === pwd;
}
async function sendNotify(env, user, msg) {
let notify = null;
const perUserStr = await env.CLOCKIN_KV.get(kNotify(user));
if (perUserStr) {
try { notify = JSON.parse(perUserStr); } catch {}
}
const type = notify?.type || CONFIG.DEFAULT_NOTIFY.type;
const url = notify?.url || CONFIG.DEFAULT_NOTIFY.url;
if (!url) return false;
if (type === "dingtalk") return await sendDingTalk(url, msg);
return await sendBark(url, msg);
}
async function sendBark(baseUrl, msg) {
const title = encodeURIComponent(msg.title || "提醒");
const body = encodeURIComponent(msg.body || "");
const u = `${baseUrl.replace(/\/$/, "")}/${title}/${body}`;
const r = await fetch(u, { method: "GET" });
return r.ok;
}
async function sendDingTalk(webhookUrl, msg) {
const payload = { msgtype: "text", text: { content: `${msg.title || "提醒"}\n\n${msg.body || ""}` } };
const r = await fetch(webhookUrl, {
method: "POST",
headers: { "content-type": "application/json;charset=utf-8" },
body: JSON.stringify(payload),
});
return r.ok;
}
const kUserPwd = (user) => `user_pwd:${user}`;
const kLastTime = (user) => `last_time:${user}`;
const kLastAlert = (user) => `last_alert:${user}`;
const kNotify = (user) => `notify:${user}`;
function json(obj, status = 200) {
return new Response(JSON.stringify(obj, null, 2), {
status,
headers: { "content-type": "application/json; charset=utf-8" },
});
}
function formatTime(ms) { return new Date(ms).toISOString(); }
function round2(n) { return Math.round(n * 100) / 100; }




