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:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,18 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,7 @@
# pieces-sendpulse
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build pieces-sendpulse` to build the library.

View File

@@ -0,0 +1,10 @@
{
"name": "@activepieces/piece-sendpulse",
"version": "0.0.8",
"type": "commonjs",
"main": "./src/index.js",
"types": "./src/index.d.ts",
"dependencies": {
"tslib": "^2.3.0"
}
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-sendpulse",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/sendpulse/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/sendpulse",
"tsConfig": "packages/pieces/community/sendpulse/tsconfig.lib.json",
"packageJson": "packages/pieces/community/sendpulse/package.json",
"main": "packages/pieces/community/sendpulse/src/index.ts",
"assets": [
"packages/pieces/community/sendpulse/*.md",
{
"input": "packages/pieces/community/sendpulse/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
},
"dependsOn": [
"^build",
"prebuild"
]
},
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
},
"prebuild": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/pieces/community/sendpulse",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
}
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "Kunden-ID",
"Client Secret": "Kundengeheimnis",
"Enter your SendPulse client credentials": "Geben Sie Ihre SendPulse Client-Zugangsdaten ein",
"Add Subscriber": "Abonnent hinzufügen",
"Change Variable for Subscriber": "Variable für Abonnenten ändern",
"Delete Contact": "Kontakt löschen",
"Unsubscribe User": "Benutzer abbestellen",
"Update Subscriber": "Abonnent aktualisieren",
"Custom API Call": "Eigener API-Aufruf",
"Add subscriber to mailing list": "Abonnent zur Mailingliste hinzufügen",
"Update subscriber variable": "Abonnenten-Variable aktualisieren",
"Permanently delete contact from all mailing lists": "Kontakt dauerhaft von allen Mailinglisten löschen",
"Remove subscribers from mailing list": "Abonnenten von der Mailingliste entfernen",
"Update subscriber details and variables": "Abonnenten-Details und Variablen aktualisieren",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Mailing List": "Mailingliste",
"Email Address": "E-Mail-Adresse",
"Variables": "Variablen",
"Tag IDs": "Tag-IDs",
"Variable Name": "Variablenname",
"Variable Value": "Variablenwert",
"Email Addresses": "E-Mail-Adressen",
"Phone Number": "Telefonnummer",
"Method": "Methode",
"Headers": "Kopfzeilen",
"Query Parameters": "Abfrageparameter",
"Body": "Körper",
"Response is Binary ?": "Antwort ist binär?",
"No Error on Failure": "Kein Fehler bei Fehler",
"Timeout (in seconds)": "Timeout (in Sekunden)",
"Select one of your SendPulse mailing lists": "Wählen Sie eine Ihrer SendPulse Mailinglisten",
"Subscriber email address": "E-Mail Adresse des Abonnenten",
"Optional subscriber variables (e.g., name, phone)": "Optionale Abonnentenvariablen (z.B. Name, Telefon)",
"Optional tag IDs to assign": "Optionale Tag-IDs zum Zuweisen",
"Select variable to update": "Wählen Sie die zu aktualisierende Variable",
"New value for the variable": "Neuer Wert für die Variable",
"Email address to delete from all mailing lists": "E-Mail-Adresse, die von allen Mailinglisten gelöscht werden soll",
"Email addresses to unsubscribe (max 100)": "E-Mail-Adressen zum Abbestellen (max. 100)",
"Email address of the subscriber to update": "E-Mail-Adresse des zu aktualisierenden Abonnenten",
"New phone number (optional)": "Neue Telefonnummer (optional)",
"Subscriber variables to update (e.g., name, custom fields)": "Abonnenten-Variablen zum Aktualisieren (z. B. Name, benutzerdefinierte Felder)",
"Authorization headers are injected automatically from your connection.": "Autorisierungs-Header werden automatisch von Ihrer Verbindung injiziert.",
"Enable for files like PDFs, images, etc..": "Aktivieren für Dateien wie PDFs, Bilder, etc..",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"New Subscriber": "Neuer Abonnent",
"New Unsubscriber": "Neuer Abmelder",
"Updated Subscriber": "Abonnent aktualisiert",
"Fires when new subscriber is added": "Feuert wenn neuer Abonnent hinzugefügt wird",
"Fires when subscriber unsubscribes": "Löscht ab, wenn Abonnent abbestellt wird",
"Fires when subscriber details change (polling)": "Feuer, wenn Abonnentendetails geändert werden (Umfrage)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "ID de cliente",
"Client Secret": "Cliente secreto",
"Enter your SendPulse client credentials": "Introduzca sus credenciales de cliente de SendPulse",
"Add Subscriber": "Añadir suscriptor",
"Change Variable for Subscriber": "Cambiar variable para el suscriptor",
"Delete Contact": "Eliminar contacto",
"Unsubscribe User": "Darse de baja de usuario",
"Update Subscriber": "Actualizar suscriptor",
"Custom API Call": "Llamada API personalizada",
"Add subscriber to mailing list": "Añadir suscriptor a la lista de correo",
"Update subscriber variable": "Actualizar variable de suscriptor",
"Permanently delete contact from all mailing lists": "Borrar permanentemente el contacto de todas las listas de correo",
"Remove subscribers from mailing list": "Eliminar suscriptores de la lista de correo",
"Update subscriber details and variables": "Actualizar detalles del suscriptor y variables",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Mailing List": "Lista de correo",
"Email Address": "Dirección de email",
"Variables": "Variables",
"Tag IDs": "ID de etiqueta",
"Variable Name": "Nombre variable",
"Variable Value": "Valor variable",
"Email Addresses": "Direcciones de email",
"Phone Number": "Número de teléfono",
"Method": "Método",
"Headers": "Encabezados",
"Query Parameters": "Parámetros de consulta",
"Body": "Cuerpo",
"Response is Binary ?": "¿Respuesta es binaria?",
"No Error on Failure": "No hay ningún error en fallo",
"Timeout (in seconds)": "Tiempo de espera (en segundos)",
"Select one of your SendPulse mailing lists": "Seleccione una de sus listas de correo de SendPulse",
"Subscriber email address": "Dirección de correo del suscriptor",
"Optional subscriber variables (e.g., name, phone)": "Variables opcionales de suscriptores (por ejemplo, nombre, teléfono)",
"Optional tag IDs to assign": "ID de etiqueta opcional a asignar",
"Select variable to update": "Seleccione la variable a actualizar",
"New value for the variable": "Nuevo valor para la variable",
"Email address to delete from all mailing lists": "Dirección de correo electrónico a eliminar de todas las listas de correo",
"Email addresses to unsubscribe (max 100)": "Direcciones de correo para darse de baja (máx. 100)",
"Email address of the subscriber to update": "Dirección de correo electrónico del suscriptor a actualizar",
"New phone number (optional)": "Nuevo número de teléfono (opcional)",
"Subscriber variables to update (e.g., name, custom fields)": "Variables de suscriptores a actualizar (por ejemplo, nombre, campos personalizados)",
"Authorization headers are injected automatically from your connection.": "Las cabeceras de autorización se inyectan automáticamente desde tu conexión.",
"Enable for files like PDFs, images, etc..": "Activar para archivos como PDFs, imágenes, etc.",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"New Subscriber": "Nuevo suscriptor",
"New Unsubscriber": "Nuevo desuscriptor",
"Updated Subscriber": "Suscriptor actualizado",
"Fires when new subscriber is added": "Dispara cuando se añade un nuevo suscriptor",
"Fires when subscriber unsubscribes": "Se activa cuando los suscriptores se dan de baja",
"Fires when subscriber details change (polling)": "Dispara cuando los detalles del suscriptor cambian (encuesta)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "ID client",
"Client Secret": "Clé secrète du client",
"Enter your SendPulse client credentials": "Entrez vos identifiants de client SendPulse",
"Add Subscriber": "Ajouter un abonné",
"Change Variable for Subscriber": "Modifier la variable pour l'abonné",
"Delete Contact": "Supprimer le contact",
"Unsubscribe User": "Se désabonner de l'utilisateur",
"Update Subscriber": "Mettre à jour l'abonné",
"Custom API Call": "Appel d'API personnalisé",
"Add subscriber to mailing list": "Ajouter un abonné à la liste de diffusion",
"Update subscriber variable": "Mettre à jour la variable d'abonné",
"Permanently delete contact from all mailing lists": "Supprimer définitivement le contact de toutes les listes de diffusion",
"Remove subscribers from mailing list": "Retirer les abonnés de la liste de diffusion",
"Update subscriber details and variables": "Mettre à jour les détails de l'abonné et les variables",
"Make a custom API call to a specific endpoint": "Passer un appel API personnalisé à un endpoint spécifique",
"Mailing List": "Liste de diffusion",
"Email Address": "Adresse e-mail",
"Variables": "Variables",
"Tag IDs": "ID du tag",
"Variable Name": "Nom de la variable",
"Variable Value": "Valeur de la variable",
"Email Addresses": "Adresses e-mail",
"Phone Number": "Numéro de téléphone",
"Method": "Méthode",
"Headers": "En-têtes",
"Query Parameters": "Paramètres de requête",
"Body": "Corps",
"Response is Binary ?": "La réponse est Binaire ?",
"No Error on Failure": "Aucune erreur en cas d'échec",
"Timeout (in seconds)": "Délai d'expiration (en secondes)",
"Select one of your SendPulse mailing lists": "Sélectionnez une de vos listes de diffusion SendPulse",
"Subscriber email address": "Adresse e-mail de l'abonné",
"Optional subscriber variables (e.g., name, phone)": "Variables optionnelles d'abonné (par exemple, nom, téléphone)",
"Optional tag IDs to assign": "ID de tag facultatif à assigner",
"Select variable to update": "Sélectionnez la variable à mettre à jour",
"New value for the variable": "Nouvelle valeur pour la variable",
"Email address to delete from all mailing lists": "Adresse e-mail à supprimer de toutes les listes de diffusion",
"Email addresses to unsubscribe (max 100)": "Adresses e-mail pour se désabonner (max 100)",
"Email address of the subscriber to update": "Adresse e-mail de l'abonné à mettre à jour",
"New phone number (optional)": "Nouveau numéro de téléphone (facultatif)",
"Subscriber variables to update (e.g., name, custom fields)": "Variables d'abonné à mettre à jour (par exemple, nom, champs personnalisés)",
"Authorization headers are injected automatically from your connection.": "Les en-têtes d'autorisation sont injectés automatiquement à partir de votre connexion.",
"Enable for files like PDFs, images, etc..": "Activer pour les fichiers comme les PDFs, les images, etc.",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Subscriber": "Nouvel abonné",
"New Unsubscriber": "Nouveau désabonné",
"Updated Subscriber": "Abonné mis à jour",
"Fires when new subscriber is added": "Déclenche quand un nouvel abonné est ajouté",
"Fires when subscriber unsubscribes": "Déclenche lorsque l'abonné se désabonne",
"Fires when subscriber details change (polling)": "Déclenche lorsque les détails de l'abonné changent (sondage)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "クライアント ID",
"Client Secret": "クライアントシークレット",
"Enter your SendPulse client credentials": "SendPulseクライアントの資格情報を入力してください",
"Add Subscriber": "購読者を追加",
"Change Variable for Subscriber": "購読者の変数を変更する",
"Delete Contact": "連絡先を削除",
"Unsubscribe User": "Unsubscribe User",
"Update Subscriber": "購読者を更新",
"Custom API Call": "カスタムAPI通話",
"Add subscriber to mailing list": "購読者をメーリングリストに追加",
"Update subscriber variable": "購読者変数を更新",
"Permanently delete contact from all mailing lists": "すべてのメーリングリストから連絡先を完全に削除",
"Remove subscribers from mailing list": "メーリングリストから購読者を削除",
"Update subscriber details and variables": "購読者の詳細と変数を更新",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Mailing List": "メーリングリスト:",
"Email Address": "メールアドレス",
"Variables": "変数",
"Tag IDs": "タグID",
"Variable Name": "変数名",
"Variable Value": "変数値",
"Email Addresses": "メールアドレス",
"Phone Number": "電話番号",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"Select one of your SendPulse mailing lists": "SendPulseメーリングリストのいずれかを選択してください",
"Subscriber email address": "購読者メールアドレス",
"Optional subscriber variables (e.g., name, phone)": "オプションの購読者変数(例、名前、電話番号)",
"Optional tag IDs to assign": "割り当てるオプションのタグID",
"Select variable to update": "更新する変数を選択",
"New value for the variable": "変数の新しい値",
"Email address to delete from all mailing lists": "すべてのメーリングリストから削除するメールアドレス",
"Email addresses to unsubscribe (max 100)": "購読解除メールアドレス最大100",
"Email address of the subscriber to update": "更新する購読者のメールアドレス",
"New phone number (optional)": "新しい電話番号 (オプション)",
"Subscriber variables to update (e.g., name, custom fields)": "更新する変数(例、名前、カスタムフィールド)",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"New Subscriber": "新規購読",
"New Unsubscriber": "新しい購読解除",
"Updated Subscriber": "購読者の更新",
"Fires when new subscriber is added": "新しい購読者が追加されたときに発火します",
"Fires when subscriber unsubscribes": "購読者が購読解除した時に発火する",
"Fires when subscriber details change (polling)": "購読者の詳細が変更されたときに発火します(ポーリング)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "Klant ID",
"Client Secret": "Client Secret",
"Enter your SendPulse client credentials": "Voer uw SendPulse clientreferenties in",
"Add Subscriber": "Voeg abonnee toe",
"Change Variable for Subscriber": "Variabele voor abonnee wijzigen",
"Delete Contact": "Contactpersoon verwijderen",
"Unsubscribe User": "Gebruiker afmelden",
"Update Subscriber": "Abonnee bijwerken",
"Custom API Call": "Custom API Call",
"Add subscriber to mailing list": "Voeg abonnee toe aan de mailinglijst",
"Update subscriber variable": "Abonnee variabele bijwerken",
"Permanently delete contact from all mailing lists": "Contact permanent verwijderen uit alle mailinglijsten",
"Remove subscribers from mailing list": "Abonnees van mailinglijst verwijderen",
"Update subscriber details and variables": "Abonnee details en variabelen bijwerken",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Mailing List": "Mailing lijst",
"Email Address": "Uw e-mailadres",
"Variables": "Variabelen",
"Tag IDs": "Tag ID's",
"Variable Name": "Variabele naam",
"Variable Value": "Variabele waarde",
"Email Addresses": "E-mail adressen",
"Phone Number": "Telefoon nummer",
"Method": "Methode",
"Headers": "Kopteksten",
"Query Parameters": "Query parameters",
"Body": "Lichaam",
"Response is Binary ?": "Antwoord is binair?",
"No Error on Failure": "Geen fout bij fout",
"Timeout (in seconds)": "Time-out (in seconden)",
"Select one of your SendPulse mailing lists": "Selecteer een van je SendPulse mailing lijsten",
"Subscriber email address": "E-mailadres abonnee",
"Optional subscriber variables (e.g., name, phone)": "Optionele abonnee variabelen (bijv. naam, telefoon)",
"Optional tag IDs to assign": "Optionele tag IDs toe te wijzen",
"Select variable to update": "Selecteer een variabele om bij te werken",
"New value for the variable": "Nieuwe waarde voor de variabele",
"Email address to delete from all mailing lists": "E-mailadres om te verwijderen uit alle mailinglijsten",
"Email addresses to unsubscribe (max 100)": "Email adressen om af te melden (max 100)",
"Email address of the subscriber to update": "E-mailadres van de te updaten abonnee",
"New phone number (optional)": "Nieuw telefoonnummer (optioneel)",
"Subscriber variables to update (e.g., name, custom fields)": "Bijwerken abonnee variabelen (bijv. naam, aangepaste velden)",
"Authorization headers are injected automatically from your connection.": "Autorisatie headers worden automatisch geïnjecteerd vanuit uw verbinding.",
"Enable for files like PDFs, images, etc..": "Inschakelen voor bestanden zoals PDF's, afbeeldingen etc..",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"New Subscriber": "Nieuwe abonnee",
"New Unsubscriber": "Nieuwe afmelder",
"Updated Subscriber": "Abonnee bijgewerkt",
"Fires when new subscriber is added": "Vuurt wanneer nieuwe abonnee is toegevoegd",
"Fires when subscriber unsubscribes": "Vuurt wanneer abonnee zich uitschrijft",
"Fires when subscriber details change (polling)": "Vuurt wanneer details van abonnee veranderen (polling)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "ID do Cliente",
"Client Secret": "Chave secreta",
"Enter your SendPulse client credentials": "Digite suas credenciais do cliente SendPulse",
"Add Subscriber": "Adicionar Assinante",
"Change Variable for Subscriber": "Alterar variável para Assinante",
"Delete Contact": "Excluir contato",
"Unsubscribe User": "Desinscrever usuário",
"Update Subscriber": "Atualizar Assinante",
"Custom API Call": "Chamada de API personalizada",
"Add subscriber to mailing list": "Adicionar assinante à lista de e-mails",
"Update subscriber variable": "Atualizar variável de assinante",
"Permanently delete contact from all mailing lists": "Excluir permanentemente contatos de todas as listas de discussão",
"Remove subscribers from mailing list": "Remover membros da lista de e-mails",
"Update subscriber details and variables": "Atualizar detalhes e variáveis de assinantes",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Mailing List": "Lista de Correio",
"Email Address": "Endereço de e-mail",
"Variables": "Variáveis",
"Tag IDs": "IDs de tags",
"Variable Name": "Nome da Variável",
"Variable Value": "Valor da Variável",
"Email Addresses": "Endereços de e-mail",
"Phone Number": "Número de telefone",
"Method": "Método",
"Headers": "Cabeçalhos",
"Query Parameters": "Parâmetros da consulta",
"Body": "Conteúdo",
"Response is Binary ?": "A resposta é binária ?",
"No Error on Failure": "Nenhum erro no Failure",
"Timeout (in seconds)": "Tempo limite (em segundos)",
"Select one of your SendPulse mailing lists": "Selecione uma das suas listas de discussão do SendPulse",
"Subscriber email address": "Endereço de e-mail assinante",
"Optional subscriber variables (e.g., name, phone)": "Variáveis opcionais de assinante (por exemplo, nome, telefone)",
"Optional tag IDs to assign": "IDs de tags opcionais para atribuir",
"Select variable to update": "Selecione a variável para atualizar",
"New value for the variable": "Novo valor para a variável",
"Email address to delete from all mailing lists": "Endereço de email a ser excluído de todas as listas de email",
"Email addresses to unsubscribe (max 100)": "Endereços de e-mail para cancelar inscrição (máx. 100)",
"Email address of the subscriber to update": "Endereço de e-mail do assinante para atualizar",
"New phone number (optional)": "Novo número de telefone (opcional)",
"Subscriber variables to update (e.g., name, custom fields)": "Variáveis de assinante para atualizar (por exemplo, nome, campos personalizados)",
"Authorization headers are injected automatically from your connection.": "Os cabeçalhos de autorização são inseridos automaticamente a partir da sua conexão.",
"Enable for files like PDFs, images, etc..": "Habilitar para arquivos como PDFs, imagens, etc..",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"New Subscriber": "Novo Assinante",
"New Unsubscriber": "Novo Desassinante",
"Updated Subscriber": "Assinante atualizado",
"Fires when new subscriber is added": "Efetua quando um novo assinante é adicionado",
"Fires when subscriber unsubscribes": "Atira quando o assinante cancelar a assinatura",
"Fires when subscriber details change (polling)": "Efetua quando os detalhes do inscrito mudam (enquete)"
}

View File

@@ -0,0 +1,56 @@
{
"SendPulse": "SendPulse",
"Client ID": "ID клиента",
"Client Secret": "Секрет клиента",
"Enter your SendPulse client credentials": "Введите учетные данные клиента SendPulse",
"Add Subscriber": "Добавить подписчика",
"Change Variable for Subscriber": "Изменить переменную для подписчика",
"Delete Contact": "Удалить контакт",
"Unsubscribe User": "Отписаться от пользователя",
"Update Subscriber": "Обновить подписчика",
"Custom API Call": "Пользовательский вызов API",
"Add subscriber to mailing list": "Добавить подписчика в список рассылки",
"Update subscriber variable": "Обновить переменную подписчика",
"Permanently delete contact from all mailing lists": "Удалить контакт из всех списков рассылки",
"Remove subscribers from mailing list": "Удалить подписчиков из списка рассылки",
"Update subscriber details and variables": "Обновить детали и переменные подписчика",
"Make a custom API call to a specific endpoint": "Сделать пользовательский API вызов к определенной конечной точке",
"Mailing List": "Список рассылки",
"Email Address": "Email Address",
"Variables": "Переменные",
"Tag IDs": "ID тегов",
"Variable Name": "Имя переменной",
"Variable Value": "Переменное значение",
"Email Addresses": "Адреса E-mail",
"Phone Number": "Номер телефона",
"Method": "Метод",
"Headers": "Заголовки",
"Query Parameters": "Параметры запроса",
"Body": "Тело",
"No Error on Failure": "Нет ошибок при ошибке",
"Timeout (in seconds)": "Таймаут (в секундах)",
"Select one of your SendPulse mailing lists": "Выберите один из ваших списков рассылки SendPulse",
"Subscriber email address": "Адрес электронной почты подписчика",
"Optional subscriber variables (e.g., name, phone)": "Необязательные переменные подписчика (например, имя, телефон)",
"Optional tag IDs to assign": "Необязательные идентификаторы тегов для назначения",
"Select variable to update": "Выберите переменную для обновления",
"New value for the variable": "Новое значение для переменной",
"Email address to delete from all mailing lists": "Адрес электронной почты для удаления из всех списков рассылки",
"Email addresses to unsubscribe (max 100)": "Отписаться от e-mail адресов (максимум 100)",
"Email address of the subscriber to update": "Адрес электронной почты подписчика для обновления",
"New phone number (optional)": "Новый номер телефона (необязательно)",
"Subscriber variables to update (e.g., name, custom fields)": "Абонентские переменные для обновления (например, имя, пользовательские поля)",
"Authorization headers are injected automatically from your connection.": "Заголовки авторизации включаются автоматически из вашего соединения.",
"GET": "ПОЛУЧИТЬ",
"POST": "ПОСТ",
"PATCH": "ПАТЧ",
"PUT": "ПОКУПИТЬ",
"DELETE": "УДАЛИТЬ",
"HEAD": "HEAD",
"New Subscriber": "Новый подписчик",
"New Unsubscriber": "Новый отказ от подписки",
"Updated Subscriber": "Абонент обновлен",
"Fires when new subscriber is added": "Оказывается при добавлении нового абонента",
"Fires when subscriber unsubscribes": "Огнестрел при отказе от подписки",
"Fires when subscriber details change (polling)": "Огнестрелы при смене сведений подписчика (опрос)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "Client ID",
"Client Secret": "Client Secret",
"Enter your SendPulse client credentials": "Enter your SendPulse client credentials",
"Add Subscriber": "Add Subscriber",
"Change Variable for Subscriber": "Change Variable for Subscriber",
"Delete Contact": "Delete Contact",
"Unsubscribe User": "Unsubscribe User",
"Update Subscriber": "Update Subscriber",
"Custom API Call": "Custom API Call",
"Add subscriber to mailing list": "Add subscriber to mailing list",
"Update subscriber variable": "Update subscriber variable",
"Permanently delete contact from all mailing lists": "Permanently delete contact from all mailing lists",
"Remove subscribers from mailing list": "Remove subscribers from mailing list",
"Update subscriber details and variables": "Update subscriber details and variables",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Mailing List": "Mailing List",
"Email Address": "Email Address",
"Variables": "Variables",
"Tag IDs": "Tag IDs",
"Variable Name": "Variable Name",
"Variable Value": "Variable Value",
"Email Addresses": "Email Addresses",
"Phone Number": "Phone Number",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Select one of your SendPulse mailing lists": "Select one of your SendPulse mailing lists",
"Subscriber email address": "Subscriber email address",
"Optional subscriber variables (e.g., name, phone)": "Optional subscriber variables (e.g., name, phone)",
"Optional tag IDs to assign": "Optional tag IDs to assign",
"Select variable to update": "Select variable to update",
"New value for the variable": "New value for the variable",
"Email address to delete from all mailing lists": "Email address to delete from all mailing lists",
"Email addresses to unsubscribe (max 100)": "Email addresses to unsubscribe (max 100)",
"Email address of the subscriber to update": "Email address of the subscriber to update",
"New phone number (optional)": "New phone number (optional)",
"Subscriber variables to update (e.g., name, custom fields)": "Subscriber variables to update (e.g., name, custom fields)",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Subscriber": "New Subscriber",
"New Unsubscriber": "New Unsubscriber",
"Updated Subscriber": "Updated Subscriber",
"Fires when new subscriber is added": "Fires when new subscriber is added",
"Fires when subscriber unsubscribes": "Fires when subscriber unsubscribes",
"Fires when subscriber details change (polling)": "Fires when subscriber details change (polling)"
}

View File

@@ -0,0 +1,56 @@
{
"SendPulse": "SendPulse",
"Client ID": "Client ID",
"Client Secret": "Client Secret",
"Enter your SendPulse client credentials": "Enter your SendPulse client credentials",
"Add Subscriber": "Add Subscriber",
"Change Variable for Subscriber": "Change Variable for Subscriber",
"Delete Contact": "Delete Contact",
"Unsubscribe User": "Unsubscribe User",
"Update Subscriber": "Update Subscriber",
"Custom API Call": "Custom API Call",
"Add subscriber to mailing list": "Add subscriber to mailing list",
"Update subscriber variable": "Update subscriber variable",
"Permanently delete contact from all mailing lists": "Permanently delete contact from all mailing lists",
"Remove subscribers from mailing list": "Remove subscribers from mailing list",
"Update subscriber details and variables": "Update subscriber details and variables",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Mailing List": "Mailing List",
"Email Address": "Email Address",
"Variables": "Variables",
"Tag IDs": "Tag IDs",
"Variable Name": "Variable Name",
"Variable Value": "Variable Value",
"Email Addresses": "Email Addresses",
"Phone Number": "Phone Number",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Select one of your SendPulse mailing lists": "Select one of your SendPulse mailing lists",
"Subscriber email address": "Subscriber email address",
"Optional subscriber variables (e.g., name, phone)": "Optional subscriber variables (e.g., name, phone)",
"Optional tag IDs to assign": "Optional tag IDs to assign",
"Select variable to update": "Select variable to update",
"New value for the variable": "New value for the variable",
"Email address to delete from all mailing lists": "Email address to delete from all mailing lists",
"Email addresses to unsubscribe (max 100)": "Email addresses to unsubscribe (max 100)",
"Email address of the subscriber to update": "Email address of the subscriber to update",
"New phone number (optional)": "New phone number (optional)",
"Subscriber variables to update (e.g., name, custom fields)": "Subscriber variables to update (e.g., name, custom fields)",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Subscriber": "New Subscriber",
"New Unsubscriber": "New Unsubscriber",
"Updated Subscriber": "Updated Subscriber",
"Fires when new subscriber is added": "Fires when new subscriber is added",
"Fires when subscriber unsubscribes": "Fires when subscriber unsubscribes",
"Fires when subscriber details change (polling)": "Fires when subscriber details change (polling)"
}

View File

@@ -0,0 +1,57 @@
{
"Client ID": "客户端ID",
"Client Secret": "客户端密钥",
"Enter your SendPulse client credentials": "Enter your SendPulse client credentials",
"Add Subscriber": "Add Subscriber",
"Change Variable for Subscriber": "Change Variable for Subscriber",
"Delete Contact": "Delete Contact",
"Unsubscribe User": "Unsubscribe User",
"Update Subscriber": "Update Subscriber",
"Custom API Call": "自定义 API 呼叫",
"Add subscriber to mailing list": "Add subscriber to mailing list",
"Update subscriber variable": "Update subscriber variable",
"Permanently delete contact from all mailing lists": "Permanently delete contact from all mailing lists",
"Remove subscribers from mailing list": "Remove subscribers from mailing list",
"Update subscriber details and variables": "Update subscriber details and variables",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Mailing List": "Mailing List",
"Email Address": "Email Address",
"Variables": "Variables",
"Tag IDs": "Tag IDs",
"Variable Name": "Variable Name",
"Variable Value": "Variable Value",
"Email Addresses": "Email Addresses",
"Phone Number": "Phone Number",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"Select one of your SendPulse mailing lists": "Select one of your SendPulse mailing lists",
"Subscriber email address": "Subscriber email address",
"Optional subscriber variables (e.g., name, phone)": "Optional subscriber variables (e.g., name, phone)",
"Optional tag IDs to assign": "Optional tag IDs to assign",
"Select variable to update": "Select variable to update",
"New value for the variable": "New value for the variable",
"Email address to delete from all mailing lists": "Email address to delete from all mailing lists",
"Email addresses to unsubscribe (max 100)": "Email addresses to unsubscribe (max 100)",
"Email address of the subscriber to update": "Email address of the subscriber to update",
"New phone number (optional)": "New phone number (optional)",
"Subscriber variables to update (e.g., name, custom fields)": "Subscriber variables to update (e.g., name, custom fields)",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"New Subscriber": "New Subscriber",
"New Unsubscriber": "New Unsubscriber",
"Updated Subscriber": "Updated Subscriber",
"Fires when new subscriber is added": "Fires when new subscriber is added",
"Fires when subscriber unsubscribes": "Fires when subscriber unsubscribes",
"Fires when subscriber details change (polling)": "Fires when subscriber details change (polling)"
}

View File

@@ -0,0 +1,58 @@
import {
createCustomApiCallAction,
HttpMethod,
} from '@activepieces/pieces-common';
import { createPiece } from '@activepieces/pieces-framework';
import { sendpulseAuth } from './lib/common/auth';
import { sendpulseApiCall } from './lib/common/client';
import { newSubscriberTrigger } from './lib/triggers/new-subscriber';
import { newUnsubscriberTrigger } from './lib/triggers/new-unsubscriber';
import { updatedSubscriberTrigger } from './lib/triggers/updated-subscriber';
import { addSubscriberAction } from './lib/actions/add-subscriber';
import { changeVariableForSubscriberAction } from './lib/actions/change-variable-for-subscriber';
import { deleteContactAction } from './lib/actions/delete-contact';
import { unsubscribeUserAction } from './lib/actions/unsubscribe-user';
import { updateSubscriberAction } from './lib/actions/update-subscriber';
export const sendpulse = createPiece({
displayName: 'SendPulse',
auth: sendpulseAuth,
minimumSupportedRelease: '0.36.1',
logoUrl: 'https://cdn.activepieces.com/pieces/sendpulse.png',
authors: ['aryel780', 'onyedikachi-david'],
actions: [
addSubscriberAction,
changeVariableForSubscriberAction,
deleteContactAction,
unsubscribeUserAction,
updateSubscriberAction,
createCustomApiCallAction({
auth: sendpulseAuth,
baseUrl: () => 'https://api.sendpulse.com',
authMapping: async (auth) => {
const typedAuth = auth.props;
const token = await sendpulseApiCall<{ access_token: string }>({
method: HttpMethod.POST,
auth: typedAuth,
resourceUri: '/oauth/access_token',
body: {
grant_type: 'client_credentials',
client_id: typedAuth.clientId,
client_secret: typedAuth.clientSecret,
},
});
return {
Authorization: `Bearer ${token.access_token}`,
};
},
}),
],
triggers: [
newSubscriberTrigger,
newUnsubscriberTrigger,
updatedSubscriberTrigger
],
});

View File

@@ -0,0 +1,83 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction, Property } from '@activepieces/pieces-framework';
import { sendpulseApiCall } from '../common/client';
import { sendpulseAuth } from '../common/auth';
import { mailingListDropdown } from '../common/props';
export const addSubscriberAction = createAction({
auth: sendpulseAuth,
name: 'add-subscriber',
displayName: 'Add Subscriber',
description: 'Add subscriber to mailing list',
props: {
mailingListId: mailingListDropdown,
email: Property.ShortText({
displayName: 'Email Address',
description: 'Subscriber email address',
required: true,
}),
variables: Property.Object({
displayName: 'Variables',
description: 'Optional subscriber variables (e.g., name, phone)',
required: false,
}),
tags: Property.Array({
displayName: 'Tag IDs',
description: 'Optional tag IDs to assign',
required: false,
}),
},
async run(context) {
const { mailingListId, email, variables, tags } = context.propsValue;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error(`Invalid email format: ${email}`);
}
const emailObject: any = { email };
if (variables && Object.keys(variables).length > 0) {
emailObject.variables = variables;
}
const requestBody: any = {
emails: [emailObject],
};
if (tags && tags.length > 0) {
const tagNumbers = tags.map((tag) => {
const num = Number(tag);
if (isNaN(num)) {
throw new Error(`Invalid tag ID: ${tag}. Tag IDs must be numbers.`);
}
return num;
});
requestBody.tags = tagNumbers;
}
try {
const result = await sendpulseApiCall<{ result: boolean }>({
method: HttpMethod.POST,
auth: context.auth.props,
resourceUri: `/addressbooks/${mailingListId}/emails`,
body: requestBody,
});
if (result.result === true) {
return {
success: true,
message: 'Subscriber added successfully',
email,
variables: variables || {},
mailingListId,
tags: requestBody.tags || [],
};
}
throw new Error('SendPulse API returned failure');
} catch (error: any) {
throw new Error(`Failed to add subscriber: ${error.message || 'Unknown error'}`);
}
},
});

View File

@@ -0,0 +1,152 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction, Property } from '@activepieces/pieces-framework';
import { sendpulseAuth } from '../common/auth';
import { sendpulseApiCall } from '../common/client';
import { mailingListDropdown } from '../common/props';
type SendpulseVariable = {
name: string;
type: string;
};
const variableDropdown = Property.Dropdown({
auth: sendpulseAuth,
displayName: 'Variable Name',
description: 'Select variable to update',
required: true,
refreshers: ['mailingListId'],
options: async ({ auth, mailingListId }) => {
if (!auth || !mailingListId) {
return {
disabled: true,
options: [],
placeholder: 'Please select a mailing list first',
};
}
try {
const variables = await sendpulseApiCall<SendpulseVariable[]>({
auth: auth as any,
method: HttpMethod.GET,
resourceUri: `/addressbooks/${mailingListId}/variables`,
});
if (!variables.length) {
return {
disabled: true,
options: [],
placeholder: 'No variables found in this mailing list',
};
}
return {
disabled: false,
options: variables
.filter(variable => variable.name !== 'email')
.map((variable) => ({
label: `${variable.name} (${variable.type})`,
value: variable.name,
})),
};
} catch (error: any) {
return {
disabled: true,
options: [],
placeholder: `Failed to load variables: ${error.message}`,
};
}
},
});
export const changeVariableForSubscriberAction = createAction({
auth: sendpulseAuth,
name: 'change-variable-for-subscriber',
displayName: 'Change Variable for Subscriber',
description: 'Update subscriber variable',
props: {
mailingListId: mailingListDropdown,
email: Property.ShortText({
displayName: 'Email Address',
description: 'Subscriber email address',
required: true,
}),
variableName: variableDropdown,
variableValue: Property.ShortText({
displayName: 'Variable Value',
description: 'New value for the variable',
required: true,
}),
},
async run(context) {
const { mailingListId, email, variableName, variableValue } = context.propsValue;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error(`Invalid email format: ${email}`);
}
let variableType: string | null = null;
try {
const variables = await sendpulseApiCall<SendpulseVariable[]>({
auth: context.auth.props,
method: HttpMethod.GET,
resourceUri: `/addressbooks/${mailingListId}/variables`,
});
const variable = variables.find(v => v.name === variableName);
if (variable) {
variableType = variable.type;
}
} catch (error) {
// Continue without type validation if variable fetch fails
}
if (variableType) {
if (variableType === 'number' && isNaN(Number(variableValue))) {
throw new Error(`Variable "${variableName}" expects a number, but got: ${variableValue}`);
}
if (variableType === 'date') {
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(variableValue) && !Date.parse(variableValue)) {
throw new Error(`Variable "${variableName}" expects a date format (YYYY-MM-DD), but got: ${variableValue}`);
}
}
}
const body = {
email,
variables: [
{
name: variableName,
value: variableValue,
},
],
};
try {
const result = await sendpulseApiCall<{ result: boolean }>({
method: HttpMethod.POST,
auth: context.auth.props,
resourceUri: `/addressbooks/${mailingListId}/emails/variable`,
body,
});
if (result.result) {
return {
success: true,
message: `Variable "${variableName}" updated for ${email}`,
email,
variableName,
variableValue,
variableType,
};
} else {
throw new Error('SendPulse API returned failure');
}
} catch (error: any) {
throw new Error(`Failed to update variable: ${error.message || 'Unknown error'}`);
}
},
});

View File

@@ -0,0 +1,47 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction, Property } from '@activepieces/pieces-framework';
import { sendpulseApiCall } from '../common/client';
import { sendpulseAuth } from '../common/auth';
export const deleteContactAction = createAction({
auth: sendpulseAuth,
name: 'delete-contact',
displayName: 'Delete Contact',
description: 'Permanently delete contact from all mailing lists',
props: {
email: Property.ShortText({
displayName: 'Email Address',
description: 'Email address to delete from all mailing lists',
required: true,
}),
},
async run(context) {
const { email } = context.propsValue;
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new Error(`Invalid email format: ${email}`);
}
try {
const result = await sendpulseApiCall<{ result: boolean }>({
method: HttpMethod.DELETE,
auth: context.auth.props,
resourceUri: `/emails/${encodeURIComponent(email)}`,
});
if (result.result) {
return {
success: true,
message: `Contact ${email} deleted from all mailing lists`,
email,
};
}
throw new Error('SendPulse API returned failure');
} catch (error: any) {
throw new Error(`Failed to delete contact: ${error.message || 'Unknown error'}`);
}
},
});

View File

@@ -0,0 +1,64 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction, Property } from '@activepieces/pieces-framework';
import { sendpulseApiCall } from '../common/client';
import { sendpulseAuth } from '../common/auth';
import { mailingListDropdown } from '../common/props';
export const unsubscribeUserAction = createAction({
auth: sendpulseAuth,
name: 'unsubscribe-user',
displayName: 'Unsubscribe User',
description: 'Remove subscribers from mailing list',
props: {
mailingListId: mailingListDropdown,
emails: Property.Array({
displayName: 'Email Addresses',
description: 'Email addresses to unsubscribe (max 100)',
required: true,
}),
},
async run(context) {
const { mailingListId, emails } = context.propsValue;
if (emails.length === 0) {
throw new Error('At least one email address is required');
}
if (emails.length > 100) {
throw new Error('Maximum 100 email addresses allowed per request');
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const invalidEmails = emails.filter(email => !emailRegex.test(email as string));
if (invalidEmails.length > 0) {
throw new Error(`Invalid email format(s): ${invalidEmails.join(', ')}`);
}
const body = { emails };
try {
const result = await sendpulseApiCall<{ result: boolean }>({
method: HttpMethod.DELETE,
auth: context.auth.props,
resourceUri: `/addressbooks/${mailingListId}/emails`,
body,
});
if (result.result) {
return {
success: true,
message: `${emails.length} subscriber(s) unsubscribed successfully`,
unsubscribed: emails,
mailingListId,
count: emails.length,
};
} else {
throw new Error('Unsubscription failed - API returned failure');
}
} catch (error: any) {
throw new Error(`Failed to unsubscribe users: ${error.message || 'Unknown error'}`);
}
},
});

View File

@@ -0,0 +1,101 @@
import { HttpMethod } from '@activepieces/pieces-common';
import { createAction, Property } from '@activepieces/pieces-framework';
import { sendpulseApiCall } from '../common/client';
import { sendpulseAuth } from '../common/auth';
import { mailingListDropdown } from '../common/props';
export const updateSubscriberAction = createAction({
auth: sendpulseAuth,
name: 'update-subscriber',
displayName: 'Update Subscriber',
description: 'Update subscriber details and variables',
props: {
mailingListId: mailingListDropdown,
email: Property.ShortText({
displayName: 'Email Address',
description: 'Email address of the subscriber to update',
required: true,
}),
phone: Property.ShortText({
displayName: 'Phone Number',
description: 'New phone number (optional)',
required: false,
}),
variables: Property.Object({
displayName: 'Variables',
description: 'Subscriber variables to update (e.g., name, custom fields)',
required: false,
}),
},
async run(context) {
const { mailingListId, email, phone, variables } = context.propsValue;
const results: any[] = [];
let hasUpdates = false;
if (phone) {
try {
const phoneResult = await sendpulseApiCall<{ result: boolean }>({
method: HttpMethod.PUT,
auth: context.auth.props,
resourceUri: `/addressbooks/${mailingListId}/phone`,
body: { email, phone },
});
if (phoneResult.result) {
results.push({ type: 'phone', success: true, value: phone });
hasUpdates = true;
} else {
results.push({ type: 'phone', success: false, error: 'API returned failure' });
}
} catch (error: any) {
results.push({ type: 'phone', success: false, error: error.message });
}
}
if (variables && Object.keys(variables).length > 0) {
const variableUpdates = Object.entries(variables).map(([name, value]) => ({
name,
value: String(value),
}));
try {
const variableResult = await sendpulseApiCall<{ result: boolean }>({
method: HttpMethod.POST,
auth: context.auth.props,
resourceUri: `/addressbooks/${mailingListId}/emails/variable`,
body: { email, variables: variableUpdates },
});
if (variableResult.result) {
results.push({ type: 'variables', success: true, count: variableUpdates.length });
hasUpdates = true;
} else {
results.push({ type: 'variables', success: false, error: 'API returned failure' });
}
} catch (error: any) {
results.push({ type: 'variables', success: false, error: error.message });
}
}
if (!hasUpdates && !phone && (!variables || Object.keys(variables).length === 0)) {
throw new Error('No updates provided. Please specify phone number or variables to update.');
}
const successfulUpdates = results.filter(r => r.success);
const failedUpdates = results.filter(r => !r.success);
if (successfulUpdates.length === 0) {
throw new Error(`All updates failed: ${failedUpdates.map(f => f.error).join(', ')}`);
}
return {
success: true,
message: `Subscriber ${email} updated successfully`,
email,
updates: results,
hasFailures: failedUpdates.length > 0,
};
},
});

View File

@@ -0,0 +1,33 @@
import { PieceAuth } from '@activepieces/pieces-framework';
import { sendpulseApiCall } from './client';
import { HttpMethod } from '@activepieces/pieces-common';
export const sendpulseAuth = PieceAuth.CustomAuth({
description: 'Enter your SendPulse client credentials',
props: {
clientId: PieceAuth.SecretText({
displayName: 'Client ID',
required: true,
}),
clientSecret: PieceAuth.SecretText({
displayName: 'Client Secret',
required: true,
}),
},
validate: async ({ auth }) => {
try {
await sendpulseApiCall({
method: HttpMethod.GET,
resourceUri: '/addressbooks',
auth,
});
return { valid: true };
} catch {
return {
valid: false,
error: 'Invalid Client ID or Client Secret',
};
}
},
required: true,
});

View File

@@ -0,0 +1,89 @@
import {
httpClient,
HttpMethod,
HttpRequest,
HttpMessageBody,
QueryParams,
} from '@activepieces/pieces-common';
export type SendPulseAuthProps = {
clientId: string;
clientSecret: string;
};
let cachedToken: string | null = null;
let tokenExpiresAt = 0;
async function getAccessToken(auth: SendPulseAuthProps): Promise<string> {
if (cachedToken && Date.now() < tokenExpiresAt) {
return cachedToken;
}
const response = await httpClient.sendRequest<{
access_token: string;
expires_in: number;
}>({
method: HttpMethod.POST,
url: 'https://api.sendpulse.com/oauth/access_token',
headers: {
'Content-Type': 'application/json',
},
body: {
grant_type: 'client_credentials',
client_id: auth.clientId,
client_secret: auth.clientSecret,
},
});
cachedToken = response.body.access_token;
tokenExpiresAt = Date.now() + response.body.expires_in * 1000 - 30 * 1000;
return cachedToken!;
}
export type SendPulseApiCallParams = {
method: HttpMethod;
resourceUri: string;
query?: Record<string, string | number | string[] | undefined>;
body?: any;
auth: SendPulseAuthProps;
};
export async function sendpulseApiCall<T extends HttpMessageBody>({
method,
resourceUri,
query,
body,
auth,
}: SendPulseApiCallParams): Promise<T> {
const token = await getAccessToken(auth);
const queryParams: QueryParams = {};
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== null && value !== undefined) {
queryParams[key] = String(value);
}
}
}
const request: HttpRequest = {
method,
url: `https://api.sendpulse.com${resourceUri}`,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
queryParams,
body,
};
try {
const response = await httpClient.sendRequest<T>(request);
return response.body;
} catch (error: any) {
const statusCode = error.response?.status;
const errorMessage = error.response?.data?.message || error.message || 'Unknown error';
throw new Error(`SendPulse API Error (${statusCode || 'Unknown'}): ${errorMessage}`);
}
}

