import { createLogger } from '@sim/logger' import { GmailIcon } from '@/components/icons' import { requestJson } from '@/lib/api/client/request' import { gmailLabelsSelectorContract } from '@/lib/api/contracts/selectors/google' import { useSubBlockStore } from '@/stores/workflows/subblock/store' import type { TriggerConfig } from '@/triggers/types' const logger = createLogger('GmailPollingTrigger') export const gmailPollingTrigger: TriggerConfig = { id: 'gmail_poller', name: 'Gmail Email Trigger', provider: 'gmail', description: 'Triggers when new emails are received in Gmail (requires Gmail credentials)', version: '1.0.0', icon: GmailIcon, polling: true, subBlocks: [ { id: 'triggerCredentials', title: 'Credentials', type: 'oauth-input', description: 'This trigger requires google email credentials to access your account.', serviceId: 'gmail', requiredScopes: [], required: true, mode: 'trigger', }, { id: 'labelIds', title: 'Gmail Labels to Monitor', type: 'dropdown', multiSelect: true, placeholder: 'Select Gmail labels to monitor for new emails', description: 'Choose which Gmail labels to monitor. Leave empty to monitor all emails.', required: false, options: [], // Will be populated dynamically from user's Gmail labels fetchOptions: async (blockId: string) => { const credentialId = useSubBlockStore.getState().getValue(blockId, 'triggerCredentials') as | string | null if (!credentialId) { // Return a sentinel to prevent infinite retry loops when credential is missing throw new Error('No Gmail credential selected') } try { const data = await requestJson(gmailLabelsSelectorContract, { query: { credentialId }, }) return data.labels.map((label) => ({ id: label.id, label: label.name, })) } catch (error) { logger.error('Error fetching Gmail labels:', error) throw error } }, dependsOn: ['triggerCredentials'], mode: 'trigger', }, { id: 'labelFilterBehavior', title: 'Label Filter Behavior', type: 'dropdown', options: [ { label: 'INCLUDE', id: 'INCLUDE' }, { label: 'EXCLUDE', id: 'EXCLUDE' }, ], defaultValue: 'INCLUDE', description: 'Include only emails with selected labels, or exclude emails with selected labels', required: true, mode: 'trigger', }, { id: 'searchQuery', title: 'Gmail Search Query', type: 'short-input', placeholder: 'subject:report OR from:important@example.com', description: 'Optional Gmail search query to filter emails. Use the same format as Gmail search box (e.g., "subject:invoice", "from:boss@company.com", "has:attachment"). Leave empty to search all emails.', required: false, mode: 'trigger', wandConfig: { enabled: true, maintainHistory: true, prompt: `You are an expert in Gmail search syntax. Generate Gmail search queries based on user descriptions. Gmail search operators include: - from: / to: / cc: / bcc: - Filter by sender/recipient - subject: - Search in subject line - has:attachment - Emails with attachments - filename: - Search attachment filenames - is:unread / is:read / is:starred - after: / before: / older: / newer: - Date filters (YYYY/MM/DD) - label: - Filter by label - in:inbox / in:spam / in:trash - larger: / smaller: - Size filters (e.g., 10M, 1K) - OR / AND / - (NOT) - Boolean operators - "exact phrase" - Exact match - ( ) - Grouping Current query: {context} Return ONLY the Gmail search query, no explanations or markdown.`, placeholder: 'Describe what emails you want to filter...', }, }, { id: 'markAsRead', title: 'Mark as Read', type: 'switch', defaultValue: false, description: 'Automatically mark emails as read after processing', required: false, mode: 'trigger', }, { id: 'includeAttachments', title: 'Include Attachments', type: 'switch', defaultValue: false, description: 'Download and include email attachments in the trigger payload', required: false, mode: 'trigger', }, { id: 'triggerInstructions', title: 'Setup Instructions', hideFromPreview: true, type: 'text', defaultValue: [ 'Connect your Gmail account using OAuth credentials', 'Configure which Gmail labels to monitor (optional)', 'The system will automatically check for new emails and trigger your workflow', ] .map( (instruction, index) => `