@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,173 @@
1
+ import {
2
+ createTrigger,
3
+ TriggerStrategy,
4
+ FilesService,
5
+ } from '@agnocon/pieces-framework';
6
+ import { GmailLabel } from '../common/models';
7
+ import { GmailProps } from '../common/props';
8
+ import { gmailAuth, createGoogleClient, GmailAuthValue } from '../auth';
9
+ import {
10
+ parseStream,
11
+ convertAttachment,
12
+ getFirstFiveOrAll,
13
+ } from '../common/data';
14
+ import { gmail as googleGmail } from '@googleapis/gmail';
15
+ import { gmailNewEmailReceivedTriggerOutputSchema } from '../output-schemas';
16
+
17
+ export const gmailNewEmailTrigger = createTrigger({
18
+ auth: gmailAuth,
19
+ name: 'gmail_new_email_received',
20
+ displayName: 'New Email',
21
+ description: 'Triggers when new mail is found in your Gmail inbox',
22
+ aiMetadata: {
23
+ description:
24
+ 'Fires when a new email arrives in the connected Gmail account, optionally narrowed by sender, recipient, subject, label, or category filters. Each event represents one newly received message with its parsed contents and thread.',
25
+ },
26
+ props: {
27
+ subject: GmailProps.subject,
28
+ from: GmailProps.from,
29
+ to: GmailProps.to,
30
+ label: GmailProps.label({ required: false }),
31
+ category: GmailProps.category,
32
+ },
33
+ outputSchema: gmailNewEmailReceivedTriggerOutputSchema,
34
+ sampleData: {},
35
+ type: TriggerStrategy.POLLING,
36
+ async onEnable(context) {
37
+ await context.store.put('lastPoll', Date.now());
38
+ },
39
+ async onDisable(context) {
40
+ return;
41
+ },
42
+ async run(context) {
43
+ const lastFetchEpochMS = (await context.store.get<number>('lastPoll')) ?? 0;
44
+
45
+ const items = await pollRecentMessages({
46
+ auth: context.auth,
47
+ props: context.propsValue,
48
+ files: context.files,
49
+ lastFetchEpochMS: lastFetchEpochMS,
50
+ });
51
+
52
+ const newLastEpochMilliSeconds = items.reduce(
53
+ (acc, item) => Math.max(acc, item.epochMilliSeconds),
54
+ lastFetchEpochMS
55
+ );
56
+ await context.store.put('lastPoll', newLastEpochMilliSeconds);
57
+ return items
58
+ .filter((f) => f.epochMilliSeconds > lastFetchEpochMS)
59
+ .map((item) => item.data);
60
+ },
61
+ async test(context) {
62
+ const lastFetchEpochMS = (await context.store.get<number>('lastPoll')) ?? 0;
63
+
64
+ const items = await pollRecentMessages({
65
+ auth: context.auth,
66
+ props: context.propsValue,
67
+ files: context.files,
68
+ lastFetchEpochMS: lastFetchEpochMS,
69
+ });
70
+
71
+ return getFirstFiveOrAll(items.map((item) => item.data));
72
+ },
73
+ });
74
+
75
+ interface PropsValue {
76
+ from: string | undefined;
77
+ to: string | undefined;
78
+ subject: string | undefined;
79
+ label: GmailLabel | undefined;
80
+ category: string | undefined;
81
+ }
82
+
83
+ async function pollRecentMessages({
84
+ auth,
85
+ props,
86
+ files,
87
+ lastFetchEpochMS,
88
+ }: {
89
+ auth: GmailAuthValue;
90
+ props: PropsValue;
91
+ files: FilesService;
92
+ lastFetchEpochMS: number;
93
+ }): Promise<
94
+ {
95
+ epochMilliSeconds: number;
96
+ data: unknown;
97
+ }[]
98
+ > {
99
+ const authClient = await createGoogleClient(auth);
100
+
101
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
102
+
103
+ // construct query
104
+ const query = [];
105
+ const maxResults = lastFetchEpochMS === 0 ? 5 : 20;
106
+ const afterUnixSeconds = Math.floor(lastFetchEpochMS / 1000);
107
+
108
+ if (props.from) query.push(`from:(${props.from})`);
109
+ if (props.to) query.push(`to:(${props.to})`);
110
+ if (props.subject) query.push(`subject:(${props.subject})`);
111
+ if (props.label) query.push(`label:${props.label.name}`);
112
+ if (props.category) query.push(`category:${props.category}`);
113
+ if (afterUnixSeconds != null && afterUnixSeconds > 0)
114
+ query.push(`after:${afterUnixSeconds}`);
115
+
116
+ // List Messages
117
+ const messagesResponse = await gmail.users.messages.list({
118
+ userId: 'me',
119
+ q: query.join(' '),
120
+ maxResults,
121
+ });
122
+
123
+ // Reverse to process oldest-first so partial progress doesn't skip messages
124
+ const messages = (messagesResponse.data.messages || []).slice().reverse();
125
+
126
+ const pollingResponse = [];
127
+ for (const message of messages) {
128
+ try {
129
+ const rawMailResponse = await gmail.users.messages.get({
130
+ userId: 'me',
131
+ id: message.id!,
132
+ format: 'raw',
133
+ });
134
+ const threadResponse = await gmail.users.threads.get({
135
+ userId: 'me',
136
+ id: message.threadId!,
137
+ });
138
+
139
+ const parsedMailResponse = await parseStream(
140
+ Buffer.from(rawMailResponse.data.raw as string, 'base64').toString(
141
+ 'utf-8'
142
+ )
143
+ );
144
+
145
+ pollingResponse.push({
146
+ epochMilliSeconds: Number(rawMailResponse.data.internalDate),
147
+ data: {
148
+ message: {
149
+ ...parsedMailResponse,
150
+ attachments: await convertAttachment(
151
+ parsedMailResponse.attachments,
152
+ files
153
+ ),
154
+ },
155
+ thread: {
156
+ ...threadResponse,
157
+ },
158
+ },
159
+ });
160
+ } catch (error: any) {
161
+ const isRateLimit =
162
+ error.status === 429 ||
163
+ (error.status === 403 &&
164
+ /quota|rate.?limit/i.test(error.message ?? ''));
165
+ if (isRateLimit) {
166
+ break;
167
+ }
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ return pollingResponse;
173
+ }
@@ -0,0 +1,83 @@
1
+ import { createTrigger, TriggerStrategy } from '@agnocon/pieces-framework';
2
+ import { gmailAuth, createGoogleClient } from '../auth';
3
+ import { gmail as googleGmail } from '@googleapis/gmail';
4
+ import { getFirstFiveOrAll } from '../common/data';
5
+ import { isNil } from '@agnocon/pieces-framework';
6
+ import { newLabelTriggerOutputSchema } from '../output-schemas';
7
+
8
+ const TRIGGER_KEY = 'labels';
9
+
10
+ export const gmailNewLabelTrigger = createTrigger({
11
+ auth: gmailAuth,
12
+ name: 'new_label',
13
+ displayName: 'New Label',
14
+ description: 'Triggers when a new label is created.',
15
+ aiMetadata: {
16
+ description:
17
+ 'Fires when a new label is created in the connected Gmail account. Each event represents one newly added label not seen on a prior poll.',
18
+ },
19
+ props: {},
20
+ outputSchema: newLabelTriggerOutputSchema,
21
+ sampleData: {},
22
+ type: TriggerStrategy.POLLING,
23
+ async onEnable(context) {
24
+ const authClient = await createGoogleClient(context.auth);
25
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
26
+
27
+ const response = await gmail.users.labels.list({
28
+ userId: 'me',
29
+ });
30
+
31
+ const labels = response.data.labels || [];
32
+
33
+ const existingLabelIds = labels.map((label) => label.id);
34
+ await context.store.put(TRIGGER_KEY, JSON.stringify(existingLabelIds));
35
+ },
36
+ async onDisable(context) {
37
+ await context.store.delete(TRIGGER_KEY);
38
+ },
39
+ async test(context) {
40
+ const authClient = await createGoogleClient(context.auth);
41
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
42
+
43
+ const response = await gmail.users.labels.list({
44
+ userId: 'me',
45
+ });
46
+
47
+ const labels = response.data.labels || [];
48
+
49
+ return getFirstFiveOrAll(labels);
50
+ },
51
+ async run(context) {
52
+ const existingIds = (await context.store.get<string>(TRIGGER_KEY)) ?? '[]';
53
+ const parsedExistingIds = JSON.parse(existingIds) as string[];
54
+
55
+ const authClient = await createGoogleClient(context.auth);
56
+
57
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
58
+
59
+ const response = await gmail.users.labels.list({
60
+ userId: 'me',
61
+ });
62
+
63
+ if (isNil(response.data.labels) || response.data.labels.length === 0) {
64
+ await context.store.put(TRIGGER_KEY, '[]');
65
+ return [];
66
+ }
67
+
68
+ const allCurrentIds = response.data.labels.map((label) => label.id);
69
+
70
+ const newLables = response.data.labels.filter((label) => {
71
+ const labelId = label.id ?? undefined;
72
+ return labelId !== undefined && !parsedExistingIds.includes(labelId);
73
+ });
74
+
75
+ await context.store.put(TRIGGER_KEY, JSON.stringify(allCurrentIds));
76
+
77
+ if (newLables.length === 0) {
78
+ return [];
79
+ }
80
+
81
+ return newLables;
82
+ },
83
+ });
@@ -0,0 +1,197 @@
1
+ import {
2
+ createTrigger,
3
+ TriggerStrategy,
4
+ FilesService,
5
+ } from '@agnocon/pieces-framework';
6
+ import { GmailProps } from '../common/props';
7
+ import { gmailAuth, createGoogleClient } from '../auth';
8
+ import { gmail as googleGmail } from '@googleapis/gmail';
9
+ import { parseStream, convertAttachment } from '../common/data';
10
+ import { newLabeledEmailTriggerOutputSchema } from '../output-schemas';
11
+
12
+ async function enrichGmailMessage({
13
+ gmail,
14
+ messageId,
15
+ files,
16
+ labelInfo,
17
+ }: {
18
+ gmail: any;
19
+ messageId: string;
20
+ files: FilesService;
21
+ labelInfo: {
22
+ labelId: string;
23
+ labelName: string;
24
+ };
25
+ }) {
26
+ const rawMailResponse = await gmail.users.messages.get({
27
+ userId: 'me',
28
+ id: messageId,
29
+ format: 'raw',
30
+ });
31
+
32
+ const threadResponse = await gmail.users.threads.get({
33
+ userId: 'me',
34
+ id: rawMailResponse.data.threadId!,
35
+ });
36
+
37
+ const parsedMailResponse = await parseStream(
38
+ Buffer.from(rawMailResponse.data.raw as string, 'base64').toString('utf-8')
39
+ );
40
+
41
+ return {
42
+ message: {
43
+ ...parsedMailResponse,
44
+ attachments: await convertAttachment(
45
+ parsedMailResponse.attachments,
46
+ files
47
+ ),
48
+ },
49
+ thread: threadResponse.data,
50
+ labelInfo: {
51
+ ...labelInfo,
52
+ addedAt: Date.now(),
53
+ },
54
+ };
55
+ }
56
+
57
+ export const gmailNewLabeledEmailTrigger = createTrigger({
58
+ auth: gmailAuth,
59
+ name: 'new_labeled_email',
60
+ displayName: 'New Labeled Email',
61
+ description: 'Triggers when a label is added to an email',
62
+ aiMetadata: {
63
+ description:
64
+ 'Fires when the specified label is applied to an email (either newly received with the label or labeled afterward). Each event represents one message that just gained that label, with its parsed contents and label info.',
65
+ },
66
+ props: {
67
+ label: GmailProps.label({
68
+ required: true,
69
+ }),
70
+ },
71
+ outputSchema: newLabeledEmailTriggerOutputSchema,
72
+ sampleData: {},
73
+ type: TriggerStrategy.POLLING,
74
+ onEnable: async (context) => {
75
+ const authClient = await createGoogleClient(context.auth);
76
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
77
+
78
+ const profile = await gmail.users.getProfile({
79
+ userId: 'me',
80
+ });
81
+
82
+ await context.store.put('lastHistoryId', profile.data.historyId);
83
+ },
84
+ onDisable: async (context) => {
85
+ await context.store.delete('lastHistoryId');
86
+ },
87
+ run: async (context) => {
88
+ const authClient = await createGoogleClient(context.auth);
89
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
90
+
91
+ const lastHistoryId = await context.store.get('lastHistoryId');
92
+
93
+ const historyResponse = await gmail.users.history.list({
94
+ userId: 'me',
95
+ startHistoryId: lastHistoryId as string,
96
+ labelId: context.propsValue.label.id,
97
+ historyTypes: ['labelAdded', 'messageAdded'],
98
+ });
99
+
100
+ const labeledMessages = new Map<string, string>();
101
+ const results = [];
102
+
103
+ if (historyResponse.data.history) {
104
+ for (const history of historyResponse.data.history) {
105
+ if (history.labelsAdded) {
106
+ for (const labelAdded of history.labelsAdded) {
107
+ if (
108
+ labelAdded.labelIds?.includes(context.propsValue.label.id) &&
109
+ labelAdded.message?.id
110
+ ) {
111
+ labeledMessages.set(
112
+ labelAdded.message.id,
113
+ history.id?.toString() || ''
114
+ );
115
+ }
116
+ }
117
+ } else if (history.messagesAdded) {
118
+ for (const messageAdded of history.messagesAdded) {
119
+ if (
120
+ messageAdded.message?.id &&
121
+ messageAdded.message.labelIds?.includes(
122
+ context.propsValue.label.id
123
+ )
124
+ ) {
125
+ labeledMessages.set(
126
+ messageAdded.message.id,
127
+ history.id?.toString() || ''
128
+ );
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+
135
+ for (const [messageId, historyId] of labeledMessages) {
136
+ const enrichedMessage = await enrichGmailMessage({
137
+ gmail,
138
+ messageId,
139
+ files: context.files,
140
+ labelInfo: {
141
+ labelId: context.propsValue.label.id,
142
+ labelName: context.propsValue.label.name,
143
+ },
144
+ });
145
+
146
+ if (lastHistoryId !== historyId) {
147
+ results.push({
148
+ id: historyId,
149
+ data: enrichedMessage,
150
+ });
151
+ }
152
+ }
153
+
154
+ if (historyResponse.data.historyId) {
155
+ await context.store.put('lastHistoryId', historyResponse.data.historyId);
156
+ }
157
+
158
+ return results;
159
+ },
160
+ test: async (context) => {
161
+ const authClient = await createGoogleClient(context.auth);
162
+ const gmail = googleGmail({ version: 'v1', auth: authClient });
163
+
164
+ const messagesResponse = await gmail.users.messages.list({
165
+ userId: 'me',
166
+ labelIds: [context.propsValue.label.id],
167
+ maxResults: 5,
168
+ });
169
+
170
+ const results = [];
171
+
172
+ if (messagesResponse.data.messages) {
173
+ for (const message of messagesResponse.data.messages) {
174
+ const messageId = message.id;
175
+ if (!messageId) {
176
+ continue;
177
+ }
178
+ const enrichedMessage = await enrichGmailMessage({
179
+ gmail,
180
+ messageId: messageId,
181
+ files: context.files,
182
+ labelInfo: {
183
+ labelId: context.propsValue.label.id,
184
+ labelName: context.propsValue.label.name,
185
+ },
186
+ });
187
+
188
+ results.push({
189
+ id: messageId,
190
+ data: enrichedMessage,
191
+ });
192
+ }
193
+ }
194
+
195
+ return results;
196
+ },
197
+ });