View File

@@ -0,0 +1,58 @@
import { Property } from '@activepieces/pieces-framework';
import { HttpMethod } from '@activepieces/pieces-common';
import { sendpulseApiCall } from './client';
import { sendpulseAuth } from './auth';
type SendpulseMailingList = {
id: number;
name: string;
status_explain: string;
};
export const mailingListDropdown = Property.Dropdown({
auth: sendpulseAuth,
displayName: 'Mailing List',
description: 'Select one of your SendPulse mailing lists',
required: true,
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'Please connect your SendPulse account.',
};
}
const typedAuth = auth.props
try {
const lists = await sendpulseApiCall<SendpulseMailingList[]>({
auth: auth.props,
method: HttpMethod.GET,
resourceUri: '/addressbooks?limit=100&offset=0',
});
if (!lists.length) {
return {
disabled: true,
options: [],
placeholder: 'No mailing lists found in your account.',
};
}
return {
disabled: false,
options: lists.map((list) => ({
label: `${list.name} (${list.status_explain})`,
value: list.id,
})),
};
} catch (error: any) {
return {
disabled: true,
options: [],
placeholder: `Failed to load mailing lists: ${error.message}`,
};
}
},
});

View File

@@ -0,0 +1,131 @@
import {
createTrigger,
TriggerStrategy,
WebhookResponse,
} from '@activepieces/pieces-framework';
import { sendpulseAuth } from '../common/auth';
import { sendpulseApiCall } from '../common/client';
import { mailingListDropdown } from '../common/props';
import { HttpMethod } from '@activepieces/pieces-common';
export const newSubscriberTrigger = createTrigger({
auth: sendpulseAuth,
name: 'new_subscriber',
displayName: 'New Subscriber',
description: 'Fires when new subscriber is added',
type: TriggerStrategy.WEBHOOK,
props: {
mailingListId: mailingListDropdown,
},
async onEnable(context) {
const { mailingListId } = context.propsValue;
try {
const webhookResponse = await sendpulseApiCall<{
success: boolean;
data: Array<{ id: number; action: string; url: string }>;
}>({
method: HttpMethod.POST,
auth: context.auth.props,
resourceUri: '/v2/email-service/webhook',
body: {
url: context.webhookUrl,
actions: ['new_emails'],
},
});
if (webhookResponse.success && webhookResponse.data?.length > 0) {
const webhook = webhookResponse.data.find(w => w.action === 'new_emails');
if (webhook) {
await context.store.put('webhook_id', webhook.id);
await context.store.put('mailing_list_id', mailingListId);
}
} else {
throw new Error('Failed to create webhook');
}
} catch (error: any) {
throw new Error(`Failed to enable new subscriber trigger: ${error.message}`);
}
},
async onDisable(context) {
try {
const webhookId = await context.store.get('webhook_id');
if (webhookId) {
await sendpulseApiCall({
method: HttpMethod.DELETE,
auth: context.auth.props,
resourceUri: `/v2/email-service/webhook/${webhookId}`,
});
await context.store.delete('webhook_id');
await context.store.delete('mailing_list_id');
}
} catch (error) {
console.warn('Failed to delete webhook during disable:', error);
}
},
async run(context) {
const storedListId = await context.store.get('mailing_list_id');
interface NewSubscriberPayload {
timestamp: string;
variables: any[];
email: string;
source: string;
book_id: string;
event: string;
}
const payload = context.payload.body as NewSubscriberPayload | NewSubscriberPayload[];
const payloads = Array.isArray(payload) ? payload : [payload];
const results = [];
for (const item of payloads) {
if (item.event === 'new_emails' && item.book_id === storedListId) {
results.push({
id: `${item.email}_${item.timestamp}`,
email: item.email,
mailingListId: item.book_id,
source: item.source,
variables: item.variables || {},
addedAt: new Date(parseInt(item.timestamp) * 1000).toISOString(),
timestamp: item.timestamp,
});
}
}
return results;
},
async test() {
return [
{
id: 'demo@example.com_1496827625',
email: 'demo@example.com',
mailingListId: '123456',
source: 'address book',
variables: {},
addedAt: new Date().toISOString(),
timestamp: '1496827625',
},
];
},
sampleData: {
id: 'subscriber@example.com_1496827625',
email: 'subscriber@example.com',
mailingListId: '123456',
source: 'subscription form',
variables: {
name: 'John Doe',
},
addedAt: '2023-06-01T12:00:00.000Z',
timestamp: '1496827625',
},
});

