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,63 @@
{
"Gmail": "Gmail",
"Email service by Google": "Email service by Google",
"Send Email": "Send Email",
"Custom API Call": "Custom API Call",
"Send an email through a Gmail account": "Send an email through a Gmail account",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Receiver Email (To)": "Receiver Email (To)",
"CC Email": "CC Email",
"BCC Email": "BCC Email",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Reply-To Email": "Reply-To Email",
"Sender Name": "Sender Name",
"Sender Email": "Sender Email",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"In reply to": "In reply to",
"Create draft": "Create draft",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Body for the email you want to send": "Body for the email you want to send",
"Email address to set as the \"Reply-To\" header": "Email address to set as the \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"File to attach to the email you want to send": "File to attach to the email you want to send",
"In case you want to change the name of the attachment": "In case you want to change the name of the attachment",
"Reply to this Message-ID": "Reply to this Message-ID",
"Create draft without sending the actual email": "Create draft without sending the actual email",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"plain text": "plain text",
"html": "html",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Email": "New Email",
"New Labeled Email": "New Labeled Email",
"Triggers when new mail is found in your Gmail inbox": "Triggers when new mail is found in your Gmail inbox",
"Triggers when a label is added to an email": "Triggers when a label is added to an email",
"Email subject": "Email subject",
"Email sender": "Email sender",
"Email recipient": "Email recipient",
"Label": "Label",
"Category": "Category",
"The email subject": "The email subject",
"Optional filteration, leave empty to filter based on the email sender": "Optional filteration, leave empty to filter based on the email sender",
"Optional filteration, leave empty to filter based on the email recipient": "Optional filteration, leave empty to filter based on the email recipient",
"Optional filteration, leave unselected to filter based on the email label": "Optional filteration, leave unselected to filter based on the email label",
"Optional filteration, leave unselected to filter based on the email category": "Optional filteration, leave unselected to filter based on the email category",
"Primary": "Primary",
"Social": "Social",
"Promotions": "Promotions",
"Updates": "Updates",
"Forums": "Forums",
"Reservations": "Reservations",
"Purchases": "Purchases"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "E-Mail-Dienst von Google",
"Send Email": "E-Mail senden",
"Custom API Call": "Eigener API-Aufruf",
"Send an email through a Gmail account": "Senden Sie eine E-Mail über ein Gmail-Konto",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Receiver Email (To)": "Empfänger E-Mail (An)",
"CC Email": "CC E-Mail",
"BCC Email": "BCC E-Mail",
"Subject": "Betreff",
"Body Type": "Körpertyp",
"Body": "Körper",
"Reply-To Email": "Antwort-An E-Mail",
"Sender Name": "Absendername",
"Sender Email": "Absender-E-Mail",
"Attachments": "Anhänge",
"In reply to": "In Antwort auf",
"Create draft": "Entwurf erstellen",
"Method": "Methode",
"Headers": "Kopfzeilen",
"Query Parameters": "Abfrageparameter",
"Response is Binary ?": "Antwort ist binär?",
"No Error on Failure": "Kein Fehler bei Fehler",
"Timeout (in seconds)": "Timeout (in Sekunden)",
"Body for the email you want to send": "Body für die E-Mail, die Sie senden möchten",
"Email address to set as the \"Reply-To\" header": "E-Mail-Adresse, die als \"Antwort-An\"-Header festgelegt wird",
"The address must be listed in your GMail account's settings": "Die Adresse muss in den Einstellungen Ihres GMail-Kontos aufgelistet werden",
"Reply to this Message-ID": "Auf diese Nachrichten-ID antworten",
"Create draft without sending the actual email": "Erstellen Sie einen Entwurf, ohne die eigentliche E-Mail zu senden",
"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..",
"plain text": "schlichter Text",
"html": "html",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD",
"New Email": "Neue E-Mail",
"New Labeled Email": "Neue etikettierte E-Mail",
"Triggers when new mail is found in your Gmail inbox": "Auslöser wenn neue Nachrichten in Ihrem Google Mail-Posteingang gefunden werden",
"Triggers when a label is added to an email": "Löst aus, wenn ein Label zu einer E-Mail hinzugefügt wird",
"Email subject": "E-Mail-Betreff",
"Email sender": "E-Mail Absender",
"Email recipient": "E-Mail-Empfänger",
"Label": "Label",
"Category": "Kategorie",
"The email subject": "Der E-Mail-Betreff",
"Optional filteration, leave empty to filter based on the email sender": "Optionale Filterung, leer lassen, um nach dem E-Mail-Absender zu filtern",
"Optional filteration, leave empty to filter based on the email recipient": "Optionale Filterung, leer lassen, um nach dem E-Mail-Empfänger zu filtern",
"Optional filteration, leave unselected to filter based on the email label": "Optionale Filterung, lassen Sie nicht ausgewählt, um nach der E-Mail-Bezeichnung zu filtern",
"Optional filteration, leave unselected to filter based on the email category": "Optionale Filterung, lassen Sie nicht ausgewählt um nach der E-Mail-Kategorie zu filtern",
"Primary": "Primär",
"Social": "Soziale",
"Promotions": "Promotionen",
"Updates": "Updates",
"Forums": "Foren",
"Reservations": "Reservierungen",
"Purchases": "Einkäufe"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "Servicio de correo por Google",
"Send Email": "Enviar Email",
"Custom API Call": "Llamada API personalizada",
"Send an email through a Gmail account": "Enviar un correo electrónico a través de una cuenta de Gmail",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Receiver Email (To)": "Email del receptor (Para)",
"CC Email": "Email CC",
"BCC Email": "Correo BCC",
"Subject": "Asunto",
"Body Type": "Tipo de cuerpo",
"Body": "Cuerpo",
"Reply-To Email": "Responder a Email",
"Sender Name": "Nombre del remitente",
"Sender Email": "Email del remitente",
"Attachments": "Adjuntos",
"In reply to": "En respuesta a",
"Create draft": "Crear borrador",
"Method": "Método",
"Headers": "Encabezados",
"Query Parameters": "Parámetros de consulta",
"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)",
"Body for the email you want to send": "Cuerpo para el correo electrónico que desea enviar",
"Email address to set as the \"Reply-To\" header": "Dirección de correo electrónico a establecer como la cabecera \"Responder a\"",
"The address must be listed in your GMail account's settings": "La dirección debe aparecer en la configuración de su cuenta de GMail",
"Reply to this Message-ID": "Responder a este mensaje-ID",
"Create draft without sending the actual email": "Crear borrador sin enviar el correo electrónico actual",
"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.",
"plain text": "texto plano",
"html": "html",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO",
"New Email": "Nuevo Email",
"New Labeled Email": "Nuevo Email Etiquetado",
"Triggers when new mail is found in your Gmail inbox": "Dispara cuando se encuentra un nuevo correo en tu bandeja de entrada de Gmail",
"Triggers when a label is added to an email": "Dispara cuando se agrega una etiqueta a un correo electrónico",
"Email subject": "Asunto del email",
"Email sender": "Remitente de email",
"Email recipient": "Destinatario de email",
"Label": "Etiqueta",
"Category": "Categoría",
"The email subject": "El asunto del correo electrónico",
"Optional filteration, leave empty to filter based on the email sender": "Filtro opcional, dejar vacío para filtrar basado en el remitente de correo electrónico",
"Optional filteration, leave empty to filter based on the email recipient": "Filtro opcional, dejar vacío para filtrar basado en el destinatario del correo electrónico",
"Optional filteration, leave unselected to filter based on the email label": "Filtro opcional, dejar sin seleccionar para filtrar basado en la etiqueta de correo electrónico",
"Optional filteration, leave unselected to filter based on the email category": "Filtro opcional, dejar sin seleccionar para filtrar basado en la categoría de correo electrónico",
"Primary": "Principal",
"Social": "Social",
"Promotions": "Promociones",
"Updates": "Actualizaciones",
"Forums": "Foros",
"Reservations": "Reservas",
"Purchases": "Compras"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "Service de messagerie par Google",
"Send Email": "Envoyer un e-mail",
"Custom API Call": "Appel API personnalisé",
"Send an email through a Gmail account": "Envoyer un email via un compte Gmail",
"Make a custom API call to a specific endpoint": "Passez un appel API personnalisé à un endpoint spécifique",
"Receiver Email (To)": "E-mail du destinataire (À)",
"CC Email": "E-mail CC",
"BCC Email": "E-mail BCC",
"Subject": "Sujet",
"Body Type": "Type de Corps",
"Body": "Corps",
"Reply-To Email": "E-mail répondre-à",
"Sender Name": "Nom de l'expéditeur",
"Sender Email": "E-mail de l'expéditeur",
"Attachments": "Fichiers joints",
"In reply to": "En réponse à",
"Create draft": "Créer un brouillon",
"Method": "Méthode",
"Headers": "En-têtes",
"Query Parameters": "Paramètres de requête",
"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)",
"Body for the email you want to send": "Corps de l'e-mail que vous souhaitez envoyer",
"Email address to set as the \"Reply-To\" header": "Adresse e-mail à définir comme en-tête de \"Répondre à\"",
"The address must be listed in your GMail account's settings": "L'adresse doit être listée dans les paramètres de votre compte Gmail",
"Reply to this Message-ID": "Répondre à cet ID de message",
"Create draft without sending the actual email": "Créer un brouillon sans envoyer l'e-mail actuel",
"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.",
"plain text": "texte brut",
"html": "html",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Email": "Nouvel e-mail",
"New Labeled Email": "Nouvel e-mail libellé",
"Triggers when new mail is found in your Gmail inbox": "Se déclenche quand un nouveau courrier est trouvé dans votre boîte de réception Gmail",
"Triggers when a label is added to an email": "Se déclenche quand un label est ajouté à un e-mail",
"Email subject": "Sujet de l'e-mail",
"Email sender": "Expéditeur de l'e-mail",
"Email recipient": "Destinataire de l'e-mail",
"Label": "Libellé",
"Category": "Catégorie",
"The email subject": "Le sujet de l'e-mail",
"Optional filteration, leave empty to filter based on the email sender": "Filtrage facultatif, laisser vide pour filtrer en fonction de l'expéditeur de l'e-mail",
"Optional filteration, leave empty to filter based on the email recipient": "Filtrage facultatif, laisser vide pour filtrer en fonction du destinataire de l'e-mail",
"Optional filteration, leave unselected to filter based on the email label": "Filtrage optionnel, laisser non sélectionné pour filtrer sur la base du libellé de l'e-mail",
"Optional filteration, leave unselected to filter based on the email category": "Filtrage optionnel, laisser non sélectionné pour filtrer selon la catégorie d'email",
"Primary": "Primaire",
"Social": "Réseaux sociaux",
"Promotions": "Promotions",
"Updates": "Mises à jour",
"Forums": "Forums",
"Reservations": "Réservations",
"Purchases": "Achats"
}

