文章摘要
前一段时间死了么app火了一阵,没想到这么快我就要用上了。作者也得了绝症,也将不久人世,再见了~(无需过问)
自己简单搓了一个非app的,使用github action每天更新状态,action workflowk可手动签到或死亡,死亡后自动更新README.md在自己的主页上。
存活判定方式:
- workflowk 手动签到;
- 任务每天执行,7天内没签到默认存活;
死亡判定方式:
1.超过7天未手动签到即宣布死亡。
2.手动执行 workflowk 立即死亡。
死亡后:
更新首页README,宣布死亡消息(没了)。
因每个人的信息不同,需要修改成自己的,项目名用自己的用户名(这样可以显示在自己主页上)
自动任务 life-monitor.yml
- name: Life Monitor
- on:
- schedule:
- – cron: ’00 19 * * *’
- workflow_dispatch:
- inputs:
- mode:
- description: ‘选择运行模式’
- required: true
- type: choice
- options:
- – 签到
- – 死亡
- jobs:
- monitor:
- runs-on: ubuntu-24.04
- permissions:
- contents: write
- steps:
- – name: Checkout
- uses: actions/checkout@v6
- – name: Setup Python
- uses: actions/setup-python@v7.1.0
- with:
- python-version: ‘3.13’
- – name: Run life monitor
- run: |
- MODE=”${{ github.event.inputs.mode }}”
- if [ “$MODE” = “签到” ]; then
- python life_monitor.py
- elif [ “$MODE” = “死亡” ]; then
- python life_monitor.py –force-death
- else
- # 定时触发:只检查不签到,超过7天自动宣告死亡
- python life_monitor.py –auto
- fi
- – name: Commit and push
- run: |
- MODE=”${{ github.event.inputs.mode }}”
- MSG=”${MODE:-自动签到}”
- git config user.name “github-actions[bot]”
复制代码
死亡逻辑life_monitor.py
- #!/usr/bin/env python3
- “””生命监测系统”””
- import os
- import datetime
- BASE_DIR = os.path.dirname(os.path.abspath(__file__))
- STATE_FILE = os.path.join(BASE_DIR, “.last_run”)
- README_PATH = os.path.join(BASE_DIR, “README.md”)
- MAX_DAYS = 7
- BIRTH_DATE = datetime.date(1995, 4, 23)
- BJ = datetime.timezone(datetime.timedelta(hours=8))
- def read_last_run():
- if not os.path.exists(STATE_FILE):
- return None
- try:
- with open(STATE_FILE, “r”, encoding=”utf-8″) as f:
- raw = f.read().strip()
- dt = datetime.datetime.fromisoformat(raw)
- if dt.tzinfo is None:
- dt = dt.replace(tzinfo=BJ)
- return dt
- except Exception:
- return None
- def write_last_run(dt):
- with open(STATE_FILE, “w”, encoding=”utf-8″) as f:
- f.write(dt.isoformat())
- def now():
- return datetime.datetime.now(BJ)
- def generate_alive_content(last_run, days, scan_time):
- lines = [
- f”🟢 上次存活时间: {last_run.strftime(‘%Y-%m-%d %H:%M:%S’)} (UTC+8)”,
- “”,
- f”更新时间: {scan_time.strftime(‘%Y-%m-%d %H:%M:%S’)} (UTC+8)”,
- ]
- return “\n”.join(lines)
- def generate_obituary_content(last_run, days, scan_time, force_death=False):
- birth = BIRTH_DATE
- if force_death:
- # 手动确认死亡:死亡日期为当前扫描日期,保留真实最后存活时间
- death_date = scan_time.date()
- death_detail = “”
- death_reason = “手动确认死亡。”
- days_line = f”> **距上次执行**: {days} 天”
- else:
- # 自动判定死亡:超过阈值,估算死亡日期
- death_date = (last_run + datetime.timedelta(days=MAX_DAYS)).date()
- death_detail = f”(最后手动执行后第 {MAX_DAYS} 天(估算))”
- death_reason = f”超过 {MAX_DAYS} 天未检测到生命体征,按协议发布讣告。”
- days_line = f”> **距上次执行**: {days} 天”
- age = death_date.year – birth.year – ((death_date.month, death_date.day) < (birth.month, birth.day))
- with open(os.path.join(BASE_DIR, “obituary.md”), “r”, encoding=”utf-8″) as f:
- content = f.read()
- replacements = {
- “{{BIRTH_DATE}}”: birth.strftime(‘%Y-%m-%d’),
- “{{BIRTH_LUNAR}}”: “农历一九九五年三月廿四”,
- “{{NAME}}”: “朱俊伟”,
- “{{DEATH_DATE}}”: death_date.strftime(‘%Y-%m-%d’),
- “{{AGE}}”: str(age),
- “{{MAX_DAYS}}”: str(MAX_DAYS),
- “{{LAST_RUN}}”: last_run.strftime(‘%Y-%m-%d %H:%M:%S’),
- “{{SCAN_TIME}}”: scan_time.strftime(‘%Y-%m-%d %H:%M:%S’),
- “{{DEATH_DETAIL}}”: death_detail,
- “{{DEATH_REASON}}”: death_reason,
- “{{DAYS_LINE}}”: days_line,
- }
- for key, value in replacements.items():
- content = content.replace(key, value)
- return content
- def run():
- import sys
- auto_mode = “–auto” in sys.argv
- force_death = “–force-death” in sys.argv
- scan_time = now()
- last_run = read_last_run()
- if force_death:
- # 死亡模式:不修改 .last_run,保留真实的上次存活时间
- # 如果没有历史记录,用当前时间兜底
- if last_run is None:
- last_run = scan_time
- elif not auto_mode:
- # 手动签到:更新 .last_run 为当前时间
- write_last_run(scan_time)
- last_run = scan_time
- if last_run is None:
- days = 0
- if force_death:
- # 没有历史记录但手动确认死亡
- content = generate_obituary_content(scan_time, days, scan_time, force_death)
- else:
- content = generate_alive_content(scan_time, days, scan_time)
- else:
- days = (scan_time – last_run).days
- if force_death:
- # 手动确认死亡:直接生成讣告,不经过阈值判断
- print(f”💀 手动确认死亡。最后存活时间: {last_run.strftime(‘%Y-%m-%d %H:%M:%S’)}”)
- content = generate_obituary_content(last_run, days, scan_time, force_death)
- elif days > MAX_DAYS:
- print(f”⚠️ 距上次手动执行已过去 {days} 天,超过 {MAX_DAYS} 天阈值!”)
- content = generate_obituary_content(last_run, days, scan_time, force_death)
- else:
- print(f”✅ 距上次手动执行 {days} 天,在 {MAX_DAYS} 天阈值内。”)
- content = generate_alive_content(last_run, days, scan_time)
- with open(README_PATH, “w”, encoding=”utf-8″) as f:
- f.write(content)
- print(f”📝 README.md 已更新”)
- print(f” 间隔: {days} 天 | 模式: {‘自动检查’ if auto_mode else (‘强制死亡’ if force_death else ‘手动打卡’)}”)
- print(f” 状态: {‘讣告’ if last_run and days > MAX_DAYS else ‘正常’}”)
- if __name__ == “__main__”:
- run()
复制代码


