Merge pull request #1571 from automatisch/AUT-632
feat(mailchimp): add mailchimp integration
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
import defineAction from '../../../../helpers/define-action.js';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create campaign',
|
||||
key: 'createCampaign',
|
||||
description: 'Creates a new campaign draft.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Campaign Name',
|
||||
key: 'campaignName',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description: '',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Audience',
|
||||
key: 'audienceId',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listAudiences',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Segment or Tag',
|
||||
key: 'segmentOrTagId',
|
||||
type: 'dropdown',
|
||||
required: false,
|
||||
dependsOn: ['parameters.audienceId'],
|
||||
description:
|
||||
'Choose the specific segment or tag to which you"d like to direct the campaign. If no segment or tag is chosen, the campaign will be distributed to the entire audience previously selected.',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listSegmentsOrTags',
|
||||
},
|
||||
{
|
||||
name: 'parameters.audienceId',
|
||||
value: '{parameters.audienceId}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Email Subject',
|
||||
key: 'emailSubject',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Preview Text',
|
||||
key: 'previewText',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description:
|
||||
'The snippet will be visible in the inbox following the subject line.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'From Name',
|
||||
key: 'fromName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The "from" name on the campaign (not an email address).',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'From Email Address',
|
||||
key: 'fromEmailAddress',
|
||||
type: 'string',
|
||||
required: true,
|
||||
description: 'The reply-to email address for the campaign.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'To Name',
|
||||
key: 'toName',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description:
|
||||
'Supports *|MERGETAGS|* for recipient name, such as *|FNAME|*, *|LNAME|*, *|FNAME|* *|LNAME|*, etc.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Template',
|
||||
key: 'templateId',
|
||||
type: 'dropdown',
|
||||
required: false,
|
||||
description:
|
||||
'Select either a template or provide HTML email content, you cannot provide both. If both fields are left blank, the campaign draft will have no content.',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listTemplates',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Email Content (HTML)',
|
||||
key: 'emailContent',
|
||||
type: 'string',
|
||||
required: false,
|
||||
description:
|
||||
'Select either a template or provide HTML email content, you cannot provide both. If both fields are left blank, the campaign draft will have no content.',
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const {
|
||||
campaignName,
|
||||
audienceId,
|
||||
segmentOrTagId,
|
||||
emailSubject,
|
||||
previewText,
|
||||
fromName,
|
||||
fromEmailAddress,
|
||||
toName,
|
||||
templateId,
|
||||
emailContent,
|
||||
} = $.step.parameters;
|
||||
|
||||
const body = {
|
||||
type: 'regular',
|
||||
recipients: {
|
||||
list_id: audienceId,
|
||||
segment_opts: {
|
||||
saved_segment_id: Number(segmentOrTagId),
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
subject_line: emailSubject,
|
||||
reply_to: fromEmailAddress,
|
||||
title: campaignName,
|
||||
preview_text: previewText,
|
||||
from_name: fromName,
|
||||
to_name: toName,
|
||||
},
|
||||
};
|
||||
|
||||
const { data: campaign } = await $.http.post('/3.0/campaigns', body);
|
||||
|
||||
const campaignBody = {
|
||||
template: {
|
||||
id: Number(templateId),
|
||||
},
|
||||
html: emailContent,
|
||||
};
|
||||
|
||||
const { data } = await $.http.put(
|
||||
`/3.0/campaigns/${campaign.id}/content`,
|
||||
campaignBody
|
||||
);
|
||||
|
||||
$.setActionItem({
|
||||
raw: data,
|
||||
});
|
||||
},
|
||||
});
|
4
packages/backend/src/apps/mailchimp/actions/index.js
Normal file
4
packages/backend/src/apps/mailchimp/actions/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import createCampaign from './create-campaign/index.js';
|
||||
import sendCampaign from './send-campaign/index.js';
|
||||
|
||||
export default [createCampaign, sendCampaign];
|
@@ -0,0 +1,39 @@
|
||||
import defineAction from '../../../../helpers/define-action.js';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Send campaign',
|
||||
key: 'sendCampaign',
|
||||
description: 'Sends a campaign draft.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Campaign',
|
||||
key: 'campaignId',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listCampaigns',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const campaignId = $.step.parameters.campaignId;
|
||||
|
||||
await $.http.post(`/3.0/campaigns/${campaignId}/actions/send`);
|
||||
|
||||
$.setActionItem({
|
||||
raw: {
|
||||
output: 'sent',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
1
packages/backend/src/apps/mailchimp/assets/favicon.svg
Normal file
1
packages/backend/src/apps/mailchimp/assets/favicon.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,19 @@
|
||||
import { URLSearchParams } from 'url';
|
||||
|
||||
export default async function generateAuthUrl($) {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const redirectUri = oauthRedirectUrlField.value;
|
||||
const searchParams = new URLSearchParams({
|
||||
response_type: 'code',
|
||||
client_id: $.auth.data.clientId,
|
||||
redirect_uri: redirectUri,
|
||||
});
|
||||
|
||||
const url = `https://login.mailchimp.com/oauth2/authorize?${searchParams.toString()}`;
|
||||
|
||||
await $.auth.set({
|
||||
url,
|
||||
});
|
||||
}
|
46
packages/backend/src/apps/mailchimp/auth/index.js
Normal file
46
packages/backend/src/apps/mailchimp/auth/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
import generateAuthUrl from './generate-auth-url.js';
|
||||
import verifyCredentials from './verify-credentials.js';
|
||||
import isStillVerified from './is-still-verified.js';
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
key: 'oAuthRedirectUrl',
|
||||
label: 'OAuth Redirect URL',
|
||||
type: 'string',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
value: '{WEB_APP_URL}/app/mailchimp/connections/add',
|
||||
placeholder: null,
|
||||
description:
|
||||
'When asked to input a redirect URL in Mailchimp, enter the URL above.',
|
||||
clickToCopy: true,
|
||||
},
|
||||
{
|
||||
key: 'clientId',
|
||||
label: 'Client ID',
|
||||
type: 'string',
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'clientSecret',
|
||||
label: 'Client Secret',
|
||||
type: 'string',
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: null,
|
||||
clickToCopy: false,
|
||||
},
|
||||
],
|
||||
|
||||
generateAuthUrl,
|
||||
verifyCredentials,
|
||||
isStillVerified,
|
||||
};
|
@@ -0,0 +1,8 @@
|
||||
import getCurrentUser from '../common/get-current-user.js';
|
||||
|
||||
const isStillVerified = async ($) => {
|
||||
const currentUser = await getCurrentUser($);
|
||||
return !!currentUser.user_id;
|
||||
};
|
||||
|
||||
export default isStillVerified;
|
@@ -0,0 +1,40 @@
|
||||
import getCurrentUser from '../common/get-current-user.js';
|
||||
|
||||
const verifyCredentials = async ($) => {
|
||||
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||
(field) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const redirectUri = oauthRedirectUrlField.value;
|
||||
const params = new URLSearchParams({
|
||||
grant_type: 'authorization_code',
|
||||
client_id: $.auth.data.clientId,
|
||||
client_secret: $.auth.data.clientSecret,
|
||||
redirect_uri: redirectUri,
|
||||
code: $.auth.data.code,
|
||||
});
|
||||
|
||||
const { data } = await $.http.post(
|
||||
'https://login.mailchimp.com/oauth2/token',
|
||||
params.toString()
|
||||
);
|
||||
|
||||
await $.auth.set({
|
||||
accessToken: data.access_token,
|
||||
tokenType: data.token_type,
|
||||
});
|
||||
|
||||
const currentUser = await getCurrentUser($);
|
||||
|
||||
await $.auth.set({
|
||||
clientId: $.auth.data.clientId,
|
||||
clientSecret: $.auth.data.clientSecret,
|
||||
scope: $.auth.data.scope,
|
||||
idToken: data.id_token,
|
||||
expiresIn: data.expires_in,
|
||||
refreshToken: data.refresh_token,
|
||||
serverPrefix: currentUser.dc,
|
||||
screenName: currentUser.login.login_name,
|
||||
});
|
||||
};
|
||||
|
||||
export default verifyCredentials;
|
@@ -0,0 +1,12 @@
|
||||
const addAuthHeader = ($, requestConfig) => {
|
||||
if (
|
||||
!requestConfig.additionalProperties?.skipAddingAuthHeader &&
|
||||
$.auth.data?.accessToken
|
||||
) {
|
||||
requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default addAuthHeader;
|
@@ -0,0 +1,18 @@
|
||||
const getCurrentUser = async ($) => {
|
||||
const { data: currentUser } = await $.http.get(
|
||||
'https://login.mailchimp.com/oauth2/metadata',
|
||||
{
|
||||
headers: {
|
||||
Authorization: `OAuth ${$.auth.data.accessToken}`,
|
||||
},
|
||||
additionalProperties: {
|
||||
skipAddingAuthHeader: true,
|
||||
skipAddingBaseUrl: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return currentUser;
|
||||
};
|
||||
|
||||
export default getCurrentUser;
|
10
packages/backend/src/apps/mailchimp/common/set-base-url.js
Normal file
10
packages/backend/src/apps/mailchimp/common/set-base-url.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const setBaseUrl = ($, requestConfig) => {
|
||||
const serverPrefix = $.auth.data.serverPrefix;
|
||||
if (!requestConfig.additionalProperties?.skipAddingBaseUrl && serverPrefix) {
|
||||
requestConfig.baseURL = `https://${serverPrefix}.api.mailchimp.com`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
export default setBaseUrl;
|
@@ -0,0 +1,6 @@
|
||||
import listAudiences from './list-audiences/index.js';
|
||||
import listCampaigns from './list-campaigns/index.js';
|
||||
import listTags from './list-segments-or-tags/index.js';
|
||||
import listTemplates from './list-templates/index.js';
|
||||
|
||||
export default [listAudiences, listCampaigns, listTags, listTemplates];
|
@@ -0,0 +1,40 @@
|
||||
export default {
|
||||
name: 'List audiences',
|
||||
key: 'listAudiences',
|
||||
|
||||
async run($) {
|
||||
const audiences = {
|
||||
data: [],
|
||||
};
|
||||
let hasMore = false;
|
||||
|
||||
const params = {
|
||||
sort_field: 'date_created',
|
||||
sort_dir: 'DESC',
|
||||
count: 1000,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
do {
|
||||
const { data } = await $.http.get('/3.0/lists', { params });
|
||||
params.offset = params.offset + params.count;
|
||||
|
||||
if (data?.lists) {
|
||||
for (const audience of data.lists) {
|
||||
audiences.data.push({
|
||||
value: audience.id,
|
||||
name: audience.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (data.total_items > params.offset) {
|
||||
hasMore = true;
|
||||
} else {
|
||||
hasMore = false;
|
||||
}
|
||||
} while (hasMore);
|
||||
|
||||
return audiences;
|
||||
},
|
||||
};
|
@@ -0,0 +1,42 @@
|
||||
export default {
|
||||
name: 'List campaigns',
|
||||
key: 'listCampaigns',
|
||||
|
||||
async run($) {
|
||||
const campaigns = {
|
||||
data: [],
|
||||
};
|
||||
let hasMore = false;
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
const params = {
|
||||
list_id: audienceId,
|
||||
sort_field: 'create_time',
|
||||
sort_dir: 'DESC',
|
||||
count: 1000,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
do {
|
||||
const { data } = await $.http.get('/3.0/campaigns', { params });
|
||||
params.offset = params.offset + params.count;
|
||||
|
||||
if (data?.campaigns) {
|
||||
for (const campaign of data.campaigns) {
|
||||
campaigns.data.push({
|
||||
value: campaign.id,
|
||||
name: campaign.settings.title || campaign.settings.subject_line || 'Unnamed campaign',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (data.total_items > params.offset) {
|
||||
hasMore = true;
|
||||
} else {
|
||||
hasMore = false;
|
||||
}
|
||||
} while (hasMore);
|
||||
|
||||
return campaigns;
|
||||
},
|
||||
};
|
@@ -0,0 +1,44 @@
|
||||
export default {
|
||||
name: 'List segments or tags',
|
||||
key: 'listSegmentsOrTags',
|
||||
|
||||
async run($) {
|
||||
const segmentsOrTags = {
|
||||
data: [],
|
||||
};
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
if (!audienceId) {
|
||||
return segmentsOrTags;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { tags: allTags },
|
||||
} = await $.http.get(`/3.0/lists/${audienceId}/tag-search`);
|
||||
|
||||
const {
|
||||
data: { segments },
|
||||
} = await $.http.get(`/3.0/lists/${audienceId}/segments`);
|
||||
|
||||
const mergedArray = [...allTags, ...segments].reduce(
|
||||
(accumulator, current) => {
|
||||
if (!accumulator.some((item) => item.id === current.id)) {
|
||||
accumulator.push(current);
|
||||
}
|
||||
return accumulator;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
if (mergedArray?.length) {
|
||||
for (const tagOrSegment of mergedArray) {
|
||||
segmentsOrTags.data.push({
|
||||
value: tagOrSegment.id,
|
||||
name: tagOrSegment.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return segmentsOrTags;
|
||||
},
|
||||
};
|
@@ -0,0 +1,30 @@
|
||||
export default {
|
||||
name: 'List templates',
|
||||
key: 'listTemplates',
|
||||
|
||||
async run($) {
|
||||
const templates = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const params = {
|
||||
sort_field: 'date_created',
|
||||
sort_dir: 'DESC',
|
||||
count: 1000,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
const { data } = await $.http.get('/3.0/templates', { params });
|
||||
|
||||
if (data?.templates) {
|
||||
for (const template of data.templates) {
|
||||
templates.data.push({
|
||||
value: template.id,
|
||||
name: template.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return templates;
|
||||
},
|
||||
};
|
23
packages/backend/src/apps/mailchimp/index.js
Normal file
23
packages/backend/src/apps/mailchimp/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import defineApp from '../../helpers/define-app.js';
|
||||
import addAuthHeader from './common/add-auth-header.js';
|
||||
import setBaseUrl from './common/set-base-url.js';
|
||||
import auth from './auth/index.js';
|
||||
import triggers from './triggers/index.js';
|
||||
import dynamicData from './dynamic-data/index.js';
|
||||
import actions from './actions/index.js';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Mailchimp',
|
||||
key: 'mailchimp',
|
||||
baseUrl: 'https://mailchimp.com',
|
||||
apiBaseUrl: '',
|
||||
iconUrl: '{BASE_URL}/apps/mailchimp/assets/favicon.svg',
|
||||
authDocUrl: 'https://automatisch.io/docs/apps/mailchimp/connection',
|
||||
primaryColor: '000000',
|
||||
supportsConnections: true,
|
||||
beforeRequest: [setBaseUrl, addAuthHeader],
|
||||
auth,
|
||||
triggers,
|
||||
dynamicData,
|
||||
actions,
|
||||
});
|
@@ -0,0 +1,101 @@
|
||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'Email opened',
|
||||
key: 'emailOpened',
|
||||
pollInterval: 15,
|
||||
description:
|
||||
'Triggers when a recipient opens an email as part of a particular campaign.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Audience',
|
||||
key: 'audienceId',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listAudiences',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Campaign Type',
|
||||
key: 'campaignType',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
options: [
|
||||
{
|
||||
label: 'Campaign',
|
||||
value: 'campaign',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Campaign',
|
||||
key: 'campaignId',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
dependsOn: ['parameters.audienceId'],
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listCampaigns',
|
||||
},
|
||||
{
|
||||
name: 'parameters.audienceId',
|
||||
value: '{parameters.audienceId}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const campaignId = $.step.parameters.campaignId;
|
||||
let hasMore = false;
|
||||
|
||||
const params = {
|
||||
count: 1000,
|
||||
offset: 0,
|
||||
};
|
||||
|
||||
do {
|
||||
const { data } = await $.http.get(
|
||||
`/3.0/reports/${campaignId}/open-details`,
|
||||
{ params }
|
||||
);
|
||||
params.offset = params.offset + params.count;
|
||||
|
||||
if (data.members?.length) {
|
||||
for (const member of data.members) {
|
||||
$.pushTriggerItem({
|
||||
raw: member,
|
||||
meta: {
|
||||
internalId: member.email_id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (data.total_items > params.offset) {
|
||||
hasMore = true;
|
||||
} else {
|
||||
hasMore = false;
|
||||
}
|
||||
} while (hasMore);
|
||||
},
|
||||
});
|
5
packages/backend/src/apps/mailchimp/triggers/index.js
Normal file
5
packages/backend/src/apps/mailchimp/triggers/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import emailOpened from './email-opened/index.js';
|
||||
import newSubscribers from './new-subscribers/index.js';
|
||||
import newUnsubscribers from './new-unsubscribers/index.js';
|
||||
|
||||
export default [emailOpened, newSubscribers, newUnsubscribers];
|
@@ -0,0 +1,105 @@
|
||||
import Crypto from 'crypto';
|
||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New subscribers',
|
||||
key: 'newSubscribers',
|
||||
type: 'webhook',
|
||||
description: 'Triggers when a new subscriber is appended to an audience.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Audience',
|
||||
key: 'audienceId',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listAudiences',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const dataItem = {
|
||||
raw: $.request.body,
|
||||
meta: {
|
||||
internalId: Crypto.randomUUID(),
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
},
|
||||
|
||||
async testRun($) {
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
const computedWebhookEvent = {
|
||||
data: {
|
||||
id: Crypto.randomUUID(),
|
||||
email: 'user@automatisch.io',
|
||||
ip_opt: '127.0.0.1',
|
||||
merges: {
|
||||
EMAIL: 'user@automatisch.io',
|
||||
FNAME: 'FNAME',
|
||||
LNAME: 'LNAME',
|
||||
PHONE: '',
|
||||
ADDRESS: '',
|
||||
BIRTHDAY: '',
|
||||
},
|
||||
web_id: Crypto.randomUUID(),
|
||||
list_id: audienceId,
|
||||
email_type: 'html',
|
||||
},
|
||||
type: 'subscribe',
|
||||
fired_at: new Date().toLocaleString(),
|
||||
};
|
||||
|
||||
const dataItem = {
|
||||
raw: computedWebhookEvent,
|
||||
meta: {
|
||||
internalId: '',
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
},
|
||||
|
||||
async registerHook($) {
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
const payload = {
|
||||
url: $.webhookUrl,
|
||||
events: {
|
||||
subscribe: true,
|
||||
},
|
||||
sources: {
|
||||
user: true,
|
||||
admin: true,
|
||||
api: true,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await $.http.post(
|
||||
`/3.0/lists/${audienceId}/webhooks`,
|
||||
payload
|
||||
);
|
||||
|
||||
await $.flow.setRemoteWebhookId(response.data.id);
|
||||
},
|
||||
|
||||
async unregisterHook($) {
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
await $.http.delete(
|
||||
`/3.0/lists/${audienceId}/webhooks/${$.flow.remoteWebhookId}`
|
||||
);
|
||||
},
|
||||
});
|
@@ -0,0 +1,108 @@
|
||||
import Crypto from 'crypto';
|
||||
import defineTrigger from '../../../../helpers/define-trigger.js';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New unsubscribers',
|
||||
key: 'newUnsubscribers',
|
||||
type: 'webhook',
|
||||
description: 'Triggers when any existing subscriber opts out of an audience.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Audience',
|
||||
key: 'audienceId',
|
||||
type: 'dropdown',
|
||||
required: true,
|
||||
description: '',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listAudiences',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const dataItem = {
|
||||
raw: $.request.body,
|
||||
meta: {
|
||||
internalId: Crypto.randomUUID(),
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
},
|
||||
|
||||
async testRun($) {
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
const computedWebhookEvent = {
|
||||
data: {
|
||||
id: Crypto.randomUUID(),
|
||||
email: 'user@automatisch.io',
|
||||
action: 'unsub',
|
||||
ip_opt: '127.0.0.1',
|
||||
merges: {
|
||||
EMAIL: 'user@automatisch.io',
|
||||
FNAME: 'FNAME',
|
||||
LNAME: 'LNAME',
|
||||
PHONE: '',
|
||||
ADDRESS: '',
|
||||
BIRTHDAY: '',
|
||||
},
|
||||
reason: 'manual',
|
||||
web_id: Crypto.randomUUID(),
|
||||
list_id: audienceId,
|
||||
email_type: 'html',
|
||||
campaign_id: Crypto.randomUUID(),
|
||||
},
|
||||
type: 'unsubscribe',
|
||||
fired_at: new Date().toLocaleString(),
|
||||
};
|
||||
|
||||
const dataItem = {
|
||||
raw: computedWebhookEvent,
|
||||
meta: {
|
||||
internalId: '',
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
},
|
||||
|
||||
async registerHook($) {
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
const payload = {
|
||||
url: $.webhookUrl,
|
||||
events: {
|
||||
unsubscribe: true,
|
||||
},
|
||||
sources: {
|
||||
user: true,
|
||||
admin: true,
|
||||
api: true,
|
||||
},
|
||||
};
|
||||
|
||||
const response = await $.http.post(
|
||||
`/3.0/lists/${audienceId}/webhooks`,
|
||||
payload
|
||||
);
|
||||
|
||||
await $.flow.setRemoteWebhookId(response.data.id);
|
||||
},
|
||||
|
||||
async unregisterHook($) {
|
||||
const audienceId = $.step.parameters.audienceId;
|
||||
|
||||
await $.http.delete(
|
||||
`/3.0/lists/${audienceId}/webhooks/${$.flow.remoteWebhookId}`
|
||||
);
|
||||
},
|
||||
});
|
@@ -252,6 +252,12 @@ export default defineConfig({
|
||||
{ text: 'Connection', link: '/apps/invoice-ninja/connection' },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Mailchimp',
|
||||
collapsible: true,
|
||||
collapsed: true,
|
||||
items: [{ text: 'Connection', link: '/apps/mailchimp/connection' }],
|
||||
},
|
||||
{
|
||||
text: 'MailerLite',
|
||||
collapsible: true,
|
||||
|
14
packages/docs/pages/apps/mailchimp/actions.md
Normal file
14
packages/docs/pages/apps/mailchimp/actions.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
favicon: /favicons/mailchimp.svg
|
||||
items:
|
||||
- name: Create campaign
|
||||
desc: Creates a new campaign draft.
|
||||
- name: Send campaign
|
||||
desc: Sends a campaign draft.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import CustomListing from '../../components/CustomListing.vue'
|
||||
</script>
|
||||
|
||||
<CustomListing />
|
17
packages/docs/pages/apps/mailchimp/connection.md
Normal file
17
packages/docs/pages/apps/mailchimp/connection.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Mailchimp
|
||||
|
||||
:::info
|
||||
This page explains the steps you need to follow to set up the Mailchimp
|
||||
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||
:::
|
||||
|
||||
1. Login to your [Mailchimp account](https://mailchimp.com/) to create an app.
|
||||
2. Click on the account image and go to your **profile** page.
|
||||
3. Click on the **Extras** tab and choose the **Registered apps**.
|
||||
4. Click on the **Register An App** button.
|
||||
5. Fill the registration form.
|
||||
6. Copy **OAuth Redirect URL** from Automatisch to **Redirect URI** field, and click on the **Create** button.
|
||||
7. Copy the **Your Client ID** value to the `Client ID` field on Automatisch.
|
||||
8. Copy the **Your Client Secret** value to the `Client Secret` field on Automatisch.
|
||||
9. Click **Submit** button on Automatisch.
|
||||
10. Congrats! Start using your new Mailchimp connection within the flows.
|
16
packages/docs/pages/apps/mailchimp/triggers.md
Normal file
16
packages/docs/pages/apps/mailchimp/triggers.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
favicon: /favicons/mailchimp.svg
|
||||
items:
|
||||
- name: Email opened
|
||||
desc: Triggers when a recipient opens an email as part of a particular campaign.
|
||||
- name: New subscribers
|
||||
desc: Triggers when a new subscriber is appended to an audience.
|
||||
- name: New unsubscribers
|
||||
desc: Triggers when any existing subscriber opts out of an audience.
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import CustomListing from '../../components/CustomListing.vue'
|
||||
</script>
|
||||
|
||||
<CustomListing />
|
@@ -25,6 +25,7 @@ The following integrations are currently supported by Automatisch.
|
||||
- [HTTP Request](/apps/http-request/actions)
|
||||
- [HubSpot](/apps/hubspot/actions)
|
||||
- [Invoice Ninja](/apps/invoice-ninja/triggers)
|
||||
- [Mailchimp](/apps/mailchimp/triggers)
|
||||
- [MailerLite](/apps/mailerlite/triggers)
|
||||
- [Mattermost](/apps/mattermost/actions)
|
||||
- [Miro](/apps/miro/actions)
|
||||
|
1
packages/docs/pages/public/favicons/mailchimp.svg
Normal file
1
packages/docs/pages/public/favicons/mailchimp.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.0 KiB |
Reference in New Issue
Block a user