View File

@@ -0,0 +1,63 @@
{
"Gmail": "Gmail",
"Email service by Google": "Email service by Google",
"Send Email": "Send Email",
"Custom API Call": "Custom API Call",
"Send an email through a Gmail account": "Send an email through a Gmail account",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Receiver Email (To)": "Receiver Email (To)",
"CC Email": "CC Email",
"BCC Email": "BCC Email",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Reply-To Email": "Reply-To Email",
"Sender Name": "Sender Name",
"Sender Email": "Sender Email",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"In reply to": "In reply to",
"Create draft": "Create draft",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Body for the email you want to send": "Body for the email you want to send",
"Email address to set as the \"Reply-To\" header": "Email address to set as the \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"File to attach to the email you want to send": "File to attach to the email you want to send",
"In case you want to change the name of the attachment": "In case you want to change the name of the attachment",
"Reply to this Message-ID": "Reply to this Message-ID",
"Create draft without sending the actual email": "Create draft without sending the actual email",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"plain text": "plain text",
"html": "html",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Email": "New Email",
"New Labeled Email": "New Labeled Email",
"Triggers when new mail is found in your Gmail inbox": "Triggers when new mail is found in your Gmail inbox",
"Triggers when a label is added to an email": "Triggers when a label is added to an email",
"Email subject": "Email subject",
"Email sender": "Email sender",
"Email recipient": "Email recipient",
"Label": "Label",
"Category": "Category",
"The email subject": "The email subject",
"Optional filteration, leave empty to filter based on the email sender": "Optional filteration, leave empty to filter based on the email sender",
"Optional filteration, leave empty to filter based on the email recipient": "Optional filteration, leave empty to filter based on the email recipient",
"Optional filteration, leave unselected to filter based on the email label": "Optional filteration, leave unselected to filter based on the email label",
"Optional filteration, leave unselected to filter based on the email category": "Optional filteration, leave unselected to filter based on the email category",
"Primary": "Primary",
"Social": "Social",
"Promotions": "Promotions",
"Updates": "Updates",
"Forums": "Forums",
"Reservations": "Reservations",
"Purchases": "Purchases"
}

View File

