| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- /**
- * @node imap-search
- * @name IMAP Search
- * @category email
- * @version 1.2.0
- * @description Search and list emails from an IMAP server with various filters.
- * @icon search
- */
- 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' }
- }
- },
- mailbox: {
- type: 'string',
- title: 'Mailbox',
- description: 'The mailbox to search (e.g., INBOX, Sent, Drafts)',
- default: 'INBOX'
- },
- criteria: {
- type: 'string',
- title: 'Search Criteria',
- description: 'IMAP search criteria (e.g., ALL, UNSEEN, FROM user@example.com)',
- default: 'ALL'
- },
- filterUnread: {
- type: 'boolean',
- title: 'Only Unread',
- description: 'Add UNSEEN filter to search criteria',
- default: false
- },
- filterFrom: {
- type: 'string',
- title: 'Filter by From',
- description: 'Filter emails from this address (optional)'
- },
- filterSubject: {
- type: 'string',
- title: 'Filter by Subject',
- description: 'Filter emails containing this text in subject (optional)'
- },
- filterSince: {
- type: 'string',
- title: 'Since Date',
- description: 'Filter emails since this date (DD-Mon-YYYY format, e.g., 01-Jan-2024)'
- },
- limit: {
- type: 'integer',
- title: 'Limit',
- description: 'Maximum number of emails to return',
- default: 50,
- minimum: 1,
- maximum: 1000
- },
- fetchContent: {
- type: 'boolean',
- title: 'Fetch Full Content',
- description: 'Fetch full email content including body and raw data (required for attachment extraction)',
- default: false
- },
- markAsRead: {
- type: 'boolean',
- title: 'Mark as Read',
- description: 'Mark emails as read after fetching (only applies when Fetch Full Content is enabled)',
- default: false
- }
- },
- required: ['credentialId']
- };
- const inputSchema = {
- type: 'object',
- properties: {
- criteria: {
- type: 'string',
- description: 'Override search criteria from input'
- },
- mailbox: {
- type: 'string',
- description: 'Override mailbox from input'
- }
- }
- };
- const outputSchema = {
- type: 'object',
- properties: {
- uids: {
- type: 'array',
- items: { type: 'string' },
- description: 'Array of message UIDs'
- },
- count: {
- type: 'integer',
- description: 'Number of emails found'
- },
- emails: {
- type: 'array',
- description: 'Array of email objects (if fetchContent is enabled)',
- 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' },
- raw: { type: 'string', description: 'Raw email content' },
- hasAttachments: { type: 'boolean', description: 'Whether email has attachments' }
- }
- }
- },
- mailbox: { type: 'string' },
- criteria: { type: 'string' }
- }
- };
- const outputs = [
- { name: 'main', displayName: 'Search Results', type: 'object', color: '#3b82f6' }
- ];
- /**
- * 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,
- inputSchema,
- outputSchema,
- outputs,
- async execute(config, input, context) {
- const credentialId = config.credentialId;
- const mailbox = input.mailbox || config.mailbox || 'INBOX';
- const limit = config.limit || 50;
- const fetchContent = config.fetchContent || false;
- const markAsRead = config.markAsRead || false;
- if (!credentialId) {
- throw new Error('IMAP credential is required');
- }
- // Build search criteria
- let criteria = input.criteria || config.criteria || 'ALL';
- if (config.filterUnread && criteria.indexOf('UNSEEN') === -1) {
- criteria = criteria === 'ALL' ? 'UNSEEN' : `${criteria} UNSEEN`;
- }
- if (config.filterFrom) {
- criteria += ` FROM "${config.filterFrom}"`;
- }
- if (config.filterSubject) {
- criteria += ` SUBJECT "${config.filterSubject}"`;
- }
- if (config.filterSince) {
- criteria += ` SINCE "${config.filterSince}"`;
- }
- // Perform search
- const searchResult = smartbotic.imap.search({
- credentialId,
- mailbox,
- criteria,
- limit
- });
- if (!searchResult.success) {
- throw new Error(`IMAP search failed: ${searchResult.error}`);
- }
- const result = {
- uids: searchResult.uids,
- count: searchResult.count,
- mailbox,
- criteria
- };
- // Optionally fetch content for each email
- if (fetchContent && searchResult.uids.length > 0) {
- result.emails = [];
- for (const uid of searchResult.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) {
- result.emails.push({
- 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)
- });
- }
- }
- }
- return result;
- }
- };
|