From 1afdeed244dab82f7147f8cf062aa9c84a3da712 Mon Sep 17 00:00:00 2001 From: Adam Gough Date: Fri, 11 Jul 2025 15:47:14 -0700 Subject: [PATCH] improvement: added gmail draft operation --- apps/sim/blocks/blocks/gmail.ts | 55 ++++++++--------- apps/sim/tools/gmail/draft.ts | 101 ++++++++++++++++++++++++++++++++ apps/sim/tools/gmail/index.ts | 3 +- apps/sim/tools/registry.ts | 3 +- 4 files changed, 130 insertions(+), 32 deletions(-) create mode 100644 apps/sim/tools/gmail/draft.ts diff --git a/apps/sim/blocks/blocks/gmail.ts b/apps/sim/blocks/blocks/gmail.ts index d7be4e604..269482735 100644 --- a/apps/sim/blocks/blocks/gmail.ts +++ b/apps/sim/blocks/blocks/gmail.ts @@ -14,17 +14,17 @@ export const GmailBlock: BlockConfig = { icon: GmailIcon, subBlocks: [ // Operation selector - // { - // id: 'operation', - // title: 'Operation', - // type: 'dropdown', - // layout: 'full', - // options: [ - // { label: 'Send Email', id: 'send_gmail' }, - // // { label: 'Read Email', id: 'read_gmail' }, - // // { label: 'Search Emails', id: 'search_gmail' }, - // ], - // }, + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + layout: 'full', + options: [ + { label: 'Send Email', id: 'send_gmail' }, + // { label: 'Read Email', id: 'read_gmail' }, + { label: 'Draft Email', id: 'draft_gmail' }, + ], + }, // Gmail Credentials { id: 'credential', @@ -48,7 +48,7 @@ export const GmailBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Recipient email address', - // condition: { field: 'operation', value: 'send_gmail' }, + condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] }, }, { id: 'subject', @@ -56,7 +56,7 @@ export const GmailBlock: BlockConfig = { type: 'short-input', layout: 'full', placeholder: 'Email subject', - // condition: { field: 'operation', value: 'send_gmail' }, + condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] }, }, { id: 'body', @@ -64,7 +64,7 @@ export const GmailBlock: BlockConfig = { type: 'long-input', layout: 'full', placeholder: 'Email content', - // condition: { field: 'operation', value: 'send_gmail' }, + condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] }, }, // Read Email Fields - Add folder selector // { @@ -130,22 +130,17 @@ export const GmailBlock: BlockConfig = { // }, ], tools: { - access: ['gmail_send', 'gmail_read', 'gmail_search'], + access: ['gmail_send', 'gmail_draft'], config: { tool: (params) => { - // Since we only have send_gmail now, we can simplify this - return 'gmail_send' - - // switch (params.operation) { - // case 'send_gmail': - // return 'gmail_send' - // case 'read_gmail': - // return 'gmail_read' - // case 'search_gmail': - // return 'gmail_search' - // default: - // throw new Error(`Invalid Gmail operation: ${params.operation}`) - // } + switch (params.operation) { + case 'send_gmail': + return 'gmail_send' + case 'draft_gmail': + return 'gmail_draft' + default: + throw new Error(`Invalid Gmail operation: ${params.operation}`) + } }, params: (params) => { // Pass the credential directly from the credential field @@ -158,13 +153,13 @@ export const GmailBlock: BlockConfig = { return { ...rest, - credential, // Keep the credential parameter + credential, } }, }, }, inputs: { - // operation: { type: 'string', required: true }, + operation: { type: 'string', required: true }, credential: { type: 'string', required: true }, // Send operation inputs to: { type: 'string', required: false }, diff --git a/apps/sim/tools/gmail/draft.ts b/apps/sim/tools/gmail/draft.ts new file mode 100644 index 000000000..36d38899c --- /dev/null +++ b/apps/sim/tools/gmail/draft.ts @@ -0,0 +1,101 @@ +import type { ToolConfig } from '../types' +import type { GmailSendParams, GmailToolResponse } from './types' + +const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me' + +export const gmailDraftTool: ToolConfig = { + id: 'gmail_draft', + name: 'Gmail Draft', + description: 'Draft emails using Gmail', + version: '1.0.0', + + oauth: { + required: true, + provider: 'google-email', + additionalScopes: ['https://www.googleapis.com/auth/gmail.compose'], + }, + + params: { + accessToken: { + type: 'string', + required: true, + description: 'Access token for Gmail API', + }, + to: { + type: 'string', + required: true, + description: 'Recipient email address', + }, + subject: { + type: 'string', + required: true, + description: 'Email subject', + }, + body: { + type: 'string', + required: true, + description: 'Email body content', + }, + }, + + request: { + url: () => `${GMAIL_API_BASE}/drafts`, + method: 'POST', + headers: (params: GmailSendParams) => ({ + Authorization: `Bearer ${params.accessToken}`, + 'Content-Type': 'application/json', + }), + body: (params: GmailSendParams): Record => { + const email = [ + 'Content-Type: text/plain; charset="UTF-8"', + 'MIME-Version: 1.0', + `To: ${params.to}`, + `Subject: ${params.subject}`, + '', + params.body, + ].join('\n') + + return { + message: { + raw: Buffer.from(email).toString('base64url'), + }, + } + }, + }, + + transformResponse: async (response) => { + const data = await response.json() + + if (!response.ok) { + throw new Error(data.error?.message || 'Failed to draft email') + } + + return { + success: true, + output: { + content: 'Email drafted successfully', + metadata: { + id: data.id, + message: { + id: data.message?.id, + threadId: data.message?.threadId, + labelIds: data.message?.labelIds, + }, + }, + }, + } + }, + + transformError: (error) => { + if (error.error?.message) { + if (error.error.message.includes('invalid authentication credentials')) { + return 'Invalid or expired access token. Please reauthenticate.' + } + if (error.error.message.includes('quota')) { + return 'Gmail API quota exceeded. Please try again later.' + } + return error.error.message + } + return error.message || 'An unexpected error occurred while drafting email' + }, +} diff --git a/apps/sim/tools/gmail/index.ts b/apps/sim/tools/gmail/index.ts index 5f2c35861..3558d482c 100644 --- a/apps/sim/tools/gmail/index.ts +++ b/apps/sim/tools/gmail/index.ts @@ -1,5 +1,6 @@ +import { gmailDraftTool } from './draft' import { gmailReadTool } from './read' import { gmailSearchTool } from './search' import { gmailSendTool } from './send' -export { gmailSendTool, gmailReadTool, gmailSearchTool } +export { gmailSendTool, gmailReadTool, gmailSearchTool, gmailDraftTool } diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index fbaeb2dbb..258c707df 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -25,7 +25,7 @@ import { githubPrTool, githubRepoInfoTool, } from './github' -import { gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail' +import { gmailDraftTool, gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail' import { searchTool as googleSearchTool } from './google' import { googleCalendarCreateTool, @@ -142,6 +142,7 @@ export const tools: Record = { gmail_send: gmailSendTool, gmail_read: gmailReadTool, gmail_search: gmailSearchTool, + gmail_draft: gmailDraftTool, whatsapp_send_message: whatsappSendMessageTool, x_write: xWriteTool, x_read: xReadTool,