@@ -0,0 +1,63 @@
{
"Gmail": "Gmail",
"Email service by Google": "Email service by Google",
"Send Email": "Send Email",
"Custom API Call": "Custom API Call",
"Send an email through a Gmail account": "Send an email through a Gmail account",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Receiver Email (To)": "Receiver Email (To)",
"CC Email": "CC Email",
"BCC Email": "BCC Email",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Reply-To Email": "Reply-To Email",
"Sender Name": "Sender Name",
"Sender Email": "Sender Email",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"In reply to": "In reply to",
"Create draft": "Create draft",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Body for the email you want to send": "Body for the email you want to send",
"Email address to set as the \"Reply-To\" header": "Email address to set as the \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"File to attach to the email you want to send": "File to attach to the email you want to send",
"In case you want to change the name of the attachment": "In case you want to change the name of the attachment",
"Reply to this Message-ID": "Reply to this Message-ID",
"Create draft without sending the actual email": "Create draft without sending the actual email",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"plain text": "plain text",
"html": "html",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Email": "New Email",
"New Labeled Email": "New Labeled Email",
"Triggers when new mail is found in your Gmail inbox": "Triggers when new mail is found in your Gmail inbox",
"Triggers when a label is added to an email": "Triggers when a label is added to an email",
"Email subject": "Email subject",
"Email sender": "Email sender",
"Email recipient": "Email recipient",
"Label": "Label",
"Category": "Category",
"The email subject": "The email subject",
"Optional filteration, leave empty to filter based on the email sender": "Optional filteration, leave empty to filter based on the email sender",
"Optional filteration, leave empty to filter based on the email recipient": "Optional filteration, leave empty to filter based on the email recipient",
"Optional filteration, leave unselected to filter based on the email label": "Optional filteration, leave unselected to filter based on the email label",
"Optional filteration, leave unselected to filter based on the email category": "Optional filteration, leave unselected to filter based on the email category",
"Primary": "Primary",
"Social": "Social",
"Promotions": "Promotions",
"Updates": "Updates",
"Forums": "Forums",
"Reservations": "Reservations",
"Purchases": "Purchases"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "Google のメール サービス",
"Send Email": "メール送信",
"Custom API Call": "カスタムAPI通話",
"Send an email through a Gmail account": "Gmail アカウントを通じてメールを送信",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Receiver Email (To)": "受信者メールアドレス (宛先)",
"CC Email": "CCメール",
"BCC Email": "BCCのメール",
"Subject": "件名",
"Body Type": "ボディタイプ",
"Body": "本文",
"Reply-To Email": "返信先メールアドレス",
"Sender Name": "送信者名",
"Sender Email": "送信者メールアドレス",
"Attachments": "添付ファイル",
"In reply to": "返信先",
"Create draft": "下書きを作成",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"Body for the email you want to send": "送信したいメールの本文",
"Email address to set as the \"Reply-To\" header": "\"Reply-To\" ヘッダーとして設定するメールアドレス",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"Reply to this Message-ID": "このメッセージIDへの返信",
"Create draft without sending the actual email": "実際のメールを送信せずに下書きを作成する",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"plain text": "プレーンテキスト",
"html": "html",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭",
"New Email": "新しいメール",
"New Labeled Email": "新しいラベル付きメール",
"Triggers when new mail is found in your Gmail inbox": "Gmailの受信トレイに新しいメールが見つかったときにトリガーします",
"Triggers when a label is added to an email": "電子メールにラベルが追加されたときにトリガーします",
"Email subject": "メールの件名",
"Email sender": "メール送信者",
"Email recipient": "受信者のメールアドレス",
"Label": "ラベル",
"Category": "カテゴリ",
"The email subject": "メールの件名",
"Optional filteration, leave empty to filter based on the email sender": "オプションのフィルタリング。メール送信者に基づいてフィルタリングするには空のままにしてください",
"Optional filteration, leave empty to filter based on the email recipient": "オプションのフィルタ、メールの受信者に基づいてフィルタリングするには空白のままにしてください",
"Optional filteration, leave unselected to filter based on the email label": "オプションのフィルタリング、メールラベルに基づいてフィルタリングするために未選択のままにしてください",
"Optional filteration, leave unselected to filter based on the email category": "オプションのフィルタ、メールカテゴリに基づいてフィルタリングするために未選択のままにしてください",
"Primary": "プライマリ(プライマリ)",
"Social": "ソーシャル",
"Promotions": "プロモーション",
"Updates": "更新",
"Forums": "フォーラム",
"Reservations": "予約",
"Purchases": "購入"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "E-mail service door Google",
"Send Email": "E-mail verzenden",
"Custom API Call": "Custom API Call",
"Send an email through a Gmail account": "Stuur een e-mail via een Gmail account",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Receiver Email (To)": "Ontvanger E-mail (Aan)",
"CC Email": "CC e-mail",
"BCC Email": "BCC e-mail",
"Subject": "Onderwerp",
"Body Type": "Type lichaam",
"Body": "Lichaam",
"Reply-To Email": "Antwoord-Aan E-mail",
"Sender Name": "Naam afzender",
"Sender Email": "E-mailadres afzender",
"Attachments": "Bijlagen",
"In reply to": "In antwoord op",
"Create draft": "Concept maken",
"Method": "Methode",
"Headers": "Kopteksten",
"Query Parameters": "Query parameters",
"Response is Binary ?": "Antwoord is binair?",
"No Error on Failure": "Geen fout bij fout",
"Timeout (in seconds)": "Time-out (in seconden)",
"Body for the email you want to send": "Body voor de e-mail die u wilt verzenden",
"Email address to set as the \"Reply-To\" header": "E-mailadres om in te stellen als de \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "Het adres moet worden vermeld in de instellingen van uw GMail account",
"Reply to this Message-ID": "Reageer op dit bericht-ID",
"Create draft without sending the actual email": "Concept maken zonder de werkelijke e-mail te verzenden",
"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..",
"plain text": "onopgemaakte tekst",
"html": "html",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD",
"New Email": "Nieuw e-mailadres",
"New Labeled Email": "Nieuw label e-mailadres",
"Triggers when new mail is found in your Gmail inbox": "Triggert wanneer nieuwe mail gevonden is in uw Gmail inbox",
"Triggers when a label is added to an email": "Triggert wanneer een label wordt toegevoegd aan een e-mail",
"Email subject": "E-mail onderwerp",
"Email sender": "E-mail afzender",
"Email recipient": "E-mail ontvanger",
"Label": "Omschrijving",
"Category": "categorie",
"The email subject": "Het e-mail onderwerp",
"Optional filteration, leave empty to filter based on the email sender": "Optionele filtering, laat leeg om te filteren op basis van de afzender",
"Optional filteration, leave empty to filter based on the email recipient": "Optionele filtering, laat leeg om te filteren op basis van de e-mail ontvanger",
"Optional filteration, leave unselected to filter based on the email label": "Optionele filtering, laat niet geselecteerd om te filteren op basis van het e-maillabel",
"Optional filteration, leave unselected to filter based on the email category": "Optionele filtering, laat niet geselecteerd om te filteren op basis van de e-mail categorie",
"Primary": "Primair",
"Social": "Sociaal",
"Promotions": "Aanbiedingen",
"Updates": "Bijwerken",
"Forums": "Forums",
"Reservations": "Reserveringen",
"Purchases": "Aankopen"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "Serviço de e-mail do Google",
"Send Email": "Enviar e-mail",
"Custom API Call": "Chamada de API personalizada",
"Send an email through a Gmail account": "Envie um e-mail através de uma conta do Gmail",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Receiver Email (To)": "E-mail do destinatário (Para)",
"CC Email": "E-mail em CC",
"BCC Email": "E-mail de Cco",
"Subject": "Cargo",
"Body Type": "Tipo de Corpo",
"Body": "Conteúdo",
"Reply-To Email": "E-mail para resposta",
"Sender Name": "Nome do Remetente",
"Sender Email": "E-mail do remetente",
"Attachments": "Anexos",
"In reply to": "Em resposta a",
"Create draft": "Criar rascunho",
"Method": "Método",
"Headers": "Cabeçalhos",
"Query Parameters": "Parâmetros da consulta",
"Response is Binary ?": "A resposta é binária ?",
"No Error on Failure": "Nenhum erro no Failure",
"Timeout (in seconds)": "Tempo limite (em segundos)",
"Body for the email you want to send": "Corpo para o e-mail que deseja enviar",
"Email address to set as the \"Reply-To\" header": "Endereço de e-mail para definir como cabeçalho \"Responder-Para\"",
"The address must be listed in your GMail account's settings": "O endereço deve ser listado nas configurações da sua conta do GMail",
"Reply to this Message-ID": "Responder a esta Mensagem-ID",
"Create draft without sending the actual email": "Criar rascunho sem enviar o próprio e-mail",
"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..",
"plain text": "texto simples",
"html": "HTML",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA",
"New Email": "Novo E-mail",
"New Labeled Email": "Novo E-mail Marcado",
"Triggers when new mail is found in your Gmail inbox": "Dispara quando novas mensagens forem encontradas em sua caixa de entrada do Gmail",
"Triggers when a label is added to an email": "Dispara quando uma etiqueta é adicionada a um email",
"Email subject": "Assunto do email",
"Email sender": "Remetente do e-mail",
"Email recipient": "Destinatário do e-mail",
"Label": "Descrição",
"Category": "categoria",
"The email subject": "O assunto do e-mail",
"Optional filteration, leave empty to filter based on the email sender": "Filtro opcional, deixe em branco para filtrar com base no remetente de e-mail",
"Optional filteration, leave empty to filter based on the email recipient": "Filtro opcional, deixe vazio para filtrar com base no destinatário de e-mail",
"Optional filteration, leave unselected to filter based on the email label": "Filtro opcional, deixe não selecionado para filtrar com base no rótulo de e-mail",
"Optional filteration, leave unselected to filter based on the email category": "Filtro opcional, deixe não selecionado para filtrar com base na categoria de e-mail",
"Primary": "Primário",
"Social": "Sócio",
"Promotions": "Promoções",
"Updates": "Atualizações",
"Forums": "Fóruns",
"Reservations": "Reservas",
"Purchases": "Compras"
}

