Revert "Rename mail-buddy.js to mail_buddy.js (snake_case convention)"
This reverts commit 62228d595e.
This commit is contained in:
112
mail-buddy.js
Normal file
112
mail-buddy.js
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env node
|
||||
import { ImapFlow } from 'imapflow';
|
||||
import { readFileSync } from 'fs';
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
const CONFIG_PATH = process.env.MAIL_BUDDY_CONFIG ?? '/home/devilholk/.config/claude-mail-buddy/config.json';
|
||||
|
||||
function load_config() {
|
||||
try {
|
||||
return JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
|
||||
} catch (err) {
|
||||
console.error(`Failed to load config from ${CONFIG_PATH}: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function make_prompt(message) {
|
||||
const from = message.envelope.from?.[0]?.address ?? 'unknown';
|
||||
const subject = message.envelope.subject ?? '(no subject)';
|
||||
const body = message.text ?? '(no body)';
|
||||
|
||||
return [
|
||||
`[mail-buddy] New email from ${from}`,
|
||||
`Subject: ${subject}`,
|
||||
``,
|
||||
body.trim(),
|
||||
``,
|
||||
`Please reply to this email using the send-email CCC action, addressed to ${from} with an appropriate subject.`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
function send_to_claude(text, config) {
|
||||
const window_id = String(config.claude_window_id);
|
||||
execFileSync('node', [config.claude_remote_path, '--window-id', window_id], {
|
||||
input: text,
|
||||
encoding: 'utf8',
|
||||
stdio: ['pipe', 'inherit', 'inherit'],
|
||||
});
|
||||
}
|
||||
|
||||
async function handle_message(client, uid, config) {
|
||||
const msg = await client.fetchOne(uid, { envelope: true, bodyStructure: true, source: true });
|
||||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch plain text part
|
||||
let text = '';
|
||||
for await (const part of client.fetch(uid, { bodyParts: ['text/plain'] })) {
|
||||
text = part.bodyParts?.get('text/plain')?.toString('utf8') ?? '';
|
||||
}
|
||||
msg.text = text;
|
||||
|
||||
const sender = msg.envelope.from?.[0]?.address ?? '';
|
||||
if (!sender.toLowerCase().includes(config.allowed_sender.toLowerCase())) {
|
||||
console.log(`Ignoring message from ${sender}`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Dispatching message from ${sender}: ${msg.envelope.subject}`);
|
||||
const prompt = make_prompt(msg);
|
||||
send_to_claude(prompt, config);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const config = load_config();
|
||||
|
||||
const client = new ImapFlow({
|
||||
host: config.imap.host,
|
||||
port: config.imap.port ?? 993,
|
||||
secure: config.imap.secure ?? true,
|
||||
auth: {
|
||||
user: config.imap.user,
|
||||
pass: config.imap.pass,
|
||||
},
|
||||
logger: false,
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
console.log(`Connected to ${config.imap.host}`);
|
||||
|
||||
const lock = await client.getMailboxLock(config.imap.mailbox ?? 'INBOX');
|
||||
|
||||
try {
|
||||
// Process any unseen messages already waiting
|
||||
const unseen = await client.search({ unseen: true });
|
||||
for (const uid of unseen) {
|
||||
await handle_message(client, uid, config);
|
||||
}
|
||||
|
||||
console.log('Waiting for new mail (IDLE)...');
|
||||
|
||||
// IDLE loop — re-enters IDLE after each notification
|
||||
while (true) {
|
||||
await client.idle();
|
||||
|
||||
const new_msgs = await client.search({ unseen: true });
|
||||
for (const uid of new_msgs) {
|
||||
await handle_message(client, uid, config);
|
||||
await client.messageFlagsAdd(uid, ['\\Seen']);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.release();
|
||||
await client.logout();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user