定时提醒并检查待办事项脚本

wen 实用脚本 2

这是为您生成的定时提醒并检查待办事项脚本,它是一个 Python 工具,能在后台运行,定期检查您设定的任务列表,并在任务到期时弹出通知。

定时提醒并检查待办事项脚本

#!/usr/bin/env python3
"""
定时提醒并检查待办事项脚本
功能:
1. 从 JSON 文件中加载待办事项(可指定时间,"2025-12-31 23:59")
2. 每 30 秒检查一次是否有到期的任务(基于本地时间)
3. 如果有任务到期,通过系统通知(notify-send)提醒用户
4. 支持标记任务为已完成(从列表中移除)
5. 支持交互式添加新任务
6. 提供简单的命令行菜单
依赖:
- Python 3.6+
- Linux/Unix 环境(使用 notify-send 发送通知)
- 若使用 Windows/macOS,可自行替换通知方式(如 win10toast、pyobjc)
用法:
    python todo_reminder.py              # 启动提醒与检查
    python todo_reminder.py --add         # 添加新任务后立即退出
    python todo_reminder.py --list        # 列出所有待办事项
    python todo_reminder.py --done <id>   # 将指定 ID 的任务标记为完成
"""
import json
import os
import sys
import time
import argparse
from datetime import datetime
from threading import Thread, Event
# ---------- 配置 ----------
TODO_FILE = os.path.expanduser("~/.todo_reminder.json")
CHECK_INTERVAL = 30  # 检查间隔(秒)
# --------------------------
# 全局退出事件
exit_event = Event()
def load_tasks() -> list:
    """从 JSON 文件加载待办事项列表"""
    if not os.path.exists(TODO_FILE):
        return []
    with open(TODO_FILE, "r", encoding="utf-8") as f:
        try:
            return json.load(f)
        except json.JSONDecodeError:
            print("警告:任务文件损坏,已重置为空列表。")
            return []
def save_tasks(tasks: list):
    """保存待办事项列表到 JSON 文件"""
    with open(TODO_FILE, "w", encoding="utf-8") as f:
        json.dump(tasks, f, ensure_ascii=False, indent=2)
def notify(title: str, message: str):
    """发送系统通知(跨平台兼容处理)"""
    # Linux 下使用 notify-send
    if sys.platform.startswith("linux"):
        try:
            os.system(f'notify-send "{title}" "{message}"')
        except Exception:
            pass
    # macOS 下使用 osascript
    elif sys.platform == "darwin":
        try:
            os.system(f'osascript -e \'display notification "{message}" with title "{title}"\'')
        except Exception:
            pass
    # Windows 下使用 win10toast(需安装:pip install win10toast)
    elif sys.platform == "win32":
        try:
            from win10toast import ToastNotifier
            toaster = ToastNotifier()
            toaster.show_toast(title, message, duration=5)
        except ImportError:
            print(f"[通知] {title}: {message}")
    else:
        print(f"[通知] {title}: {message}")
def check_due_tasks(tasks: list) -> list:
    """检查所有到期任务,返回已到期的任务列表(同时自动移除重复提醒)"""
    now = datetime.now().strftime("%Y-%m-%d %H:%M")
    due_tasks = []
    remaining = []
    for task in tasks:
        task_time = task.get("time", "")
        if task_time and task_time <= now:
            due_tasks.append(task)
        else:
            remaining.append(task)
    return due_tasks, remaining
def show_tasks(tasks: list):
    """以编号形式显示所有待办事项"""
    if not tasks:
        print("✅ 当前没有待办事项。")
        return
    print(f"\n📋 待办事项列表(共 {len(tasks)} 项):")
    print("-" * 50)
    for idx, task in enumerate(tasks, 1):
        time_str = task.get("time", "无时间")
        desc = task.get("desc", "无描述")
        print(f"  {idx}. [{time_str}] {desc}")
    print("-" * 50)