View File

@@ -0,0 +1,137 @@
import {
createTrigger,
TriggerStrategy,
WebhookResponse,
} from '@activepieces/pieces-framework';
import { sendpulseAuth } from '../common/auth';
import { sendpulseApiCall } from '../common/client';
import { mailingListDropdown } from '../common/props';
import { HttpMethod } from '@activepieces/pieces-common';
export const newUnsubscriberTrigger = createTrigger({
auth: sendpulseAuth,
name: 'new_unsubscriber',
displayName: 'New Unsubscriber',
description: 'Fires when subscriber unsubscribes',
type: TriggerStrategy.WEBHOOK,
props: {
mailingListId: mailingListDropdown,
},
async onEnable(context) {
const { mailingListId } = context.propsValue;
try {
const webhookResponse = await sendpulseApiCall<{
success: boolean;
data: Array<{ id: number; action: string; url: string }>;
}>({
method: HttpMethod.POST,
auth: context.auth.props,
resourceUri: '/v2/email-service/webhook',
body: {
url: context.webhookUrl,
actions: ['unsubscribe'],
},
});
if (webhookResponse.success && webhookResponse.data?.length > 0) {
const webhook = webhookResponse.data.find(w => w.action === 'unsubscribe');
if (webhook) {
await context.store.put('webhook_id', webhook.id);
await context.store.put('mailing_list_id', mailingListId);
}
} else {
throw new Error('Failed to create webhook');
}
} catch (error: any) {
throw new Error(`Failed to enable unsubscriber trigger: ${error.message}`);
}
},
async onDisable(context) {
try {
const webhookId = await context.store.get('webhook_id');
if (webhookId) {
await sendpulseApiCall({
method: HttpMethod.DELETE,
auth: context.auth.props,
resourceUri: `/v2/email-service/webhook/${webhookId}`,
});
await context.store.delete('webhook_id');
await context.store.delete('mailing_list_id');
}
} catch (error) {
console.warn('Failed to delete webhook during disable:', error);
}
},
async run(context) {
const storedListId = await context.store.get('mailing_list_id');
interface UnsubscribePayload {
task_id: string;
timestamp: string;
from_all: string;
email: string;
reason: string | null;
book_id: string;
event: string;
categories: string;
}
const payload = context.payload.body as UnsubscribePayload | UnsubscribePayload[];
const payloads = Array.isArray(payload) ? payload : [payload];
const results = [];
for (const item of payloads) {
if (item.event === 'unsubscribe' && item.book_id === storedListId) {
results.push({
id: `${item.email}_${item.timestamp}`,
email: item.email,
mailingListId: item.book_id,
taskId: item.task_id,
fromAll: item.from_all === '1',
reason: item.reason || 'No reason provided',
categories: item.categories || '',
unsubscribedAt: new Date(parseInt(item.timestamp) * 1000).toISOString(),
timestamp: item.timestamp,
});
}
}
return results;
},
async test() {
return [
{
id: 'test-unsubscribe@example.com_1496827872',
email: 'test-unsubscribe@example.com',
mailingListId: '123456',
taskId: '3668141',
fromAll: true,
reason: 'User clicked unsubscribe link',
categories: '',
unsubscribedAt: new Date().toISOString(),
timestamp: '1496827872',
},
];
},
sampleData: {
id: 'unsub@example.com_1496827872',
email: 'unsub@example.com',
mailingListId: '123456',
taskId: '3668141',
fromAll: false,
reason: 'Manual unsubscribe',
categories: 'newsletter',
unsubscribedAt: '2023-06-01T12:30:00.000Z',
timestamp: '1496827872',
},
});

