@divyacon/piece-gmail 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/LICENSE.MIT-AP +24 -0
  2. package/README.md +8 -2
  3. package/dist/index.d.ts +6 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +111 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/actions/ai-get-message-action.d.ts +7 -0
  8. package/dist/lib/actions/ai-get-message-action.d.ts.map +1 -0
  9. package/dist/lib/actions/ai-get-message-action.js +24 -0
  10. package/dist/lib/actions/ai-get-message-action.js.map +1 -0
  11. package/dist/lib/actions/ai-reply-to-thread-action.d.ts +16 -0
  12. package/dist/lib/actions/ai-reply-to-thread-action.d.ts.map +1 -0
  13. package/dist/lib/actions/ai-reply-to-thread-action.js +24 -0
  14. package/dist/lib/actions/ai-reply-to-thread-action.js.map +1 -0
  15. package/dist/lib/actions/ai-search-email-action.d.ts +21 -0
  16. package/dist/lib/actions/ai-search-email-action.d.ts.map +1 -0
  17. package/dist/lib/actions/ai-search-email-action.js +24 -0
  18. package/dist/lib/actions/ai-search-email-action.js.map +1 -0
  19. package/dist/lib/actions/ai-send-email-action.d.ts +18 -0
  20. package/dist/lib/actions/ai-send-email-action.d.ts.map +1 -0
  21. package/dist/lib/actions/ai-send-email-action.js +24 -0
  22. package/dist/lib/actions/ai-send-email-action.js.map +1 -0
  23. package/dist/lib/actions/create-draft-action.d.ts +16 -0
  24. package/dist/lib/actions/create-draft-action.d.ts.map +1 -0
  25. package/dist/lib/actions/create-draft-action.js +137 -0
  26. package/dist/lib/actions/create-draft-action.js.map +1 -0
  27. package/dist/lib/actions/create-draft-reply-action.d.ts +17 -0
  28. package/dist/lib/actions/create-draft-reply-action.d.ts.map +1 -0
  29. package/dist/lib/actions/create-draft-reply-action.js +269 -0
  30. package/dist/lib/actions/create-draft-reply-action.js.map +1 -0
  31. package/dist/lib/actions/delete-draft-action.d.ts +7 -0
  32. package/dist/lib/actions/delete-draft-action.d.ts.map +1 -0
  33. package/dist/lib/actions/delete-draft-action.js +62 -0
  34. package/dist/lib/actions/delete-draft-action.js.map +1 -0
  35. package/dist/lib/actions/forward-message-action.d.ts +12 -0
  36. package/dist/lib/actions/forward-message-action.d.ts.map +1 -0
  37. package/dist/lib/actions/forward-message-action.js +139 -0
  38. package/dist/lib/actions/forward-message-action.js.map +1 -0
  39. package/dist/lib/actions/get-attachment-action.d.ts +9 -0
  40. package/dist/lib/actions/get-attachment-action.d.ts.map +1 -0
  41. package/dist/lib/actions/get-attachment-action.js +80 -0
  42. package/dist/lib/actions/get-attachment-action.js.map +1 -0
  43. package/dist/lib/actions/get-draft-action.d.ts +7 -0
  44. package/dist/lib/actions/get-draft-action.d.ts.map +1 -0
  45. package/dist/lib/actions/get-draft-action.js +56 -0
  46. package/dist/lib/actions/get-draft-action.js.map +1 -0
  47. package/dist/lib/actions/get-label-action.d.ts +7 -0
  48. package/dist/lib/actions/get-label-action.d.ts.map +1 -0
  49. package/dist/lib/actions/get-label-action.js +55 -0
  50. package/dist/lib/actions/get-label-action.js.map +1 -0
  51. package/dist/lib/actions/get-mail-action.d.ts +7 -0
  52. package/dist/lib/actions/get-mail-action.d.ts.map +1 -0
  53. package/dist/lib/actions/get-mail-action.js +44 -0
  54. package/dist/lib/actions/get-mail-action.js.map +1 -0
  55. package/dist/lib/actions/get-profile-action.d.ts +5 -0
  56. package/dist/lib/actions/get-profile-action.d.ts.map +1 -0
  57. package/dist/lib/actions/get-profile-action.js +43 -0
  58. package/dist/lib/actions/get-profile-action.js.map +1 -0
  59. package/dist/lib/actions/get-thread-action.d.ts +9 -0
  60. package/dist/lib/actions/get-thread-action.d.ts.map +1 -0
  61. package/dist/lib/actions/get-thread-action.js +53 -0
  62. package/dist/lib/actions/get-thread-action.js.map +1 -0
  63. package/dist/lib/actions/list-drafts-action.d.ts +8 -0
  64. package/dist/lib/actions/list-drafts-action.d.ts.map +1 -0
  65. package/dist/lib/actions/list-drafts-action.js +62 -0
  66. package/dist/lib/actions/list-drafts-action.js.map +1 -0
  67. package/dist/lib/actions/list-history-action.d.ts +9 -0
  68. package/dist/lib/actions/list-history-action.d.ts.map +1 -0
  69. package/dist/lib/actions/list-history-action.js +74 -0
  70. package/dist/lib/actions/list-history-action.js.map +1 -0
  71. package/dist/lib/actions/list-labels-action.d.ts +5 -0
  72. package/dist/lib/actions/list-labels-action.d.ts.map +1 -0
  73. package/dist/lib/actions/list-labels-action.js +47 -0
  74. package/dist/lib/actions/list-labels-action.js.map +1 -0
  75. package/dist/lib/actions/list-threads-action.d.ts +10 -0
  76. package/dist/lib/actions/list-threads-action.d.ts.map +1 -0
  77. package/dist/lib/actions/list-threads-action.js +74 -0
  78. package/dist/lib/actions/list-threads-action.js.map +1 -0
  79. package/dist/lib/actions/reply-to-email-action.d.ts +16 -0
  80. package/dist/lib/actions/reply-to-email-action.d.ts.map +1 -0
  81. package/dist/lib/actions/reply-to-email-action.js +202 -0
  82. package/dist/lib/actions/reply-to-email-action.js.map +1 -0
  83. package/dist/lib/actions/request-approval-in-email.d.ts +15 -0
  84. package/dist/lib/actions/request-approval-in-email.d.ts.map +1 -0
  85. package/dist/lib/actions/request-approval-in-email.js +174 -0
  86. package/dist/lib/actions/request-approval-in-email.js.map +1 -0
  87. package/dist/lib/actions/search-email-action.d.ts +22 -0
  88. package/dist/lib/actions/search-email-action.d.ts.map +1 -0
  89. package/dist/lib/actions/search-email-action.js +179 -0
  90. package/dist/lib/actions/search-email-action.js.map +1 -0
  91. package/dist/lib/actions/send-draft-action.d.ts +7 -0
  92. package/dist/lib/actions/send-draft-action.d.ts.map +1 -0
  93. package/dist/lib/actions/send-draft-action.js +57 -0
  94. package/dist/lib/actions/send-draft-action.js.map +1 -0
  95. package/dist/lib/actions/send-email-action.d.ts +18 -0
  96. package/dist/lib/actions/send-email-action.d.ts.map +1 -0
  97. package/dist/lib/actions/send-email-action.js +196 -0
  98. package/dist/lib/actions/send-email-action.js.map +1 -0
  99. package/dist/lib/actions/stop-watch-action.d.ts +5 -0
  100. package/dist/lib/actions/stop-watch-action.d.ts.map +1 -0
  101. package/dist/lib/actions/stop-watch-action.js +41 -0
  102. package/dist/lib/actions/stop-watch-action.js.map +1 -0
  103. package/dist/lib/actions/update-draft-action.d.ts +17 -0
  104. package/dist/lib/actions/update-draft-action.d.ts.map +1 -0
  105. package/dist/lib/actions/update-draft-action.js +193 -0
  106. package/dist/lib/actions/update-draft-action.js.map +1 -0
  107. package/dist/lib/auth.d.ts +12 -0
  108. package/dist/lib/auth.d.ts.map +1 -0
  109. package/dist/lib/auth.js +108 -0
  110. package/dist/lib/auth.js.map +1 -0
  111. package/dist/lib/common/data.d.ts +132 -0
  112. package/dist/lib/common/data.d.ts.map +1 -0
  113. package/dist/lib/common/data.js +209 -0
  114. package/dist/lib/common/data.js.map +1 -0
  115. package/dist/lib/common/mime.d.ts +29 -0
  116. package/dist/lib/common/mime.d.ts.map +1 -0
  117. package/dist/lib/common/mime.js +59 -0
  118. package/dist/lib/common/mime.js.map +1 -0
  119. package/dist/lib/common/models.d.ts +86 -0
  120. package/dist/lib/common/models.d.ts.map +1 -0
  121. package/dist/lib/common/models.js +26 -0
  122. package/dist/lib/common/models.js.map +1 -0
  123. package/dist/lib/common/props.d.ts +28 -0
  124. package/dist/lib/common/props.d.ts.map +1 -0
  125. package/dist/lib/common/props.js +231 -0
  126. package/dist/lib/common/props.js.map +1 -0
  127. package/dist/lib/output-schemas.d.ts +29 -0
  128. package/dist/lib/output-schemas.d.ts.map +1 -0
  129. package/dist/lib/output-schemas.js +1029 -0
  130. package/dist/lib/output-schemas.js.map +1 -0
  131. package/dist/lib/triggers/new-attachment.d.ts +144 -0
  132. package/dist/lib/triggers/new-attachment.d.ts.map +1 -0
  133. package/dist/lib/triggers/new-attachment.js +150 -0
  134. package/dist/lib/triggers/new-attachment.js.map +1 -0
  135. package/dist/lib/triggers/new-conversation.d.ts +59 -0
  136. package/dist/lib/triggers/new-conversation.d.ts.map +1 -0
  137. package/dist/lib/triggers/new-conversation.js +308 -0
  138. package/dist/lib/triggers/new-conversation.js.map +1 -0
  139. package/dist/lib/triggers/new-email.d.ts +52 -0
  140. package/dist/lib/triggers/new-email.d.ts.map +1 -0
  141. package/dist/lib/triggers/new-email.js +135 -0
  142. package/dist/lib/triggers/new-email.js.map +1 -0
  143. package/dist/lib/triggers/new-label.d.ts +15 -0
  144. package/dist/lib/triggers/new-label.d.ts.map +1 -0
  145. package/dist/lib/triggers/new-label.js +82 -0
  146. package/dist/lib/triggers/new-label.js.map +1 -0
  147. package/dist/lib/triggers/new-labeled-email.d.ts +35 -0
  148. package/dist/lib/triggers/new-labeled-email.d.ts.map +1 -0
  149. package/dist/lib/triggers/new-labeled-email.js +147 -0
  150. package/dist/lib/triggers/new-labeled-email.js.map +1 -0
  151. package/package.json +53 -3
  152. package/src/index.ts +107 -0
  153. package/src/lib/actions/ai-get-message-action.ts +20 -0
  154. package/src/lib/actions/ai-reply-to-thread-action.ts +20 -0
  155. package/src/lib/actions/ai-search-email-action.ts +21 -0
  156. package/src/lib/actions/ai-send-email-action.ts +20 -0
  157. package/src/lib/actions/create-draft-action.ts +148 -0
  158. package/src/lib/actions/create-draft-reply-action.ts +314 -0
  159. package/src/lib/actions/delete-draft-action.ts +57 -0
  160. package/src/lib/actions/forward-message-action.ts +159 -0
  161. package/src/lib/actions/get-attachment-action.ts +83 -0
  162. package/src/lib/actions/get-draft-action.ts +53 -0
  163. package/src/lib/actions/get-label-action.ts +53 -0
  164. package/src/lib/actions/get-mail-action.ts +52 -0
  165. package/src/lib/actions/get-profile-action.ts +39 -0
  166. package/src/lib/actions/get-thread-action.ts +47 -0
  167. package/src/lib/actions/list-drafts-action.ts +72 -0
  168. package/src/lib/actions/list-history-action.ts +83 -0
  169. package/src/lib/actions/list-labels-action.ts +43 -0
  170. package/src/lib/actions/list-threads-action.ts +89 -0
  171. package/src/lib/actions/reply-to-email-action.ts +228 -0
  172. package/src/lib/actions/request-approval-in-email.ts +197 -0
  173. package/src/lib/actions/search-email-action.ts +219 -0
  174. package/src/lib/actions/send-draft-action.ts +54 -0
  175. package/src/lib/actions/send-email-action.ts +213 -0
  176. package/src/lib/actions/stop-watch-action.ts +37 -0
  177. package/src/lib/actions/update-draft-action.ts +211 -0
  178. package/src/lib/auth.ts +116 -0
  179. package/src/lib/common/data.ts +268 -0
  180. package/src/lib/common/mime.ts +82 -0
  181. package/src/lib/common/models.ts +91 -0
  182. package/src/lib/common/props.ts +262 -0
  183. package/src/lib/output-schemas.ts +1061 -0
  184. package/src/lib/triggers/new-attachment.ts +203 -0
  185. package/src/lib/triggers/new-conversation.ts +413 -0
  186. package/src/lib/triggers/new-email.ts +173 -0
  187. package/src/lib/triggers/new-label.ts +83 -0
  188. package/src/lib/triggers/new-labeled-email.ts +197 -0