View File

@@ -0,0 +1,63 @@
{
"Gmail": "Gmail",
"Email service by Google": "Почтовый сервис от Google",
"Send Email": "Отправить письмо",
"Custom API Call": "Пользовательский вызов API",
"Send an email through a Gmail account": "Отправить письмо с помощью учетной записи Gmail",
"Make a custom API call to a specific endpoint": "Сделать пользовательский API вызов к определенной конечной точке",
"Receiver Email (To)": "Email получателя (для)",
"CC Email": "Копия E-mail",
"BCC Email": "Скрытая почта",
"Subject": "Тема",
"Body Type": "Тип тела",
"Body": "Тело",
"Reply-To Email": "Письмо для ответа",
"Sender Name": "Имя отправителя",
"Sender Email": "Email отправителя",
"Attachment": "Вложение",
"Attachment Name": "Название вложения",
"In reply to": "В ответ на",
"Create draft": "Создать черновик",
"Method": "Метод",
"Headers": "Заголовки",
"Query Parameters": "Параметры запроса",
"No Error on Failure": "Нет ошибок при ошибке",
"Timeout (in seconds)": "Таймаут (в секундах)",
"Body for the email you want to send": "Тело письма, которое вы хотите отправить",
"Email address to set as the \"Reply-To\" header": "Адрес электронной почты для указания заголовка \"Ответить-Кому\"",
"The address must be listed in your GMail account's settings": "Адрес должен быть указан в настройках учетной записи GMail",
"File to attach to the email you want to send": "Файл для прикрепления к электронной почте, которую вы хотите отправить",
"In case you want to change the name of the attachment": "В случае, если вы хотите изменить имя вложения",
"Reply to this Message-ID": "Ответить на это сообщение",
"Create draft without sending the actual email": "Создать черновик без отправки фактического письма",
"Authorization headers are injected automatically from your connection.": "Заголовки авторизации включаются автоматически из вашего соединения.",
"plain text": "простой текст",
"html": "html",
"GET": "ПОЛУЧИТЬ",
"POST": "ПОСТ",
"PATCH": "ПАТЧ",
"PUT": "ПОКУПИТЬ",
"DELETE": "УДАЛИТЬ",
"HEAD": "HEAD",
"New Email": "Новое письмо",
"New Labeled Email": "Новое письмо с меткой",
"Triggers when new mail is found in your Gmail inbox": "Включает при обнаружении нового письма в папке Gmail",
"Triggers when a label is added to an email": "Триггеры при добавлении метки к электронной почте",
"Email subject": "Тема письма",
"Email sender": "Отправитель письма",
"Email recipient": "Получатель электронной почты",
"Label": "Метка",
"Category": "Категория",
"The email subject": "Тема письма",
"Optional filteration, leave empty to filter based on the email sender": "Необязательная фильтрация, оставьте пустым для фильтрации по электронной почте",
"Optional filteration, leave empty to filter based on the email recipient": "Необязательный фильтр, оставьте пустым для фильтрации на основе email получателя",
"Optional filteration, leave unselected to filter based on the email label": "Необязательный фильтр, оставьте не выбранным для фильтрации на основе метки электронной почты",
"Optional filteration, leave unselected to filter based on the email category": "Необязательный фильтр, не выбран для фильтрации на основе категории электронной почты",
"Primary": "Первичный",
"Social": "Соцсети",
"Promotions": "Продвижение",
"Updates": "Обновления",
"Forums": "Форумы",
"Reservations": "Оговорки",
"Purchases": "Покупки"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "Email service by Google",
"Send Email": "Send Email",
"Custom API Call": "Custom API Call",
"Send an email through a Gmail account": "Send an email through a Gmail account",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Receiver Email (To)": "Receiver Email (To)",
"CC Email": "CC Email",
"BCC Email": "BCC Email",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Reply-To Email": "Reply-To Email",
"Sender Name": "Sender Name",
"Sender Email": "Sender Email",
"Attachments": "Attachments",
"In reply to": "In reply to",
"Create draft": "Create draft",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Body for the email you want to send": "Body for the email you want to send",
"Email address to set as the \"Reply-To\" header": "Email address to set as the \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"Reply to this Message-ID": "Reply to this Message-ID",
"Create draft without sending the actual email": "Create draft without sending the actual email",
"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..",
"plain text": "plain text",
"html": "html",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Email": "New Email",
"New Labeled Email": "New Labeled Email",
"Triggers when new mail is found in your Gmail inbox": "Triggers when new mail is found in your Gmail inbox",
"Triggers when a label is added to an email": "Triggers when a label is added to an email",
"Email subject": "Email subject",
"Email sender": "Email sender",
"Email recipient": "Email recipient",
"Label": "Label",
"Category": "Category",
"The email subject": "The email subject",
"Optional filteration, leave empty to filter based on the email sender": "Optional filteration, leave empty to filter based on the email sender",
"Optional filteration, leave empty to filter based on the email recipient": "Optional filteration, leave empty to filter based on the email recipient",
"Optional filteration, leave unselected to filter based on the email label": "Optional filteration, leave unselected to filter based on the email label",
"Optional filteration, leave unselected to filter based on the email category": "Optional filteration, leave unselected to filter based on the email category",
"Primary": "Primary",
"Social": "Social",
"Promotions": "Promotions",
"Updates": "Updates",
"Forums": "Forums",
"Reservations": "Reservations",
"Purchases": "Purchases"
}

View File

@@ -0,0 +1,63 @@
{
"Gmail": "Gmail",
"Email service by Google": "Email service by Google",
"Send Email": "Send Email",
"Custom API Call": "Custom API Call",
"Send an email through a Gmail account": "Send an email through a Gmail account",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Receiver Email (To)": "Receiver Email (To)",
"CC Email": "CC Email",
"BCC Email": "BCC Email",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "Body",
"Reply-To Email": "Reply-To Email",
"Sender Name": "Sender Name",
"Sender Email": "Sender Email",
"Attachment": "Attachment",
"Attachment Name": "Attachment Name",
"In reply to": "In reply to",
"Create draft": "Create draft",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"Body for the email you want to send": "Body for the email you want to send",
"Email address to set as the \"Reply-To\" header": "Email address to set as the \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"File to attach to the email you want to send": "File to attach to the email you want to send",
"In case you want to change the name of the attachment": "In case you want to change the name of the attachment",
"Reply to this Message-ID": "Reply to this Message-ID",
"Create draft without sending the actual email": "Create draft without sending the actual email",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"plain text": "plain text",
"html": "html",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD",
"New Email": "New Email",
"New Labeled Email": "New Labeled Email",
"Triggers when new mail is found in your Gmail inbox": "Triggers when new mail is found in your Gmail inbox",
"Triggers when a label is added to an email": "Triggers when a label is added to an email",
"Email subject": "Email subject",
"Email sender": "Email sender",
"Email recipient": "Email recipient",
"Label": "Label",
"Category": "Category",
"The email subject": "The email subject",
"Optional filteration, leave empty to filter based on the email sender": "Optional filteration, leave empty to filter based on the email sender",
"Optional filteration, leave empty to filter based on the email recipient": "Optional filteration, leave empty to filter based on the email recipient",
"Optional filteration, leave unselected to filter based on the email label": "Optional filteration, leave unselected to filter based on the email label",
"Optional filteration, leave unselected to filter based on the email category": "Optional filteration, leave unselected to filter based on the email category",
"Primary": "Primary",
"Social": "Social",
"Promotions": "Promotions",
"Updates": "Updates",
"Forums": "Forums",
"Reservations": "Reservations",
"Purchases": "Purchases"
}

