diff --git a/config/channels.json b/config/channels.json index 387ceb3..1e8ba0a 100644 --- a/config/channels.json +++ b/config/channels.json @@ -18,12 +18,12 @@ } }, "imap": { - "host": "imap.gmail.com", + "host": "imap.feishu.cn", "port": 993, "secure": true, "auth": { - "user": "jiaxicui446@gmail.com", - "pass": "your-gmail-app-password" + "user": "noreply@pandalla.ai", + "pass": "kKgS3tNReRTL3RQC" } }, "from": "TaskPing 通知系统 ", diff --git a/src/data/session-map.json b/src/data/session-map.json index a083e7c..4686283 100644 --- a/src/data/session-map.json +++ b/src/data/session-map.json @@ -5,5 +5,12 @@ "expiresAt": 1753559287, "cwd": "/Users/jessytsui/dev/TaskPing", "description": "测试会话" + }, + "TESTMDKM0Q3M": { + "type": "pty", + "createdAt": 1753556089, + "expiresAt": 1753559689, + "cwd": "/Users/jessytsui/dev/TaskPing", + "description": "测试会话" } } \ No newline at end of file diff --git a/src/data/sessions/ace02d65-154a-4c95-9616-cb6e66ad06ec.json b/src/data/sessions/ace02d65-154a-4c95-9616-cb6e66ad06ec.json new file mode 100644 index 0000000..9db2073 --- /dev/null +++ b/src/data/sessions/ace02d65-154a-4c95-9616-cb6e66ad06ec.json @@ -0,0 +1,13 @@ +{ + "id": "ace02d65-154a-4c95-9616-cb6e66ad06ec", + "created": "2025-07-26T18:49:27.369Z", + "expires": "2025-07-27T18:49:27.369Z", + "notification": { + "type": "completed", + "project": "TaskPing", + "message": "[TaskPing] 任务已完成,Claude正在等待下一步指令" + }, + "status": "waiting", + "commandCount": 0, + "maxCommands": 10 +} \ No newline at end of file diff --git a/src/relay/relay-pty.js b/src/relay/relay-pty.js index 2f7740c..a391ddc 100644 --- a/src/relay/relay-pty.js +++ b/src/relay/relay-pty.js @@ -165,9 +165,9 @@ function stripReply(text = '') { } // 处理邮件消息 -async function handleMailMessage(source, uid) { +async function handleMailMessage(source, uid, parsedEmail = null) { try { - const parsed = await simpleParser(source); + const parsed = parsedEmail || await simpleParser(source); // 检查是否已处理过 const messageId = parsed.messageId; @@ -365,45 +365,132 @@ async function startImap() { log.info(`Found ${messages.length} unread messages`); for (const uid of messages) { - const { source } = await client.download(uid, '1', { uid: true }); - const chunks = []; - for await (const chunk of source) { - chunks.push(chunk); - } - await handleMailMessage(Buffer.concat(chunks), uid); - - // 标记为已读 - await client.messageFlagsAdd(uid, ['\\Seen'], { uid: true }); - } - } - - // 监听新邮件 - log.info('Starting IMAP monitor...'); - - for await (const msg of client.idle()) { - if (msg.path === 'INBOX' && msg.type === 'exists') { - log.debug({ count: msg.count }, 'New message notification'); - - // 获取最新的邮件 - const messages = await client.search({ seen: false }); - for (const uid of messages) { - const { source } = await client.download(uid, '1', { uid: true }); - const chunks = []; - for await (const chunk of source) { - chunks.push(chunk); + try { + log.debug({ uid }, 'Downloading message'); + const message = await client.fetchOne(uid, { + bodyText: true, + bodyStructure: true, + envelope: true + }, { uid: true }); + + if (!message) { + log.warn({ uid }, 'Could not fetch message'); + continue; } - await handleMailMessage(Buffer.concat(chunks), uid); + + // 使用简单的方式获取邮件内容 + const emailText = message.bodyText?.value || ''; + const envelope = message.envelope; + + // 构造简单的邮件对象用于解析 + const simpleEmail = { + from: envelope.from?.[0] ? { + text: `${envelope.from[0].name || ''} <${envelope.from[0].address}>`, + value: envelope.from + } : null, + subject: envelope.subject, + text: emailText, + date: envelope.date, + messageId: envelope.messageId, + headers: new Map() + }; + + await handleMailMessage(null, uid, simpleEmail); // 标记为已读 await client.messageFlagsAdd(uid, ['\\Seen'], { uid: true }); + } catch (downloadError) { + log.error({ + uid, + error: downloadError.message, + code: downloadError.code + }, 'Failed to download message'); } } } + + // 使用定期检查替代IDLE监听(更稳定) + log.info('Starting periodic email check...'); + + setInterval(async () => { + try { + const newMessages = await client.search({ seen: false }); + if (newMessages.length > 0) { + log.debug({ count: newMessages.length }, 'Found new messages'); + + for (const uid of newMessages) { + // 检查是否已处理过这个UID + if (PROCESSED_MESSAGES.has(`uid_${uid}`)) { + continue; + } + + try { + log.debug({ uid }, 'Processing new message'); + const message = await client.fetchOne(uid, { + bodyText: true, + bodyStructure: true, + envelope: true + }, { uid: true }); + + if (!message) { + log.warn({ uid }, 'Could not fetch new message'); + continue; + } + + // 使用简单的方式获取邮件内容 + const emailText = message.bodyText?.value || ''; + const envelope = message.envelope; + + // 构造简单的邮件对象用于解析 + const simpleEmail = { + from: envelope.from?.[0] ? { + text: `${envelope.from[0].name || ''} <${envelope.from[0].address}>`, + value: envelope.from + } : null, + subject: envelope.subject, + text: emailText, + date: envelope.date, + messageId: envelope.messageId, + headers: new Map() + }; + + await handleMailMessage(null, uid, simpleEmail); + + // 标记为已处理 + PROCESSED_MESSAGES.add(`uid_${uid}`); + + // 标记为已读 + await client.messageFlagsAdd(uid, ['\\Seen'], { uid: true }); + } catch (processError) { + log.error({ + uid, + error: processError.message, + code: processError.code + }, 'Failed to process new message'); + } + } + } + } catch (checkError) { + log.error({ error: checkError.message }, 'Error during periodic check'); + } + }, 10000); // 每10秒检查一次 + + // 保持连接活跃 + log.info('Email monitoring active, checking every 10 seconds...'); + + // 无限等待(保持进程运行) + await new Promise(resolve => { + // 进程会一直运行直到被终止 + }); } finally { lock.release(); } } catch (error) { - log.error({ error }, 'IMAP error'); + log.error({ + error: error.message, + code: error.code, + stack: error.stack + }, 'IMAP error'); throw error; } finally { await client.logout(); diff --git a/test-imap-connection.js b/test-imap-connection.js new file mode 100755 index 0000000..ee85088 --- /dev/null +++ b/test-imap-connection.js @@ -0,0 +1,160 @@ +#!/usr/bin/env node + +/** + * 测试 IMAP 连接 + * 验证邮箱服务器连接和邮件读取 + */ + +const { ImapFlow } = require('imapflow'); +const { simpleParser } = require('mailparser'); +require('dotenv').config(); + +async function testImapConnection() { + console.log('🔍 测试 IMAP 连接...\n'); + + const client = new ImapFlow({ + host: process.env.IMAP_HOST, + port: Number(process.env.IMAP_PORT || 993), + secure: process.env.IMAP_SECURE === 'true', + auth: { + user: process.env.IMAP_USER, + pass: process.env.IMAP_PASS + }, + logger: false + }); + + try { + console.log(`连接到: ${process.env.IMAP_HOST}:${process.env.IMAP_PORT}`); + console.log(`账号: ${process.env.IMAP_USER}`); + console.log(`SSL: ${process.env.IMAP_SECURE}`); + console.log(''); + + // 连接 + await client.connect(); + console.log('✅ IMAP 连接成功'); + + // 打开收件箱 + const lock = await client.getMailboxLock('INBOX'); + console.log('✅ 收件箱已打开'); + + try { + // 获取邮箱状态 + const status = await client.status('INBOX', { + messages: true, + unseen: true, + recent: true + }); + console.log(`📧 邮箱状态:`); + console.log(` 总邮件数: ${status.messages}`); + console.log(` 未读邮件: ${status.unseen}`); + console.log(` 新邮件: ${status.recent}`); + console.log(''); + + // 搜索未读邮件 + const unseenMessages = await client.search({ seen: false }); + console.log(`🔍 找到 ${unseenMessages.length} 封未读邮件`); + + if (unseenMessages.length > 0) { + console.log('📋 未读邮件列表:'); + + // 只处理最近的5封邮件 + const recentMessages = unseenMessages.slice(-5); + + for (const uid of recentMessages) { + try { + console.log(`\n📧 处理邮件 UID: ${uid}`); + + // 获取邮件头信息 + const envelope = await client.fetchOne(uid, { + envelope: true, + flags: true + }, { uid: true }); + + console.log(` 发件人: ${envelope.envelope?.from?.[0]?.address || 'unknown'}`); + console.log(` 主题: ${envelope.envelope?.subject || 'no subject'}`); + console.log(` 日期: ${envelope.envelope?.date?.toLocaleString('zh-CN') || 'unknown'}`); + console.log(` 标志: ${Array.isArray(envelope.flags) ? envelope.flags.join(', ') : 'none'}`); + + // 下载并解析邮件 + const message = await client.fetchOne(uid, { + source: true + }, { uid: true }); + + if (!message || !message.source) { + console.log(` ⚠️ 无法获取邮件内容`); + continue; + } + + const chunks = []; + for await (const chunk of message.source) { + chunks.push(chunk); + } + + const parsed = await simpleParser(Buffer.concat(chunks)); + + // 检查是否是 TaskPing 相关邮件 + const isTaskPingReply = parsed.subject?.includes('TaskPing') || + parsed.subject?.includes('[TaskPing') || + parsed.text?.includes('TaskPing') || + parsed.headers?.get('x-taskping-session-id'); + + if (isTaskPingReply) { + console.log(` 🎯 这是 TaskPing 相关邮件!`); + console.log(` 正文预览: ${(parsed.text || '').substring(0, 100)}...`); + + // 尝试提取 Token + const tokenMatch = parsed.subject?.match(/\[TaskPing\s+#([A-Za-z0-9_-]+)\]/); + if (tokenMatch) { + console.log(` 🔑 找到 Token: ${tokenMatch[1]}`); + } + } else { + console.log(` ℹ️ 非 TaskPing 邮件,跳过`); + } + + } catch (messageError) { + console.log(` ❌ 处理邮件失败: ${messageError.message}`); + } + } + } + + } finally { + lock.release(); + } + + console.log('\n✅ IMAP 测试完成'); + + } catch (error) { + console.error('\n❌ IMAP 连接失败:', error.message); + console.log('\n可能的原因:'); + console.log('1. 邮箱服务器地址或端口错误'); + console.log('2. 用户名或密码错误'); + console.log('3. IMAP 服务未开启'); + console.log('4. 网络连接问题'); + console.log('5. SSL/TLS 配置错误'); + + if (error.code) { + console.log(`\n错误代码: ${error.code}`); + } + + } finally { + await client.logout(); + } +} + +// 主函数 +async function main() { + console.log('╔══════════════════════════════════════════════════════════╗'); + console.log('║ TaskPing IMAP Connection Test ║'); + console.log('╚══════════════════════════════════════════════════════════╝\n'); + + // 检查配置 + if (!process.env.IMAP_HOST || !process.env.IMAP_USER || !process.env.IMAP_PASS) { + console.error('❌ 缺少 IMAP 配置,请检查 .env 文件'); + process.exit(1); + } + + await testImapConnection(); +} + +// 运行 +main().catch(console.error); \ No newline at end of file