Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions integrations/whatsapp/definitions/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { z } from '@botpress/sdk'

export const qualityScoreSchema = z.enum(['GREEN', 'RED', 'YELLOW', 'UNKNOWN'])

export const WhatsAppMessageTemplateComponentsUpdateValueSchema = z
.object({
id: z.number().describe('Template ID.').title('Template Id'),
name: z.string().describe('Template name.').title('Template Name'),
language: z.string().describe('Template language and locale code.').title('Template Language'),
element: z.string().describe('Template body text.').title('Template Body Text'),
title: z.string().describe('Template Header Text.').title('Template Header Text').optional(),
footer: z.string().describe('Template Footer Text.').title('Template Footer Text').optional(),
buttons: z
.array(
z.object({
button_type: z
.enum([
'CATALOG',
'COPY_CODE',
'EXTENSION',
'FLOW',
'MPM',
'ORDER_DETAILS',
'OTP',
'PHONE_NUMBER',
'POSTBACK',
'REMINDER',
'SEND_LOCATION',
'SPM',
'QUICK_REPLY',
'URL',
'VOICE_CALL',
])
.describe('Button type.')
.title('Button Type'),
button_text: z.string().describe('Button label text.').title('Button Label Text'),
button_url: z.string().describe('Button URL.').title('Button URL').optional(),
button_phone_number: z.string().describe('Button phone number.').title('Button Phone Number').optional(),
})
)
.describe('Array of button objects, if present.')
.title('Buttons')
.optional(),
})
.describe("The message_template_components_update webhook notifies you of changes to a template's components.")
.title('Message Template Components Update')
.describe("The message_template_components_update webhook notifies you of changes to a template's components.")

export const WhatsAppMessageTemplateQualityUpdateValueSchema = z
.object({
previous_quality_score: qualityScoreSchema
.describe('Previous template quality score.')
.title('Previous Quality Score'),
new_quality_score: qualityScoreSchema.describe('New template quality score.').title('New Quality Score'),
id: z.number().describe('Template ID.').title('Template Id'),
name: z.string().describe('Template name.').title('Template Name'),
language: z.string().describe('Template language and locale code.').title('Template Language'),
})
.describe("The message_template_quality_update webhook notifies you of changes to a template's quality score.")
.title('Message Template Quality Update')

export const WhatsAppMessageTemplateStatusUpdateValueSchema = z
.object({
event: z
.enum([
'APPROVED',
'ARCHIVED',
'DELETED',
'DISABLED',
'FLAGGED',
'IN_APPEAL',
'LIMIT_EXCEEDED',
'LOCKED',
'PAUSED',
'PENDING',
'REINSTATED',
'PENDING_DELETION',
'REJECTED',
])
.describe('Template status event.')
.title('Template Status Event'),
id: z.number().describe('Template ID.').title('Template Id'),
name: z.string().describe('Template name.').title('Template Name'),
language: z.string().describe('Language and locale code.').title('Template Language'),
reason: z
.enum([
'ABUSIVE_CONTENT',
'CATEGORY_NOT_AVAILABLE',
'INCORRECT_CATEGORY',
'INVALID_FORMAT',
'NONE',
'PROMOTIONAL',
'SCAM',
'TAG_CONTENT_MISMATCH',
])
.describe('Template rejection reason, if rejected.')
.title('Rejection Reason')
.nullable(),
disable_info: z
.object({
disable_date: z
.number()
.describe('Unix timestamp indicating when the template was disabled.')
.title('Disable Timestamp'),
})
.describe('only included if template disabled')
.title('Disable Info')
.optional(),
other_info: z
.object({
title: z
.enum(['FIRST_PAUSE', 'SECOND_PAUSE', 'RATE_LIMITING_PAUSE', 'UNPAUSE', 'DISABLED'])
.describe('Title of template pause or unpause event.')
.title('Title'),
description: z
.string()
.describe('String describing why the template was locked or unlocked.')
.title('Description'),
})
.describe('only included if template locked or unlocked')
.title('Other Info')
.optional(),
})
.describe('The message_template_status_update webhook notifies you of changes to the status of an existing template.')
.title('Message Template Status Update')