View File

@@ -0,0 +1,61 @@
{
"Email service by Google": "Email service by Google",
"Send Email": "Send Email",
"Custom API Call": "自定义 API 呼叫",
"Send an email through a Gmail account": "Send an email through a Gmail account",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Receiver Email (To)": "Receiver Email (To)",
"CC Email": "CC Email",
"BCC Email": "BCC Email",
"Subject": "Subject",
"Body Type": "Body Type",
"Body": "正文内容",
"Reply-To Email": "Reply-To Email",
"Sender Name": "发件人姓名",
"Sender Email": "发件人电子邮件",
"Attachments": "Attachments",
"In reply to": "In reply to",
"Create draft": "Create draft",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"Body for the email you want to send": "Body for the email you want to send",
"Email address to set as the \"Reply-To\" header": "Email address to set as the \"Reply-To\" header",
"The address must be listed in your GMail account's settings": "The address must be listed in your GMail account's settings",
"Reply to this Message-ID": "Reply to this Message-ID",
"Create draft without sending the actual email": "Create draft without sending the actual email",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"plain text": "plain text",
"html": "html",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色",
"New Email": "New Email",
"New Labeled Email": "New Labeled Email",
"Triggers when new mail is found in your Gmail inbox": "Triggers when new mail is found in your Gmail inbox",
"Triggers when a label is added to an email": "Triggers when a label is added to an email",
"Email subject": "Email subject",
"Email sender": "Email sender",
"Email recipient": "Email recipient",
"Label": "Label",
"Category": "Category",
"The email subject": "The email subject",
"Optional filteration, leave empty to filter based on the email sender": "Optional filteration, leave empty to filter based on the email sender",
"Optional filteration, leave empty to filter based on the email recipient": "Optional filteration, leave empty to filter based on the email recipient",
"Optional filteration, leave unselected to filter based on the email label": "Optional filteration, leave unselected to filter based on the email label",
"Optional filteration, leave unselected to filter based on the email category": "Optional filteration, leave unselected to filter based on the email category",
"Primary": "Primary",
"Social": "Social",
"Promotions": "Promotions",
"Updates": "Updates",
"Forums": "Forums",
"Reservations": "Reservations",
"Purchases": "Purchases"
}

View File

@@ -0,0 +1,59 @@
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import {
OAuth2PropertyValue,
PieceAuth,
createPiece,
} from '@activepieces/pieces-framework';
import { PieceCategory } from '@activepieces/shared';
import { gmailSendEmailAction } from './lib/actions/send-email-action';
import { gmailNewEmailTrigger } from './lib/triggers/new-email';
import { gmailNewLabeledEmailTrigger } from './lib/triggers/new-labeled-email';
export const gmailAuth = PieceAuth.OAuth2({
description: '',
authUrl: 'https://accounts.google.com/o/oauth2/auth',
tokenUrl: 'https://oauth2.googleapis.com/token',
required: true,
scope: [
'https://www.googleapis.com/auth/gmail.send',
'email',
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.compose',
],
});
export const gmail = createPiece({
minimumSupportedRelease: '0.30.0',
logoUrl: 'https://cdn.activepieces.com/pieces/gmail.png',
categories: [
PieceCategory.COMMUNICATION,
PieceCategory.BUSINESS_INTELLIGENCE,
],
actions: [
gmailSendEmailAction,
createCustomApiCallAction({
baseUrl: () => 'https://gmail.googleapis.com/gmail/v1',
auth: gmailAuth,
authMapping: async (auth) => ({
Authorization: `Bearer ${(auth as OAuth2PropertyValue).access_token}`,
}),
}),
],
displayName: 'Gmail',
description: 'Email service by Google',
authors: [
'kanarelo',
'abdullahranginwala',
'BastienMe',
'Salem-Alaa',
'kishanprmr',
'MoShizzle',
'AbdulTheActivePiecer',
'khaledmashaly',
'abuaboud',
'AdamSelene',
],
triggers: [gmailNewEmailTrigger, gmailNewLabeledEmailTrigger],
auth: gmailAuth,
});

View File

@@ -0,0 +1,39 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { GmailRequests } from '../common/data';
import { GmailMessageFormat } from '../common/models';
import { gmailAuth } from '../../';
export const gmailGetEmail = createAction({
auth: gmailAuth,
name: 'gmail_get_mail',
description: 'Get an email from your Gmail account via Id',
displayName: 'Get Email',
props: {
message_id: Property.ShortText({
displayName: 'Message ID',
description: 'The messageId of the mail to read',
required: true,
}),
format: Property.StaticDropdown<GmailMessageFormat>({
displayName: 'Format',
description: 'Format of the mail',
required: false,
defaultValue: GmailMessageFormat.FULL,
options: {
disabled: false,
options: [
{ value: GmailMessageFormat.MINIMAL, label: 'Minimal' },
{ value: GmailMessageFormat.FULL, label: 'Full' },
{ value: GmailMessageFormat.RAW, label: 'Raw' },
{ value: GmailMessageFormat.METADATA, label: 'Metadata' },
],
},
}),
},
run: async ({ auth, propsValue: { format, message_id } }) =>
await GmailRequests.getMail({
access_token: auth.access_token,
message_id,
format: format ?? GmailMessageFormat.FULL,
}),
});

View File

@@ -0,0 +1,39 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { GmailRequests } from '../common/data';
import { GmailMessageFormat } from '../common/models';
import { gmailAuth } from '../../';
export const gmailGetThread = createAction({
auth: gmailAuth,
name: 'gmail_get_thread',
description: 'Get a thread from your Gmail account via Id',
displayName: 'Get Thread',
props: {
thread_id: Property.ShortText({
displayName: 'Thread ID',
description: 'The thread Id of the mail to read',
required: true,
}),
format: Property.StaticDropdown<GmailMessageFormat>({
displayName: 'Format',
description: 'Format of the mail',
required: false,
defaultValue: 'full',
options: {
disabled: false,
options: [
{ value: GmailMessageFormat.MINIMAL, label: 'Minimal' },
{ value: GmailMessageFormat.FULL, label: 'Full' },
{ value: GmailMessageFormat.RAW, label: 'Raw' },
{ value: GmailMessageFormat.METADATA, label: 'Metadata' },
],
},
}),
},
run: async ({ auth, propsValue: { format, thread_id } }) =>
await GmailRequests.getThread({
access_token: auth.access_token,
thread_id,
format: format ?? GmailMessageFormat.FULL,
}),
});

View File

@@ -0,0 +1,28 @@
import { createAction } from '@activepieces/pieces-framework';
import { GmailRequests } from '../common/data';
import { GmailLabel } from '../common/models';
import { GmailProps } from '../common/props';
import { gmailAuth } from '../../';
export const gmailSearchMail = createAction({
auth: gmailAuth,
name: 'gmail_search_mail',
description: 'Find for an email in your Gmail account',
displayName: 'Find Email',
props: {
subject: GmailProps.subject,
from: GmailProps.from,
to: GmailProps.to,
label: GmailProps.label,
category: GmailProps.category,
},
run: async ({ auth, propsValue: { from, to, subject, label, category } }) =>
await GmailRequests.searchMail({
access_token: auth.access_token,
from: from as string,
to: to as string,
subject: subject as string,
label: label as GmailLabel,
category: category as string,
}),
});

View File

