Skip to content

Commit 5b7c07c

Browse files
Merge pull request #670 from simstudioai/improvement/gmail
improvement(gmail): added gmail draft operation
2 parents e216b17 + c81f881 commit 5b7c07c

File tree

5 files changed

+143
-57
lines changed

5 files changed

+143
-57
lines changed

apps/docs/content/docs/tools/gmail.mdx

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -79,50 +79,38 @@ Send emails using Gmail
7979
| `threadId` | string |
8080
| `labelIds` | string |
8181

82-
### `gmail_read`
82+
### `gmail_draft`
8383

84-
Read emails from Gmail
84+
Draft emails using Gmail
8585

8686
#### Input
8787

8888
| Parameter | Type | Required | Description |
8989
| --------- | ---- | -------- | ----------- |
9090
| `accessToken` | string | Yes | Access token for Gmail API |
91-
| `messageId` | string | No | ID of the message to read |
92-
| `folder` | string | No | Folder/label to read emails from |
93-
| `unreadOnly` | boolean | No | Only retrieve unread messages |
94-
| `maxResults` | number | No | Maximum number of messages to retrieve \(default: 1, max: 10\) |
91+
| `to` | string | Yes | Recipient email address |
92+
| `subject` | string | Yes | Email subject |
93+
| `body` | string | Yes | Email body content |
9594

9695
#### Output
9796

9897
| Parameter | Type |
9998
| --------- | ---- |
10099
| `content` | string |
101100
| `metadata` | string |
102-
103-
### `gmail_search`
104-
105-
Search emails in Gmail
106-
107-
#### Input
108-
109-
| Parameter | Type | Required | Description |
110-
| --------- | ---- | -------- | ----------- |
111-
| `accessToken` | string | Yes | Access token for Gmail API |
112-
| `query` | string | Yes | Search query for emails |
113-
| `maxResults` | number | No | Maximum number of results to return |
114-
115-
#### Output
116-
117-
| Parameter | Type |
118-
| --------- | ---- |
119-
| `content` | string |
101+
| `message` | string |
102+
| `threadId` | string |
103+
| `labelIds` | string |
120104

121105

122106

123107
## Block Configuration
124108

125-
No configuration parameters required.
109+
### Input
110+
111+
| Parameter | Type | Required | Description |
112+
| --------- | ---- | -------- | ----------- |
113+
| `operation` | string | Yes | Operation (e.g., 'send', 'draft') |
126114

127115

128116

