chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:48 +08:00
commit 77bb5bf71f
3762 changed files with 353249 additions and 0 deletions
@@ -0,0 +1,28 @@
import { getMailchimpClient, getValidCustomer, parseError } from 'src/utils'
import { addCustomerOutputSchema, addCustomerToCampaignInputSchema } from '../misc/custom-schemas'
import * as bp from '.botpress'
export const addCustomerToCampaign: bp.IntegrationProps['actions']['addCustomerToCampaign'] = async ({
ctx,
logger,
input,
}) => {
try {
const mailchimpClient = getMailchimpClient(ctx.configuration, logger)
const validatedInput = addCustomerToCampaignInputSchema.parse(input)
const customer = getValidCustomer(validatedInput)
const response = await mailchimpClient.addCustomerToCampaignList(validatedInput.campaignId, customer)
return addCustomerOutputSchema.parse({
email_address: response.email_address,
status: response.status,
list_id: response.list_id,
id: response.id,
})
} catch (err: any) {
const error = parseError(err)
logger.forBot().error('Error adding customer to campaign', error)
throw error
}
}
@@ -0,0 +1,28 @@
import { getMailchimpClient, getValidCustomer, parseError } from 'src/utils'
import { addCustomerOutputSchema, addCustomerToListInputSchema } from '../misc/custom-schemas'
import * as bp from '.botpress'
export const addCustomerToList: bp.IntegrationProps['actions']['addCustomerToList'] = async ({
ctx,
input,
logger,
}) => {
try {
const validatedInput = addCustomerToListInputSchema.parse(input)
const mailchimpClient = getMailchimpClient(ctx.configuration, logger)
const customer = getValidCustomer(validatedInput)
const response = await mailchimpClient.addCustomerToList(validatedInput.listId, customer)
return addCustomerOutputSchema.parse({
email_address: response.email_address,
status: response.status,
list_id: response.list_id,
id: response.id,
})
} catch (err: any) {
const error = parseError(err)
logger.forBot().error('Error adding customer to campaign', error)
throw error
}
}
@@ -0,0 +1,16 @@
import { getMailchimpClient, parseError } from 'src/utils'
import * as bp from '.botpress'
export const getAllCampaigns: bp.IntegrationProps['actions']['getAllCampaigns'] = async ({ ctx, logger, input }) => {
try {
const mailchimpClient = getMailchimpClient(ctx.configuration, logger)
const allCampaigns = await mailchimpClient.getAllCampaigns({
count: input.count || 100,
})
return allCampaigns as bp.actions.getAllCampaigns.output.Output
} catch (err) {
const error = parseError(err)
logger.forBot().error('Error adding customer to campaign', error)
throw error
}
}
@@ -0,0 +1,15 @@
import { getMailchimpClient, parseError } from 'src/utils'
import * as bp from '.botpress'
export const getAllLists: bp.IntegrationProps['actions']['getAllLists'] = async ({ ctx, logger, input }) => {
try {
const mailchimpClient = getMailchimpClient(ctx.configuration)
return await mailchimpClient.getAllLists({
count: input.count || 100,
})
} catch (err) {
const error = parseError(err)
logger.forBot().error('Error adding customer to campaign', error)
throw error
}
}
@@ -0,0 +1,13 @@
import { addCustomerToCampaign } from './add-customer-to-campaign'
import { addCustomerToList } from './add-customer-to-list'
import { getAllCampaigns } from './get-all-campaigns'
import { getAllLists } from './get-all-lists'
import { sendMassEmailCampaign } from './send-mass-email-campaign'
export default {
addCustomerToCampaign,
addCustomerToList,
sendMassEmailCampaign,
getAllLists,
getAllCampaigns,
}
@@ -0,0 +1,31 @@
import { RuntimeError } from '@botpress/sdk'
import { sendMassEmailCampaignInputSchema, sendMassEmailCampaignOutputSchema } from '../misc/custom-schemas'
import { getMailchimpClient, parseError } from '../utils'
import * as bp from '.botpress'
export const sendMassEmailCampaign: bp.IntegrationProps['actions']['sendMassEmailCampaign'] = async ({
ctx,
input,
logger,
}) => {
try {
const validatedInput = sendMassEmailCampaignInputSchema.parse(input)
const mailchimpClient = getMailchimpClient(ctx.configuration, logger)
const response = await mailchimpClient.sendMassEmailCampaign(validatedInput.campaignIds)
if (typeof response === 'undefined') {
throw new RuntimeError('Batch client is not available')
}
return sendMassEmailCampaignOutputSchema.parse({
id: response.id,
status: response.status,
total_operations: response.total_operations,
_links: response._links,
})
} catch (err) {
const error = parseError(err)
logger.forBot().error('Error adding customer to campaign', error)
throw error
}
}
+159
View File
@@ -0,0 +1,159 @@
import { RuntimeError } from '@botpress/sdk'
import mailchimp, { lists } from '@mailchimp/mailchimp_marketing'
import type {
MailchimpClient,
Customer,
Operation,
addCustomerFullOutputType,
getAllListsOutputType,
getAllListsInputType,
getAllCampaignsOutputType,
getAllCampaignsInputType,
} from 'src/misc/custom-types'
import { Logger as IntegrationLogger } from 'src/misc/types'
import { isMailchimpError } from 'src/utils'
import {
addCustomerFullOutputSchema,
getAllCampaignsOutputSchema,
getAllListsOutputSchema,
} from '../misc/custom-schemas'
export class MailchimpApi {
private _client: MailchimpClient
private _logger?: IntegrationLogger
public constructor(apiKey: string, serverPrefix: string, logger?: IntegrationLogger) {
this._client = mailchimp
this._client.setConfig({
apiKey,
server: serverPrefix,
})
this._logger = logger
}
public getAllCampaigns = async (input: getAllCampaignsInputType): Promise<getAllCampaignsOutputType> => {
const response = await this._client.campaigns?.list(input)
return getAllCampaignsOutputSchema.parse(response)
}
public getAllLists = async (input: getAllListsInputType): Promise<getAllListsOutputType> => {
const response = await this._client.lists.getAllLists(input)
return getAllListsOutputSchema.parse(response)
}
public addCustomerToList = async (listId: string, customer: Customer): Promise<addCustomerFullOutputType> => {
this._logger?.forBot().debug('Adding customer to list', { listId, customer })
const existing = await this.getListCustomer(listId, customer.email)
if (existing) {
this._logger?.forBot().debug('Customer already exists in list', { existing })
return addCustomerFullOutputSchema.parse(existing)
}
const listResponse = await this._client.lists.addListMember(
listId,
{
email_address: customer.email,
status: 'subscribed',
language: customer.language || '',
merge_fields: {
FNAME: customer.firstName || '',
LNAME: customer.lastName || '',
BIRTHDAY: customer.birthday || '',
ADDRESS: {
addr1: customer.address1 || '',
addr2: customer.address2 || '',
city: customer.city || '',
state: customer.state || '',
zip: customer.zip || '',
country: customer.country || '',
},
PHONE: customer.phone || '',
},
},
{ skipMergeValidation: true }
)
return addCustomerFullOutputSchema.parse(listResponse)
}
public addCustomerToCampaignList = async (
campaignId: string,
customer: Customer
): Promise<addCustomerFullOutputType> => {
this._logger?.forBot().debug('Adding customer to campaign list', { campaignId, customer })
const listId = await this._getCampaignListID(campaignId)
this._logger?.forBot().debug('Found Campaign list ID', { listId })
if (!listId) {
throw new RuntimeError(`Campaign ${campaignId} does not have an associated list`)
}
const existing = await this.getListCustomer(listId, customer.email)
if (existing) {
this._logger?.forBot().debug('Customer already exists in list', { existing })
return addCustomerFullOutputSchema.parse(existing)
}
this._logger?.forBot().debug('Customer does not exist in list, adding', { customer })
return this.addCustomerToList(listId, customer)
}
public getListCustomer = async (
listId: string,
customerEmail: string
): Promise<lists.MembersSuccessResponse | null> => {
try {
const response = await this._client.lists.getListMember(listId, customerEmail)
this._logger?.forBot().debug('Found customer in list', { response })
if (isMailchimpError(response)) {
throw response
}
return response as lists.MembersSuccessResponse
} catch (error) {
if ((error as mailchimp.ErrorResponse).status === 404) {
this._logger?.forBot().debug('Customer does not exist in list', { customerEmail })
return null
} else {
throw error
}
}
}
public getCampaignListCustomer = async (campaignId: string, customerEmail: string) => {
const listId = await this._getCampaignListID(campaignId)
if (!listId) {
throw new RuntimeError(`Campaign ${campaignId} does not have an associated list, is the ID correct? `)
}
return this.getListCustomer(listId, customerEmail)
}
public sendMassEmailCampaign = async (campaignIds: string | string[]) => {
let campaignIdsArray = typeof campaignIds === 'string' ? campaignIds.split(',') : campaignIds
campaignIdsArray = campaignIdsArray.map((campaignId) => campaignId.trim())
const operations: Operation[] = campaignIdsArray.map((campaignId) => ({
method: 'POST',
path: `/campaigns/${campaignId}/actions/send`,
}))
const response = await this._client.batches?.start({
operations,
})
return response
}
private _getCampaignListID = async (campaignId: string): Promise<string | null> => {
try {
const campaignResponse = await this._client.campaigns?.get(campaignId)
return campaignResponse?.recipients.list_id || null
} catch (error: any) {
if (isMailchimpError(error) && error.status === 404) {
return null
}
throw error
}
}
}
+12
View File
@@ -0,0 +1,12 @@
import actions from './actions'
import * as bp from '.botpress'
export default new bp.Integration({
register: async () => {},
unregister: async () => {},
actions,
channels: {},
handler: async () => {
throw new Error('Not implemented')
},
})
@@ -0,0 +1,179 @@
import { z } from '@botpress/sdk'
import {
linkSchema,
tagsSchema,
memberLastNoteSchema,
memberMarketingPermissionsSchema,
fullMemberLocationSchema,
memberStatsSchema,
batchStatusSchema,
listSchema,
constraintsSchema,
recipientsSchema,
settingsSchema,
variateSettingsSchema,
trackingSchema,
rssOptsSchema,
abSplitOptsSchema,
socialCardSchema,
reportSummarySchema,
deliveryStatusSchema,
} from './sub-schemas'
export const customerSchema = z.object({
email: z.string().email().describe('The email address of the customer (e.g. example@example.com)').title('Email'),
firstName: z.string().optional().describe('The first name of the customer (e.g. John)').title('First Name'),
lastName: z.string().optional().describe('The last name of the customer (e.g. Doe)').title('Last Name'),
company: z.string().optional().describe('The company of the customer (e.g. Acme Inc.)').title('Company'),
birthday: z.string().optional().describe('The birthday of the customer (e.g. 01/01/2000)').title('Birthday'),
language: z.string().optional().describe('The language of the customer (e.g. en)').title('Language'),
address1: z
.string()
.optional()
.describe('The first line of the address of the customer (e.g. 123 St Marie.)')
.title('Address 1'),
address2: z
.string()
.optional()
.describe('The second line of the address of the customer (e.g. Apt. 4B)')
.title('Address 2'),
city: z.string().optional().describe('The city of the customer (e.g. Anytown)').title('City'),
state: z.string().optional().describe('The state or province of the customer (e.g. CA)').title('State'),
zip: z.string().optional().describe('The zip or postal code of the customer (e.g. 12345)').title('Zip'),
country: z.string().optional().describe('The country of the customer (e.g. USA)').title('Country'),
phone: z.string().optional().describe('The phone number of the customer (e.g. 555-1234)').title('Phone'),
})
export const addCustomerToCampaignInputSchema = customerSchema.extend({
campaignId: z
.string()
.describe('The ID of the Mailchimp campaign (e.g. f6g7h8i9j0)')
.title('The ID of the Mailchimp campaign (e.g. f6g7h8i9j0)'),
})
export const addCustomerToListInputSchema = customerSchema.extend({
listId: z
.string()
.describe('The ID of the Mailchimp list or audience (e.g. a1b2c3d4e5)')
.title('The ID of the Mailchimp list or audience (e.g. a1b2c3d4e5)'),
})
export const sendMassEmailCampaignInputSchema = z.object({
campaignIds: z
.string()
.describe('The Campaign IDs (Can be either a string with comma-separated IDs)')
.title('The Campaign IDs (Can be either a string with comma-separated IDs)'),
})
export const addCustomerOutputSchema = z.object({
id: z.string().describe('The id of the customer').title('ID'),
email_address: z.string().describe('The email address of the customer').title('Email Address'),
status: z.string().describe('The status of the customer').title('Status'),
list_id: z.string().describe('The list id of the customer').title('List ID'),
})
export const addCustomerFullOutputSchema = z.object({
id: z.string().optional(),
email_address: z.string().optional(),
unique_email_id: z.string().optional(),
contact_id: z.string().optional(),
full_name: z.string().optional(),
web_id: z.number().optional(),
email_type: z.string().optional(),
status: z.string().optional(),
unsubscribe_reason: z.string().optional(),
consents_to_one_to_one_messaging: z.boolean().optional(),
merge_fields: z.record(z.any()).optional(),
interests: z.record(z.any()).optional(),
stats: memberStatsSchema.optional(),
ip_signup: z.string().optional(),
timestamp_signup: z.string().optional(),
ip_opt: z.string().optional(),
timestamp_opt: z.string().optional(),
member_rating: z.union([z.number(), z.string()]).optional(),
last_changed: z.string().optional(),
language: z.string().optional(),
vip: z.boolean().optional(),
email_client: z.string().optional(),
location: fullMemberLocationSchema.optional(),
marketing_permissions: z.array(memberMarketingPermissionsSchema).optional(),
last_note: memberLastNoteSchema.optional(),
source: z.string().optional(),
tags_count: z.number().optional(),
tags: z.array(tagsSchema).optional(),
list_id: z.string().optional(),
_links: z.array(linkSchema).optional(),
message: z.string().optional(),
})
export const sendMassEmailCampaignOutputSchema = z.object({
id: z.string().optional().describe('The id of the campaign').title('ID'),
status: batchStatusSchema.optional().describe('The status of the campaign').title('Status'),
total_operations: z
.number()
.optional()
.describe('The number of operation done in the camplain')
.title('Total Operation'),
_links: z.array(linkSchema).optional().describe('Link').title('Link'),
})
export const sendMassEmailCampaignFullOutputSchema = z.object({
id: z.string(),
status: batchStatusSchema,
total_operations: z.number(),
finished_operations: z.number(),
errored_operations: z.number(),
submitted_at: z.string(),
completed_at: z.string(),
response_body_url: z.string(),
_links: z.array(linkSchema),
})
export const getAllListsOutputSchema = z.object({
lists: z.array(listSchema).describe('The array of list').title('Lists'),
constraints: constraintsSchema.describe('The constraints of the lists').title('Constraints'),
_links: z.array(linkSchema).describe('Link').title('Link'),
})
export const getAllListsInputSchema = z.object({
count: z.number().optional().default(100).describe('List count to retrieve').title('Count'),
})
export const getAllCampaignsInputSchema = getAllListsInputSchema.describe('List count to retrieve').title('Lists')
const campaignSchema = z.object({
id: z.string().title('ID').describe('Campaign ID'),
web_id: z.number().title('Web ID').describe('Campaign web ID'),
parent_campaign_id: z
.string()
.optional()
.title('Parent Campaign ID')
.describe('Parent campaign ID if this is a child campaign'),
type: z.string().title('Type').describe('Campaign type'),
create_time: z.string().title('Create Time').describe('Campaign creation time'),
archive_url: z.string().title('Archive URL').describe('URL to the campaign archive'),
long_archive_url: z.string().title('Long Archive URL').describe('Long URL to the campaign archive'),
status: z.string().title('Status').describe('Campaign status'),
emails_sent: z.number().title('Emails Sent').describe('Number of emails sent'),
send_time: z.string().title('Send Time').describe('Time when the campaign was sent'),
content_type: z.string().title('Content Type').describe('Campaign content type'),
needs_block_refresh: z.boolean().title('Needs Block Refresh').describe('Whether the campaign needs block refresh'),
resendable: z.boolean().title('Resendable').describe('Whether the campaign can be resent'),
recipients: recipientsSchema.optional().title('Recipients').describe('Campaign recipients'),
settings: settingsSchema.title('Settings').describe('Campaign settings'),
variate_settings: variateSettingsSchema.optional().title('Variate Settings').describe('A/B testing settings'),
tracking: trackingSchema.title('Tracking').describe('Campaign tracking settings'),
rss_opts: rssOptsSchema.optional().title('RSS Options').describe('RSS campaign options'),
ab_split_opts: abSplitOptsSchema.optional().title('AB Split Options').describe('A/B split test options'),
social_card: socialCardSchema.optional().title('Social Card').describe('Social media card settings'),
report_summary: reportSummarySchema.optional().title('Report Summary').describe('Campaign report summary'),
delivery_status: deliveryStatusSchema.title('Delivery Status').describe('Campaign delivery status'),
_links: z.array(linkSchema).title('Links').describe('Related links'),
})
export const getAllCampaignsOutputSchema = z.object({
campaigns: z.array(campaignSchema).describe('The list of campaings').title('Campaigns'),
total_items: z.number().describe('Total number of items').title('Total Items'),
_links: z.array(linkSchema).describe('Links').title('Links'),
})
@@ -0,0 +1,97 @@
import { z } from '@botpress/sdk'
import { HttpMethod, Link, Config, lists, ErrorResponse } from '@mailchimp/mailchimp_marketing'
import {
addCustomerFullOutputSchema,
getAllCampaignsInputSchema,
getAllCampaignsOutputSchema,
getAllListsInputSchema,
getAllListsOutputSchema,
} from './custom-schemas'
export type Operation = {
method: HttpMethod
path: string
params?: object
body?: string
operation_id?: string
}
export type BatchStatus = 'pending' | 'preprocessing' | 'started' | 'finalizing' | 'finished'
export type BatchResponse = {
id: string
status: BatchStatus
total_operations: number
finished_operations: number
errored_operations: number
submitted_at: string
completed_at: string
response_body_url: string
_links: Link[]
}
export type MailchimpClient = {
setConfig: (config: Config) => void
lists: {
getAllLists: typeof lists.getAllLists
addListMember: typeof lists.addListMember
getListMember: typeof lists.getListMember
}
campaigns?: {
get: (campaignId: string) => Promise<{ recipients: { list_id: string } }>
list: (input: { count?: number }) => Promise<any>
}
batches?: {
start: (batch: { operations: Operation[] }) => Promise<BatchResponse>
}
}
export type addCustomerFullOutputType = z.infer<typeof addCustomerFullOutputSchema>
export type getAllListsInputType = z.infer<typeof getAllListsInputSchema>
export type getAllListsOutputType = z.infer<typeof getAllListsOutputSchema>
export type getAllCampaignsInputType = z.infer<typeof getAllCampaignsInputSchema>
export type getAllCampaignsOutputType = z.infer<typeof getAllCampaignsOutputSchema>
export type Customer = {
email: string
firstName?: string
lastName?: string
company?: string
birthday?: string
language?: string
address1?: string
address2?: string
city?: string
state?: string
zip?: string
country?: string
phone?: string
}
export type MailchimpAPIError = {
status: number
response: HTTPResponse<ErrorResponse>
}
type HTTPResponse<T> = {
request: any
req: any
text: string
body: T
headers: Record<string, string>
status: number
clientError: boolean
serverError: boolean
accepted: boolean
noContent: boolean
badRequest: boolean
unauthorized: boolean
notAcceptable: boolean
forbidden: boolean
notFound: boolean
type: string
charset: string
links: Record<string, string>
}
@@ -0,0 +1,353 @@
import { z } from '@botpress/sdk'
const HttpMethodSchema = z.union([
z.literal('GET'),
z.literal('POST'),
z.literal('PUT'),
z.literal('PATCH'),
z.literal('DELETE'),
z.literal('OPTIONS'),
z.literal('HEAD'),
])
const linkSchema = z.object({
rel: z.string().optional().title('Rel').describe('Link relation type'),
href: z.string().optional().title('Href').describe('Link URL'),
method: HttpMethodSchema.optional().title('Method').describe('HTTP method'),
targetSchema: z.string().optional().title('Target Schema').describe('Target schema URL'),
schema: z.string().optional().title('Schema').describe('Schema URL'),
})
const tagsSchema = z.object({
id: z.number().optional(),
name: z.string().optional(),
})
const memberLastNoteSchema = z.object({
note_id: z.number().optional(),
created_at: z.string().optional(),
created_by: z.string().optional(),
note: z.string().optional(),
})
const memberMarketingPermissionsInputSchema = z.object({
marketing_permission_id: z.string().optional(),
enabled: z.boolean().optional(),
})
const memberMarketingPermissionsSchema = memberMarketingPermissionsInputSchema.extend({
text: z.string().optional(),
})
const memberLocationSchema = z.object({
latitude: z.number(),
longitude: z.number(),
})
const fullMemberLocationSchema = memberLocationSchema.extend({
gmtoff: z.number().optional(),
dstoff: z.number().optional(),
country_code: z.string().optional(),
timezone: z.string().optional(),
region: z.string().optional(),
})
const memberEcommerceDataSchema = z.object({
total_revenue: z.number().optional(),
number_of_orders: z.number().optional(),
currency_code: z.number().optional(),
})
const memberStatsSchema = z.object({
avg_open_rate: z.number().optional(),
avg_click_rate: z.number().optional(),
ecommerce_data: memberEcommerceDataSchema.optional(),
})
const batchStatusSchema = z.union([
z.literal('pending'),
z.literal('preprocessing'),
z.literal('started'),
z.literal('finalizing'),
z.literal('finished'),
])
const contactSchema = z.object({
company: z.string().describe('Company name'),
address1: z.string().describe('Address line 1'),
address2: z.string().describe('Address line 2'),
city: z.string().describe('City'),
state: z.string().describe('State'),
zip: z.string().describe('ZIP code'),
country: z.string().describe('Country'),
phone: z.string().describe('Phone number'),
})
const campaignDefaultsSchema = z.object({
from_name: z.string().describe('Default sender name'),
from_email: z.string().email().describe('Default sender email'),
subject: z.string().describe('Default email subject'),
language: z.string().describe('Default email language'),
})
const statsSchema = z.object({
member_count: z.number().describe('Member count'),
unsubscribe_count: z.number().describe('Unsubscribe count'),
cleaned_count: z.number().describe('Cleaned count'),
member_count_since_send: z.number().describe('Member count since last send'),
unsubscribe_count_since_send: z.number().describe('Unsubscribe count since last send'),
cleaned_count_since_send: z.number().describe('Cleaned count since last send'),
campaign_count: z.number().describe('Campaign count'),
campaign_last_sent: z.string().describe('Last campaign sent date'),
merge_field_count: z.number().describe('Merge field count'),
avg_sub_rate: z.number().describe('Average subscription rate'),
avg_unsub_rate: z.number().describe('Average unsubscribe rate'),
target_sub_rate: z.number().describe('Target subscription rate'),
open_rate: z.number().describe('Open rate'),
click_rate: z.number().describe('Click rate'),
last_sub_date: z.string().describe('Last subscription date'),
last_unsub_date: z.string().describe('Last unsubscribe date'),
})
const listSchema = z.object({
id: z.string().title('ID').describe('List ID'),
web_id: z.number().title('Web ID').describe('List web ID'),
name: z.string().title('Name').describe('List name'),
contact: contactSchema.title('Contact').describe('Contact information for the list'),
permission_reminder: z.string().title('Permission Reminder').describe('Permission reminder message'),
use_archive_bar: z.boolean().title('Use Archive Bar').describe('Whether to use the archive bar'),
campaign_defaults: campaignDefaultsSchema.title('Campaign Defaults').describe('Default campaign settings'),
notify_on_subscribe: z.string().title('Notify on Subscribe').describe('Email to notify on subscription'),
notify_on_unsubscribe: z.string().title('Notify on Unsubscribe').describe('Email to notify on unsubscription'),
date_created: z.string().title('Date Created').describe('Date when the list was created'),
list_rating: z.number().title('List Rating').describe('List quality rating'),
email_type_option: z.boolean().title('Email Type Option').describe('Whether email type option is enabled'),
subscribe_url_short: z.string().title('Subscribe URL Short').describe('Short subscription URL'),
subscribe_url_long: z.string().title('Subscribe URL Long').describe('Long subscription URL'),
beamer_address: z.string().title('Beamer Address').describe('Beamer email address'),
visibility: z.string().title('Visibility').describe('List visibility setting'),
double_optin: z.boolean().title('Double Opt-in').describe('Whether double opt-in is required'),
has_welcome: z.boolean().title('Has Welcome').describe('Whether the list has a welcome email'),
marketing_permissions: z
.boolean()
.title('Marketing Permissions')
.describe('Whether marketing permissions are enabled'),
modules: z.array(z.any()).title('Modules').describe('List modules'),
stats: statsSchema.title('Stats').describe('List statistics'),
_links: z.array(linkSchema).title('Links').describe('Related links'),
})
const constraintsSchema = z.object({
may_create: z.boolean().describe('May create flag'),
max_instances: z.number().describe('Maximum instances allowed'),
current_total_instances: z.number().describe('Current total instances'),
})
const recipientsSchema = z
.object({
list_id: z.string().describe('ID of the list'),
list_is_active: z.boolean().describe('Indicates whether the list is active'),
list_name: z.string().describe('Name of the list'),
segment_text: z.string().describe('Text description of the segment'),
recipient_count: z.number().describe('Count of recipients'),
segment_opts: z
.object({
saved_segment_id: z.number().describe('ID of the saved segment'),
prebuilt_segment_id: z.string().describe('ID of the prebuilt segment'),
match: z.string().describe('Match condition'),
conditions: z.array(z.unknown()).describe('Array of conditions'), // Adjust the type accordingly
})
.partial()
.title('Segment Options')
.describe('Segment options for targeting specific recipients'),
})
.passthrough()
.describe('Recipients information')
const settingsSchema = z
.object({
subject_line: z.string().describe('Subject line of the campaign'),
preview_text: z.string().describe('Preview text of the campaign'),
title: z.string().describe('Title of the campaign'),
from_name: z.string().describe("Sender's name"),
reply_to: z.string().describe('Reply-to email address'),
use_conversation: z.boolean().describe('Indicates whether to use conversation view'),
to_name: z.string().describe("Recipient's name"),
folder_id: z.string().describe('ID of the folder'),
authenticate: z.boolean().describe('Indicates whether authentication is enabled'),
auto_footer: z.boolean().describe('Indicates whether auto-footer is enabled'),
inline_css: z.boolean().describe('Indicates whether inline CSS is enabled'),
auto_tweet: z.boolean().describe('Indicates whether auto-tweet is enabled'),
auto_fb_post: z.array(z.string()).optional().describe('Array of Facebook post strings'),
fb_comments: z.boolean().describe('Indicates whether Facebook comments are enabled'),
timewarp: z.boolean().describe('Indicates whether timewarp is enabled'),
template_id: z.number().describe('ID of the template'),
drag_and_drop: z.boolean().describe('Indicates whether drag-and-drop is enabled'),
})
.passthrough()
.describe('Settings for the campaign')
const variateSettingsSchema = z
.object({
winning_combination_id: z.string().describe('ID of the winning combination'),
winning_campaign_id: z.string().describe('ID of the winning campaign'),
winner_criteria: z.string().describe('Criteria for choosing the winner'),
wait_time: z.number().describe('Wait time'),
test_size: z.number().describe('Size of the test'),
subject_lines: z.array(z.string()).describe('Array of subject lines'),
send_times: z.array(z.string()).describe('Array of send times'),
from_names: z.array(z.string()).describe('Array of sender names'),
reply_to_addresses: z.array(z.string()).describe('Array of reply-to email addresses'),
contents: z.array(z.string()).describe('Array of content strings'),
combinations: z
.array(
z.object({
id: z.string().describe('ID of the combination'),
subject_line: z.number().describe('Subject line'),
send_time: z.number().describe('Send time'),
from_name: z.number().describe("Sender's name"),
reply_to: z.number().describe('Reply-to'),
content_description: z.number().describe('Content description'),
recipients: z.number().describe('Recipients'),
})
)
.describe('Array of combinations'),
})
.passthrough()
const trackingSchema = z
.object({
opens: z.boolean().describe('Indicates whether opens tracking is enabled'),
html_clicks: z.boolean().describe('Indicates whether HTML clicks tracking is enabled'),
text_clicks: z.boolean().describe('Indicates whether text clicks tracking is enabled'),
goal_tracking: z.boolean().describe('Indicates whether goal tracking is enabled'),
ecomm360: z.boolean().describe('Indicates whether Ecomm360 is enabled'),
google_analytics: z.string().describe('Google Analytics tracking code'),
clicktale: z.string().describe('Clicktale tracking code'),
salesforce: z
.object({
campaign: z.boolean().describe('Indicates whether Salesforce campaign tracking is enabled'),
notes: z.boolean().describe('Indicates whether Salesforce notes tracking is enabled'),
})
.optional()
.describe('Salesforce tracking options'),
capsule: z
.object({
notes: z.boolean().describe('Indicates whether Capsule notes tracking is enabled'),
})
.optional()
.describe('Capsule tracking options'),
})
.passthrough()
const rssOptsSchema = z
.object({
feed_url: z.string().describe('URL of the RSS feed'),
frequency: z.string().describe('Frequency of sending (e.g., "daily")'),
schedule: z
.object({
hour: z.number().describe('Hour of the day for sending'),
daily_send: z
.object({
sunday: z.boolean().title('Sunday').describe('Send on Sunday'),
monday: z.boolean().title('Monday').describe('Send on Monday'),
tuesday: z.boolean().title('Tuesday').describe('Send on Tuesday'),
wednesday: z.boolean().title('Wednesday').describe('Send on Wednesday'),
thursday: z.boolean().title('Thursday').describe('Send on Thursday'),
friday: z.boolean().title('Friday').describe('Send on Friday'),
saturday: z.boolean().title('Saturday').describe('Send on Saturday'),
})
.title('Daily Send')
.describe('Daily send options'),
weekly_send_day: z.string().describe('Day of the week for weekly sending'),
monthly_send_date: z.number().describe('Date of the month for monthly sending'),
})
.title('Schedule')
.describe('RSS feed schedule'),
last_sent: z.string().describe('Last time the RSS feed was sent'),
constrain_rss_img: z.boolean().describe('Indicates whether to constrain RSS images'),
})
.passthrough()
const abSplitOptsSchema = z
.object({
split_test: z.string().describe('Type of split test (e.g., "subject")'),
pick_winner: z.string().describe('Criteria for picking the winner'),
wait_units: z.string().describe('Units for waiting (e.g., "hours")'),
wait_time: z.number().describe('Wait time'),
split_size: z.number().describe('Size of the split'),
from_name_a: z.string().describe("Sender's name for variation A"),
from_name_b: z.string().describe("Sender's name for variation B"),
reply_email_a: z.string().describe('Reply-to email address for variation A'),
reply_email_b: z.string().describe('Reply-to email address for variation B'),
subject_a: z.string().describe('Subject line for variation A'),
subject_b: z.string().describe('Subject line for variation B'),
send_time_a: z.string().describe('Send time for variation A'),
send_time_b: z.string().describe('Send time for variation B'),
send_time_winner: z.string().describe('Send time for the winner'),
})
.passthrough()
const socialCardSchema = z.object({
image_url: z.string().describe('URL of the social card image'),
description: z.string().describe('Description of the social card'),
title: z.string().describe('Title of the social card'),
})
const reportSummarySchema = z
.object({
opens: z.number().describe('Total opens'),
unique_opens: z.number().describe('Unique opens'),
open_rate: z.number().describe('Open rate'),
clicks: z.number().describe('Total clicks'),
subscriber_clicks: z.number().describe('Subscriber clicks'),
click_rate: z.number().describe('Click rate'),
ecommerce: z
.object({
total_orders: z.number().describe('Total ecommerce orders'),
total_spent: z.number().describe('Total amount spent in ecommerce'),
total_revenue: z.number().describe('Total ecommerce revenue'),
})
.title('Ecommerce')
.describe('Ecommerce data for the campaign'),
})
.passthrough()
const deliveryStatusSchema = z.union([
z.object({
enabled: z.literal(true).describe('Indicates whether delivery status tracking is enabled'),
can_cancel: z.boolean().describe('Indicates whether the campaign can be canceled'),
status: z.string().describe('Current delivery status'),
emails_sent: z.number().describe('Total emails sent'),
emails_canceled: z.number().describe('Total emails canceled'),
}),
z.object({ enabled: z.literal(false).describe('Delivery status tracking is disabled') }),
])
export {
HttpMethodSchema,
linkSchema,
tagsSchema,
memberLastNoteSchema,
memberMarketingPermissionsInputSchema,
memberMarketingPermissionsSchema,
memberLocationSchema,
fullMemberLocationSchema,
memberEcommerceDataSchema,
memberStatsSchema,
batchStatusSchema,
constraintsSchema,
abSplitOptsSchema,
rssOptsSchema,
campaignDefaultsSchema,
contactSchema,
deliveryStatusSchema,
recipientsSchema,
settingsSchema,
reportSummarySchema,
socialCardSchema,
statsSchema,
trackingSchema,
variateSettingsSchema,
listSchema,
}
+22
View File
@@ -0,0 +1,22 @@
import * as sdk from '@botpress/sdk'
import * as bp from '.botpress'
export type Handler = bp.IntegrationProps['handler']
export type HandlerProps = bp.HandlerProps
export type IntegrationCtx = bp.Context
export type Logger = bp.Logger
export type Client = bp.Client
export type Conversation = bp.ClientResponses['getConversation']['conversation']
export type Message = bp.ClientResponses['getMessage']['message']
export type User = bp.ClientResponses['getUser']['user']
export type Event = bp.ClientResponses['getEvent']['event']
export type EventDefinition = sdk.EventDefinition
export type ActionDefinition = sdk.ActionDefinition
export type ChannelDefinition = sdk.ChannelDefinition
export type MessageDefinition = sdk.MessageDefinition
export type ActionProps = bp.AnyActionProps
export type MessageHandlerProps = bp.AnyMessageProps
export type AckFunction = bp.AnyAckFunction
+48
View File
@@ -0,0 +1,48 @@
import { RuntimeError, z } from '@botpress/sdk'
import { MailchimpApi } from './client'
import { Customer, MailchimpAPIError } from './misc/custom-types'
import { Logger } from './misc/types'
import * as bp from '.botpress'
export const getMailchimpClient = (config: bp.configuration.Configuration, logger?: Logger) =>
new MailchimpApi(config.apiKey, config.serverPrefix, logger)
export const getValidCustomer = (validatedInput: Customer) => ({
email: validatedInput.email,
firstName: validatedInput.firstName,
lastName: validatedInput.lastName,
company: validatedInput.company,
birthday: validatedInput.birthday,
language: validatedInput.language,
address1: validatedInput.address1,
address2: validatedInput.address2,
city: validatedInput.city,
state: validatedInput.state,
zip: validatedInput.zip,
country: validatedInput.country,
phone: validatedInput.phone,
})
export const isMailchimpError = (error: any): error is MailchimpAPIError => {
return 'status' in error && 'response' in error && 'body' in error.response
}
export const isZodError = (error: any): error is z.ZodError => {
return error && typeof error === 'object' && z.is.zuiError(error) && 'errors' in error
}
export const parseError = (error: any): RuntimeError => {
if (isMailchimpError(error)) {
return new RuntimeError(
`Mailchimp API errored with ${error.response.body.status} status code: ${error.response.body.detail}`,
{
name: error.response.body.title,
message: error.response.body.detail,
}
)
}
if (isZodError(error)) {
return new RuntimeError(`Output does not conform to expected schema: ${error.message}`, error)
}
return new RuntimeError(`Unexpected error: ${error.message}`, error)
}