/** * @node imap-trigger * @name IMAP Email Trigger * @category email * @version 1.3.0 * @description Triggers when new emails are received. Polls the IMAP server for new messages. * @trigger * @scheduled * @icon mail */ const configSchema = { type: 'object', properties: { credentialId: { type: 'string', title: 'IMAP Credential', description: 'Select the IMAP credential to use for authentication', dynamicOptions: { source: 'credentials', filter: { type: 'imap' } } }, pollInterval: { type: 'integer', title: 'Poll Interval (minutes)', description: 'How often to check for new emails when workflow is active. Set to 0 for manual-only execution.', default: 5, minimum: 0, maximum: 1440 }, mailbox: { type: 'string', title: 'Mailbox', description: 'The mailbox to monitor (e.g., INBOX)', default: 'INBOX' }, filterUnread: { type: 'boolean', title: 'Only Unread', description: 'Only trigger on unread (unseen) emails', default: true }, filterFrom: { type: 'string', title: 'Filter by From', description: 'Only trigger on emails from this address (optional)' }, filterSubject: { type: 'string', title: 'Filter by Subject', description: 'Only trigger on emails containing this text in subject (optional)' }, markAsRead: { type: 'boolean', title: 'Mark as Read', description: 'Mark emails as read after processing', default: true }, limit: { type: 'integer', title: 'Max Emails', description: 'Maximum number of emails to process per trigger', default: 10, minimum: 1, maximum: 100 }, fetchContent: { type: 'boolean', title: 'Fetch Full Content', description: 'Fetch full email content including body and raw data (required for attachment extraction)', default: true } }, required: ['credentialId'] }; const outputSchema = { type: 'object', properties: { emails: { type: 'array', items: { type: 'object', properties: { uid: { type: 'string', description: 'Unique email identifier' }, subject: { type: 'string' }, from: { type: 'string' }, to: { type: 'string' }, date: { type: 'string' }, body: { type: 'string', description: 'Email body (if fetchContent enabled)' }, raw: { type: 'string', description: 'Raw email content (if fetchContent enabled)' }, hasAttachments: { type: 'boolean', description: 'Whether email has attachments' } } } }, uids: { type: 'array', items: { type: 'string' }, description: 'Array of message UIDs' }, count: { type: 'integer' }, mailbox: { type: 'string' }, timestamp: { type: 'number' } } }; const outputs = [ { name: 'main', displayName: 'Email Received', type: 'object', color: '#22c55e' } ]; /** * Check if email has attachments by looking for Content-Disposition: attachment * or multipart content types */ function detectAttachments(rawEmail) { if (!rawEmail) return false; const lower = rawEmail.toLowerCase(); return lower.includes('content-disposition: attachment') || lower.includes('content-disposition:attachment') || (lower.includes('multipart/mixed') && lower.includes('boundary=')); } module.exports = { configSchema, outputSchema, outputs, async execute(config, input, context) { const { credentialId, mailbox = 'INBOX', filterUnread = true, filterFrom, filterSubject, markAsRead = true, limit = 10, fetchContent = true } = config; if (!credentialId) { throw new Error('IMAP credential is required'); } // Build search criteria let criteria = filterUnread ? 'UNSEEN' : 'ALL'; if (filterFrom) { criteria += ` FROM "${filterFrom}"`; } if (filterSubject) { criteria += ` SUBJECT "${filterSubject}"`; } // Search for emails const searchResult = smartbotic.imap.search({ credentialId, mailbox, criteria, limit }); if (!searchResult.success) { throw new Error(`IMAP search failed: ${searchResult.error}`); } const emails = []; const uids = searchResult.uids || []; // Fetch each email if fetchContent is enabled if (fetchContent) { for (const uid of uids) { // Use peek: true to avoid marking as read during fetch // Only mark as read explicitly if markAsRead is enabled const fetchResult = smartbotic.imap.fetch({ credentialId, mailbox, uid, peek: !markAsRead // peek=true prevents marking as read }); if (fetchResult.success) { const email = { uid: fetchResult.uid, subject: fetchResult.subject, from: fetchResult.from, to: fetchResult.to, date: fetchResult.date, body: fetchResult.body, raw: fetchResult.raw, hasAttachments: detectAttachments(fetchResult.raw) }; emails.push(email); } } } else { // Just return UIDs without fetching content for (const uid of uids) { emails.push({ uid }); // Mark as read if configured (when not fetching content) if (markAsRead) { smartbotic.imap.modify({ credentialId, mailbox, uid, action: 'markRead' }); } } } return { emails, uids, count: emails.length, mailbox, timestamp: Date.now() }; } };