Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
9848268d34
commit
3aa7199503
+43
@@ -0,0 +1,43 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { messageIdDropdown } from '../common/props';
|
||||
|
||||
export const addLabelToEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'addLabelToEmail',
|
||||
displayName: 'Add Label to Email',
|
||||
description: 'Adds a category (label) to an email message.',
|
||||
props: {
|
||||
messageId: messageIdDropdown({
|
||||
displayName: 'Email',
|
||||
description: 'Select the email message to add the label to.',
|
||||
required: true,
|
||||
}),
|
||||
categories: Property.Array({
|
||||
displayName: 'Categories',
|
||||
description: 'Categories to add to the email.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { messageId, categories } = context.propsValue;
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const message = await client.api(`/me/messages/${messageId}`).get();
|
||||
const existingCategories = message.categories || [];
|
||||
|
||||
const updatedCategories = [...new Set([...existingCategories, ...categories])];
|
||||
|
||||
const response = await client.api(`/me/messages/${messageId}`).patch({
|
||||
categories: updatedCategories,
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
import { ApFile, createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { BodyType, Message } from '@microsoft/microsoft-graph-types';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
|
||||
export const createDraftEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'createDraftEmail',
|
||||
displayName: 'Create Draft Email',
|
||||
description: 'Creates a draft email message.',
|
||||
props: {
|
||||
recipients: Property.Array({
|
||||
displayName: 'To Email(s)',
|
||||
required: true,
|
||||
}),
|
||||
ccRecipients: Property.Array({
|
||||
displayName: 'CC Email(s)',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
}),
|
||||
bccRecipients: Property.Array({
|
||||
displayName: 'BCC Email(s)',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
}),
|
||||
subject: Property.ShortText({
|
||||
displayName: 'Subject',
|
||||
required: true,
|
||||
}),
|
||||
bodyFormat: Property.StaticDropdown({
|
||||
displayName: 'Body Format',
|
||||
required: true,
|
||||
defaultValue: 'text',
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'HTML', value: 'html' },
|
||||
{ label: 'Text', value: 'text' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
body: Property.LongText({
|
||||
displayName: 'Body',
|
||||
required: true,
|
||||
}),
|
||||
attachments: Property.Array({
|
||||
displayName: 'Attachments',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
properties: {
|
||||
file: Property.File({
|
||||
displayName: 'File',
|
||||
required: true,
|
||||
}),
|
||||
fileName: Property.ShortText({
|
||||
displayName: 'File Name',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const recipients = context.propsValue.recipients as string[];
|
||||
const ccRecipients = context.propsValue.ccRecipients as string[];
|
||||
const bccRecipients = context.propsValue.bccRecipients as string[];
|
||||
const attachments = context.propsValue.attachments as Array<{ file: ApFile; fileName: string }>;
|
||||
|
||||
const { subject, body, bodyFormat } = context.propsValue;
|
||||
|
||||
const mailPayload: Message = {
|
||||
subject,
|
||||
body: {
|
||||
content: body,
|
||||
contentType: bodyFormat as BodyType,
|
||||
},
|
||||
toRecipients: recipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
ccRecipients: ccRecipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
bccRecipients: bccRecipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
attachments: attachments.map((attachment) => ({
|
||||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
name: attachment.fileName || attachment.file.filename,
|
||||
contentBytes: attachment.file.base64,
|
||||
})),
|
||||
};
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const response = await client.api('/me/messages').post(mailPayload);
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
import { FileAttachment } from '@microsoft/microsoft-graph-types';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
|
||||
export const downloadAttachmentAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'downloadAttachment',
|
||||
displayName: 'Download Attachment',
|
||||
description: 'Download attachments from a specific email message.',
|
||||
props: {
|
||||
messageId: Property.ShortText({
|
||||
displayName: 'Message ID',
|
||||
description: 'The ID of the email message containing the attachment.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { messageId } = context.propsValue;
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const response: PageCollection = await client
|
||||
.api(`/me/messages/${messageId}/attachments`)
|
||||
.get();
|
||||
|
||||
const attachments = [];
|
||||
|
||||
for (const attachment of response.value as FileAttachment[]) {
|
||||
if (attachment.name && attachment.contentBytes) {
|
||||
attachments.push({
|
||||
...attachment,
|
||||
file: await context.files.write({
|
||||
fileName: attachment.name || 'test.png',
|
||||
data: Buffer.from(attachment.contentBytes, 'base64'),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return attachments;
|
||||
},
|
||||
});
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
import { Message } from '@microsoft/microsoft-graph-types';
|
||||
import dayjs from 'dayjs';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { mailFolderIdDropdown } from '../common/props';
|
||||
|
||||
export const findEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'findEmail',
|
||||
displayName: 'Find Email',
|
||||
description: 'Searches for emails using full-text search.',
|
||||
props: {
|
||||
searchQuery: Property.ShortText({
|
||||
displayName: 'Search Query',
|
||||
description:
|
||||
'Search terms to find emails (e.g., "from:john@example.com", "subject:urgent", "hasAttachments:true")',
|
||||
required: true,
|
||||
}),
|
||||
folderId: mailFolderIdDropdown({
|
||||
displayName: 'Folder',
|
||||
description: 'Search in a specific folder. Leave empty to search all folders.',
|
||||
required: false,
|
||||
}),
|
||||
top: Property.Number({
|
||||
displayName: 'Max Results',
|
||||
description: 'Maximum number of results to return (1-1000).',
|
||||
required: false,
|
||||
defaultValue: 25,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { searchQuery, folderId, top } = context.propsValue;
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const baseUrl = folderId ? `/me/mailFolders/${folderId}/messages` : '/me/messages';
|
||||
const searchParam = `$search="${searchQuery}"`;
|
||||
const topParam = top ? `$top=${Math.min(Math.max(top, 1), 1000)}` : '$top=25';
|
||||
const selectParam = ['id', 'subject', 'from', 'toRecipients', 'receivedDateTime'].join(',');
|
||||
|
||||
const queryParams = [searchParam, topParam, selectParam].filter(Boolean).join('&');
|
||||
const url = `${baseUrl}?${queryParams}`;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
ConsistencyLevel: 'eventual',
|
||||
Prefer: 'outlook.body-content-type="text"',
|
||||
};
|
||||
|
||||
const response: PageCollection = await client.api(url).headers(headers).get();
|
||||
|
||||
const messages = response.value as Message[];
|
||||
const nextPageUrl = response['@odata.nextLink'];
|
||||
|
||||
if (searchQuery) {
|
||||
messages.sort(
|
||||
(a, b) => dayjs(b.receivedDateTime).valueOf() - dayjs(a.receivedDateTime).valueOf(),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
found: messages.length > 0,
|
||||
result: messages,
|
||||
hasMore: !!nextPageUrl,
|
||||
nextPageUrl: nextPageUrl,
|
||||
totalCount: messages.length,
|
||||
};
|
||||
},
|
||||
});
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { BodyType, Message } from '@microsoft/microsoft-graph-types';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { messageIdDropdown } from '../common/props';
|
||||
|
||||
export const forwardEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'forwardEmail',
|
||||
displayName: 'Forward Email',
|
||||
description: 'Forwards an email message.',
|
||||
props: {
|
||||
messageId: messageIdDropdown({
|
||||
displayName: 'Email',
|
||||
description: 'Select the email message to forward.',
|
||||
required: true,
|
||||
}),
|
||||
recipients: Property.Array({
|
||||
displayName: 'To Email(s)',
|
||||
required: true,
|
||||
}),
|
||||
comment: Property.LongText({
|
||||
displayName: 'Comment',
|
||||
description: 'Optional comment to include with the forwarded message.',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { messageId, comment } = context.propsValue;
|
||||
const recipients = context.propsValue.recipients as string[];
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const message = await client.api(`/me/messages/${messageId}`).get();
|
||||
|
||||
const messagePayload: Message = {
|
||||
toRecipients: recipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
body: {
|
||||
contentType: 'html',
|
||||
content: (comment ?? '') + '<br><br>' + message.body.content,
|
||||
},
|
||||
attachments: message.attachments,
|
||||
};
|
||||
|
||||
const response = await client
|
||||
.api(`/me/messages/${messageId}/forward`)
|
||||
.post({
|
||||
message:messagePayload,
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Email forwarded successfully.',
|
||||
messageId: response.id,
|
||||
...response,
|
||||
};
|
||||
},
|
||||
});
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { createAction, Property, OAuth2PropertyValue } from '@activepieces/pieces-framework';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
import { MailFolder } from '@microsoft/microsoft-graph-types';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { mailFolderIdDropdown, messageIdDropdown } from '../common/props';
|
||||
|
||||
export const moveEmailToFolderAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'moveEmailToFolder',
|
||||
displayName: 'Move Email to Folder',
|
||||
description: 'Moves an email message to a specific folder.',
|
||||
props: {
|
||||
messageId: messageIdDropdown({
|
||||
displayName: 'Email',
|
||||
description: 'Select the email message to move.',
|
||||
required: true,
|
||||
}),
|
||||
destinationFolderId: mailFolderIdDropdown({
|
||||
displayName: 'Destination Folder',
|
||||
description: 'The folder to move the email to.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { messageId, destinationFolderId } = context.propsValue;
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const response = await client.api(`/me/messages/${messageId}/move`).post({
|
||||
destinationId: destinationFolderId,
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { messageIdDropdown } from '../common/props';
|
||||
|
||||
export const removeLabelFromEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'removeLabelFromEmail',
|
||||
displayName: 'Remove Label from Email',
|
||||
description: 'Removes a category (label) from an email message.',
|
||||
props: {
|
||||
messageId: messageIdDropdown({
|
||||
displayName: 'Email',
|
||||
description: 'Select the email message to remove the label from.',
|
||||
required: true,
|
||||
}),
|
||||
categories: Property.Array({
|
||||
displayName: 'Categories to Remove',
|
||||
description: 'Categories to remove from the email.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { messageId, categories } = context.propsValue;
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const message = await client.api(`/me/messages/${messageId}`).get();
|
||||
const existingCategories = message.categories || [];
|
||||
|
||||
const updatedCategories = existingCategories.filter(
|
||||
(category: string) => !categories.includes(category)
|
||||
);
|
||||
|
||||
const response = await client.api(`/me/messages/${messageId}`).patch({
|
||||
categories: updatedCategories,
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
import { ApFile, createAction, Property, OAuth2PropertyValue } from '@activepieces/pieces-framework';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { BodyType, Message } from '@microsoft/microsoft-graph-types';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
|
||||
export const replyEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'reply-email',
|
||||
displayName: 'Reply to Email',
|
||||
description: 'Reply to an outlook email.',
|
||||
props: {
|
||||
messageId: Property.Dropdown({
|
||||
auth: microsoftOutlookAuth,
|
||||
displayName: 'Email',
|
||||
description: 'Select the email message to reply to.',
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve((auth as OAuth2PropertyValue).access_token),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const response: PageCollection = await client
|
||||
.api('/me/messages?$top=50&$select=id,subject,from,receivedDateTime')
|
||||
.orderby('receivedDateTime desc')
|
||||
.get();
|
||||
|
||||
const messages = response.value as Message[];
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: messages.map((message) => ({
|
||||
label: `${message.subject || 'No Subject'} - ${message.from?.emailAddress?.name || message.from?.emailAddress?.address || 'Unknown Sender'}`,
|
||||
value: message.id || '',
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
}),
|
||||
bodyFormat: Property.StaticDropdown({
|
||||
displayName: 'Body Format',
|
||||
required: true,
|
||||
defaultValue: 'text',
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'HTML', value: 'html' },
|
||||
{ label: 'Text', value: 'text' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
replyBody: Property.LongText({
|
||||
displayName: 'Reply Body',
|
||||
required: true,
|
||||
}),
|
||||
ccRecipients: Property.Array({
|
||||
displayName: 'CC Recipients',
|
||||
required: false,
|
||||
}),
|
||||
bccRecipients: Property.Array({
|
||||
displayName: 'BCC Recipients',
|
||||
required: false,
|
||||
}),
|
||||
attachments: Property.Array({
|
||||
displayName: 'Attachments',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
properties: {
|
||||
file: Property.File({
|
||||
displayName: 'File',
|
||||
required: true,
|
||||
}),
|
||||
fileName: Property.ShortText({
|
||||
displayName: 'File Name',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
draft: Property.Checkbox({
|
||||
displayName: 'Create Draft',
|
||||
description: 'If enabled, creates draft without sending.',
|
||||
required: true,
|
||||
defaultValue: false,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { replyBody, bodyFormat, messageId, draft } = context.propsValue;
|
||||
const ccRecipients = context.propsValue.ccRecipients as string[];
|
||||
const bccRecipients = context.propsValue.bccRecipients as string[];
|
||||
const attachments = context.propsValue.attachments as Array<{
|
||||
file: ApFile;
|
||||
fileName: string;
|
||||
}>;
|
||||
const mailPayload: Message = {
|
||||
body: {
|
||||
content: replyBody,
|
||||
contentType: bodyFormat as BodyType,
|
||||
},
|
||||
ccRecipients: ccRecipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
bccRecipients: bccRecipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
attachments: attachments.map((attachment) => ({
|
||||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
name: attachment.fileName || attachment.file.filename,
|
||||
contentBytes: attachment.file.base64,
|
||||
})),
|
||||
};
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
try {
|
||||
const response: Message = await client
|
||||
.api(`/me/messages/${messageId}/createReply`)
|
||||
.post({
|
||||
message: mailPayload,
|
||||
});
|
||||
const draftId = response.id;
|
||||
if (!draft) {
|
||||
await client.api(`/me/messages/${draftId}/send`).post({});
|
||||
return {
|
||||
success: true,
|
||||
message: 'Reply sent successfully.',
|
||||
draftId: draftId,
|
||||
};
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
message: 'Draft created successfully.',
|
||||
draftId: draftId,
|
||||
draftLink: `https://outlook.office.com/mail/drafts/id/${draftId}`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Reply Email Error:', error);
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'Unknown error';
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
},
|
||||
});
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { draftMessageIdDropdown } from '../common/props';
|
||||
|
||||
export const sendDraftEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'sendDraftEmail',
|
||||
displayName: 'Send Draft Email',
|
||||
description: 'Sends a draft email message.',
|
||||
props: {
|
||||
messageId: draftMessageIdDropdown({
|
||||
displayName: 'Draft Email',
|
||||
description: 'Select the draft email message to send.',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { messageId } = context.propsValue;
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
await client.api(`/me/messages/${messageId}/send`).post({});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Draft sent successfully.',
|
||||
messageId: messageId,
|
||||
};
|
||||
},
|
||||
});
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
import { ApFile, createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
import { BodyType, Message } from '@microsoft/microsoft-graph-types';
|
||||
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
|
||||
export const sendEmailAction = createAction({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'send-email',
|
||||
displayName: 'Send Email',
|
||||
description: 'Sends an email using Microsoft Outlook.',
|
||||
props: {
|
||||
recipients: Property.Array({
|
||||
displayName: 'To Email(s)',
|
||||
required: true,
|
||||
}),
|
||||
ccRecipients: Property.Array({
|
||||
displayName: 'CC Email(s)',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
}),
|
||||
bccRecipients: Property.Array({
|
||||
displayName: 'BCC Email(s)',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
}),
|
||||
subject: Property.ShortText({
|
||||
displayName: 'Subject',
|
||||
required: true,
|
||||
}),
|
||||
bodyFormat: Property.StaticDropdown({
|
||||
displayName: 'Body Format',
|
||||
required: true,
|
||||
defaultValue: 'text',
|
||||
options: {
|
||||
disabled: false,
|
||||
options: [
|
||||
{ label: 'HTML', value: 'html' },
|
||||
{ label: 'Text', value: 'text' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
body: Property.LongText({
|
||||
displayName: 'Body',
|
||||
required: true,
|
||||
}),
|
||||
attachments: Property.Array({
|
||||
displayName: 'Attachments',
|
||||
required: false,
|
||||
defaultValue: [],
|
||||
properties: {
|
||||
file: Property.File({
|
||||
displayName: 'File',
|
||||
required: true,
|
||||
}),
|
||||
fileName: Property.ShortText({
|
||||
displayName: 'File Name',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const recipients = context.propsValue.recipients as string[];
|
||||
const ccRecipients = context.propsValue.ccRecipients as string[];
|
||||
const bccRecipients = context.propsValue.bccRecipients as string[];
|
||||
const attachments = context.propsValue.attachments as Array<{ file: ApFile; fileName: string }>;
|
||||
|
||||
const { subject, body, bodyFormat } = context.propsValue;
|
||||
|
||||
const mailPayload: Message = {
|
||||
subject,
|
||||
body: {
|
||||
content: body,
|
||||
contentType: bodyFormat as BodyType,
|
||||
},
|
||||
toRecipients: recipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
ccRecipients: ccRecipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
bccRecipients: bccRecipients.map((mail) => ({
|
||||
emailAddress: {
|
||||
address: mail,
|
||||
},
|
||||
})),
|
||||
attachments: attachments.map((attachment) => ({
|
||||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
name: attachment.fileName || attachment.file.filename,
|
||||
contentBytes: attachment.file.base64,
|
||||
})),
|
||||
};
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const response = await client.api('/me/sendMail').post({
|
||||
message: mailPayload,
|
||||
saveToSentItems: 'true',
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { OAuth2PropertyValue, PieceAuth } from '@activepieces/pieces-framework';
|
||||
import { Client } from '@microsoft/microsoft-graph-client';
|
||||
|
||||
const authDesc = `
|
||||
1. Sign in to [Microsoft Azure Portal](https://portal.azure.com/).
|
||||
2. From the left sidebar, go to **Microsoft Enfra ID**.
|
||||
3. Under **Manage**, click on **App registrations**.
|
||||
4. Click the **New registration** button.
|
||||
5. Enter a **Name** for your app.
|
||||
6. For **Supported account types**, choose:
|
||||
- **Accounts in any organizational directory (Any Microsoft Entra ID tenant - Multitenant) and personal Microsoft accounts**
|
||||
- Or select based on your requirement.
|
||||
7. In **Redirect URI**, select **Web** and add the given URL.
|
||||
8. Click **Register**.
|
||||
9. After registration, you’ll be redirected to the app’s overview page. Copy the **Application (client) ID**.
|
||||
10. From the left menu, go to **Certificates & secrets**.
|
||||
- Under **Client secrets**, click **New client secret**.
|
||||
- Provide a description, set an expiry, and click **Add**.
|
||||
- Copy the **Value** of the client secret (this will not be shown again).
|
||||
11. Go to **API permissions** from the left menu.
|
||||
- Click **Add a permission**.
|
||||
- Select **Microsoft Graph** → **Delegated permissions**.
|
||||
- Add the following scopes:
|
||||
- Mail.ReadWrite
|
||||
- Mail.Send
|
||||
- Calendars.Read
|
||||
- offline_access
|
||||
- User.Read
|
||||
- Click **Add permissions**.
|
||||
12. Copy your **Client ID** and **Client Secret**.
|
||||
`
|
||||
|
||||
export const microsoftOutlookAuth = PieceAuth.OAuth2({
|
||||
description:authDesc,
|
||||
authUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
||||
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
|
||||
required: true,
|
||||
scope: ['Mail.ReadWrite', 'Mail.Send', 'Calendars.Read', 'offline_access', 'User.Read'],
|
||||
prompt: 'omit',
|
||||
validate: async ({ auth }) => {
|
||||
try {
|
||||
const authValue = auth as OAuth2PropertyValue;
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(authValue.access_token),
|
||||
},
|
||||
});
|
||||
await client.api('/me').get();
|
||||
return { valid: true };
|
||||
} catch (error) {
|
||||
return { valid: false, error: 'Invalid Credentials.' };
|
||||
}
|
||||
},
|
||||
});
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
import { OAuth2PropertyValue, Property } from '@activepieces/pieces-framework';
|
||||
import { PageCollection, Client } from '@microsoft/microsoft-graph-client';
|
||||
import { MailFolder, Message } from '@microsoft/microsoft-graph-types';
|
||||
import { microsoftOutlookAuth } from './auth';
|
||||
|
||||
type DropdownParams = {
|
||||
displayName: string;
|
||||
description: string;
|
||||
required: boolean;
|
||||
};
|
||||
|
||||
export const messageIdDropdown = (params: DropdownParams) =>
|
||||
Property.Dropdown({
|
||||
auth: microsoftOutlookAuth,
|
||||
displayName: params.displayName,
|
||||
description: params.description,
|
||||
required: params.required,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
placeholder: 'Please connect your account first.',
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve((auth as OAuth2PropertyValue).access_token),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const response: PageCollection = await client
|
||||
.api('/me/messages?$top=50&$select=id,subject,from,receivedDateTime')
|
||||
.orderby('receivedDateTime desc')
|
||||
.get();
|
||||
|
||||
const messages = response.value as Message[];
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: messages.map((message) => ({
|
||||
label: `${message.subject || 'No Subject'}`,
|
||||
value: message.id,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const draftMessageIdDropdown = (params: DropdownParams) =>
|
||||
Property.Dropdown({
|
||||
auth: microsoftOutlookAuth,
|
||||
displayName: params.displayName,
|
||||
description: params.description,
|
||||
required: params.required,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
placeholder: 'Please connect your account first.',
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve((auth as OAuth2PropertyValue).access_token),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const response: PageCollection = await client
|
||||
.api('/me/mailFolders/drafts/messages?$top=50&$select=id,subject,from,receivedDateTime')
|
||||
.orderby('receivedDateTime desc')
|
||||
.get();
|
||||
|
||||
const messages = response.value as Message[];
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: messages.map((message) => ({
|
||||
label: `${message.subject || 'No Subject'}`,
|
||||
value: message.id,
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const mailFolderIdDropdown = (params: DropdownParams) =>
|
||||
Property.Dropdown({
|
||||
auth: microsoftOutlookAuth,
|
||||
displayName: params.displayName,
|
||||
description: params.description,
|
||||
required: params.required,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
placeholder: 'Please connect your account first.',
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve((auth as OAuth2PropertyValue).access_token),
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const response: PageCollection = await client.api('/me/mailFolders').get();
|
||||
|
||||
const folders = response.value as MailFolder[];
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: folders.map((folder) => ({
|
||||
label: folder.displayName || folder.id || 'Unknown',
|
||||
value: folder.id || '',
|
||||
})),
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
import { FilesService, TriggerStrategy, createTrigger } from '@activepieces/pieces-framework';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
import { Message, FileAttachment } from '@microsoft/microsoft-graph-types';
|
||||
import dayjs from 'dayjs';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { mailFolderIdDropdown } from '../common/props';
|
||||
import { isNil } from '@activepieces/shared';
|
||||
|
||||
async function enrichAttachments(
|
||||
client: Client,
|
||||
messages: Message[],
|
||||
files: FilesService,
|
||||
): Promise<Record<string, any>[]> {
|
||||
const attachments: Record<string, any>[] = [];
|
||||
|
||||
for (const message of messages) {
|
||||
const attachmentResponse: PageCollection = await client
|
||||
.api(`/me/messages/${message.id}/attachments`)
|
||||
.get();
|
||||
|
||||
for (const attachment of attachmentResponse.value as FileAttachment[]) {
|
||||
const { contentBytes, ...rest } = attachment;
|
||||
|
||||
if (attachment.name && contentBytes) {
|
||||
const file = await files.write({
|
||||
fileName: attachment.name,
|
||||
data: Buffer.from(contentBytes, 'base64'),
|
||||
});
|
||||
|
||||
attachments.push({
|
||||
file,
|
||||
messageId: message.id!,
|
||||
messageSubject: message.subject,
|
||||
messageSender: message.sender,
|
||||
messageReceivedDateTime: message.receivedDateTime,
|
||||
parentFolderId: message.parentFolderId,
|
||||
...rest,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return attachments;
|
||||
}
|
||||
|
||||
export const newAttachmentTrigger = createTrigger({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'newAttachment',
|
||||
displayName: 'New Attachment',
|
||||
description: 'Triggers when a new email containing one or more attachments arrives.',
|
||||
props: {
|
||||
folderId: mailFolderIdDropdown({
|
||||
displayName: 'Folder',
|
||||
description: 'Monitor attachments in a specific folder. Leave empty to monitor all folders.',
|
||||
required: false,
|
||||
}),
|
||||
},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.POLLING,
|
||||
async onEnable(context) {
|
||||
await context.store.put('lastPoll', Date.now());
|
||||
},
|
||||
async onDisable(context) {
|
||||
// return
|
||||
},
|
||||
async test(context) {
|
||||
const { folderId } = context.propsValue;
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
const baseUrl = folderId ? `/me/mailFolders/${folderId}/messages` : '/me/messages';
|
||||
|
||||
const response: PageCollection = await client
|
||||
.api(`${baseUrl}?$filter=hasAttachments eq true`)
|
||||
.top(10)
|
||||
.get();
|
||||
|
||||
const attachments = await enrichAttachments(client, response.value as Message[], context.files);
|
||||
|
||||
const items = attachments.map((attachment) => ({
|
||||
epochMilliSeconds: dayjs(attachment['messageReceivedDateTime']).valueOf(),
|
||||
data: attachment,
|
||||
}));
|
||||
|
||||
return items.map((item) => item.data);
|
||||
},
|
||||
async run(context) {
|
||||
const lastFetchEpochMS = await context.store.get<number>('lastPoll');
|
||||
if (isNil(lastFetchEpochMS)) {
|
||||
throw new Error("lastPoll doesn't exist in the store.");
|
||||
}
|
||||
|
||||
const { folderId } = context.propsValue;
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(context.auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const baseUrl = folderId ? `/me/mailFolders/${folderId}/messages` : '/me/messages';
|
||||
let response: PageCollection = await client
|
||||
.api(
|
||||
`${baseUrl}?$filter=receivedDateTime gt ${dayjs(
|
||||
lastFetchEpochMS,
|
||||
).toISOString()} and hasAttachments eq true`,
|
||||
)
|
||||
.orderby('receivedDateTime desc')
|
||||
.get();
|
||||
|
||||
const messages: Message[] = [];
|
||||
|
||||
while (response.value.length > 0) {
|
||||
messages.push(...(response.value as Message[]));
|
||||
|
||||
if (response['@odata.nextLink']) {
|
||||
response = await client.api(response['@odata.nextLink']).get();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const attachments = await enrichAttachments(client, messages, context.files);
|
||||
|
||||
const items = attachments.map((attachment) => ({
|
||||
epochMilliSeconds: dayjs(attachment['messageReceivedDateTime']).valueOf(),
|
||||
data: attachment,
|
||||
}));
|
||||
|
||||
const newLastEpochMilliSeconds = items.reduce(
|
||||
(acc, item) => Math.max(acc, item.epochMilliSeconds),
|
||||
lastFetchEpochMS,
|
||||
);
|
||||
await context.store.put('lastPoll', newLastEpochMilliSeconds);
|
||||
return items.filter((f) => f.epochMilliSeconds > lastFetchEpochMS).map((item) => item.data);
|
||||
},
|
||||
});
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
|
||||
import {
|
||||
AppConnectionValueForAuthProperty,
|
||||
PiecePropValueSchema,
|
||||
TriggerStrategy,
|
||||
createTrigger,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
import { Message } from '@microsoft/microsoft-graph-types';
|
||||
import dayjs from 'dayjs';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
import { mailFolderIdDropdown } from '../common/props';
|
||||
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof microsoftOutlookAuth>, { folderId?: string }> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, lastFetchEpochMS, propsValue }) => {
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const messages = [];
|
||||
const folderId = propsValue.folderId;
|
||||
|
||||
const filter =
|
||||
lastFetchEpochMS === 0
|
||||
? '$top=10'
|
||||
: `$filter=createdDateTime gt ${dayjs(lastFetchEpochMS).toISOString()}`;
|
||||
|
||||
let response: PageCollection = await client
|
||||
.api(`/me/mailFolders/${folderId}/messages?${filter}`)
|
||||
.orderby('createdDateTime desc')
|
||||
.get();
|
||||
|
||||
if (lastFetchEpochMS === 0) {
|
||||
for (const message of response.value as Message[]) {
|
||||
messages.push(message);
|
||||
}
|
||||
} else {
|
||||
while (response.value.length > 0) {
|
||||
for (const message of response.value as Message[]) {
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
if (response['@odata.nextLink']) {
|
||||
response = await client.api(response['@odata.nextLink']).get();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages.map((message) => ({
|
||||
epochMilliSeconds: dayjs(message.createdDateTime).valueOf(),
|
||||
data: message,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export const newEmailInFolderTrigger = createTrigger({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'newEmailInFolder',
|
||||
displayName: 'New Email in Folder',
|
||||
description: 'Triggers when a new email is delivered into the specified folder.',
|
||||
props: {
|
||||
folderId: mailFolderIdDropdown({
|
||||
displayName: 'Folder',
|
||||
description: '',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.POLLING,
|
||||
async onEnable(context) {
|
||||
await pollingHelper.onEnable(polling, {
|
||||
auth: context.auth,
|
||||
store: context.store,
|
||||
propsValue: context.propsValue,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
await pollingHelper.onDisable(polling, {
|
||||
auth: context.auth,
|
||||
store: context.store,
|
||||
propsValue: context.propsValue,
|
||||
});
|
||||
},
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
import { DedupeStrategy, Polling, pollingHelper } from '@activepieces/pieces-common';
|
||||
import {
|
||||
AppConnectionValueForAuthProperty,
|
||||
PiecePropValueSchema,
|
||||
TriggerStrategy,
|
||||
createTrigger,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { Client, PageCollection } from '@microsoft/microsoft-graph-client';
|
||||
import { Message } from '@microsoft/microsoft-graph-types';
|
||||
import dayjs from 'dayjs';
|
||||
import { microsoftOutlookAuth } from '../common/auth';
|
||||
|
||||
const polling: Polling<AppConnectionValueForAuthProperty<typeof microsoftOutlookAuth>, Record<string,any>> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
items: async ({ auth, lastFetchEpochMS }) => {
|
||||
const client = Client.initWithMiddleware({
|
||||
authProvider: {
|
||||
getAccessToken: () => Promise.resolve(auth.access_token),
|
||||
},
|
||||
});
|
||||
|
||||
const messages = [];
|
||||
|
||||
const filter =
|
||||
lastFetchEpochMS === 0
|
||||
? '$top=10'
|
||||
: `$filter=receivedDateTime gt ${dayjs(lastFetchEpochMS).toISOString()}`;
|
||||
|
||||
let response: PageCollection = await client
|
||||
.api(`/me/mailFolders/inbox/messages?${filter}`)
|
||||
.orderby('receivedDateTime desc')
|
||||
.get();
|
||||
|
||||
if (lastFetchEpochMS === 0) {
|
||||
for (const message of response.value as Message[]) {
|
||||
messages.push(message);
|
||||
}
|
||||
} else {
|
||||
while (response.value.length > 0) {
|
||||
for (const message of response.value as Message[]) {
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
if (response['@odata.nextLink']) {
|
||||
response = await client.api(response['@odata.nextLink']).get();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages.map((message) => ({
|
||||
epochMilliSeconds: dayjs(message.receivedDateTime).valueOf(),
|
||||
data: message,
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
export const newEmailTrigger = createTrigger({
|
||||
auth: microsoftOutlookAuth,
|
||||
name: 'newEmail',
|
||||
displayName: 'New Email',
|
||||
description: 'Triggers when a new email is received in the inbox.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.POLLING,
|
||||
async onEnable(context) {
|
||||
await pollingHelper.onEnable(polling, {
|
||||
auth: context.auth,
|
||||
store: context.store,
|
||||
propsValue: context.propsValue,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
await pollingHelper.onDisable(polling, {
|
||||
auth: context.auth,
|
||||
store: context.store,
|
||||
propsValue: context.propsValue,
|
||||
});
|
||||
},
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user