View File

@@ -0,0 +1,151 @@
import {
createTrigger,
TriggerStrategy,
} from '@activepieces/pieces-framework';
import { sendpulseAuth } from '../common/auth';
import { sendpulseApiCall } from '../common/client';
import { mailingListDropdown } from '../common/props';
import { HttpMethod } from '@activepieces/pieces-common';
function detectChanges(previous: any, current: any): Record<string, { from: any; to: any }> {
const changes: Record<string, { from: any; to: any }> = {};
const allKeys = new Set([...Object.keys(previous), ...Object.keys(current)]);
for (const key of allKeys) {
if (JSON.stringify(previous[key]) !== JSON.stringify(current[key])) {
changes[key] = {
from: previous[key],
to: current[key],
};
}
}
return changes;
}
export const updatedSubscriberTrigger = createTrigger({
auth: sendpulseAuth,
name: 'updated_subscriber',
displayName: 'Updated Subscriber',
description: 'Fires when subscriber details change (polling)',
type: TriggerStrategy.POLLING,
props: {
mailingListId: mailingListDropdown,
},
async onEnable(context) {
await context.store.put('mailing_list_id', String(context.propsValue.mailingListId));
await context.store.put('last_check', Date.now().toString());
},
async onDisable(context) {
await context.store.delete('mailing_list_id');
await context.store.delete('last_check');
await context.store.delete('subscribers_cache');
},
async run(context) {
const mailingListId = await context.store.get('mailing_list_id');
if (!mailingListId) {
return [];
}
try {
const currentSubscribers = await sendpulseApiCall<any[]>({
method: HttpMethod.GET,
auth: context.auth.props,
resourceUri: `/addressbooks/${mailingListId}/emails`,
});
const cachedSubscribers = await context.store.get('subscribers_cache');
if (!cachedSubscribers) {
await context.store.put('subscribers_cache', JSON.stringify(currentSubscribers));
return [];
}
const previousSubscribers = JSON.parse(cachedSubscribers as string);
const changes = [];
for (const current of currentSubscribers) {
const previous = previousSubscribers.find((p: any) => p.email === current.email);
if (previous) {
const hasChanges = JSON.stringify(current) !== JSON.stringify(previous);
if (hasChanges) {
changes.push({
id: `${current.email}_${Date.now()}`,
email: current.email,
mailingListId,
previousData: previous,
currentData: current,
updatedAt: new Date().toISOString(),
changes: detectChanges(previous, current),
});
}
}
}
await context.store.put('subscribers_cache', JSON.stringify(currentSubscribers));
await context.store.put('last_check', Date.now().toString());
return changes;
} catch (error: any) {
console.error('Error checking for subscriber updates:', error);
return [];
}
},
async test() {
return [
{
id: 'test-updated@example.com_1234567890',
email: 'test-updated@example.com',
mailingListId: '123456',
previousData: {
email: 'test-updated@example.com',
variables: { name: 'Old Name' },
},
currentData: {
email: 'test-updated@example.com',
variables: { name: 'New Name' },
},
updatedAt: new Date().toISOString(),
changes: {
'variables.name': {
from: 'Old Name',
to: 'New Name',
},
},
},
];
},
sampleData: {
id: 'updated-user@example.com_1234567890',
email: 'updated-user@example.com',
mailingListId: '123456',
previousData: {
email: 'updated-user@example.com',
variables: { phone: '+1234567890', name: 'John' },
},
currentData: {
email: 'updated-user@example.com',
variables: { phone: '+0987654321', name: 'John Updated' },
},
updatedAt: '2023-06-01T12:30:00.000Z',
changes: {
'variables.phone': {
from: '+1234567890',
to: '+0987654321',
},
'variables.name': {
from: 'John',
to: 'John Updated',
},
},
},
});

View File

@@ -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"
}
]
}

View File

@@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}