2024-09-19 08:33:57 +00:00
|
|
|
import * as plugins from './smartimap.plugins.js';
|
|
|
|
|
2024-11-26 21:58:26 +00:00
|
|
|
export interface ImapClientConfig {
|
2024-09-19 08:33:57 +00:00
|
|
|
host: string;
|
|
|
|
port?: number; // Defaults to 993 if secure, else 143
|
|
|
|
secure?: boolean; // Defaults to true
|
|
|
|
auth: {
|
|
|
|
user: string;
|
|
|
|
pass: string;
|
|
|
|
};
|
|
|
|
mailbox?: string; // Defaults to 'INBOX'
|
|
|
|
filter?: plugins.imapflow.SearchObject; // IMAP search criteria object
|
|
|
|
}
|
|
|
|
|
2024-11-26 21:58:26 +00:00
|
|
|
export type SmartImapMessage = plugins.mailparser.ParsedMail;
|
|
|
|
|
|
|
|
export class ImapClient extends plugins.events.EventEmitter {
|
2024-09-19 08:33:57 +00:00
|
|
|
private client: plugins.imapflow.ImapFlow;
|
|
|
|
private mailbox: string;
|
|
|
|
private filter: plugins.imapflow.SearchObject;
|
|
|
|
private connected: boolean = false;
|
|
|
|
private processing: boolean = false;
|
|
|
|
private seenUids: Set<number> = new Set();
|
|
|
|
|
2024-11-26 21:58:26 +00:00
|
|
|
constructor(private config: ImapClientConfig) {
|
2024-09-19 08:33:57 +00:00
|
|
|
super();
|
|
|
|
|
|
|
|
this.mailbox = config.mailbox || 'INBOX';
|
|
|
|
this.filter = config.filter || { seen: false }; // Updated default filter
|
|
|
|
|
|
|
|
this.client = new plugins.imapflow.ImapFlow({
|
|
|
|
host: config.host,
|
|
|
|
port: config.port || (config.secure === false ? 143 : 993),
|
|
|
|
secure: config.secure !== false,
|
|
|
|
auth: config.auth,
|
|
|
|
logger: false, // Set to console for debugging
|
|
|
|
});
|
|
|
|
|
|
|
|
this.client.on('error', (error) => {
|
|
|
|
this.emit('error', error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async connect(): Promise<void> {
|
|
|
|
try {
|
|
|
|
await this.client.connect();
|
|
|
|
this.connected = true;
|
|
|
|
await this.client.mailboxOpen(this.mailbox); // Removed { reopen: true }
|
|
|
|
this.emit('connected');
|
|
|
|
this.setupIdle();
|
|
|
|
} catch (error) {
|
|
|
|
this.emit('error', error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private async setupIdle(): Promise<void> {
|
|
|
|
this.client.on('exists', async () => {
|
|
|
|
await this.fetchNewMessages();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Initial fetch
|
|
|
|
await this.fetchNewMessages();
|
|
|
|
}
|
|
|
|
|
|
|
|
private async fetchNewMessages(): Promise<void> {
|
|
|
|
if (this.processing) return;
|
|
|
|
this.processing = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
const searchResult = await this.client.search(this.filter);
|
|
|
|
|
|
|
|
// Convert searchResult to a regular array
|
|
|
|
const searchResultArray = Array.from(searchResult);
|
|
|
|
|
|
|
|
const newUids = searchResultArray.filter((uid) => !this.seenUids.has(uid));
|
|
|
|
if (newUids.length === 0) {
|
|
|
|
this.processing = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch messages
|
|
|
|
for await (const message of this.client.fetch(newUids, { envelope: true, source: true })) {
|
|
|
|
const parsed = await plugins.mailparser.simpleParser(message.source);
|
|
|
|
this.emit('message', parsed);
|
|
|
|
this.seenUids.add(message.uid);
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
this.emit('error', error);
|
|
|
|
} finally {
|
|
|
|
this.processing = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public setFilter(filter: plugins.imapflow.SearchObject): void {
|
|
|
|
this.filter = filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async disconnect(): Promise<void> {
|
|
|
|
if (this.connected) {
|
|
|
|
await this.client.logout();
|
|
|
|
this.connected = false;
|
|
|
|
this.emit('disconnected');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|