export const WhatsAppTemplateCategoryUpdateValueSchema = z
.object({
id: z.number().describe('Template ID.').title('Template Id'),
name: z.string().describe('Template name.').title('Template Name'),
language: z.string().describe('Template language and locale code.').title('Template Language'),
correct_category: z
.string()
.describe('The category that the template will be recategorized as in 24 hours.')
.title('Correct Category')
.optional(),
previous_category: z.string().describe("The template's previous category.").title('Previous Category').optional(),
new_category: z.string().describe("The template's new category.").title('New Category').optional(),
})
.describe("The template_category_update webhook notifies you of changes to template's category.")
.title('Template Category Update')
30 changes: 29 additions & 1 deletion integrations/whatsapp/integration.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { z, IntegrationDefinition, messages } from '@botpress/sdk'
import { sentry as sentryHelpers } from '@botpress/sdk-addons'
import proactiveConversation from 'bp_modules/proactive-conversation'
import typingIndicator from 'bp_modules/typing-indicator'
import {
WhatsAppMessageTemplateComponentsUpdateValueSchema,
WhatsAppMessageTemplateQualityUpdateValueSchema,
WhatsAppMessageTemplateStatusUpdateValueSchema,
WhatsAppTemplateCategoryUpdateValueSchema,
} from 'definitions/events'

export const INTEGRATION_NAME = 'whatsapp'

