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,33 @@
|
||||
{
|
||||
"extends": [
|
||||
"../../../../.eslintrc.base.json"
|
||||
],
|
||||
"ignorePatterns": [
|
||||
"!**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx"
|
||||
],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.js",
|
||||
"*.jsx"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# pieces-tl-dv
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Building
|
||||
|
||||
Run `nx build pieces-tl-dv` to build the library.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@activepieces/piece-tl-dv",
|
||||
"version": "0.0.1",
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"types": "./src/index.d.ts",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "pieces-tl-dv",
|
||||
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/pieces/community/tl-dv/src",
|
||||
"projectType": "library",
|
||||
"release": {
|
||||
"version": {
|
||||
"manifestRootsToUpdate": [
|
||||
"dist/{projectRoot}"
|
||||
],
|
||||
"currentVersionResolver": "git-tag",
|
||||
"fallbackCurrentVersionResolver": "disk"
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:tsc",
|
||||
"outputs": [
|
||||
"{options.outputPath}"
|
||||
],
|
||||
"options": {
|
||||
"outputPath": "dist/packages/pieces/community/tl-dv",
|
||||
"tsConfig": "packages/pieces/community/tl-dv/tsconfig.lib.json",
|
||||
"packageJson": "packages/pieces/community/tl-dv/package.json",
|
||||
"main": "packages/pieces/community/tl-dv/src/index.ts",
|
||||
"assets": [
|
||||
"packages/pieces/community/tl-dv/*.md",
|
||||
{
|
||||
"input": "packages/pieces/community/tl-dv/src/i18n",
|
||||
"output": "./src/i18n",
|
||||
"glob": "**/!(i18n.json)"
|
||||
}
|
||||
],
|
||||
"buildableProjectDepsInPackageJsonType": "dependencies",
|
||||
"updateBuildableProjectDepsInPackageJson": true
|
||||
},
|
||||
"dependsOn": [
|
||||
"prebuild",
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"nx-release-publish": {
|
||||
"options": {
|
||||
"packageRoot": "dist/{projectRoot}"
|
||||
}
|
||||
},
|
||||
"prebuild": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "packages/pieces/community/tl-dv",
|
||||
"command": "bun install --no-save --silent"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": [
|
||||
"{options.outputFile}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { createPiece } from "@activepieces/pieces-framework";
|
||||
import { PieceCategory } from "@activepieces/shared";
|
||||
import { tldvAuth } from "./lib/common/auth";
|
||||
import { uploadRecording } from "./lib/actions/upload-recording";
|
||||
import { listMeetings } from "./lib/actions/list-meetings";
|
||||
import { getMeeting } from "./lib/actions/get-meeting";
|
||||
import { getTranscript } from "./lib/actions/get-transcript";
|
||||
import { getHighlights } from "./lib/actions/get-highlights";
|
||||
import { meetingReady } from "./lib/triggers/meeting-ready";
|
||||
import { transcriptReady } from "./lib/triggers/transcript-ready";
|
||||
|
||||
export { tldvAuth } from "./lib/common/auth";
|
||||
|
||||
export const tlDv = createPiece({
|
||||
displayName: "tl;dv",
|
||||
description: "Record meetings, get transcripts, and access meeting notes automatically.",
|
||||
auth: tldvAuth,
|
||||
minimumSupportedRelease: '0.36.1',
|
||||
logoUrl: "https://cdn.activepieces.com/pieces/tl-dv.png",
|
||||
categories: [PieceCategory.PRODUCTIVITY],
|
||||
authors: ["onyedikachi-david"],
|
||||
actions: [
|
||||
uploadRecording,
|
||||
listMeetings,
|
||||
getMeeting,
|
||||
getTranscript,
|
||||
getHighlights,
|
||||
],
|
||||
triggers: [
|
||||
meetingReady,
|
||||
transcriptReady,
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from '../common/client';
|
||||
import { meetingIdProperty } from '../common/props';
|
||||
|
||||
export const getHighlights = createAction({
|
||||
auth: tldvAuth,
|
||||
name: 'get_highlights',
|
||||
displayName: 'Get Highlights',
|
||||
description: 'Get meeting highlights (notes) by meeting ID',
|
||||
props: {
|
||||
meetingId: meetingIdProperty,
|
||||
},
|
||||
async run(context) {
|
||||
const { meetingId } = context.propsValue;
|
||||
|
||||
const response = await tldvCommon.apiCall<{
|
||||
meetingId: string;
|
||||
data: Array<{
|
||||
id?: string;
|
||||
text?: string;
|
||||
timestamp?: number;
|
||||
[key: string]: any;
|
||||
}>;
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url: `/v1alpha1/meetings/${meetingId}/highlights`,
|
||||
auth: { apiKey: context.auth.secret_text },
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from '../common/client';
|
||||
import { meetingIdProperty } from '../common/props';
|
||||
|
||||
export const getMeeting = createAction({
|
||||
auth: tldvAuth,
|
||||
name: 'get_meeting',
|
||||
displayName: 'Get Meeting',
|
||||
description: 'Get meeting details by ID',
|
||||
props: {
|
||||
meetingId: meetingIdProperty,
|
||||
},
|
||||
async run(context) {
|
||||
const { meetingId } = context.propsValue;
|
||||
|
||||
const response = await tldvCommon.apiCall<{
|
||||
id: string;
|
||||
name: string;
|
||||
happenedAt: string;
|
||||
url: string;
|
||||
duration: number;
|
||||
organizer: {
|
||||
name: string;
|
||||
email: string;
|
||||
};
|
||||
invitees: Array<{
|
||||
name: string;
|
||||
email: string;
|
||||
}>;
|
||||
template: string;
|
||||
extraProperties: Record<string, any>;
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url: `/v1alpha1/meetings/${meetingId}`,
|
||||
auth: { apiKey: context.auth.secret_text },
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { createAction } from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from '../common/client';
|
||||
import { meetingIdProperty } from '../common/props';
|
||||
|
||||
export const getTranscript = createAction({
|
||||
auth: tldvAuth,
|
||||
name: 'get_transcript',
|
||||
displayName: 'Get Transcript',
|
||||
description: 'Get meeting transcript by meeting ID',
|
||||
props: {
|
||||
meetingId: meetingIdProperty,
|
||||
},
|
||||
async run(context) {
|
||||
const { meetingId } = context.propsValue;
|
||||
|
||||
const response = await tldvCommon.apiCall<{
|
||||
id: string;
|
||||
meetingId: string;
|
||||
data: Array<{
|
||||
speaker: string;
|
||||
text: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
}>;
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url: `/v1alpha1/meetings/${meetingId}/transcript`,
|
||||
auth: { apiKey: context.auth.secret_text },
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../../index';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from '../common/client';
|
||||
|
||||
export const listMeetings = createAction({
|
||||
auth: tldvAuth,
|
||||
name: 'list_meetings',
|
||||
displayName: 'List Meetings',
|
||||
description: 'Search and list meetings',
|
||||
props: {
|
||||
query: Property.ShortText({
|
||||
displayName: 'Search Query',
|
||||
description: 'Search for meetings by name or content',
|
||||
required: false,
|
||||
}),
|
||||
page: Property.Number({
|
||||
displayName: 'Page',
|
||||
description: 'Page number to return (default: 1)',
|
||||
required: false,
|
||||
defaultValue: 1,
|
||||
}),
|
||||
limit: Property.Number({
|
||||
displayName: 'Results Per Page',
|
||||
description: 'Number of results per page (default: 50)',
|
||||
required: false,
|
||||
defaultValue: 50,
|
||||
}),
|
||||
from: Property.DateTime({
|
||||
displayName: 'From Date',
|
||||
description: 'Search meetings from this date',
|
||||
required: false,
|
||||
}),
|
||||
to: Property.DateTime({
|
||||
displayName: 'To Date',
|
||||
description: 'Search meetings up to this date',
|
||||
required: false,
|
||||
}),
|
||||
onlyParticipated: Property.Checkbox({
|
||||
displayName: 'Only Participated',
|
||||
description: 'Only return meetings you participated in',
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
}),
|
||||
meetingType: Property.StaticDropdown({
|
||||
displayName: 'Meeting Type',
|
||||
description: 'Filter by meeting type',
|
||||
required: false,
|
||||
options: {
|
||||
options: [
|
||||
{ label: 'All', value: '' },
|
||||
{ label: 'Internal', value: 'internal' },
|
||||
{ label: 'External', value: 'external' },
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { query, page, limit, from, to, onlyParticipated, meetingType } = context.propsValue;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (query) {
|
||||
params.append('query', query);
|
||||
}
|
||||
if (page) {
|
||||
params.append('page', page.toString());
|
||||
}
|
||||
if (limit) {
|
||||
params.append('limit', limit.toString());
|
||||
}
|
||||
if (from) {
|
||||
params.append('from', new Date(from).toISOString());
|
||||
}
|
||||
if (to) {
|
||||
params.append('to', new Date(to).toISOString());
|
||||
}
|
||||
if (onlyParticipated !== undefined) {
|
||||
params.append('onlyParticipated', onlyParticipated.toString());
|
||||
}
|
||||
if (meetingType) {
|
||||
params.append('meetingType', meetingType);
|
||||
}
|
||||
|
||||
const queryString = params.toString();
|
||||
const url = `/v1alpha1/meetings${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
const response = await tldvCommon.apiCall<{
|
||||
page: number;
|
||||
pages: number;
|
||||
total: number;
|
||||
pageSize: number;
|
||||
results: any[];
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url,
|
||||
auth: { apiKey: context.auth.secret_text },
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { createAction, Property } from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../common/auth';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from '../common/client';
|
||||
|
||||
export const uploadRecording = createAction({
|
||||
auth: tldvAuth,
|
||||
name: 'upload_recording',
|
||||
displayName: 'Upload Recording',
|
||||
description: 'Import a meeting or recording from a URL',
|
||||
props: {
|
||||
name: Property.ShortText({
|
||||
displayName: 'Meeting Name',
|
||||
description: 'The name of the meeting or recording',
|
||||
required: true,
|
||||
}),
|
||||
url: Property.ShortText({
|
||||
displayName: 'Recording URL',
|
||||
description: 'Publicly accessible URL of the recording. Supported formats: .mp3, .mp4, .wav, .m4a, .mkv, .mov, .avi, .wma, .flac',
|
||||
required: true,
|
||||
}),
|
||||
happenedAt: Property.DateTime({
|
||||
displayName: 'Meeting Date',
|
||||
description: 'The date and time when the meeting occurred. If not provided, the current date will be used.',
|
||||
required: false,
|
||||
}),
|
||||
dryRun: Property.Checkbox({
|
||||
displayName: 'Dry Run',
|
||||
description: 'Test the import without persisting to the database',
|
||||
required: false,
|
||||
defaultValue: false,
|
||||
}),
|
||||
participants: Property.Array({
|
||||
displayName: 'Participants',
|
||||
description: 'Email addresses of meeting participants',
|
||||
required: false,
|
||||
properties: {
|
||||
email: Property.ShortText({
|
||||
displayName: 'Email',
|
||||
required: true,
|
||||
}),
|
||||
},
|
||||
}),
|
||||
},
|
||||
async run(context) {
|
||||
const { name, url, happenedAt, dryRun, participants } = context.propsValue;
|
||||
|
||||
try {
|
||||
new URL(url);
|
||||
} catch {
|
||||
throw new Error('Invalid URL format. Please provide a valid, publicly accessible URL.');
|
||||
}
|
||||
|
||||
if (participants && Array.isArray(participants)) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
for (const participant of participants) {
|
||||
const email = typeof participant === 'string' ? participant : (participant as { email: string }).email;
|
||||
if (email && !emailRegex.test(email)) {
|
||||
throw new Error(`Invalid email format: ${email}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const body: Record<string, any> = {
|
||||
name,
|
||||
url,
|
||||
};
|
||||
|
||||
if (happenedAt) {
|
||||
body['happenedAt'] = new Date(happenedAt).toISOString();
|
||||
}
|
||||
|
||||
if (dryRun !== undefined) {
|
||||
body['dryRun'] = dryRun;
|
||||
}
|
||||
|
||||
if (participants && Array.isArray(participants)) {
|
||||
body['participants'] = participants.map((p: unknown) =>
|
||||
typeof p === 'string' ? p : (p as { email: string }).email
|
||||
);
|
||||
}
|
||||
|
||||
const response = await tldvCommon.apiCall<{
|
||||
success: boolean;
|
||||
jobId: string;
|
||||
message: string;
|
||||
}>({
|
||||
method: HttpMethod.POST,
|
||||
url: '/v1alpha1/meetings/import',
|
||||
auth: { apiKey: context.auth.secret_text },
|
||||
body,
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { PieceAuth } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from './client';
|
||||
|
||||
export const tldvAuth = PieceAuth.SecretText({
|
||||
displayName: 'API Key',
|
||||
description: 'Your tl;dv API key. You can find this at https://tldv.io/app/settings/personal-settings/api-keys',
|
||||
required: true,
|
||||
validate: async ({ auth }) => {
|
||||
try {
|
||||
await tldvCommon.apiCall({
|
||||
method: HttpMethod.GET,
|
||||
url: '/v1alpha1/health',
|
||||
auth: { apiKey: auth as string },
|
||||
});
|
||||
|
||||
return {
|
||||
valid: true,
|
||||
message: 'API key validated successfully. Connected to tl;dv.'
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.message.includes('401') || error.message.includes('403')) {
|
||||
return {
|
||||
valid: false,
|
||||
error: 'Invalid API key. Please check your API key and try again.',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
error: `Authentication failed: ${error.message}. Please verify your API key is correct.`,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
|
||||
|
||||
export const tldvCommon = {
|
||||
baseUrl: 'https://pasta.tldv.io',
|
||||
|
||||
async apiCall<T>({
|
||||
method,
|
||||
url,
|
||||
body,
|
||||
auth,
|
||||
headers,
|
||||
}: {
|
||||
method: HttpMethod;
|
||||
url: string;
|
||||
body?: any;
|
||||
auth: string | { apiKey: string };
|
||||
headers?: Record<string, string>;
|
||||
}): Promise<T> {
|
||||
const apiKey = typeof auth === 'string' ? auth : auth.apiKey;
|
||||
|
||||
const response = await httpClient.sendRequest<T>({
|
||||
method,
|
||||
url: `${this.baseUrl}${url}`,
|
||||
headers: {
|
||||
'x-api-key': apiKey,
|
||||
'Content-Type': 'application/json',
|
||||
...headers,
|
||||
},
|
||||
body,
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
throw new Error(`tl;dv API error: ${response.status} - ${JSON.stringify(response.body)}`);
|
||||
}
|
||||
|
||||
return response.body;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { Property } from '@activepieces/pieces-framework';
|
||||
import { HttpMethod } from '@activepieces/pieces-common';
|
||||
import { tldvCommon } from './client';
|
||||
import { tldvAuth } from './auth';
|
||||
|
||||
export const meetingIdProperty = Property.Dropdown({
|
||||
displayName: 'Meeting',
|
||||
description: 'Select a meeting',
|
||||
auth: tldvAuth,
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Connect your account first',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await tldvCommon.apiCall<{
|
||||
results: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
happenedAt?: string;
|
||||
}>;
|
||||
}>({
|
||||
method: HttpMethod.GET,
|
||||
url: '/v1alpha1/meetings?limit=100',
|
||||
auth: { apiKey: auth.secret_text },
|
||||
});
|
||||
|
||||
if (response.results && response.results.length > 0) {
|
||||
return {
|
||||
disabled: false,
|
||||
options: response.results.map((meeting) => ({
|
||||
label: `${meeting.name}${meeting.happenedAt ? ` (${new Date(meeting.happenedAt).toLocaleDateString()})` : ''}`,
|
||||
value: meeting.id,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'No meetings found',
|
||||
options: [],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: 'Error loading meetings',
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import {
|
||||
createTrigger,
|
||||
Property,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../common/auth';
|
||||
|
||||
export const meetingReady = createTrigger({
|
||||
auth: tldvAuth,
|
||||
name: 'meeting_ready',
|
||||
displayName: 'Meeting Ready',
|
||||
description: 'Triggers when a meeting has finished processing and is ready',
|
||||
props: {
|
||||
webhookInstructions: Property.MarkDown({
|
||||
value: `
|
||||
## Setup Instructions
|
||||
|
||||
To use this trigger, configure a webhook in tl;dv:
|
||||
|
||||
1. Go to your tl;dv dashboard
|
||||
2. Navigate to Settings > Webhooks
|
||||
3. Add a new webhook
|
||||
4. Set the **URL** to:
|
||||
\`\`\`text
|
||||
{{webhookUrl}}
|
||||
\`\`\`
|
||||
5. Select the **MeetingReady** event type
|
||||
6. Choose the scope (User, Team, or Organization level)
|
||||
7. Save the webhook
|
||||
|
||||
The webhook will trigger whenever a meeting finishes processing.
|
||||
`,
|
||||
}),
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
sampleData: {
|
||||
id: 'webhook-123',
|
||||
event: 'MeetingReady',
|
||||
data: {
|
||||
id: 'meeting-123',
|
||||
name: 'Team Standup Meeting',
|
||||
happenedAt: '2024-01-15T10:00:00Z',
|
||||
url: 'https://app.tldv.io/meetings/meeting-123',
|
||||
duration: 1800,
|
||||
organizer: {
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com',
|
||||
},
|
||||
invitees: [
|
||||
{
|
||||
name: 'Jane Smith',
|
||||
email: 'jane@example.com',
|
||||
},
|
||||
],
|
||||
template: '{}',
|
||||
extraProperties: {},
|
||||
},
|
||||
executedAt: '2024-01-15T10:30:00Z',
|
||||
},
|
||||
async onEnable(context) {
|
||||
// Webhook URL is automatically provided by Activepieces
|
||||
// User needs to manually configure the webhook in tl;dv dashboard
|
||||
},
|
||||
async onDisable(context) {
|
||||
// User should remove webhook from tl;dv dashboard
|
||||
},
|
||||
async run(context) {
|
||||
const payload = context.payload.body as {
|
||||
id: string;
|
||||
event: string;
|
||||
data: {
|
||||
id: string;
|
||||
name: string;
|
||||
happenedAt: string;
|
||||
url: string;
|
||||
duration: number;
|
||||
organizer: {
|
||||
name: string;
|
||||
email: string;
|
||||
};
|
||||
invitees: Array<{
|
||||
name: string;
|
||||
email: string;
|
||||
}>;
|
||||
template: string;
|
||||
extraProperties: Record<string, any>;
|
||||
};
|
||||
executedAt: string;
|
||||
};
|
||||
|
||||
if (payload.event === 'MeetingReady') {
|
||||
return [payload];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
createTrigger,
|
||||
Property,
|
||||
TriggerStrategy,
|
||||
} from '@activepieces/pieces-framework';
|
||||
import { tldvAuth } from '../common/auth';
|
||||
|
||||
export const transcriptReady = createTrigger({
|
||||
auth: tldvAuth,
|
||||
name: 'transcript_ready',
|
||||
displayName: 'Transcript Ready',
|
||||
description: 'Triggers when a meeting transcript has been generated',
|
||||
props: {
|
||||
webhookInstructions: Property.MarkDown({
|
||||
value: `
|
||||
## Setup Instructions
|
||||
|
||||
To use this trigger, configure a webhook in tl;dv:
|
||||
|
||||
1. Go to your tl;dv dashboard
|
||||
2. Navigate to Settings > Webhooks
|
||||
3. Add a new webhook
|
||||
4. Set the **URL** to:
|
||||
\`\`\`text
|
||||
{{webhookUrl}}
|
||||
\`\`\`
|
||||
5. Select the **TranscriptReady** event type
|
||||
6. Choose the scope (User, Team, or Organization level)
|
||||
7. Save the webhook
|
||||
|
||||
The webhook will trigger whenever a meeting transcript is generated.
|
||||
`,
|
||||
}),
|
||||
},
|
||||
type: TriggerStrategy.WEBHOOK,
|
||||
sampleData: {
|
||||
id: 'webhook-456',
|
||||
event: 'TranscriptReady',
|
||||
data: {
|
||||
id: 'transcript-123',
|
||||
meetingId: 'meeting-123',
|
||||
data: [
|
||||
{
|
||||
speaker: 'John Doe',
|
||||
text: 'Hello everyone, welcome to today\'s meeting.',
|
||||
startTime: 0,
|
||||
endTime: 3,
|
||||
},
|
||||
{
|
||||
speaker: 'Jane Smith',
|
||||
text: 'Thanks for having me.',
|
||||
startTime: 4,
|
||||
endTime: 6,
|
||||
},
|
||||
],
|
||||
},
|
||||
executedAt: '2024-01-15T10:35:00Z',
|
||||
},
|
||||
async onEnable(context) {
|
||||
// Webhook URL is automatically provided by Activepieces
|
||||
// User needs to manually configure the webhook in tl;dv dashboard
|
||||
},
|
||||
async onDisable(context) {
|
||||
// User should remove webhook from tl;dv dashboard
|
||||
},
|
||||
async run(context) {
|
||||
const payload = context.payload.body as {
|
||||
id: string;
|
||||
event: string;
|
||||
data: {
|
||||
id: string;
|
||||
meetingId: string;
|
||||
data: Array<{
|
||||
speaker: string;
|
||||
text: string;
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
}>;
|
||||
};
|
||||
executedAt: string;
|
||||
};
|
||||
|
||||
if (payload.event === 'TranscriptReady') {
|
||||
return [payload];
|
||||
}
|
||||
|
||||
return [];
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"importHelpers": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user