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:
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
createTrigger,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import {
|
||||
DedupeStrategy,
|
||||
Polling,
|
||||
pollingHelper,
|
||||
} from '@activepieces/pieces-common';
|
||||
import { CopperActivity, CopperAuth, CopperAuthType } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const polling: Polling<
|
||||
CopperAuthType,
|
||||
Record<string, never>
|
||||
> = {
|
||||
strategy: DedupeStrategy.TIMEBASED,
|
||||
|
||||
items: async ({ auth, lastFetchEpochMS }) => {
|
||||
const minCreatedUnix =
|
||||
lastFetchEpochMS != null
|
||||
? Math.max(0, Math.floor(lastFetchEpochMS / 1000) - 1)
|
||||
: undefined;
|
||||
|
||||
const collected: CopperActivity[] = [];
|
||||
const pageSize = 100;
|
||||
let page = 1;
|
||||
let hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
const batch = await CopperApiService.fetchActivities(auth, {
|
||||
minimum_activity_date: minCreatedUnix,
|
||||
page_size: pageSize,
|
||||
page_number: page,
|
||||
});
|
||||
if (!batch.length) break;
|
||||
|
||||
collected.push(...batch);
|
||||
if (batch.length < pageSize) hasMore=false;
|
||||
page += 1;
|
||||
}
|
||||
|
||||
const out = collected.map((a) => ({
|
||||
epochMilliSeconds: (a.activity_date ?? 0) * 1000,
|
||||
data: a,
|
||||
}));
|
||||
|
||||
out.sort((a, b) => a.epochMilliSeconds - b.epochMilliSeconds);
|
||||
return out;
|
||||
},
|
||||
};
|
||||
|
||||
export const newActivity = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'newActivity',
|
||||
displayName: 'New Activity',
|
||||
description: 'Triggers when a new activity is logged',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.POLLING,
|
||||
async test(context) {
|
||||
return await pollingHelper.test(polling, context);
|
||||
},
|
||||
async onEnable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onEnable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async onDisable(context) {
|
||||
const { store, auth, propsValue } = context;
|
||||
await pollingHelper.onDisable(polling, { store, auth, propsValue });
|
||||
},
|
||||
|
||||
async run(context) {
|
||||
return await pollingHelper.poll(polling, context);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const CACHE_KEY = 'copper_new_lead_trigger_key';
|
||||
|
||||
export const newLead = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'newLead',
|
||||
displayName: 'New Lead',
|
||||
description: 'Triggers when a new lead is created.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'lead',
|
||||
event: 'new',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const CACHE_KEY = 'copper_new_person_trigger_key';
|
||||
|
||||
export const newPerson = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'newPerson',
|
||||
displayName: 'New Person',
|
||||
description: 'Triggers when a new person/contact is created.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'person',
|
||||
event: 'new',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const CACHE_KEY = 'copper_new_task_trigger_key';
|
||||
|
||||
export const newTask = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'newTask',
|
||||
displayName: 'New Task',
|
||||
description: 'Triggers when a new task is created.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'task',
|
||||
event: 'new',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_lead_status_trigger_key';
|
||||
|
||||
export const updatedLeadStatus = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedLeadStatus',
|
||||
displayName: 'Updated Lead Status',
|
||||
description: 'Triggers when the status of a lead changes.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'lead',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
const body = context.payload.body as any;
|
||||
const ids = Array.isArray(body?.ids) ? body.ids : [];
|
||||
const updatedAttrs = body?.updated_attributes ?? {};
|
||||
|
||||
const statusChanged =
|
||||
Array.isArray(updatedAttrs.status) &&
|
||||
updatedAttrs.status.length === 2 &&
|
||||
updatedAttrs.status[0] !== updatedAttrs.status[1];
|
||||
|
||||
if (!statusChanged) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const events = ids.map((id: number | string) => ({
|
||||
id,
|
||||
change_type: 'status_change',
|
||||
previous_status: updatedAttrs.status?.[0] ?? null,
|
||||
current_status: updatedAttrs.status?.[1] ?? null,
|
||||
subscription_id: body.subscription_id,
|
||||
timestamp: body.timestamp,
|
||||
}));
|
||||
|
||||
return events;
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_lead_trigger_key';
|
||||
|
||||
export const updatedLead = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedLead',
|
||||
displayName: 'Updated Lead',
|
||||
description: 'Triggers when a lead is modified.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'lead',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_opportunity_stage_trigger_key';
|
||||
|
||||
export const updatedOpportunityStage = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedOpportunityStage',
|
||||
displayName: 'Updated Opportunity Stage',
|
||||
description: 'Triggers when an opportunity stage changes',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'opportunity',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
const body = context.payload.body as any;
|
||||
const ids = Array.isArray(body.ids) ? body.ids : [];
|
||||
const updatedAttrs = body.updated_attributes;
|
||||
|
||||
const idChanged =
|
||||
Array.isArray(updatedAttrs.stage_id) &&
|
||||
updatedAttrs.stage_id[0] !== updatedAttrs.stage_id[1];
|
||||
const labelChanged =
|
||||
Array.isArray(updatedAttrs.stage) &&
|
||||
updatedAttrs.stage[0] !== updatedAttrs.stage[1];
|
||||
|
||||
const isStageMove = idChanged && labelChanged;
|
||||
if (!isStageMove) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const events = ids.map((id: any) => ({
|
||||
id,
|
||||
change_type: 'stage_change',
|
||||
previous_stage_id: updatedAttrs.stage_id?.[0] ?? null,
|
||||
current_stage_id: updatedAttrs.stage_id?.[1] ?? null,
|
||||
previous_stage_label: updatedAttrs.stage?.[0] ?? null,
|
||||
current_stage_label: updatedAttrs.stage?.[1] ?? null,
|
||||
previous_last_stage_at: updatedAttrs.last_stage_at?.[0] ?? null,
|
||||
current_last_stage_at: updatedAttrs.last_stage_at?.[1] ?? null,
|
||||
previous_days_in_stage: updatedAttrs.days_in_stage?.[0] ?? null,
|
||||
current_days_in_stage: updatedAttrs.days_in_stage?.[1] ?? null,
|
||||
subscription_id: body.subscription_id,
|
||||
timestamp: body.timestamp,
|
||||
}));
|
||||
|
||||
return events;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_opportunity_status_trigger_key';
|
||||
|
||||
export const updatedOpportunityStatus = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedOpportunityStatus',
|
||||
displayName: 'Updated Opportunity Status',
|
||||
description: "Triggers when an opportunity's status changes.",
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'opportunity',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
const body = context.payload.body as any;
|
||||
const ids = Array.isArray(body?.ids) ? body.ids : [];
|
||||
const updatedAttrs = body?.updated_attributes ?? {};
|
||||
|
||||
const statusChanged =
|
||||
Array.isArray(updatedAttrs.status) &&
|
||||
updatedAttrs.status.length === 2 &&
|
||||
updatedAttrs.status[0] !== updatedAttrs.status[1];
|
||||
|
||||
if (!statusChanged) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const events = ids.map((id: number | string) => ({
|
||||
id,
|
||||
change_type: 'status_change',
|
||||
previous_status: updatedAttrs.status?.[0] ?? null,
|
||||
current_status: updatedAttrs.status?.[1] ?? null,
|
||||
subscription_id: body.subscription_id,
|
||||
timestamp: body.timestamp,
|
||||
}));
|
||||
|
||||
return events;
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_opportunity_trigger_key';
|
||||
|
||||
export const updatedOpportunity = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedOpportunity',
|
||||
displayName: 'Updated Opportunity',
|
||||
description: 'Triggers when an opportunity changes.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'opportunity',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_project_trigger_key';
|
||||
|
||||
export const updatedProject = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedProject',
|
||||
displayName: 'Updated Project',
|
||||
description: 'Triggers when a project is updated.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'project',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createTrigger, TriggerStrategy } from '@activepieces/pieces-framework';
|
||||
import { CopperApiService } from '../common/requests';
|
||||
import { CopperAuth } from '../common/constants';
|
||||
|
||||
const CACHE_KEY = 'copper_updated_task_trigger_key';
|
||||
|
||||
export const updatedTask = createTrigger({
|
||||
auth: CopperAuth,
|
||||
name: 'updatedTask',
|
||||
displayName: 'Updated Task',
|
||||
description: 'Triggers when a task is updated.',
|
||||
props: {},
|
||||
sampleData: {},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
async onEnable(context) {
|
||||
const response = await CopperApiService.createWebhook(context.auth, {
|
||||
target: context.webhookUrl,
|
||||
type: 'task',
|
||||
event: 'update',
|
||||
});
|
||||
|
||||
await context.store.put(CACHE_KEY, {
|
||||
webhookId: response.id,
|
||||
});
|
||||
},
|
||||
async onDisable(context) {
|
||||
const cachedWebhookData = (await context.store.get(CACHE_KEY)) as any;
|
||||
|
||||
if (cachedWebhookData) {
|
||||
await CopperApiService.deleteWebhook(
|
||||
context.auth,
|
||||
cachedWebhookData.webhookId
|
||||
).then(async () => {
|
||||
await context.store.delete(CACHE_KEY);
|
||||
});
|
||||
}
|
||||
},
|
||||
async run(context) {
|
||||
return [context.payload.body];
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user