Expand Down Expand Up @@ -75,6 +81,7 @@ const startConversationProps = {
.title('Bot Phone Number ID')
.describe('Phone number ID to use as sender (uses the default phone number ID if not provided)'),
})
.title('Conversation Details')
.describe('Details of the conversation'),
}),
},
Expand All @@ -87,7 +94,7 @@ const defaultBotPhoneNumberId = {

export default new IntegrationDefinition({
name: INTEGRATION_NAME,
version: '4.3.0',
version: '4.4.0',
title: 'WhatsApp',
description: 'Send and receive messages through WhatsApp.',
icon: 'icon.svg',
Expand Down Expand Up @@ -260,6 +267,27 @@ export default new IntegrationDefinition({
conversationId: z.string().optional().title('Conversation ID').describe('ID of the conversation'),
}),
},
messageTemplateComponentsUpdate: {
title: 'Message Template Components Update',
description: 'Triggered when a template is edited',
schema: WhatsAppMessageTemplateComponentsUpdateValueSchema,
},
messageTemplateQualityUpdate: {
title: 'Message Template Quality Update',
description: "Triggered when a template's quality score changes",
schema: WhatsAppMessageTemplateQualityUpdateValueSchema,
},
messageTemplateStatusUpdate: {
title: 'Message Template Status Update',
description: 'Triggered when a template is approved, rejected or disabled',
schema: WhatsAppMessageTemplateStatusUpdateValueSchema,
},
templateCategoryUpdate: {
title: 'Template Category Update',
description:
'Triggered when the category of a WhatsApp template is changed — whether manually or by an automated process, or when such a change is about to occur.',
schema: WhatsAppTemplateCategoryUpdateValueSchema,
},
},
states: {
credentials: {
Expand Down
121 changes: 116 additions & 5 deletions integrations/whatsapp/src/misc/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from '@botpress/sdk'
import { qualityScoreSchema } from 'definitions/events'

const WhatsAppContactSchema = z.object({
wa_id: z.string(),
Expand Down Expand Up @@ -151,7 +152,7 @@ export type WhatsAppReactionMessage = WhatsAppMessage & {
type: 'reaction'
}

const WhatsAppValueSchema = z.object({
const WhatsAppMessageValueSchema = z.object({
messaging_product: z.literal('whatsapp'),
metadata: z.object({
display_phone_number: z.string(),
Expand All @@ -160,13 +161,123 @@ const WhatsAppValueSchema = z.object({
contacts: z.array(WhatsAppContactSchema).optional(),
messages: z.array(WhatsAppMessageSchema).optional(),
})
export type WhatsAppValue = z.infer<typeof WhatsAppValueSchema>
export type WhatsAppMessageValue = z.infer<typeof WhatsAppMessageValueSchema>

const WhatsAppChangesSchema = z.object({
value: WhatsAppValueSchema,
field: z.literal('messages'),
export const WhatsAppMessageTemplateComponentsUpdateValueSchema = z.object({
message_template_id: z.number(),
message_template_name: z.string(),
message_template_language: z.string(),
message_template_element: z.string(),
message_template_title: z.string().optional(),
message_template_footer: z.string().optional(),
message_template_buttons: z
.array(
z.object({
message_template_button_type: z.enum([
'CATALOG',
'COPY_CODE',
'EXTENSION',
'FLOW',
'MPM',
'ORDER_DETAILS',
'OTP',
'PHONE_NUMBER',
'POSTBACK',
'REMINDER',
'SEND_LOCATION',
'SPM',
'QUICK_REPLY',
'URL',
'VOICE_CALL',
]),
message_template_button_text: z.string(),
message_template_button_url: z.string().optional(),
message_template_button_phone_number: z.string().optional(),
})
)
.optional(),
})

export const WhatsAppMessageTemplateQualityUpdateValueSchema = z.object({
previous_quality_score: qualityScoreSchema,
new_quality_score: qualityScoreSchema,
message_template_id: z.number(),
message_template_name: z.string(),
message_template_language: z.string(),
})

export const WhatsAppMessageTemplateStatusUpdateValueSchema = z.object({
event: z.enum([
'APPROVED',
'ARCHIVED',
'DELETED',
'DISABLED',
'FLAGGED',
'IN_APPEAL',
'LIMIT_EXCEEDED',
'LOCKED',
'PAUSED',
'PENDING',
'REINSTATED',
'PENDING_DELETION',
'REJECTED',
]),
message_template_id: z.number(),
message_template_name: z.string(),
message_template_language: z.string(),
reason: z
.enum([
'ABUSIVE_CONTENT',
'CATEGORY_NOT_AVAILABLE',
'INCORRECT_CATEGORY',
'INVALID_FORMAT',
'NONE',
'PROMOTIONAL',
'SCAM',
'TAG_CONTENT_MISMATCH',
])
.nullable(),
disable_info: z.object({ disable_date: z.number() }).optional(),
other_info: z
.object({
title: z.enum(['FIRST_PAUSE', 'SECOND_PAUSE', 'RATE_LIMITING_PAUSE', 'UNPAUSE', 'DISABLED']),
description: z.string(),
})
.optional(),
})

export const WhatsAppTemplateCategoryUpdateValueSchema = z.object({
message_template_id: z.number(),
message_template_name: z.string(),
message_template_language: z.string(),
correct_category: z.string().optional(),
previous_category: z.string().optional(),
new_category: z.string().optional(),
})

const WhatsAppChangesSchema = z.discriminatedUnion('field', [
z.object({
field: z.literal('messages'),
value: WhatsAppMessageValueSchema,
}),
z.object({
field: z.literal('message_template_components_update'),
value: WhatsAppMessageTemplateComponentsUpdateValueSchema,
}),
z.object({
field: z.literal('message_template_quality_update'),
value: WhatsAppMessageTemplateQualityUpdateValueSchema,
}),
z.object({
field: z.literal('message_template_status_update'),
value: WhatsAppMessageTemplateStatusUpdateValueSchema,
}),
z.object({
field: z.literal('template_category_update'),
value: WhatsAppTemplateCategoryUpdateValueSchema,
}),
])

const WhatsAppEntrySchema = z.object({
id: z.string(),
changes: z.array(WhatsAppChangesSchema),
Expand Down
Loading
Loading