2023-11-13 15:27:36 +01:00
|
|
|
import { ButtonItem, ContinueChatResponse } from '@typebot.io/schemas'
|
2023-08-29 10:01:28 +02:00
|
|
|
import { WhatsAppSendingMessage } from '@typebot.io/schemas/features/whatsapp'
|
2023-09-20 15:26:52 +02:00
|
|
|
import { isDefined, isEmpty } from '@typebot.io/lib/utils'
|
2023-11-08 15:34:16 +01:00
|
|
|
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
|
|
|
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
|
|
|
import { defaultPictureChoiceOptions } from '@typebot.io/schemas/features/blocks/inputs/pictureChoice/constants'
|
|
|
|
import { defaultChoiceInputOptions } from '@typebot.io/schemas/features/blocks/inputs/choice/constants'
|
2024-01-31 08:03:13 +01:00
|
|
|
import { convertRichTextToMarkdown } from '@typebot.io/lib/markdown/convertRichTextToMarkdown'
|
2024-03-07 05:05:08 -03:00
|
|
|
import { env } from '@typebot.io/env'
|
2023-08-29 10:01:28 +02:00
|
|
|
|
|
|
|
export const convertInputToWhatsAppMessages = (
|
2023-11-13 15:27:36 +01:00
|
|
|
input: NonNullable<ContinueChatResponse['input']>,
|
|
|
|
lastMessage: ContinueChatResponse['messages'][number] | undefined
|
2023-08-29 10:01:28 +02:00
|
|
|
): WhatsAppSendingMessage[] => {
|
|
|
|
const lastMessageText =
|
2024-05-23 16:30:56 +02:00
|
|
|
lastMessage?.type === BubbleBlockType.TEXT &&
|
|
|
|
lastMessage.content.type === 'richText'
|
2024-01-08 08:40:25 +01:00
|
|
|
? convertRichTextToMarkdown(lastMessage.content.richText ?? [], {
|
|
|
|
flavour: 'whatsapp',
|
|
|
|
})
|
2023-08-29 10:01:28 +02:00
|
|
|
: undefined
|
|
|
|
switch (input.type) {
|
|
|
|
case InputBlockType.DATE:
|
|
|
|
case InputBlockType.EMAIL:
|
|
|
|
case InputBlockType.FILE:
|
|
|
|
case InputBlockType.NUMBER:
|
|
|
|
case InputBlockType.PHONE:
|
|
|
|
case InputBlockType.URL:
|
|
|
|
case InputBlockType.PAYMENT:
|
|
|
|
case InputBlockType.RATING:
|
|
|
|
case InputBlockType.TEXT:
|
|
|
|
return []
|
|
|
|
case InputBlockType.PICTURE_CHOICE: {
|
2023-11-08 15:34:16 +01:00
|
|
|
if (
|
|
|
|
input.options?.isMultipleChoice ??
|
|
|
|
defaultPictureChoiceOptions.isMultipleChoice
|
|
|
|
)
|
2023-08-29 10:01:28 +02:00
|
|
|
return input.items.flatMap((item, idx) => {
|
|
|
|
let bodyText = ''
|
|
|
|
if (item.title) bodyText += `*${item.title}*`
|
|
|
|
if (item.description) {
|
|
|
|
if (item.title) bodyText += '\n\n'
|
|
|
|
bodyText += item.description
|
|
|
|
}
|
|
|
|
const imageMessage = item.pictureSrc
|
|
|
|
? ({
|
|
|
|
type: 'image',
|
|
|
|
image: {
|
|
|
|
link: item.pictureSrc ?? '',
|
|
|
|
},
|
|
|
|
} as const)
|
|
|
|
: undefined
|
|
|
|
const textMessage = {
|
|
|
|
type: 'text',
|
|
|
|
text: {
|
|
|
|
body: `${idx + 1}. ${bodyText}`,
|
|
|
|
},
|
|
|
|
} as const
|
|
|
|
return imageMessage ? [imageMessage, textMessage] : textMessage
|
|
|
|
})
|
|
|
|
return input.items.map((item) => {
|
|
|
|
let bodyText = ''
|
|
|
|
if (item.title) bodyText += `*${item.title}*`
|
|
|
|
if (item.description) {
|
|
|
|
if (item.title) bodyText += '\n\n'
|
|
|
|
bodyText += item.description
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
type: 'interactive',
|
|
|
|
interactive: {
|
|
|
|
type: 'button',
|
|
|
|
header: item.pictureSrc
|
|
|
|
? {
|
|
|
|
type: 'image',
|
|
|
|
image: {
|
|
|
|
link: item.pictureSrc,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
: undefined,
|
|
|
|
body: isEmpty(bodyText) ? undefined : { text: bodyText },
|
|
|
|
action: {
|
|
|
|
buttons: [
|
|
|
|
{
|
|
|
|
type: 'reply',
|
|
|
|
reply: {
|
|
|
|
id: item.id,
|
|
|
|
title: 'Select',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
case InputBlockType.CHOICE: {
|
2023-11-08 15:34:16 +01:00
|
|
|
if (
|
|
|
|
input.options?.isMultipleChoice ??
|
|
|
|
defaultChoiceInputOptions.isMultipleChoice
|
|
|
|
)
|
2023-08-29 10:01:28 +02:00
|
|
|
return [
|
|
|
|
{
|
|
|
|
type: 'text',
|
|
|
|
text: {
|
|
|
|
body:
|
|
|
|
`${lastMessageText}\n\n` +
|
|
|
|
input.items
|
|
|
|
.map((item, idx) => `${idx + 1}. ${item.content}`)
|
|
|
|
.join('\n'),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
const items = groupArrayByArraySize(
|
|
|
|
input.items.filter((item) => isDefined(item.content)),
|
2024-03-07 05:05:08 -03:00
|
|
|
env.WHATSAPP_INTERACTIVE_GROUP_SIZE
|
2023-08-29 10:01:28 +02:00
|
|
|
) as ButtonItem[][]
|
|
|
|
return items.map((items, idx) => ({
|
|
|
|
type: 'interactive',
|
|
|
|
interactive: {
|
|
|
|
type: 'button',
|
|
|
|
body: {
|
|
|
|
text: idx === 0 ? lastMessageText ?? '...' : '...',
|
|
|
|
},
|
|
|
|
action: {
|
|
|
|
buttons: items.map((item) => ({
|
|
|
|
type: 'reply',
|
|
|
|
reply: {
|
|
|
|
id: item.id,
|
|
|
|
title: trimTextTo20Chars(item.content as string),
|
|
|
|
},
|
|
|
|
})),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const trimTextTo20Chars = (text: string): string =>
|
|
|
|
text.length > 20 ? `${text.slice(0, 18)}..` : text
|
|
|
|
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const groupArrayByArraySize = (arr: any[], n: number) =>
|
|
|
|
arr.reduce(
|
|
|
|
(r, e, i) => (i % n ? r[r.length - 1].push(e) : r.push([e])) && r,
|
|
|
|
[]
|
|
|
|
)
|