@@ -0,0 +1,209 @@
import { ApFile, createAction, Property } from '@activepieces/pieces-framework';
import mime from 'mime-types';
import MailComposer from 'nodemailer/lib/mail-composer';
import Mail, { Attachment } from 'nodemailer/lib/mailer';
import { gmailAuth } from '../../';
import { google } from 'googleapis';
import { OAuth2Client } from 'googleapis-common';
export const gmailSendEmailAction = createAction({
auth: gmailAuth,
name: 'send_email',
description: 'Send an email through a Gmail account',
displayName: 'Send Email',
props: {
receiver: Property.Array({
displayName: 'Receiver Email (To)',
description: undefined,
required: true,
}),
cc: Property.Array({
displayName: 'CC Email',
description: undefined,
required: false,
}),
bcc: Property.Array({
displayName: 'BCC Email',
description: undefined,
required: false,
}),
subject: Property.ShortText({
displayName: 'Subject',
description: undefined,
required: true,
}),
body_type: Property.StaticDropdown({
displayName: 'Body Type',
required: true,
defaultValue: 'plain_text',
options: {
disabled: false,
options: [
{
label: 'plain text',
value: 'plain_text',
},
{
label: 'html',
value: 'html',
},
],
},
}),
body: Property.ShortText({
displayName: 'Body',
description: 'Body for the email you want to send',
required: true,
}),
reply_to: Property.Array({
displayName: 'Reply-To Email',
description: 'Email address to set as the "Reply-To" header',
required: false,
}),
sender_name: Property.ShortText({
displayName: 'Sender Name',
required: false,
}),
from: Property.ShortText({
displayName: 'Sender Email',
description:
"The address must be listed in your GMail account's settings",
required: false,
}),
attachments: Property.Array({
displayName: 'Attachments',
required: false,
properties: {
file: Property.File({
displayName: 'File',
description: 'File to attach to the email you want to send.',
required: true,
}),
name: Property.ShortText({
displayName: 'Attachment Name',
description: 'In case you want to change the name of the attachment.',
required: false,
}),
},
}),
in_reply_to: Property.ShortText({
displayName: 'In reply to',
description: 'Reply to this Message-ID',
required: false,
}),
draft: Property.Checkbox({
displayName: 'Create draft',
description: 'Create draft without sending the actual email',
required: true,
defaultValue: false,
}),
},
async run(context) {
const authClient = new OAuth2Client();
authClient.setCredentials(context.auth);
const gmail = google.gmail({ version: 'v1', auth: authClient });
const subjectBase64 = Buffer.from(context.propsValue['subject']).toString(
'base64'
);
const attachments = context.propsValue.attachments as {
file: ApFile;
name: string | undefined;
}[];
const replyTo = context.propsValue['reply_to']?.filter(
(email) => email !== ''
);
const receiver = context.propsValue['receiver']?.filter(
(email) => email !== ''
);
const cc = context.propsValue['cc']?.filter((email) => email !== '');
const bcc = context.propsValue['bcc']?.filter((email) => email !== '');
const mailOptions: Mail.Options = {
to: receiver.join(', '), // Join all email addresses with a comma
cc: cc ? cc.join(', ') : undefined,
bcc: bcc ? bcc.join(', ') : undefined,
subject: `=?UTF-8?B?${subjectBase64}?=`,
replyTo: replyTo ? replyTo.join(', ') : '',
text:
context.propsValue.body_type === 'plain_text'
? context.propsValue['body']
: undefined,
html:
context.propsValue.body_type === 'html'
? context.propsValue['body']
: undefined,
attachments: [],
};
let threadId = undefined;
if (context.propsValue.in_reply_to) {
mailOptions.headers = [
{
key: 'References',
value: context.propsValue.in_reply_to,
},
{
key: 'In-Reply-To',
value: context.propsValue.in_reply_to,
},
];
const messages = await gmail.users.messages.list({
userId: 'me',
q: `Rfc822msgid:${context.propsValue.in_reply_to}`,
});
threadId = messages.data.messages?.[0].threadId;
}
const senderEmail =
context.propsValue.from ||
(await google.oauth2({ version: 'v2', auth: authClient }).userinfo.get())
.data.email;
if (senderEmail) {
mailOptions.from = context.propsValue.sender_name
? `${context.propsValue['sender_name']} <${senderEmail}>`
: senderEmail;
}
if (attachments && attachments.length > 0) {
const attachmentOption: Attachment[] = attachments.map(
({ file, name }) => {
const lookupResult = mime.lookup(
file.extension ? file.extension : ''
);
return {
filename: name ?? file.filename,
content: file?.base64,
contentType: lookupResult ? lookupResult : undefined,
encoding: 'base64',
};
}
);
mailOptions.attachments = attachmentOption;
}
const mail: any = new MailComposer(mailOptions).compile();
mail.keepBcc = true;
const mailBody = await mail.build();
const encodedPayload = Buffer.from(mailBody)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_');
if (context.propsValue.draft) {
return await gmail.users.drafts.create({
userId: 'me',
requestBody: { message: { threadId, raw: encodedPayload } },
});
} else {
return await gmail.users.messages.send({
userId: 'me',
requestBody: {
threadId,
raw: encodedPayload,
},
});
}
},
});

View File