def add_task_interactive():
    """交互式添加新任务"""
    desc = input("请输入待办事项描述:").strip()
    if not desc:
        print("描述不能为空,取消添加。")
        return
    time_str = input("请输入截止时间(格式:YYYY-MM-DD HH:MM,留空表示不指定时间):").strip()
    if time_str:
        try:
            datetime.strptime(time_str, "%Y-%m-%d %H:%M")
        except ValueError:
            print("时间格式错误,请使用 YYYY-MM-DD HH:MM 格式。")
            return
    else:
        time_str = ""  # 无截止时间
    tasks = load_tasks()
    tasks.append({"desc": desc, "time": time_str})
    save_tasks(tasks)
    print(f"✅ 已添加待办事项:{desc}" + (f" (截止 {time_str})" if time_str else ""))
def mark_done(task_id: int):
    """根据序号标记任务为已完成(从列表移除)"""
    tasks = load_tasks()
    if task_id < 1 or task_id > len(tasks):
        print(f"❌ 无效的任务编号:{task_id},当前共有 {len(tasks)} 项。")
        return
    removed = tasks.pop(task_id - 1)
    save_tasks(tasks)
    print(f"✅ 已完成:{removed['desc']}" + (f" (截止 {removed['time']})" if removed['time'] else ""))
def periodic_check():
    """定时检查循环,直到退出事件被设置"""
    print(f"⏰ 提醒服务已启动,每 {CHECK_INTERVAL} 秒检查一次到期任务...")
    while not exit_event.is_set():
        tasks = load_tasks()
        due_tasks, remaining = check_due_tasks(tasks)
        if due_tasks:
            for task in due_tasks:
                msg = f"任务:{task['desc']}"
                if task.get("time"):
                    msg += f"\n截止时间:{task['time']}"
                notify("⏰ 待办事项提醒", msg)
                print(f"\n🔔 到期提醒:{task['desc']}" + (f" (截止 {task['time']})" if task['time'] else ""))
            # 保存剩余任务(已移除到期任务)
            save_tasks(remaining)
        # 等待指定间隔,期间可被退出事件中断
        exit_event.wait(CHECK_INTERVAL)
def main_loop():
    """主交互循环:在后台运行检查线程,并提供控制台菜单"""
    # 启动后台检查线程
    checker = Thread(target=periodic_check, daemon=True)
    checker.start()
    print("\n====== 待办事项提醒工具 ======")
    print("命令:")
    print("  list    - 列出所有待办事项")
    print("  add     - 添加新待办事项")
    print("  done n  - 标记编号 n 的任务为完成")
    print("  quit    - 退出程序")
    print("==============================")
    try:
        while True:
            cmd = input("\n请输入命令:").strip().lower()
            if cmd == "quit":
                print("正在退出...")
                exit_event.set()
                break
            elif cmd == "list":
                tasks = load_tasks()
                show_tasks(tasks)
            elif cmd == "add":
                add_task_interactive()
            elif cmd.startswith("done "):
                try:
                    task_id = int(cmd.split()[1])
                    mark_done(task_id)
                except (IndexError, ValueError):
                    print("用法:done <编号>,done 3")
            else:
                print("未知命令,可用命令:list, add, done n, quit")
    except KeyboardInterrupt:
        print("\n用户中断,正在退出...")
        exit_event.set()
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="定时提醒并检查待办事项")
    parser.add_argument("--add", action="store_true", help="添加新任务后立即退出")
    parser.add_argument("--list", action="store_true", help="列出所有待办事项")
    parser.add_argument("--done", type=int, metavar="ID", help="标记指定编号的任务为完成")
    args = parser.parse_args()
    # 处理命令行快捷操作
    if args.add:
        add_task_interactive()
    elif args.list:
        tasks = load_tasks()
        show_tasks(tasks)
    elif args.done is not None:
        mark_done(args.done)
    else:
        # 启动主交互循环
        main_loop()

功能与操作说明

您可以通过命令行菜单或参数来管理任务,脚本会持续在后台检查,并在任务到期时通过系统通知提醒您。

  • 任务管理:您可以通过 addlistdone <编号> 命令来添加、查看和完成待办事项,所有任务会保存在 ~/.todo_reminder.json 文件中。
  • 自动提醒:脚本启动后,会每30秒检查一次任务列表,当系统时间等于或超过任务的“截止时间”时,会自动发送系统通知,并将该任务从列表中移除。
  • 灵活使用:您可以直接运行脚本进入交互模式,也可以使用 --add--list--done <ID> 参数快速执行单项操作,方便集成到其他工作流中。

抱歉,评论功能暂时关闭!