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,35 @@
|
||||
import { PieceAuth } from "@activepieces/pieces-framework";
|
||||
import { makeRequest } from "./client";
|
||||
import { HttpMethod } from "@activepieces/pieces-common";
|
||||
|
||||
export const murfAuth = PieceAuth.SecretText({
|
||||
displayName: "API Key",
|
||||
description: "Enter your API key from https://murf.ai",
|
||||
required: true,
|
||||
validate: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
valid: false,
|
||||
error: "API Key is required",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await makeRequest(auth as string, HttpMethod.GET, "/auth/token");
|
||||
|
||||
if (response && response.token) {
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
return {
|
||||
valid: false,
|
||||
error: "Invalid API key or token could not be generated",
|
||||
};
|
||||
} catch (e: any) {
|
||||
return {
|
||||
valid: false,
|
||||
error: `Auth validation failed: ${e.message}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import { HttpMethod, httpClient } from "@activepieces/pieces-common";
|
||||
|
||||
export const BASE_URL = "https://api.murf.ai/v1";
|
||||
|
||||
export async function makeRequest(
|
||||
apiKey: string,
|
||||
method: HttpMethod,
|
||||
path: string,
|
||||
body?: any,
|
||||
isFormData = false
|
||||
) {
|
||||
try {
|
||||
let headers: Record<string, string> = {
|
||||
"api-key": apiKey,
|
||||
};
|
||||
|
||||
const requestBody = body;
|
||||
|
||||
if (!isFormData) {
|
||||
headers["Content-Type"] = "application/json";
|
||||
} else if (body && typeof (body as any).getHeaders === "function") {
|
||||
headers = { ...headers, ...(body as any).getHeaders() };
|
||||
}
|
||||
|
||||
const response = await httpClient.sendRequest({
|
||||
method,
|
||||
url: `${BASE_URL}${path}`,
|
||||
headers,
|
||||
body: requestBody,
|
||||
});
|
||||
|
||||
return response.body;
|
||||
} catch (error: any) {
|
||||
throw new Error(
|
||||
`Unexpected error: ${JSON.stringify(error.response || error.message || error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
import { Property } from "@activepieces/pieces-framework";
|
||||
import { HttpMethod } from "@activepieces/pieces-common";
|
||||
import { makeRequest } from "./client";
|
||||
import { murfAuth } from "./auth";
|
||||
|
||||
// Helper to fetch voices
|
||||
const getVoices = async (apiKey: string) => {
|
||||
return await makeRequest(apiKey, HttpMethod.GET, "/speech/voices");
|
||||
};
|
||||
|
||||
// Helper to build unique language list
|
||||
const getLanguages = async (apiKey: string) => {
|
||||
const voices = await getVoices(apiKey);
|
||||
const languageMap = new Map<string, string>();
|
||||
|
||||
voices.forEach((voice: any) => {
|
||||
if (voice.supportedLocales) {
|
||||
Object.keys(voice.supportedLocales).forEach((localeCode) => {
|
||||
if (!languageMap.has(localeCode)) {
|
||||
languageMap.set(
|
||||
localeCode,
|
||||
voice.supportedLocales[localeCode].detail || localeCode
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(languageMap, ([value, label]) => ({ label, value }));
|
||||
};
|
||||
|
||||
export const murfCommon = {
|
||||
language: Property.Dropdown({
|
||||
auth: murfAuth,
|
||||
displayName: "Language",
|
||||
description: "Select your preferred language for the translated output.",
|
||||
required: true,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: "Please connect your Murf account first.",
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const langs = await getLanguages(auth.secret_text);
|
||||
return {
|
||||
disabled: false,
|
||||
options: langs,
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
voiceId: Property.Dropdown({
|
||||
auth: murfAuth,
|
||||
displayName: "Voice",
|
||||
description: "Choose a voice for converting text into speech",
|
||||
required: true,
|
||||
refreshers: ["language"],
|
||||
options: async ({ auth, language }) => {
|
||||
if (!auth|| !language) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: "Please select a language and connect your Murf account first.",
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const voices = await getVoices(auth.secret_text);
|
||||
const filtered = voices.filter((v: any) =>
|
||||
Object.keys(v.supportedLocales || {}).includes(language as string)
|
||||
);
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: filtered.map((v: any) => ({
|
||||
label: `${v.displayName} (${v.gender}, ${v.locale})`,
|
||||
value: v.voiceId,
|
||||
})),
|
||||
};
|
||||
},
|
||||
}),
|
||||
sourceLocale: Property.Dropdown({
|
||||
auth: murfAuth,
|
||||
displayName: "Source Locale",
|
||||
description: "Select the source locale for input text.",
|
||||
required: false,
|
||||
refreshers: [],
|
||||
options: async ({ auth }) => {
|
||||
if (!auth) {
|
||||
return {
|
||||
disabled: true,
|
||||
placeholder: "Please connect your Murf account first.",
|
||||
options: [],
|
||||
};
|
||||
}
|
||||
|
||||
const voices = await getVoices(auth.secret_text);
|
||||
|
||||
const localeMap = new Map<string, string>();
|
||||
voices.forEach((voice: any) => {
|
||||
if (voice.supportedLocales) {
|
||||
Object.entries(voice.supportedLocales).forEach(([localeCode, localeData]: any) => {
|
||||
if (!localeMap.has(localeCode)) {
|
||||
localeMap.set(localeCode, localeData.detail);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
disabled: false,
|
||||
options: Array.from(localeMap, ([value, label]) => ({ value, label })),
|
||||
};
|
||||
},
|
||||
}),
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user