@@ -0,0 +1,227 @@
import { OAuth2PropertyValue } from '@activepieces/pieces-framework';
import {
GmailLabel,
GmailMessage,
GmailThread,
GmailMessageFormat,
GmailMessageResponse as GmailMessageList,
} from './models';
import {
AuthenticationType,
httpClient,
HttpMethod,
} from '@activepieces/pieces-common';
import { Attachment, ParsedMail, simpleParser } from 'mailparser';
import { FilesService } from '@activepieces/pieces-framework';
interface SearchMailProps {
access_token: string;
from: string;
to: string;
subject?: string;
label: GmailLabel;
category: string;
after?: number;
before?: number;
max_results?: number;
page_token?: string;
}
interface GetMailProps {
access_token: string;
message_id?: string;
thread_id?: string;
format: GmailMessageFormat;
}
export const GmailRequests = {
getMail: async ({ access_token, format, message_id }: GetMailProps) => {
const response = await httpClient.sendRequest<GmailMessage>({
method: HttpMethod.GET,
url: `https://gmail.googleapis.com/gmail/v1/users/me/messages/${message_id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: access_token,
},
queryParams: {
format,
},
});
let bodyHtml = '';
let bodyPlain = '';
const payload = response.body.payload;
let bodyParts = payload.parts || [];
const headers = payload.headers.reduce(
(obj: { [key: string]: string }, header) => {
obj[header.name.toLowerCase()] = header.value;
return obj;
},
{}
);
const alternateBodyPart = bodyParts.find((part) =>
part.mimeType.startsWith('multipart/alternative')
);
if (alternateBodyPart && alternateBodyPart?.parts) {
bodyParts = alternateBodyPart.parts;
}
const subject = headers['subject'] || '';
// Determine the content type of the message body
const contentType = headers['content-type'] || 'text/plain';
const isMultipart = contentType.startsWith('multipart/');
if (isMultipart) {
// If the message is multipart, extract the plain text and HTML parts
const textPart = bodyParts.find((part) => part.mimeType === 'text/plain');
const htmlPart = bodyParts.find((part) => part.mimeType === 'text/html');
// If the message is an "alternative" multipart, use the plain text part if it exists
const preferredPart = textPart || htmlPart;
bodyHtml = htmlPart ? decodeBase64(htmlPart.body.data) : '';
bodyPlain = preferredPart ? decodeBase64(preferredPart.body.data) : '';
} else {
// If the message is not multipart, use the body as-is
bodyPlain = decodeBase64(payload.body.data);
bodyHtml = '';
}
return {
subject: subject,
body_html: bodyHtml,
body_plain: bodyPlain,
...response.body,
};
},
getThread: async ({ access_token, format, thread_id }: GetMailProps) => {
const response = await httpClient.sendRequest<GmailThread>({
method: HttpMethod.GET,
url: `https://gmail.googleapis.com/gmail/v1/users/me/threads/${thread_id}`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: access_token,
},
queryParams: {
format,
},
});
return response.body;
},
getLabels: async (authentication: OAuth2PropertyValue) => {
return await httpClient.sendRequest<{ labels: GmailLabel[] }>({
method: HttpMethod.GET,
url: `https://gmail.googleapis.com/gmail/v1/users/me/labels`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: (authentication as OAuth2PropertyValue).access_token,
},
});
},
searchMail: async ({
access_token,
max_results = 1,
page_token: pageToken,
...mail
}: SearchMailProps) => {
const query = [];
if (mail.from) query.push(`from:(${mail.from})`);
if (mail.to) query.push(`to:(${mail.to})`);
if (mail.subject) query.push(`subject:(${mail.subject})`);
if (mail.label) query.push(`label:${mail.label.name}`);
if (mail.category) query.push(`category:${mail.category}`);
if (mail.after != null && mail.after > 0) query.push(`after:${mail.after}`);
if (mail.before != null) query.push(`before:${mail.before}`);
const response = await httpClient.sendRequest<GmailMessageList>({
method: HttpMethod.GET,
url: `https://www.googleapis.com/gmail/v1/users/me/messages`,
authentication: {
type: AuthenticationType.BEARER_TOKEN,
token: access_token,
},
queryParams: {
q: query.join(' '),
maxResults: `${max_results}`,
...(pageToken ? { pageToken } : {}),
},
});
if (response.body.messages) {
const messages = await Promise.all(
response.body.messages.map(
async (message: { id: string; threadId: string }) => {
const mail = await GmailRequests.getMail({
access_token,
message_id: message.id,
format: GmailMessageFormat.FULL,
});
const thread = await GmailRequests.getThread({
access_token,
thread_id: message.threadId,
format: GmailMessageFormat.FULL,
});
return {
message: mail,
thread,
};
}
)
);
return {
messages,
resultSizeEstimate: response.body.resultSizeEstimate,
...(response?.body.nextPageToken
? { nextPageToken: response.body.nextPageToken }
: {}),
};
}
return response.body;
},
};
function decodeBase64(data: any) {
return Buffer.from(data, 'base64').toString();
}
export async function parseStream(stream: any) {
return new Promise<ParsedMail>((resolve, reject) => {
simpleParser(stream, (err, parsed) => {
if (err) {
reject(err);
} else {
resolve(parsed);
}
});
});
}
export async function convertAttachment(
attachments: Attachment[],
files: FilesService
) {
const promises = attachments.map(async (attachment) => {
try {
const fileName = attachment.filename ?? `attachment-${Date.now()}`;
return {
fileName,
mimeType: attachment.contentType,
size: attachment.size,
data: await files.write({
fileName: fileName,
data: attachment.content,
}),
};
} catch (error) {
console.error(
`Failed to process attachment: ${attachment.filename}`,
error
);
return null;
}
});
const results = await Promise.all(promises);
return results.filter((result) => result !== null);
}

View File

@@ -0,0 +1,91 @@
export interface GmailLabel {
id: string;
name: string;
messageListVisibility: GmailMessageListVisibility;
labelListVisibility: GmailLabelListVisibility;
type: GmailLabelType;
messagesTotal: number;
messagesUnread: number;
threadsTotal: number;
threadsUnread: number;
color: {
textColor: string;
backgroundColor: string;
};
}
export interface GmailMessage {
id: string;
threadId: string;
labelIds: [string];
snippet: string;
historyId: string;
internalDate: number;
payload: {
partId: string;
mimeType: string;
filename: string;
headers: {
name: string;
value: string;
}[];
body: {
data: any;
size: number;
};
parts: {
parts: any[];
partId: string;
mimeType: string;
filename: string;
headers: {
name: string;
value: string;
}[];
body: {
size: number;
data: string;
};
}[];
};
sizeEstimate: number;
raw: string;
}
export interface GmailThread {
id: string;
snippet: string;
historyId: string;
messages: GmailMessage[];
}
export interface GmailMessageResponse {
messages: {
id: string;
threadId: string;
}[];
nextPageToken?: string;
resultSizeEstimate: number;
}
enum GmailMessageListVisibility {
SHOW = 'show',
HIDE = 'hide',
}
enum GmailLabelListVisibility {
SHOW = 'show',
HIDE = 'hide',
}
enum GmailLabelType {
SYSTEM = 'system',
USER = 'user',
}
export enum GmailMessageFormat {
MINIMAL = 'minimal',
FULL = 'full',
RAW = 'raw',
METADATA = 'metadata',
}

View File

@@ -0,0 +1,80 @@
import { Property, OAuth2PropertyValue } from '@activepieces/pieces-framework';
import { GmailRequests } from './data';
import { GmailLabel } from './models';
import { gmailAuth } from '../..';
export const GmailProps = {
from: Property.ShortText({
displayName: 'Email sender',
description:
'Optional filteration, leave empty to filter based on the email sender',
required: false,
defaultValue: '',
}),
to: Property.ShortText({
displayName: 'Email recipient',
description:
'Optional filteration, leave empty to filter based on the email recipient',
required: false,
defaultValue: '',
}),
subject: Property.ShortText({
displayName: 'Email subject',
description: 'The email subject',
required: false,
defaultValue: '',
}),
category: Property.StaticDropdown({
displayName: 'Category',
description:
'Optional filteration, leave unselected to filter based on the email category',
required: false,
options: {
disabled: false,
options: [
{ label: 'Primary', value: 'primary' },
{ label: 'Social', value: 'social' },
{ label: 'Promotions', value: 'promotions' },
{ label: 'Updates', value: 'updates' },
{ label: 'Forums', value: 'forums' },
{ label: 'Reservations', value: 'reservations' },
{ label: 'Purchases', value: 'purchases' },
],
},
}),
label: Property.Dropdown<GmailLabel, false, typeof gmailAuth>({
auth: gmailAuth,
displayName: 'Label',
description:
'Optional filteration, leave unselected to filter based on the email label',
required: false,
defaultValue: '',
refreshers: [],
options: async ({ auth }) => {
if (!auth) {
return {
disabled: true,
options: [],
placeholder: 'please authenticate first',
};
}
const response = await GmailRequests.getLabels(auth);
return {
disabled: false,
options: response.body.labels.map((label) => ({
label: label.name,
value: label,
})),
};
},
}),
unread: (required = false) =>
Property.Checkbox({
displayName: 'Is unread?',
description: 'Check if the email is unread or not',
required,
defaultValue: false,
}),
};

View File