@@ -0,0 +1,268 @@
1
+ import { FilesService } from '@agnocon/pieces-framework';
2
+ import {
3
+ GmailLabel,
4
+ GmailMessage,
5
+ GmailThread,
6
+ GmailMessageFormat,
7
+ GmailMessageResponse as GmailMessageList,
8
+ } from './models';
9
+ import {
10
+ AuthenticationType,
11
+ httpClient,
12
+ HttpMethod,
13
+ } from '@agnocon/pieces-common';
14
+ import { Attachment, ParsedMail, simpleParser } from 'mailparser';
15
+ import { GmailAuthValue, getAccessToken } from '../auth';
16
+
17
+ interface SearchMailProps {
18
+ access_token: string;
19
+ from: string;
20
+ to: string;
21
+ subject?: string;
22
+ label: GmailLabel;
23
+ category: string;
24
+ after?: number;
25
+ before?: number;
26
+ max_results?: number;
27
+ page_token?: string;
28
+ }
29
+
30
+ interface GetMailProps {
31
+ access_token: string;
32
+ message_id?: string;
33
+ thread_id?: string;
34
+ format: GmailMessageFormat;
35
+ }
36
+
37
+ export const GmailRequests = {
38
+ getMail: async ({ access_token, format, message_id }: GetMailProps) => {
39
+ const response = await httpClient.sendRequest<GmailMessage>({
40
+ method: HttpMethod.GET,
41
+ url: `https://gmail.googleapis.com/gmail/v1/users/me/messages/${message_id}`,
42
+ authentication: {
43
+ type: AuthenticationType.BEARER_TOKEN,
44
+ token: access_token,
45
+ },
46
+ queryParams: {
47
+ format,
48
+ },
49
+ });
50
+ let bodyHtml = '';
51
+ let bodyPlain = '';
52
+ const payload = response.body.payload;
53
+ let bodyParts = payload.parts || [];
54
+ const headers = payload.headers.reduce(
55
+ (obj: { [key: string]: string }, header) => {
56
+ obj[header.name.toLowerCase()] = header.value;
57
+ return obj;
58
+ },
59
+ {}
60
+ );
61
+ const alternateBodyPart = bodyParts.find((part) =>
62
+ part.mimeType.startsWith('multipart/alternative')
63
+ );
64
+ if (alternateBodyPart && alternateBodyPart?.parts) {
65
+ bodyParts = alternateBodyPart.parts;
66
+ }
67
+ const subject = headers['subject'] || '';
68
+ // Determine the content type of the message body
69
+ const contentType = headers['content-type'] || 'text/plain';
70
+ const isMultipart = contentType.startsWith('multipart/');
71
+
72
+ if (isMultipart) {
73
+ // If the message is multipart, extract the plain text and HTML parts
74
+ const textPart = bodyParts.find((part) => part.mimeType === 'text/plain');
75
+ const htmlPart = bodyParts.find((part) => part.mimeType === 'text/html');
76
+
77
+ // If the message is an "alternative" multipart, use the plain text part if it exists
78
+ const preferredPart = textPart || htmlPart;
79
+ bodyHtml = htmlPart ? decodeBase64(htmlPart.body.data) : '';
80
+ bodyPlain = preferredPart ? decodeBase64(preferredPart.body.data) : '';
81
+ } else {
82
+ // If the message is not multipart, use the body as-is
83
+ bodyPlain = decodeBase64(payload.body.data);
84
+ bodyHtml = '';
85
+ }
86
+
87
+ return {
88
+ subject: subject,
89
+ body_html: bodyHtml,
90
+ body_plain: bodyPlain,
91
+ ...response.body,
92
+ };
93
+ },
94
+ getThread: async ({ access_token, format, thread_id }: GetMailProps) => {
95
+ const response = await httpClient.sendRequest<GmailThread>({
96
+ method: HttpMethod.GET,
97
+ url: `https://gmail.googleapis.com/gmail/v1/users/me/threads/${thread_id}`,
98
+ authentication: {
99
+ type: AuthenticationType.BEARER_TOKEN,
100
+ token: access_token,
101
+ },
102
+ queryParams: {
103
+ format,
104
+ },
105
+ });
106
+
107
+ return response.body;
108
+ },
109
+ getLabels: async (authentication: GmailAuthValue) => {
110
+ return await httpClient.sendRequest<{ labels: GmailLabel[] }>({
111
+ method: HttpMethod.GET,
112
+ url: `https://gmail.googleapis.com/gmail/v1/users/me/labels`,
113
+ authentication: {
114
+ type: AuthenticationType.BEARER_TOKEN,
115
+ token: await getAccessToken(authentication),
116
+ },
117
+ });
118
+ },
119
+ searchMail: async ({
120
+ access_token,
121
+ max_results = 1,
122
+ page_token: pageToken,
123
+ ...mail
124
+ }: SearchMailProps) => {
125
+ const query = [];
126
+
127
+ if (mail.from) query.push(`from:(${mail.from})`);
128
+ if (mail.to) query.push(`to:(${mail.to})`);
129
+ if (mail.subject) query.push(`subject:(${mail.subject})`);
130
+ if (mail.label) query.push(`label:${mail.label.name}`);
131
+ if (mail.category) query.push(`category:${mail.category}`);
132
+ if (mail.after != null && mail.after > 0) query.push(`after:${mail.after}`);
133
+ if (mail.before != null) query.push(`before:${mail.before}`);
134
+
135
+ const response = await httpClient.sendRequest<GmailMessageList>({
136
+ method: HttpMethod.GET,
137
+ url: `https://www.googleapis.com/gmail/v1/users/me/messages`,
138
+ authentication: {
139
+ type: AuthenticationType.BEARER_TOKEN,
140
+ token: access_token,
141
+ },
142
+ queryParams: {
143
+ q: query.join(' '),
144
+ maxResults: `${max_results}`,
145
+ ...(pageToken ? { pageToken } : {}),
146
+ },
147
+ });
148
+
149
+ if (response.body.messages) {
150
+ const messages = await Promise.all(
151
+ response.body.messages.map(
152
+ async (message: { id: string; threadId: string }) => {
153
+ const mail = await GmailRequests.getMail({
154
+ access_token,
155
+ message_id: message.id,
156
+ format: GmailMessageFormat.FULL,
157
+ });
158
+ const thread = await GmailRequests.getThread({
159
+ access_token,
160
+ thread_id: message.threadId,
161
+ format: GmailMessageFormat.FULL,
162
+ });
163
+
164
+ return {
165
+ message: mail,
166
+ thread,
167
+ };
168
+ }
169
+ )
170
+ );
171
+
172
+ return {
173
+ messages,
174
+ resultSizeEstimate: response.body.resultSizeEstimate,
175
+ ...(response?.body.nextPageToken
176
+ ? { nextPageToken: response.body.nextPageToken }
177
+ : {}),
178
+ };
179
+ }
180
+
181
+ return response.body;
182
+ },
183
+ getRecentMessages: async (
184
+ authentication: GmailAuthValue,
185
+ maxResults = 20
186
+ ) => {
187
+ return await httpClient.sendRequest<GmailMessageList>({
188
+ method: HttpMethod.GET,
189
+ url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages',
190
+ authentication: {
191
+ type: AuthenticationType.BEARER_TOKEN,
192
+ token: await getAccessToken(authentication),
193
+ },
194
+ queryParams: {
195
+ maxResults: maxResults.toString(),
196
+ q: 'in:inbox OR in:sent', // Get recent messages from inbox and sent
197
+ },
198
+ });
199
+ },
200
+ getRecentThreads: async (authentication: GmailAuthValue, maxResults = 15) => {
201
+ return await httpClient.sendRequest<{
202
+ threads: { id: string; snippet?: string }[];
203
+ }>({
204
+ method: HttpMethod.GET,
205
+ url: 'https://gmail.googleapis.com/gmail/v1/users/me/threads',
206
+ authentication: {
207
+ type: AuthenticationType.BEARER_TOKEN,
208
+ token: await getAccessToken(authentication),
209
+ },
210
+ queryParams: {
211
+ maxResults: maxResults.toString(),
212
+ q: 'in:inbox OR in:sent', // Get recent threads from inbox and sent
213
+ },
214
+ });
215
+ },
216
+ };
217
+
218
+ function decodeBase64(data: any) {
219
+ return Buffer.from(data, 'base64').toString();
220
+ }
221
+
222
+ export async function parseStream(stream: any) {
223
+ return new Promise<ParsedMail>((resolve, reject) => {
224
+ simpleParser(stream, (err, parsed) => {
225
+ if (err) {
226
+ reject(err);
227
+ } else {
228
+ resolve(parsed);
229
+ }
230
+ });
231
+ });
232
+ }
233
+
234
+ export async function convertAttachment(
235
+ attachments: Attachment[],
236
+ files: FilesService
237
+ ) {
238
+ const promises = attachments.map(async (attachment) => {
239
+ try {
240
+ const fileName = attachment.filename ?? `attachment-${Date.now()}`;
241
+ return {
242
+ fileName,
243
+ mimeType: attachment.contentType,
244
+ size: attachment.size,
245
+ data: await files.write({
246
+ fileName: fileName,
247
+ data: attachment.content,
248
+ }),
249
+ };
250
+ } catch (error) {
251
+ console.error(
252
+ `Failed to process attachment: ${attachment.filename}`,
253
+ error
254
+ );
255
+ return null;
256
+ }
257
+ });
258
+ const results = await Promise.all(promises);
259
+ return results.filter((result) => result !== null);
260
+ }
261
+
262
+ export function getFirstFiveOrAll(array: unknown[]) {
263
+ if (array.length <= 5) {
264
+ return array;
265
+ } else {
266
+ return array.slice(0, 5);
267
+ }
268
+ }
@@ -0,0 +1,82 @@
1
+ import mime from 'mime-types';
2
+ import MailComposer from 'nodemailer/lib/mail-composer';
3
+ import Mail, { Attachment } from 'nodemailer/lib/mailer';
4
+ import { ApFile } from '@agnocon/pieces-framework';
5
+
6
+ export interface RawMimeAttachment {
7
+ filename?: string;
8
+ content: Buffer;
9
+ contentType?: string;
10
+ }
11
+
12
+ interface BuildRawMessageProps {
13
+ to?: string[];
14
+ cc?: string[];
15
+ bcc?: string[];
16
+ from?: string;
17
+ subject: string;
18
+ bodyType: 'plain_text' | 'html';
19
+ body: string;
20
+ headers?: { key: string; value: string }[];
21
+ attachments?: ({ file: ApFile; name?: string } | RawMimeAttachment)[];
22
+ }
23
+
24
+ function isFileAttachment(
25
+ attachment: { file: ApFile; name?: string } | RawMimeAttachment
26
+ ): attachment is { file: ApFile; name?: string } {
27
+ return 'file' in attachment;
28
+ }
29
+
30
+ async function buildRawMessage(props: BuildRawMessageProps): Promise<string> {
31
+ const subjectBase64 = Buffer.from(props.subject).toString('base64');
32
+ const mailOptions: Mail.Options = {
33
+ to: props.to && props.to.length > 0 ? props.to.join(', ') : undefined,
34
+ cc: props.cc && props.cc.length > 0 ? props.cc.join(', ') : undefined,
35
+ bcc: props.bcc && props.bcc.length > 0 ? props.bcc.join(', ') : undefined,
36
+ from: props.from,
37
+ subject: `=?UTF-8?B?${subjectBase64}?=`,
38
+ text: props.bodyType === 'plain_text' ? props.body : undefined,
39
+ html: props.bodyType === 'html' ? props.body : undefined,
40
+ attachments: [],
41
+ headers: props.headers,
42
+ };
43
+
44
+ if (props.attachments && props.attachments.length > 0) {
45
+ const attachmentOption: Attachment[] = props.attachments.map(
46
+ (attachment) => {
47
+ if (isFileAttachment(attachment)) {
48
+ const { file, name } = attachment;
49
+ const lookupResult = mime.lookup(
50
+ file.extension ? file.extension : ''
51
+ );
52
+ return {
53
+ filename: name ?? file.filename,
54
+ content: file?.base64,
55
+ contentType: lookupResult ? lookupResult : undefined,
56
+ encoding: 'base64',
57
+ };
58
+ }
59
+ return {
60
+ filename: attachment.filename,
61
+ content: attachment.content,
62
+ contentType: attachment.contentType,
63
+ };
64
+ }
65
+ );
66
+ mailOptions.attachments = attachmentOption;
67
+ }
68
+
69
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
+ const mail: any = new MailComposer(mailOptions).compile();
71
+ mail.keepBcc = true;
72
+ const mailBody = await mail.build();
73
+
74
+ return Buffer.from(mailBody)
75
+ .toString('base64')
76
+ .replace(/\+/g, '-')
77
+ .replace(/\//g, '_');
78
+ }
79
+
80
+ export const GmailMime = {
81
+ buildRawMessage,
82
+ };
@@ -0,0 +1,91 @@
1
+ export interface GmailLabel {
2
+ id: string;
3
+ name: string;
4
+ messageListVisibility: GmailMessageListVisibility;
5
+ labelListVisibility: GmailLabelListVisibility;
6
+ type: GmailLabelType;
7
+ messagesTotal: number;
8
+ messagesUnread: number;
9
+ threadsTotal: number;
10
+ threadsUnread: number;
11
+ color: {
12
+ textColor: string;
13
+ backgroundColor: string;
14
+ };
15
+ }
16
+
17
+ export interface GmailMessage {
18
+ id: string;
19
+ threadId: string;
20
+ labelIds: [string];
21
+ snippet: string;
22
+ historyId: string;
23
+ internalDate: number;
24
+ payload: {
25
+ partId: string;
26
+ mimeType: string;
27
+ filename: string;
28
+ headers: {
29
+ name: string;
30
+ value: string;
31
+ }[];
32
+ body: {
33
+ data: any;
34
+ size: number;
35
+ };
36
+ parts: {
37
+ parts: any[];
38
+ partId: string;
39
+ mimeType: string;
40
+ filename: string;
41
+ headers: {
42
+ name: string;
43
+ value: string;
44
+ }[];
45
+ body: {
46
+ size: number;
47
+ data: string;
48
+ };
49
+ }[];
50
+ };
51
+ sizeEstimate: number;
52
+ raw: string;
53
+ }
54
+
55
+ export interface GmailThread {
56
+ id: string;
57
+ snippet: string;
58
+ historyId: string;
59
+ messages: GmailMessage[];
60
+ }
61
+
62
+ export interface GmailMessageResponse {
63
+ messages: {
64
+ id: string;
65
+ threadId: string;
66
+ }[];
67
+ nextPageToken?: string;
68
+ resultSizeEstimate: number;
69
+ }
70
+
71
+ enum GmailMessageListVisibility {
72
+ SHOW = 'show',
73
+ HIDE = 'hide',
74
+ }
75
+
76
+ enum GmailLabelListVisibility {
77
+ SHOW = 'show',
78
+ HIDE = 'hide',
79
+ }
80
+
81
+ enum GmailLabelType {
82
+ SYSTEM = 'system',
83
+ USER = 'user',
84
+ }
85
+
86
+ export enum GmailMessageFormat {
87
+ MINIMAL = 'minimal',
88
+ FULL = 'full',
89
+ RAW = 'raw',
90
+ METADATA = 'metadata',
91
+ }
@@ -0,0 +1,262 @@
1
+ import { Property } from '@agnocon/pieces-framework';
2
+ import { GmailRequests } from './data';
3
+ import { GmailLabel } from './models';
4
+ import { gmailAuth, createGoogleClient, GmailAuthValue } from '../auth';
5
+ import { gmail as googleGmail } from '@googleapis/gmail';
6
+
7
+ export const GmailProps = {
8
+ from: Property.ShortText({
9
+ displayName: 'Email sender',
10
+ description:
11
+ 'Optional filteration, leave empty to filter based on the email sender',
12
+ required: false,
13
+ defaultValue: '',
14
+ }),
15
+ to: Property.ShortText({
16
+ displayName: 'Email recipient',
17
+ description:
18
+ 'Optional filteration, leave empty to filter based on the email recipient',
19
+ required: false,
20
+ defaultValue: '',
21
+ }),
22
+ subject: Property.ShortText({
23
+ displayName: 'Email subject',
24
+ description: 'The email subject',
25
+ required: false,
26
+ defaultValue: '',
27
+ }),
28
+ category: Property.StaticDropdown({
29
+ displayName: 'Category',
30
+ description:
31
+ 'Optional filteration, leave unselected to filter based on the email category',
32
+ required: false,
33
+ options: {
34
+ disabled: false,
35
+ options: [
36
+ { label: 'Primary', value: 'primary' },
37
+ { label: 'Social', value: 'social' },
38
+ { label: 'Promotions', value: 'promotions' },
39
+ { label: 'Updates', value: 'updates' },
40
+ { label: 'Forums', value: 'forums' },
41
+ { label: 'Reservations', value: 'reservations' },
42
+ { label: 'Purchases', value: 'purchases' },
43
+ ],
44
+ },
45
+ }),
46
+ label: <R extends boolean = false>(overrides: {
47
+ displayName?: string;
48
+ description?: string;
49
+ required: R;
50
+ }) =>
51
+ Property.Dropdown<GmailLabel, R, typeof gmailAuth>({
52
+ auth: gmailAuth,
53
+ displayName: overrides.displayName ?? 'Label',
54
+ description:
55
+ overrides.description ??
56
+ 'Optional filteration, leave unselected to filter based on the email label',
57
+ required: overrides.required,
58
+ defaultValue: '',
59
+ refreshers: [],
60
+ options: async ({ auth }) => {
61
+ if (!auth) {
62
+ return {
63
+ disabled: true,
64
+ options: [],
65
+ placeholder: 'please authenticate first',
66
+ };
67
+ }
68
+
69
+ const response = await GmailRequests.getLabels(auth);
70
+
71
+ return {
72
+ disabled: false,
73
+ options: response.body.labels.map((label) => ({
74
+ label: label.name,
75
+ value: label,
76
+ })),
77
+ };
78
+ },
79
+ }),
80
+ unread: (required = false) =>
81
+ Property.Checkbox({
82
+ displayName: 'Is unread?',
83
+ description: 'Check if the email is unread or not',
84
+ required,
85
+ defaultValue: false,
86
+ }),
87
+ message: Property.Dropdown({
88
+ displayName: 'Message',
89
+ description:
90
+ 'Select a message from the list or enter a message ID manually.',
91
+ required: true,
92
+ auth: gmailAuth,
93
+ refreshers: [],
94
+ options: async ({ auth }) => {
95
+ if (!auth) {
96
+ return {
97
+ disabled: true,
98
+ options: [],
99
+ placeholder: 'Please authenticate first',
100
+ };
101
+ }
102
+
103
+ try {
104
+ const authValue = auth as GmailAuthValue;
105
+ const authClient = await createGoogleClient(authValue);
106
+
107
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
108
+
109
+ const response = await GmailRequests.getRecentMessages(
110
+ authValue,
111
+ 20 // Get last 20 messages
112
+ );
113
+
114
+ if (!response.body.messages || response.body.messages.length === 0) {
115
+ return {
116
+ disabled: false,
117
+ options: [],
118
+ placeholder:
119
+ 'No recent messages found. You can enter a message ID manually.',
120
+ };
121
+ }
122
+
123
+ // Get message details for better display
124
+ const messageDetails = await Promise.all(
125
+ response.body.messages
126
+ .slice(0, 10)
127
+ .map(async (msg: { id: string; threadId: string }) => {
128
+ try {
129
+ const details = await gmail.users.messages.get({
130
+ metadataHeaders: ['Subject'],
131
+ format: 'metadata',
132
+ id: msg.id,
133
+ userId: 'me',
134
+ });
135
+
136
+ const headers = details.data.payload?.headers || [];
137
+ const subject =
138
+ headers.find((h: any) => h.name === 'Subject')?.value ||
139
+ 'No Subject';
140
+
141
+ return {
142
+ id: msg.id,
143
+ subject:
144
+ subject.length > 50
145
+ ? subject.substring(0, 50) + '...'
146
+ : subject,
147
+ };
148
+ } catch (error) {
149
+ console.log(error);
150
+ return {
151
+ id: msg.id,
152
+ subject: 'Unable to load details',
153
+ };
154
+ }
155
+ })
156
+ );
157
+
158
+ return {
159
+ disabled: false,
160
+ options: messageDetails.map((msg) => ({
161
+ label: msg.subject,
162
+ value: msg.id,
163
+ })),
164
+ };
165
+ } catch (error) {
166
+ return {
167
+ disabled: false,
168
+ options: [],
169
+ placeholder:
170
+ 'Error loading recent messages. You can enter a message ID manually.',
171
+ };
172
+ }
173
+ },
174
+ }),
175
+ thread: Property.Dropdown({
176
+ displayName: 'Thread',
177
+ description: 'Select a thread from the list or enter a thread ID manually',
178
+ required: true,
179
+ refreshers: [],
180
+ auth: gmailAuth,
181
+ options: async ({ auth }) => {
182
+ if (!auth) {
183
+ return {
184
+ disabled: true,
185
+ options: [],
186
+ placeholder: 'Please authenticate first',
187
+ };
188
+ }
189
+
190
+ try {
191
+ const authValue = auth as GmailAuthValue;
192
+ const authClient = await createGoogleClient(authValue);
193
+
194
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
195
+
196
+ const response = await GmailRequests.getRecentThreads(
197
+ authValue,
198
+ 15 // Get last 15 threads
199
+ );
200
+
201
+ if (!response.body.threads || response.body.threads.length === 0) {
202
+ return {
203
+ disabled: false,
204
+ options: [],
205
+ placeholder:
206
+ 'No recent threads found. You can enter a thread ID manually.',
207
+ };
208
+ }
209
+
210
+ // Get thread details for better display
211
+ const threadDetails = await Promise.all(
212
+ response.body.threads
213
+ .slice(0, 10)
214
+ .map(async (thread: { id: string; snippet?: string }) => {
215
+ try {
216
+ const details = await await gmail.users.threads.get({
217
+ metadataHeaders: ['Subject'],
218
+ format: 'metadata',
219
+ id: thread.id,
220
+ userId: 'me',
221
+ });
222
+ // Get the first message to extract subject and participants
223
+ const firstMessage = details.data.messages?.[0];
224
+ const headers = firstMessage?.payload?.headers || [];
225
+ const subject =
226
+ headers.find((h: any) => h.name === 'Subject')?.value ||
227
+ 'No Subject';
228
+
229
+ return {
230
+ id: thread.id,
231
+ subject:
232
+ subject.length > 50
233
+ ? subject.substring(0, 50) + '...'
234
+ : subject,
235
+ };
236
+ } catch (error) {
237
+ return {
238
+ id: thread.id,
239
+ subject: 'Unable to load details',
240
+ };
241
+ }
242
+ })
243
+ );
244
+
245
+ return {
246
+ disabled: false,
247
+ options: threadDetails.map((thread) => ({
248
+ label: thread.subject,
249
+ value: thread.id,
250
+ })),
251
+ };
252
+ } catch (error) {
253
+ return {
254
+ disabled: false,
255
+ options: [],
256
+ placeholder:
257
+ 'Error loading recent threads. You can enter a thread ID manually.',
258
+ };
259
+ }
260
+ },
261
+ }),
262
+ };