个人自动化任务集合,通过macOS launchd定时调度,执行日常任务并同步到多个平台(Git、Notion、Feishu、Obsidian)。包含日报生成、邮箱整理、桌面整理、科技新闻聚合四大核心功能。
个人自动化任务集合,通过 macOS launchd 定时调度,自动执行日常任务并同步到多个平台。
此技能实现了一套完整的个人工作自动化系统,包含四个核心任务模块,每个任务在指定时间自动运行,并将结果同步到多个平台(Obsidian、Feishu、Notion、Git)。
**调度时间**: 每天 00:00
**功能**:
**输出示例**:
```markdown
```
**调度时间**: 每天 02:00
**功能**:
**前置条件**:
**调度时间**: 每天 04:00
**功能**:
**整理规则**:
**安全特性**: 不删除任何文件,仅移动;保留最近7天文件在原位;生成详细移动日志。
**调度时间**: 每天 07:00
**功能**:
**输出格式**:
```markdown
1. [Article Title](url) (320分, 45评论)
> Repository description
```
创建以下目录结构:
```bash
mkdir -p ~/workspace/zhimeng-agent/{tasks/{config,sync_utils,daily_report,email_organizer,desktop_organizer,tech_news,launchd},logs}
cd ~/workspace/zhimeng-agent
```
创建 `pyproject.toml`:
```toml
[tool.poetry]
name = "zhimeng-agent"
version = "1.0.0"
description = "个人自动化任务集合"
authors = ["Your Name <[email protected]>"]
[tool.poetry.dependencies]
python = "^3.12"
requests = "^2.31.0"
python-dotenv = "^1.0.0"
gitpython = "^3.1.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
```
安装依赖:
```bash
poetry install
```
创建 `.env` 文件:
```bash
FEISHU_APP_ID=your_app_id
FEISHU_APP_SECRET=your_app_secret
FEISHU_RECIPIENT_OPEN_ID=your_open_id
OPENAI_API_KEY=your_api_key
NOTION_TOKEN=your_token
```
**tasks/config.py** - 统一配置:
```python
from dataclasses import dataclass
from pathlib import Path
@dataclass
class TaskConfig:
# 调度时间(小时)
DAILY_REPORT_HOUR: int = 0
EMAIL_ORGANIZE_HOUR: int = 2
DESKTOP_ORGANIZE_HOUR: int = 4
TECH_NEWS_HOUR: int = 7
# 路径配置
IDEAS_ROOT: Path = Path.home() / "workspace" / "projects"
OBSIDIAN_VAULT: Path = Path.home() / "Documents" / "Obsidian Vault"
DESKTOP_PATH: Path = Path.home() / "Desktop"
DOWNLOADS_PATH: Path = Path.home() / "Downloads"
# 飞书接收者
FEISHU_RECIPIENT_OPEN_ID: str = ""
config = TaskConfig()
```
**tasks/sync_utils.py** - 多平台同步工具:
```python
import os
from pathlib import Path
from datetime import datetime
import requests
class ContentSyncer:
def __init__(self, obsidian_vault: Path, feishu_app_id: str = None, feishu_app_secret: str = None):
self.obsidian_vault = obsidian_vault
self.feishu_app_id = feishu_app_id
self.feishu_app_secret = feishu_app_secret
self._feishu_token = None
def sync_content(self, title: str, content: str, targets: list, obsidian_folder: str = "Journal"):
"""同步内容到多个平台"""
results = {}
if "obsidian" in targets:
results["obsidian"] = self._sync_to_obsidian(title, content, obsidian_folder)
if "feishu" in targets:
results["feishu"] = self._sync_to_feishu(title, content)
return results
def _sync_to_obsidian(self, title: str, content: str, folder: str):
"""写入 Obsidian Vault"""
target_dir = self.obsidian_vault / folder
target_dir.mkdir(parents=True, exist_ok=True)
filename = f"{title}.md"
file_path = target_dir / filename
file_path.write_text(content, encoding="utf-8")
return {"success": True, "path": str(file_path)}
def _sync_to_feishu(self, title: str, content: str):
"""发送到飞书"""
if not self.feishu_app_id or not self.feishu_app_secret:
return {"success": False, "error": "Feishu not configured"}
# 获取 tenant_access_token
if not self._feishu_token:
self._feishu_token = self._get_feishu_token()
# 发送消息
url = "https://open.feishu.cn/open-apis/im/v1/messages"
headers = {
"Authorization": f"Bearer {self._feishu_token}",
"Content-Type": "application/json"
}
payload = {
"receive_id": os.getenv("FEISHU_RECIPIENT_OPEN_ID"),
"msg_type": "text",
"content": f'{{"text": "{title}\\n\\n{content}"}}'
}
response = requests.post(url, json=payload, headers=headers, params={"receive_id_type": "open_id"})
return {"success": response.ok, "response": response.json()}
def _get_feishu_token(self):
"""获取飞书 tenant_access_token"""
url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"
payload = {
"app_id": self.feishu_app_id,
"app_secret": self.feishu_app_secret
}
response = requests.post(url, json=payload)
return response.json().get("tenant_access_token")
def create_syncer():
"""创建同步器实例"""
from tasks.config import config
return ContentSyncer(
obsidian_vault=config.OBSIDIAN_VAULT,
feishu_app_id=os.getenv("FEISHU_APP_ID"),
feishu_app_secret=os.getenv("FEISHU_APP_SECRET")
)
```
每个任务模块(daily_report, email_organizer, desktop_organizer, tech_news)应实现以下结构:
```python
from tasks.config import config
from tasks.sync_utils import create_syncer
import argparse
class TaskRunner:
def __init__(self, dry_run: bool = False):
self.dry_run = dry_run
self.syncer = create_syncer()
def run(self):
# 1. 收集数据
data = self.collect_data()
# 2. 生成内容
content = self.generate_content(data)
# 3. 同步(除非 dry_run)
if not self.dry_run:
self.syncer.sync_content(
title=f"{task_name}-{datetime.now().strftime('%Y-%m-%d')}",
content=content,
targets=["obsidian", "feishu"],
obsidian_folder="Journal"
)
else:
print(content)
def collect_data(self):
# 实现数据收集逻辑
pass
def generate_content(self, data):
# 实现内容生成逻辑
pass
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--dry-run", action="store_true", help="仅生成内容不同步")
args = parser.parse_args()
runner = TaskRunner(dry_run=args.dry_run)
runner.run()
if __name__ == "__main__":
main()
```
创建 `tasks/launchd/install.sh`:
```bash
#!/bin/bash
set -e
AGENT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
PLIST_DIR="$HOME/Library/LaunchAgents"
PYTHON_BIN="$(cd "$AGENT_DIR" && poetry env info --path)/bin/python"
TASKS=(
"daily-report:0"
"email-organizer:2"
"desktop-organizer:4"
"tech-news:7"
)
create_plist() {
local task_name=$1
local hour=$2
local plist_file="$PLIST_DIR/com.zhimeng.$task_name.plist"
cat > "$plist_file" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.zhimeng.$task_name</string>
<key>ProgramArguments</key>
<array>
<string>$PYTHON_BIN</string>
<string>-m</string>
<string>tasks.${task_name//-/_}.main</string>
</array>
<key>WorkingDirectory</key>
<string>$AGENT_DIR</string>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>$hour</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>StandardOutPath</key>
<string>$AGENT_DIR/logs/$task_name.log</string>
<key>StandardErrorPath</key>
<string>$AGENT_DIR/logs/$task_name.error.log</string>
<key>RunAtLoad</key>
<false/>
</dict>
</plist>
EOF
echo "Created $plist_file"
}
case "$1" in
install)
mkdir -p "$AGENT_DIR/logs"
for task_config in "${TASKS[@]}"; do
IFS=':' read -r task hour <<< "$task_config"
create_plist "$task" "$hour"
launchctl load "$PLIST_DIR/com.zhimeng.$task.plist"
echo "Loaded com.zhimeng.$task"
done
;;
uninstall)
for task_config in "${TASKS[@]}"; do
IFS=':' read -r task hour <<< "$task_config"
launchctl unload "$PLIST_DIR/com.zhimeng.$task.plist" 2>/dev/null || true
rm -f "$PLIST_DIR/com.zhimeng.$task.plist"
echo "Uninstalled com.zhimeng.$task"
done
;;
status)
launchctl list | grep zhimeng
;;
run)
if [ -z "$2" ]; then
echo "Usage: $0 run <task-name>"
exit 1
fi
launchctl start "com.zhimeng.$2"
;;
*)
echo "Usage: $0 {install|uninstall|status|run <task-name>}"
exit 1
;;
esac
```
赋予执行权限:
```bash
chmod +x tasks/launchd/install.sh
```
```bash
cd ~/workspace/zhimeng-agent
./tasks/launchd/install.sh install
./tasks/launchd/install.sh status
poetry run python -m tasks.daily_report.main --dry-run
poetry run python -m tasks.tech_news.main --dry-run
./tasks/launchd/install.sh run tech-news
tail -f logs/tech-news.log
```
```bash
poetry run python -m tasks.daily_report.main --dry-run # 仅生成不同步
poetry run python -m tasks.daily_report.main # 生成并同步
poetry run python -m tasks.email_organizer.main --dry-run # 仅分析
poetry run python -m tasks.email_organizer.main # 执行归档
poetry run python -m tasks.desktop_organizer.main --dry-run # 仅预览
poetry run python -m tasks.desktop_organizer.main # 执行整理
poetry run python -m tasks.tech_news.main --dry-run # 仅生成
poetry run python -m tasks.tech_news.main --hn-count 20 # 自定义数量
poetry run python -m tasks.tech_news.main # 生成并同步
```
```bash
launchctl list | grep zhimeng
launchctl start com.zhimeng.tech-news
log show --predicate 'subsystem == "com.apple.launchd"' --last 1h | grep zhimeng
```
```bash
poetry env info --path
poetry run python -c "from tasks.config import config; print(config)"
```
```bash
chmod +x tasks/launchd/install.sh
mkdir -p logs
```
添加新任务的步骤:
1. 创建任务目录: `mkdir -p tasks/new_task`
2. 实现 `tasks/new_task/main.py`(参考上述结构)
3. 在 `tasks/launchd/install.sh` 的 `TASKS` 数组中添加新任务
4. 运行 `./tasks/launchd/install.sh install` 重新安装
v1.0.0 - 初始版本,包含四个核心自动化任务
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/zhimeng-agent/raw