@@ -0,0 +1,161 @@
import {
createTrigger,
TriggerStrategy,
PiecePropValueSchema,
FilesService,
} from '@activepieces/pieces-framework';
import dayjs from 'dayjs';
import { GmailLabel } from '../common/models';
import { GmailProps } from '../common/props';
import { gmailAuth } from '../../';
import { GmailRequests, parseStream, convertAttachment } from '../common/data';
import { google } from 'googleapis';
import { OAuth2Client } from 'googleapis-common';
export const gmailNewEmailTrigger = createTrigger({
auth: gmailAuth,
name: 'gmail_new_email_received',
displayName: 'New Email',
description: 'Triggers when new mail is found in your Gmail inbox',
props: {
subject: GmailProps.subject,
from: GmailProps.from,
to: GmailProps.to,
label: GmailProps.label,
category: GmailProps.category,
},
sampleData: {},
type: TriggerStrategy.POLLING,
async onEnable(context) {
await context.store.put('lastPoll', Date.now());
},
async onDisable(context) {
return;
},
async run(context) {
const lastFetchEpochMS = (await context.store.get<number>('lastPoll')) ?? 0;
const items = await pollRecentMessages({
auth: context.auth,
props: context.propsValue,
files: context.files,
lastFetchEpochMS: lastFetchEpochMS,
});
const newLastEpochMilliSeconds = items.reduce(
(acc, item) => Math.max(acc, item.epochMilliSeconds),
lastFetchEpochMS
);
await context.store.put('lastPoll', newLastEpochMilliSeconds);
return items
.filter((f) => f.epochMilliSeconds > lastFetchEpochMS)
.map((item) => item.data);
},
async test(context) {
const lastFetchEpochMS = (await context.store.get<number>('lastPoll')) ?? 0;
const items = await pollRecentMessages({
auth: context.auth,
props: context.propsValue,
files: context.files,
lastFetchEpochMS: lastFetchEpochMS,
});
return getFirstFiveOrAll(items.map((item) => item.data));
},
});
interface PropsValue {
from: string | undefined;
to: string | undefined;
subject: string | undefined;
label: GmailLabel | undefined;
category: string | undefined;
}
async function pollRecentMessages({
auth,
props,
files,
lastFetchEpochMS,
}: {
auth: PiecePropValueSchema<typeof gmailAuth>;
props: PropsValue;
files: FilesService;
lastFetchEpochMS: number;
}): Promise<
{
epochMilliSeconds: number;
data: unknown;
}[]
> {
const authClient = new OAuth2Client();
authClient.setCredentials(auth);
const gmail = google.gmail({ version: 'v1', auth: authClient });
// construct query
const query = [];
const maxResults = lastFetchEpochMS === 0 ? 5 : 100;
const afterUnixSeconds = Math.floor(lastFetchEpochMS / 1000);
if (props.from) query.push(`from:(${props.from})`);
if (props.to) query.push(`to:(${props.to})`);
if (props.subject) query.push(`subject:(${props.subject})`);
if (props.label) query.push(`label:${props.label.name}`);
if (props.category) query.push(`category:${props.category}`);
if (afterUnixSeconds != null && afterUnixSeconds > 0)
query.push(`after:${afterUnixSeconds}`);
// List Messages
const messagesResponse = await gmail.users.messages.list({
userId: 'me',
q: query.join(' '),
maxResults,
});
const pollingResponse = [];
for (const message of messagesResponse.data.messages || []) {
const rawMailResponse = await gmail.users.messages.get({
userId: 'me',
id: message.id!,
format: 'raw',
});
const threadResponse = await gmail.users.threads.get({
userId: 'me',
id: message.threadId!,
});
const parsedMailResponse = await parseStream(
Buffer.from(rawMailResponse.data.raw as string, 'base64').toString(
'utf-8'
)
);
pollingResponse.push({
epochMilliSeconds: dayjs(parsedMailResponse.date).valueOf(),
data: {
message: {
...parsedMailResponse,
attachments: await convertAttachment(
parsedMailResponse.attachments,
files
),
},
thread: {
...threadResponse,
},
},
});
}
return pollingResponse;
}
function getFirstFiveOrAll(array: unknown[]) {
if (array.length <= 5) {
return array;
} else {
return array.slice(0, 5);
}
}

View File

@@ -0,0 +1,196 @@
import {
createTrigger,
TriggerStrategy,
FilesService,
} from '@activepieces/pieces-framework';
import { GmailProps } from '../common/props';
import { gmailAuth } from '../../';
import { google } from 'googleapis';
import { OAuth2Client } from 'googleapis-common';
import { parseStream, convertAttachment } from '../common/data';
async function enrichGmailMessage({
gmail,
messageId,
files,
labelInfo,
}: {
gmail: any;
messageId: string;
files: FilesService;
labelInfo: {
labelId: string;
labelName: string;
};
}) {
const rawMailResponse = await gmail.users.messages.get({
userId: 'me',
id: messageId,
format: 'raw',
});
const threadResponse = await gmail.users.threads.get({
userId: 'me',
id: rawMailResponse.data.threadId!,
});
const parsedMailResponse = await parseStream(
Buffer.from(rawMailResponse.data.raw as string, 'base64').toString('utf-8')
);
return {
message: {
...parsedMailResponse,
attachments: await convertAttachment(
parsedMailResponse.attachments,
files
),
},
thread: threadResponse.data,
labelInfo: {
...labelInfo,
addedAt: Date.now(),
},
};
}
export const gmailNewLabeledEmailTrigger = createTrigger({
auth: gmailAuth,
name: 'new_labeled_email',
displayName: 'New Labeled Email',
description: 'Triggers when a label is added to an email',
props: {
label: {
...GmailProps.label,
required: true,
},
},
sampleData: {},
type: TriggerStrategy.POLLING,
onEnable: async (context) => {
const authClient = new OAuth2Client();
authClient.setCredentials(context.auth);
const gmail = google.gmail({ version: 'v1', auth: authClient });
const profile = await gmail.users.getProfile({
userId: 'me',
});
await context.store.put('lastHistoryId', profile.data.historyId);
},
onDisable: async (context) => {
await context.store.delete('lastHistoryId');
},
run: async (context) => {
const authClient = new OAuth2Client();
authClient.setCredentials(context.auth);
const gmail = google.gmail({ version: 'v1', auth: authClient });
const lastHistoryId = await context.store.get('lastHistoryId');
const historyResponse = await gmail.users.history.list({
userId: 'me',
startHistoryId: lastHistoryId as string,
labelId: context.propsValue.label.id,
historyTypes: ['labelAdded', 'messageAdded'],
});
const labeledMessages = new Map<string, string>();
const results = [];
if (historyResponse.data.history) {
for (const history of historyResponse.data.history) {
if (history.labelsAdded) {
for (const labelAdded of history.labelsAdded) {
if (
labelAdded.labelIds?.includes(context.propsValue.label.id) &&
labelAdded.message?.id
) {
labeledMessages.set(
labelAdded.message.id,
history.id?.toString() || ''
);
}
}
} else if (history.messagesAdded) {
for (const messageAdded of history.messagesAdded) {
if (
messageAdded.message?.id &&
messageAdded.message.labelIds?.includes(
context.propsValue.label.id
)
) {
labeledMessages.set(
messageAdded.message.id,
history.id?.toString() || ''
);
}
}
}
}
}
for (const [messageId, historyId] of labeledMessages) {
const enrichedMessage = await enrichGmailMessage({
gmail,
messageId,
files: context.files,
labelInfo: {
labelId: context.propsValue.label.id,
labelName: context.propsValue.label.name,
},
});
if (lastHistoryId !== historyId) {
results.push({
id: historyId,
data: enrichedMessage,
});
}
}
if (historyResponse.data.historyId) {
await context.store.put('lastHistoryId', historyResponse.data.historyId);
}
return results;
},
test: async (context) => {
const authClient = new OAuth2Client();
authClient.setCredentials(context.auth);
const gmail = google.gmail({ version: 'v1', auth: authClient });
const messagesResponse = await gmail.users.messages.list({
userId: 'me',
labelIds: [context.propsValue.label.id],
maxResults: 5,
});
const results = [];
if (messagesResponse.data.messages) {
for (const message of messagesResponse.data.messages) {
const messageId = message.id;
if (!messageId) {
continue;
}
const enrichedMessage = await enrichGmailMessage({
gmail,
messageId: messageId,
files: context.files,
labelInfo: {
labelId: context.propsValue.label.id,
labelName: context.propsValue.label.name,
},
});
results.push({
id: messageId,
data: enrichedMessage,
});
}
}
return results;
},
});