From 9d180fca790adb2a4ce2eb8d0ddfef2a31ef1874 Mon Sep 17 00:00:00 2001 From: panda Date: Sun, 27 Jul 2025 03:09:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=82=AE=E4=BB=B6=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=EF=BC=9A=E6=9C=8D=E5=8A=A1=E7=AB=AF=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E9=A3=9E=E4=B9=A6=E9=82=AE=E7=AE=B1=EF=BC=8C?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E9=82=AE=E7=AE=B1=E5=8F=AF=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 正确的邮件流程: - 服务端:noreply@pandalla.ai (发送+接收) - 用户端:jiaxicui446@gmail.com (接收通知) - 回复路径:任意邮箱 → noreply@pandalla.ai → 系统处理 优势: - 服务端邮箱统一管理 - 用户可用任意邮箱接收通知 - 支持多邮箱品牌回复命令 - 配置简单,维护容易 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- docs/EMAIL_ARCHITECTURE_FINAL.md | 145 ++++++++++++ package.json | 3 +- setup-gmail-app-password.js | 224 ++++++++++++++++++ src/data/session-map.json | 7 + .../c279add4-8ae5-4c7d-958a-2453d2ac6f21.json | 13 + .../fb9c0090-dad7-4482-9885-e0b84daaa4e4.json | 13 + test-email-send.js | 25 +- 7 files changed, 424 insertions(+), 6 deletions(-) create mode 100644 docs/EMAIL_ARCHITECTURE_FINAL.md create mode 100755 setup-gmail-app-password.js create mode 100644 src/data/sessions/c279add4-8ae5-4c7d-958a-2453d2ac6f21.json create mode 100644 src/data/sessions/fb9c0090-dad7-4482-9885-e0b84daaa4e4.json diff --git a/docs/EMAIL_ARCHITECTURE_FINAL.md b/docs/EMAIL_ARCHITECTURE_FINAL.md new file mode 100644 index 0000000..11ab821 --- /dev/null +++ b/docs/EMAIL_ARCHITECTURE_FINAL.md @@ -0,0 +1,145 @@ +# TaskPing 邮件架构说明 + +## 📧 正确的邮件流程 + +### 1. 服务端配置(TaskPing 系统) + +``` +发送邮箱: noreply@pandalla.ai (飞书邮箱) +接收邮箱: noreply@pandalla.ai (飞书邮箱) +``` + +**用途**: +- 发送通知邮件给用户 +- 接收用户的回复命令 +- 处理邮件并注入到 Claude Code CLI + +### 2. 用户端配置 + +``` +通知接收邮箱: jiaxicui446@gmail.com (可配置为任意邮箱) +回复目标邮箱: noreply@pandalla.ai +``` + +**用途**: +- 接收 TaskPing 的任务通知 +- 从任意邮箱回复命令到服务端 + +## 🔄 邮件流程图 + +``` +1. 任务通知流程: + TaskPing 系统 → noreply@pandalla.ai → jiaxicui446@gmail.com + +2. 命令回复流程: + 用户邮箱(任意) → noreply@pandalla.ai → TaskPing 系统 → Claude Code CLI +``` + +## 📱 支持的用户邮箱 + +用户可以从以下**任意邮箱**发送回复到 `noreply@pandalla.ai`: + +- ✅ Gmail (`jiaxicui446@gmail.com`) +- ✅ QQ邮箱 (`xxx@qq.com`) +- ✅ 163邮箱 (`xxx@163.com`) +- ✅ Outlook (`xxx@outlook.com`) +- ✅ 企业邮箱 (`xxx@company.com`) +- ✅ 任何支持SMTP的邮箱 + +## 🔧 配置文件 + +### .env 配置 + +```env +# 发件配置(飞书邮箱) +SMTP_HOST=smtp.feishu.cn +SMTP_USER=noreply@pandalla.ai +SMTP_PASS=kKgS3tNReRTL3RQC + +# 收件配置(飞书邮箱) +IMAP_HOST=imap.feishu.cn +IMAP_USER=noreply@pandalla.ai +IMAP_PASS=kKgS3tNReRTL3RQC + +# 用户通知邮箱(可配置) +EMAIL_TO=jiaxicui446@gmail.com + +# 白名单(可配置多个用户邮箱) +ALLOWED_SENDERS=jiaxicui446@gmail.com +``` + +## 📝 使用方法 + +### 1. 接收通知 +用户在 `jiaxicui446@gmail.com` 收到类似邮件: +``` +发件人: TaskPing 通知系统 +主题: [TaskPing #ABC123] 任务等待您的指示 +内容: 任务详情... +``` + +### 2. 发送命令 +用户可以: + +**方式1:直接回复** +- 直接回复邮件 +- 输入命令,如:`继续执行` + +**方式2:新邮件** +- 从任意邮箱发送到 `noreply@pandalla.ai` +- 主题包含:`[TaskPing #ABC123]` +- 内容为命令 + +### 3. 系统处理 +1. 飞书邮箱接收用户回复 +2. TaskPing 解析命令 +3. 通过 PTY 注入到 Claude Code +4. 任务继续执行 + +## 🔒 安全特性 + +1. **发件人验证**:只有白名单中的邮箱可以发送命令 +2. **Token验证**:邮件主题必须包含有效的会话Token +3. **命令过滤**:自动过滤危险命令 +4. **会话过期**:Token有时间限制 +5. **去重处理**:防止重复执行同一命令 + +## 🚀 优势 + +1. **统一管理**:服务端使用单一邮箱管理 +2. **用户灵活**:用户可用任意邮箱接收和回复 +3. **简单配置**:只需配置一个飞书邮箱 +4. **多用户支持**:可配置多个用户邮箱到白名单 +5. **跨平台**:支持所有邮件客户端 + +## 📊 实际场景 + +### 场景1:移动办公 +``` +1. 开发者在电脑上运行 Claude Code 构建项目 +2. 离开电脑,在手机 Gmail 收到构建完成通知 +3. 直接在手机回复:"继续部署" +4. 电脑上的 Claude Code 自动执行部署命令 +``` + +### 场景2:团队协作 +``` +1. 团队领导 leader@company.com 收到项目通知 +2. 从企业邮箱回复:"批准发布" +3. 系统自动执行发布流程 +4. 所有团队成员收到发布完成通知 +``` + +### 场景3:多设备同步 +``` +1. 在办公室电脑启动长时间任务 +2. 回家路上用个人 QQ 邮箱收到通知 +3. 在家用 163 邮箱发送下一步指令 +4. 办公室电脑自动执行,第二天查看结果 +``` + +这种架构实现了: +- 服务端邮箱统一管理 +- 用户邮箱灵活配置 +- 多邮箱品牌支持 +- 简单可靠的通信机制 \ No newline at end of file diff --git a/package.json b/package.json index eccfc28..dee5a6f 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "relay:test": "node test-email-reply.js", "relay:start": "INJECTION_MODE=pty node src/relay/relay-pty.js", "email:config": "node update-email-config.js", - "email:test": "node test-email-send.js" + "email:test": "node test-email-send.js", + "gmail:setup": "node setup-gmail-app-password.js" }, "bin": { "taskping-install": "./install.js", diff --git a/setup-gmail-app-password.js b/setup-gmail-app-password.js new file mode 100755 index 0000000..9ed4946 --- /dev/null +++ b/setup-gmail-app-password.js @@ -0,0 +1,224 @@ +#!/usr/bin/env node + +/** + * Gmail 应用专用密码设置指南 + * 帮助用户配置 Gmail 的应用专用密码 + */ + +const fs = require('fs'); +const readline = require('readline'); + +function displayInstructions() { + console.log('╔═══════════════════════════════════════════════════════════════╗'); + console.log('║ Gmail 应用专用密码配置指南 ║'); + console.log('╚═══════════════════════════════════════════════════════════════╝\n'); + + console.log('📋 配置步骤:\n'); + + console.log('1️⃣ 登录 Gmail 账号'); + console.log(' 🌐 访问: https://myaccount.google.com/'); + console.log(' 👤 使用您的账号: jiaxicui446@gmail.com\n'); + + console.log('2️⃣ 开启两步验证(如果未开启)'); + console.log(' 🌐 访问: https://myaccount.google.com/security'); + console.log(' 🔐 找到"两步验证"并开启'); + console.log(' 📱 可以使用手机短信或认证器应用\n'); + + console.log('3️⃣ 生成应用专用密码'); + console.log(' 🌐 访问: https://myaccount.google.com/apppasswords'); + console.log(' 📧 选择应用: "邮件"'); + console.log(' 💻 选择设备: "其他(自定义名称)"'); + console.log(' ✏️ 输入名称: "TaskPing Email Relay"'); + console.log(' 🔑 点击"生成"'); + console.log(' 📋 复制 16 位密码(格式类似:abcd efgh ijkl mnop)\n'); + + console.log('4️⃣ 启用 IMAP(如果未启用)'); + console.log(' 🌐 访问: https://mail.google.com/mail/u/0/#settings/fwdandpop'); + console.log(' 📩 找到"IMAP 访问"'); + console.log(' ✅ 选择"启用 IMAP"'); + console.log(' 💾 保存更改\n'); + + console.log('⚠️ 重要提示:'); + console.log(' • 应用专用密码只显示一次,请妥善保存'); + console.log(' • 不要使用您的 Google 账号密码'); + console.log(' • 密码格式是 16 位,包含空格(请保留空格)'); + console.log(' • 如果忘记密码,需要删除后重新生成\n'); +} + +async function promptForPassword() { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + return new Promise((resolve) => { + console.log('📝 请输入您生成的应用专用密码:'); + console.log(' (格式: abcd efgh ijkl mnop)'); + rl.question('密码: ', (password) => { + rl.close(); + resolve(password.trim()); + }); + }); +} + +function validatePassword(password) { + // Gmail 应用专用密码格式:16位字符,可能包含空格 + const cleanPassword = password.replace(/\s/g, ''); + + if (cleanPassword.length !== 16) { + return { + valid: false, + message: '密码长度应为16位字符' + }; + } + + if (!/^[a-zA-Z0-9]+$/.test(cleanPassword)) { + return { + valid: false, + message: '密码只能包含字母和数字' + }; + } + + return { + valid: true, + cleanPassword + }; +} + +function updateEnvFile(password) { + const envPath = '.env'; + + if (!fs.existsSync(envPath)) { + console.error('❌ 未找到 .env 文件'); + return false; + } + + try { + let content = fs.readFileSync(envPath, 'utf8'); + + // 替换 IMAP_PASS 行 + const updated = content.replace( + /IMAP_PASS=.*/, + `IMAP_PASS=${password}` + ); + + fs.writeFileSync(envPath, updated); + return true; + } catch (error) { + console.error('❌ 更新 .env 文件失败:', error.message); + return false; + } +} + +async function testConnection(password) { + console.log('\n🔍 测试 Gmail IMAP 连接...'); + + // 临时设置环境变量 + process.env.IMAP_PASS = password; + + try { + // 动态导入 ImapFlow + const { ImapFlow } = require('imapflow'); + + const client = new ImapFlow({ + host: 'imap.gmail.com', + port: 993, + secure: true, + auth: { + user: 'jiaxicui446@gmail.com', + pass: password + }, + logger: false + }); + + await client.connect(); + console.log('✅ Gmail IMAP 连接成功!'); + + const status = await client.status('INBOX', { messages: true, unseen: true }); + console.log(`📧 收件箱状态: ${status.messages} 封邮件,${status.unseen} 封未读`); + + await client.logout(); + return true; + + } catch (error) { + console.error('❌ 连接失败:', error.message); + + if (error.code === 'EAUTH') { + console.log('\n可能的原因:'); + console.log('• 应用专用密码错误'); + console.log('• 两步验证未开启'); + console.log('• IMAP 未启用'); + } + + return false; + } +} + +async function main() { + displayInstructions(); + + console.log('═'.repeat(65)); + console.log('现在开始配置应用专用密码\n'); + + // 检查是否已经完成网页配置 + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + const ready = await new Promise((resolve) => { + rl.question('已完成上述步骤并获得应用专用密码?(y/n): ', (answer) => { + rl.close(); + resolve(answer.toLowerCase() === 'y'); + }); + }); + + if (!ready) { + console.log('\n请先完成上述配置步骤,然后重新运行此脚本'); + return; + } + + // 获取密码 + const password = await promptForPassword(); + + if (!password) { + console.log('❌ 未输入密码'); + return; + } + + // 验证密码格式 + const validation = validatePassword(password); + if (!validation.valid) { + console.log(`❌ 密码格式错误: ${validation.message}`); + return; + } + + const cleanPassword = validation.cleanPassword; + console.log('✅ 密码格式正确'); + + // 测试连接 + const connected = await testConnection(cleanPassword); + + if (!connected) { + console.log('\n请检查配置后重试'); + return; + } + + // 更新 .env 文件 + const updated = updateEnvFile(cleanPassword); + + if (updated) { + console.log('\n✅ 配置已保存到 .env 文件'); + console.log('\n🚀 下一步操作:'); + console.log(' 1. 运行: npm run email:config'); + console.log(' 2. 运行: npm run email:test'); + console.log(' 3. 运行: npm run relay:pty'); + console.log('\n🎉 Gmail 应用专用密码配置完成!'); + } else { + console.log('\n❌ 保存配置失败,请手动编辑 .env 文件'); + console.log(` IMAP_PASS=${cleanPassword}`); + } +} + +// 运行 +main().catch(console.error); \ No newline at end of file diff --git a/src/data/session-map.json b/src/data/session-map.json index 4686283..2e23cf5 100644 --- a/src/data/session-map.json +++ b/src/data/session-map.json @@ -12,5 +12,12 @@ "expiresAt": 1753559689, "cwd": "/Users/jessytsui/dev/TaskPing", "description": "测试会话" + }, + "TESTMDKMI9XE": { + "type": "pty", + "createdAt": 1753556908, + "expiresAt": 1753560508, + "cwd": "/Users/jessytsui/dev/TaskPing", + "description": "测试会话" } } \ No newline at end of file diff --git a/src/data/sessions/c279add4-8ae5-4c7d-958a-2453d2ac6f21.json b/src/data/sessions/c279add4-8ae5-4c7d-958a-2453d2ac6f21.json new file mode 100644 index 0000000..bc23270 --- /dev/null +++ b/src/data/sessions/c279add4-8ae5-4c7d-958a-2453d2ac6f21.json @@ -0,0 +1,13 @@ +{ + "id": "c279add4-8ae5-4c7d-958a-2453d2ac6f21", + "created": "2025-07-26T18:59:12.862Z", + "expires": "2025-07-27T18:59:12.862Z", + "notification": { + "type": "completed", + "project": "TaskPing", + "message": "[TaskPing] 任务已完成,Claude正在等待下一步指令" + }, + "status": "waiting", + "commandCount": 0, + "maxCommands": 10 +} \ No newline at end of file diff --git a/src/data/sessions/fb9c0090-dad7-4482-9885-e0b84daaa4e4.json b/src/data/sessions/fb9c0090-dad7-4482-9885-e0b84daaa4e4.json new file mode 100644 index 0000000..23be2c4 --- /dev/null +++ b/src/data/sessions/fb9c0090-dad7-4482-9885-e0b84daaa4e4.json @@ -0,0 +1,13 @@ +{ + "id": "fb9c0090-dad7-4482-9885-e0b84daaa4e4", + "created": "2025-07-26T19:05:11.062Z", + "expires": "2025-07-27T19:05:11.062Z", + "notification": { + "type": "completed", + "project": "TaskPing", + "message": "[TaskPing] 任务已完成,Claude正在等待下一步指令" + }, + "status": "waiting", + "commandCount": 0, + "maxCommands": 10 +} \ No newline at end of file diff --git a/test-email-send.js b/test-email-send.js index dbf6fd4..9544307 100755 --- a/test-email-send.js +++ b/test-email-send.js @@ -75,11 +75,11 @@ async function testEmailSend() {
-

