claude-code-remote-remake/debug-email.js

494 lines
17 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* TaskPing 邮件调试工具
* 用于调试邮件监听和自动化问题
*/
const Imap = require('node-imap');
const { simpleParser } = require('mailparser');
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
class DebugEmailAutomation {
constructor() {
this.configPath = path.join(__dirname, 'config/channels.json');
this.config = null;
this.imap = null;
}
async start() {
console.log('🔍 TaskPing 邮件调试工具启动\n');
// 1. 检查配置
console.log('📋 1. 检查配置文件...');
if (!this.loadConfig()) {
return;
}
console.log('✅ 配置文件加载成功');
console.log(`📧 IMAP服务器: ${this.config.imap.host}:${this.config.imap.port}`);
console.log(`👤 用户: ${this.config.imap.auth.user}`);
console.log(`📬 通知发送到: ${this.config.to}\n`);
// 2. 测试IMAP连接
console.log('🔌 2. 测试IMAP连接...');
try {
await this.testConnection();
console.log('✅ IMAP连接成功\n');
} catch (error) {
console.error('❌ IMAP连接失败:', error.message);
return;
}
// 3. 检查最近邮件
console.log('📧 3. 检查最近邮件...');
try {
await this.checkRecentEmails();
} catch (error) {
console.error('❌ 检查邮件失败:', error.message);
}
// 4. 开始实时监听
console.log('\n👂 4. 开始实时监听邮件回复...');
console.log('💌 现在可以回复TaskPing邮件来测试自动化功能');
console.log('🔍 调试信息会实时显示\n');
this.startRealTimeListening();
}
loadConfig() {
try {
const data = fs.readFileSync(this.configPath, 'utf8');
const config = JSON.parse(data);
if (!config.email?.enabled) {
console.error('❌ 邮件功能未启用');
console.log('💡 请运行: npm run config');
return false;
}
this.config = config.email.config;
return true;
} catch (error) {
console.error('❌ 配置文件读取失败:', error.message);
console.log('💡 请运行: npm run config');
return false;
}
}
async testConnection() {
return new Promise((resolve, reject) => {
this.imap = new Imap({
user: this.config.imap.auth.user,
password: this.config.imap.auth.pass,
host: this.config.imap.host,
port: this.config.imap.port,
tls: this.config.imap.secure,
connTimeout: 30000,
authTimeout: 15000,
// debug: console.log // 暂时禁用调试
});
this.imap.once('ready', () => {
console.log('🔗 IMAP ready事件触发');
resolve();
});
this.imap.once('error', (error) => {
console.error('🔗 IMAP error事件:', error.message);
reject(error);
});
this.imap.connect();
});
}
async checkRecentEmails() {
return new Promise((resolve, reject) => {
this.imap.openBox('INBOX', true, (err, box) => {
if (err) {
reject(err);
return;
}
console.log(`📫 收件箱状态: 总邮件 ${box.messages.total}, 未读 ${box.messages.unseen}`);
// 查找最近1小时的所有邮件
const since = new Date();
since.setHours(since.getHours() - 1);
console.log(`🔍 搜索 ${since.toLocaleString()} 之后的邮件...`);
this.imap.search([['SINCE', since]], (searchErr, results) => {
if (searchErr) {
reject(searchErr);
return;
}
console.log(`📨 找到 ${results.length} 封最近邮件`);
if (results.length === 0) {
console.log(' 没有找到最近的邮件');
resolve();
return;
}
// 获取最近几封邮件的详情
const fetch = this.imap.fetch(results.slice(-3), {
bodies: 'HEADER',
struct: true
});
fetch.on('message', (msg, seqno) => {
console.log(`\n📧 邮件 ${seqno}:`);
msg.on('body', (stream, info) => {
let buffer = '';
stream.on('data', (chunk) => {
buffer += chunk.toString('utf8');
});
stream.once('end', () => {
const lines = buffer.split('\n');
const subject = lines.find(line => line.startsWith('Subject:'));
const from = lines.find(line => line.startsWith('From:'));
const date = lines.find(line => line.startsWith('Date:'));
console.log(` 📄 ${subject || 'Subject: (未知)'}`);
console.log(` 👤 ${from || 'From: (未知)'}`);
console.log(` 📅 ${date || 'Date: (未知)'}`);
if (subject && subject.includes('[TaskPing]')) {
console.log(' 🎯 这是TaskPing邮件!');
}
});
});
});
fetch.once('end', () => {
resolve();
});
fetch.once('error', (fetchErr) => {
reject(fetchErr);
});
});
});
});
}
startRealTimeListening() {
// 重新连接用于监听
this.imap.end();
setTimeout(() => {
this.connectForListening();
}, 1000);
}
async connectForListening() {
this.imap = new Imap({
user: this.config.imap.auth.user,
password: this.config.imap.auth.pass,
host: this.config.imap.host,
port: this.config.imap.port,
tls: this.config.imap.secure,
connTimeout: 60000,
authTimeout: 30000,
keepalive: true
});
this.imap.once('ready', () => {
console.log('✅ 监听连接建立');
this.openInboxForListening();
});
this.imap.once('error', (error) => {
console.error('❌ 监听连接错误:', error.message);
});
this.imap.once('end', () => {
console.log('🔄 连接断开,尝试重连...');
setTimeout(() => this.connectForListening(), 5000);
});
this.imap.connect();
}
openInboxForListening() {
this.imap.openBox('INBOX', false, (err, box) => {
if (err) {
console.error('❌ 打开收件箱失败:', err.message);
return;
}
console.log('📬 收件箱已打开,开始监听新邮件...');
// 设置定期检查
setInterval(() => {
this.checkNewEmails();
}, 10000); // 每10秒检查一次
// 立即检查一次
this.checkNewEmails();
});
}
checkNewEmails() {
const since = new Date();
since.setMinutes(since.getMinutes() - 5); // 检查最近5分钟的邮件
this.imap.search([['UNSEEN'], ['SINCE', since]], (err, results) => {
if (err) {
console.error('🔍 搜索新邮件失败:', err.message);
return;
}
if (results.length > 0) {
console.log(`\n🚨 发现 ${results.length} 封新邮件!`);
this.processNewEmails(results);
}
});
}
processNewEmails(emailUids) {
const fetch = this.imap.fetch(emailUids, {
bodies: '',
markSeen: true
});
fetch.on('message', (msg, seqno) => {
console.log(`\n📨 处理新邮件 ${seqno}:`);
let buffer = '';
msg.on('body', (stream) => {
stream.on('data', (chunk) => {
buffer += chunk.toString('utf8');
});
stream.once('end', async () => {
try {
const parsed = await simpleParser(buffer);
await this.analyzeEmail(parsed, seqno);
} catch (error) {
console.error(`❌ 解析邮件 ${seqno} 失败:`, error.message);
}
});
});
});
}
async analyzeEmail(email, seqno) {
console.log(`📧 邮件 ${seqno} 分析:`);
console.log(` 📄 主题: ${email.subject || '(无主题)'}`);
console.log(` 👤 发件人: ${email.from?.text || '(未知)'}`);
console.log(` 📅 时间: ${email.date || '(未知)'}`);
// 检查是否是TaskPing回复
const isTaskPingReply = this.isTaskPingReply(email);
console.log(` 🎯 TaskPing回复: ${isTaskPingReply ? '是' : '否'}`);
if (!isTaskPingReply) {
console.log(` ⏭️ 跳过非TaskPing邮件`);
return;
}
// 提取命令
const command = this.extractCommand(email);
console.log(` 💬 邮件内容长度: ${(email.text || '').length} 字符`);
console.log(` 🎯 提取的命令: "${command || '(无)'}"`);
if (!command || command.trim().length === 0) {
console.log(` ⚠️ 未找到有效命令`);
return;
}
console.log(`\n🚀 准备执行命令...`);
await this.executeCommand(command, seqno);
}
isTaskPingReply(email) {
const subject = email.subject || '';
return subject.includes('[TaskPing]') ||
subject.match(/^(Re:|RE:|回复:)/i);
}
extractCommand(email) {
let text = email.text || '';
console.log(` 📝 原始邮件文本:\n${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`);
const lines = text.split('\n');
const commandLines = [];
for (const line of lines) {
if (line.includes('-----Original Message-----') ||
line.includes('--- Original Message ---') ||
line.includes('在') && line.includes('写道:') ||
line.includes('On') && line.includes('wrote:') ||
line.match(/^>\s*/) ||
line.includes('会话ID:')) {
console.log(` ✂️ 在此行停止解析: ${line.substring(0, 50)}`);
break;
}
if (line.includes('--') ||
line.includes('Sent from') ||
line.includes('发自我的')) {
console.log(` ✂️ 跳过签名行: ${line.substring(0, 50)}`);
break;
}
commandLines.push(line);
}
const extractedCommand = commandLines.join('\n').trim();
console.log(` 🎯 清理后的命令:\n"${extractedCommand}"`);
return extractedCommand;
}
async executeCommand(command, seqno) {
console.log(`🤖 执行命令 (来自邮件 ${seqno}):`);
console.log(`📝 命令内容: "${command}"`);
try {
// 1. 复制到剪贴板
console.log(`📋 1. 复制到剪贴板...`);
const clipboardSuccess = await this.copyToClipboard(command);
console.log(`📋 剪贴板: ${clipboardSuccess ? '✅ 成功' : '❌ 失败'}`);
// 2. 尝试自动化
console.log(`🤖 2. 尝试自动输入...`);
const automationSuccess = await this.attemptAutomation(command);
console.log(`🤖 自动化: ${automationSuccess ? '✅ 成功' : '❌ 失败'}`);
// 3. 发送通知
console.log(`🔔 3. 发送通知...`);
const notificationSuccess = await this.sendNotification(command);
console.log(`🔔 通知: ${notificationSuccess ? '✅ 成功' : '❌ 失败'}`);
if (automationSuccess) {
console.log(`\n🎉 邮件命令已自动执行到Claude Code!`);
} else {
console.log(`\n⚠️ 自动化失败,但命令已复制到剪贴板`);
console.log(`💡 请手动在Claude Code中粘贴 (Cmd+V)`);
}
} catch (error) {
console.error(`❌ 执行命令失败:`, error.message);
}
}
async copyToClipboard(command) {
return new Promise((resolve) => {
const pbcopy = spawn('pbcopy');
pbcopy.stdin.write(command);
pbcopy.stdin.end();
pbcopy.on('close', (code) => {
resolve(code === 0);
});
pbcopy.on('error', () => resolve(false));
});
}
async attemptAutomation(command) {
return new Promise((resolve) => {
const escapedCommand = command
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/'/g, "\\'");
const script = `
tell application "System Events"
set claudeApps to {"Claude", "Claude Code", "Claude Desktop"}
set devApps to {"Terminal", "iTerm2", "iTerm", "Visual Studio Code", "Code"}
set targetApp to null
set appName to ""
-- 查找Claude应用
repeat with app in claudeApps
try
if application process app exists then
set targetApp to application process app
set appName to app
exit repeat
end if
end try
end repeat
-- 如果没找到Claude查找开发工具
if targetApp is null then
repeat with app in devApps
try
if application process app exists then
set targetApp to application process app
set appName to app
exit repeat
end if
end try
end repeat
end if
if targetApp is not null then
set frontmost of targetApp to true
delay 1
keystroke "a" using command down
delay 0.3
keystroke "${escapedCommand}"
delay 0.5
keystroke return
return "success:" & appName
else
return "no_app"
end if
end tell
`;
console.log(`🍎 执行AppleScript自动化...`);
const osascript = spawn('osascript', ['-e', script]);
let output = '';
osascript.stdout.on('data', (data) => {
output += data.toString().trim();
});
osascript.on('close', (code) => {
console.log(`🍎 AppleScript结果: 退出码=${code}, 输出="${output}"`);
const success = code === 0 && output.startsWith('success:');
if (success) {
const appName = output.split(':')[1];
console.log(`🍎 成功输入到应用: ${appName}`);
}
resolve(success);
});
osascript.on('error', (error) => {
console.log(`🍎 AppleScript错误: ${error.message}`);
resolve(false);
});
});
}
async sendNotification(command) {
const shortCommand = command.length > 50 ? command.substring(0, 50) + '...' : command;
const script = `
display notification "邮件命令: ${shortCommand.replace(/"/g, '\\"')}" with title "TaskPing Debug" sound name "default"
`;
const osascript = spawn('osascript', ['-e', script]);
return new Promise((resolve) => {
osascript.on('close', (code) => resolve(code === 0));
osascript.on('error', () => resolve(false));
});
}
}
// 启动调试工具
const debugTool = new DebugEmailAutomation();
debugTool.start().catch(console.error);