apps/sim/blocks/blocks/gmail.ts

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
1414
icon: GmailIcon,
1515
subBlocks: [
1616
// Operation selector
17-
// {
18-
// id: 'operation',
19-
// title: 'Operation',
20-
// type: 'dropdown',
21-
// layout: 'full',
22-
// options: [
23-
// { label: 'Send Email', id: 'send_gmail' },
24-
// // { label: 'Read Email', id: 'read_gmail' },
25-
// // { label: 'Search Emails', id: 'search_gmail' },
26-
// ],
27-
// },
17+
{
18+
id: 'operation',
19+
title: 'Operation',
20+
type: 'dropdown',
21+
layout: 'full',
22+
options: [
23+
{ label: 'Send Email', id: 'send_gmail' },
24+
// { label: 'Read Email', id: 'read_gmail' },
25+
{ label: 'Draft Email', id: 'draft_gmail' },
26+
],
27+
},
2828
// Gmail Credentials
2929
{
3030
id: 'credential',
@@ -48,23 +48,23 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
4848
type: 'short-input',
4949
layout: 'full',
5050
placeholder: 'Recipient email address',
51-
// condition: { field: 'operation', value: 'send_gmail' },
51+
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
5252
},
5353
{
5454
id: 'subject',
5555
title: 'Subject',
5656
type: 'short-input',
5757
layout: 'full',
5858
placeholder: 'Email subject',
59-
// condition: { field: 'operation', value: 'send_gmail' },
59+
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
6060
},
6161
{
6262
id: 'body',
6363
title: 'Body',
6464
type: 'long-input',
6565
layout: 'full',
6666
placeholder: 'Email content',
67-
// condition: { field: 'operation', value: 'send_gmail' },
67+
condition: { field: 'operation', value: ['send_gmail', 'draft_gmail'] },
6868
},
6969
// Read Email Fields - Add folder selector
7070
// {
@@ -130,22 +130,17 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
130130
// },
131131
],
132132
tools: {
133-
access: ['gmail_send', 'gmail_read', 'gmail_search'],
133+
access: ['gmail_send', 'gmail_draft'],
134134
config: {
135135
tool: (params) => {
136-
// Since we only have send_gmail now, we can simplify this
137-
return 'gmail_send'
138-
139-
// switch (params.operation) {
140-
// case 'send_gmail':
141-
// return 'gmail_send'
142-
// case 'read_gmail':
143-
// return 'gmail_read'
144-
// case 'search_gmail':
145-
// return 'gmail_search'
146-
// default:
147-
// throw new Error(`Invalid Gmail operation: ${params.operation}`)
148-
// }
136+
switch (params.operation) {
137+
case 'send_gmail':
138+
return 'gmail_send'
139+
case 'draft_gmail':
140+
return 'gmail_draft'
141+
default:
142+
throw new Error(`Invalid Gmail operation: ${params.operation}`)
143+
}
149144
},
150145
params: (params) => {
151146
// Pass the credential directly from the credential field
@@ -158,13 +153,13 @@ export const GmailBlock: BlockConfig<GmailToolResponse> = {
158153

159154
return {
160155
...rest,
161-
credential, // Keep the credential parameter
156+
credential,
162157
}
163158
},
164159
},
165160
},
166161
inputs: {
167-
// operation: { type: 'string', required: true },
162+
operation: { type: 'string', required: true },
168163
credential: { type: 'string', required: true },
169164
// Send operation inputs
170165
to: { type: 'string', required: false },

apps/sim/tools/gmail/draft.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import type { ToolConfig } from '../types'
2+
import type { GmailSendParams, GmailToolResponse } from './types'
3+
4+
const GMAIL_API_BASE = 'https://gmail.googleapis.com/gmail/v1/users/me'
5+
6+
export const gmailDraftTool: ToolConfig<GmailSendParams, GmailToolResponse> = {
7+
id: 'gmail_draft',
8+
name: 'Gmail Draft',
9+
description: 'Draft emails using Gmail',
10+
version: '1.0.0',
11+
12+
oauth: {
13+
required: true,
14+
provider: 'google-email',
15+
additionalScopes: ['https://www.googleapis.com/auth/gmail.compose'],
16+
},
17+
18+
params: {
19+
accessToken: {
20+
type: 'string',
21+
required: true,
22+
description: 'Access token for Gmail API',
23+
},
24+
to: {
25+
type: 'string',
26+
required: true,
27+
description: 'Recipient email address',
28+
},
29+
subject: {
30+
type: 'string',
31+
required: true,
32+
description: 'Email subject',
33+
},
34+
body: {
35+
type: 'string',
36+
required: true,
37+
description: 'Email body content',
38+
},
39+
},
40+
41+
request: {
42+
url: () => `${GMAIL_API_BASE}/drafts`,
43+
method: 'POST',
44+
headers: (params: GmailSendParams) => ({
45+
Authorization: `Bearer ${params.accessToken}`,
46+
'Content-Type': 'application/json',
47+
}),
48+
body: (params: GmailSendParams): Record<string, any> => {
49+
const email = [
50+
'Content-Type: text/plain; charset="UTF-8"',
51+
'MIME-Version: 1.0',
52+
`To: ${params.to}`,
53+
`Subject: ${params.subject}`,
54+
'',
55+
params.body,
56+
].join('\n')
57+
58+
return {
59+
message: {
60+
raw: Buffer.from(email).toString('base64url'),
61+
},
62+
}
63+
},
64+
},
65+
66+
transformResponse: async (response) => {
67+
const data = await response.json()
68+
69+
if (!response.ok) {
70+
throw new Error(data.error?.message || 'Failed to draft email')
71+
}
72+
73+
return {
74+
success: true,
75+
output: {
76+
content: 'Email drafted successfully',
77+
metadata: {
78+
id: data.id,
79+
message: {
80+
id: data.message?.id,
81+
threadId: data.message?.threadId,
82+
labelIds: data.message?.labelIds,
83+
},
84+
},
85+
},
86+
}
87+
},
88+
89+
transformError: (error) => {
90+
if (error.error?.message) {
91+
if (error.error.message.includes('invalid authentication credentials')) {
92+
return 'Invalid or expired access token. Please reauthenticate.'
93+
}
94+
if (error.error.message.includes('quota')) {
95+
return 'Gmail API quota exceeded. Please try again later.'
96+
}
97+
return error.error.message
98+
}
99+
return error.message || 'An unexpected error occurred while drafting email'
100+
},
101+
}

apps/sim/tools/gmail/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { gmailDraftTool } from './draft'
12
import { gmailReadTool } from './read'
23
import { gmailSearchTool } from './search'
34
import { gmailSendTool } from './send'
45

5-
export { gmailSendTool, gmailReadTool, gmailSearchTool }
6+
export { gmailSendTool, gmailReadTool, gmailSearchTool, gmailDraftTool }

apps/sim/tools/registry.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
githubPrTool,
2626
githubRepoInfoTool,
2727
} from './github'
28-
import { gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail'
28+
import { gmailDraftTool, gmailReadTool, gmailSearchTool, gmailSendTool } from './gmail'
2929
import { searchTool as googleSearchTool } from './google'
3030
import {
3131
googleCalendarCreateTool,
@@ -142,6 +142,7 @@ export const tools: Record<string, ToolConfig> = {
142142
gmail_send: gmailSendTool,
143143
gmail_read: gmailReadTool,
144144
gmail_search: gmailSearchTool,
145+
gmail_draft: gmailDraftTool,
145146
whatsapp_send_message: whatsappSendMessageTool,
146147
x_write: xWriteTool,
147148
x_read: xReadTool,

0 commit comments

Comments
 (0)