📝 测试指令:

-

请回复以下任意命令来测试:

+

📝 如何回复测试指令:

+

方式1(推荐):直接回复此邮件,内容输入:

    -
  • 直接回复: echo "Hello from email"
  • -
  • 使用CMD前缀: CMD: pwd
  • +
  • echo "Hello from email"
  • +
  • CMD: pwd
  • 使用代码块:
     \`\`\`
    @@ -88,6 +88,19 @@ ls -la
                                 
+

方式2:从任何邮箱发送邮件到 noreply@pandalla.ai

+

主题必须包含:[TaskPing #${testToken}]

+
+ +
+

💡 多邮箱支持:

+

您可以从以下任意邮箱发送回复到 noreply@pandalla.ai

+
    +
  • Gmail、QQ邮箱、163邮箱
  • +
  • Outlook、企业邮箱
  • +
  • 任何支持SMTP的邮箱
  • +
+

回复路径:您的邮箱 → noreply@pandalla.ai → TaskPing系统处理


@@ -95,7 +108,9 @@ ls -la

会话ID: ${testToken}
发送自: ${process.env.SMTP_USER}
- 发送到: ${process.env.EMAIL_TO || 'jiaxicui446@gmail.com'} + 通知邮箱: ${process.env.EMAIL_TO}
+ 回复邮箱: ${process.env.SMTP_USER}
+ 系统支持任意邮箱回复

`,