2023-09-20 15:26:52 +02:00
|
|
|
import prisma from '@typebot.io/lib/prisma'
|
2023-08-29 10:01:28 +02:00
|
|
|
import {
|
2023-11-13 15:27:36 +01:00
|
|
|
ContinueChatResponse,
|
2023-08-29 10:01:28 +02:00
|
|
|
PublicTypebot,
|
|
|
|
SessionState,
|
|
|
|
Settings,
|
|
|
|
Typebot,
|
|
|
|
} from '@typebot.io/schemas'
|
|
|
|
import {
|
|
|
|
WhatsAppCredentials,
|
2023-09-22 17:12:15 +02:00
|
|
|
defaultSessionExpiryTimeout,
|
2023-08-29 10:01:28 +02:00
|
|
|
} from '@typebot.io/schemas/features/whatsapp'
|
2023-10-04 16:47:58 +02:00
|
|
|
import { isNotDefined } from '@typebot.io/lib/utils'
|
2023-09-20 15:26:52 +02:00
|
|
|
import { startSession } from '../startSession'
|
2023-11-08 15:34:16 +01:00
|
|
|
import {
|
|
|
|
LogicalOperator,
|
|
|
|
ComparisonOperators,
|
|
|
|
} from '@typebot.io/schemas/features/blocks/logic/condition/constants'
|
|
|
|
import { VisitedEdge } from '@typebot.io/prisma'
|
2024-01-30 08:02:10 +01:00
|
|
|
import { Reply } from '../types'
|
2023-08-29 10:01:28 +02:00
|
|
|
|
|
|
|
type Props = {
|
2024-01-30 08:02:10 +01:00
|
|
|
incomingMessage?: Reply
|
2023-11-13 15:27:36 +01:00
|
|
|
workspaceId: string
|
2023-09-22 11:08:41 +02:00
|
|
|
credentials: WhatsAppCredentials['data'] & Pick<WhatsAppCredentials, 'id'>
|
2023-08-29 10:01:28 +02:00
|
|
|
contact: NonNullable<SessionState['whatsApp']>['contact']
|
|
|
|
}
|
|
|
|
|
|
|
|
export const startWhatsAppSession = async ({
|
2023-09-25 17:20:42 +02:00
|
|
|
incomingMessage,
|
2023-08-29 10:01:28 +02:00
|
|
|
workspaceId,
|
2023-09-22 11:08:41 +02:00
|
|
|
credentials,
|
2023-08-29 10:01:28 +02:00
|
|
|
contact,
|
|
|
|
}: Props): Promise<
|
2023-11-13 15:27:36 +01:00
|
|
|
| (ContinueChatResponse & {
|
2023-08-29 10:01:28 +02:00
|
|
|
newSessionState: SessionState
|
2023-11-08 15:34:16 +01:00
|
|
|
visitedEdges: VisitedEdge[]
|
2023-08-29 10:01:28 +02:00
|
|
|
})
|
2023-10-25 14:26:10 +02:00
|
|
|
| { error: string }
|
2023-08-29 10:01:28 +02:00
|
|
|
> => {
|
|
|
|
const publicTypebotsWithWhatsAppEnabled =
|
|
|
|
(await prisma.publicTypebot.findMany({
|
|
|
|
where: {
|
2023-09-22 11:08:41 +02:00
|
|
|
typebot: { workspaceId, whatsAppCredentialsId: credentials.id },
|
2023-08-29 10:01:28 +02:00
|
|
|
},
|
|
|
|
select: {
|
|
|
|
settings: true,
|
|
|
|
typebot: {
|
|
|
|
select: {
|
|
|
|
publicId: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})) as (Pick<PublicTypebot, 'settings'> & {
|
|
|
|
typebot: Pick<Typebot, 'publicId'>
|
|
|
|
})[]
|
|
|
|
|
|
|
|
const botsWithWhatsAppEnabled = publicTypebotsWithWhatsAppEnabled.filter(
|
|
|
|
(publicTypebot) =>
|
|
|
|
publicTypebot.typebot.publicId &&
|
2023-09-22 11:08:41 +02:00
|
|
|
publicTypebot.settings.whatsApp?.isEnabled
|
2023-08-29 10:01:28 +02:00
|
|
|
)
|
|
|
|
|
2023-10-03 10:13:25 +02:00
|
|
|
const publicTypebotWithMatchedCondition = botsWithWhatsAppEnabled.find(
|
|
|
|
(publicTypebot) =>
|
2023-10-07 12:03:42 +02:00
|
|
|
(publicTypebot.settings.whatsApp?.startCondition?.comparisons.length ??
|
|
|
|
0) > 0 &&
|
2023-10-03 10:13:25 +02:00
|
|
|
messageMatchStartCondition(
|
|
|
|
incomingMessage ?? '',
|
|
|
|
publicTypebot.settings.whatsApp?.startCondition
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2023-08-29 10:01:28 +02:00
|
|
|
const publicTypebot =
|
2023-10-03 10:13:25 +02:00
|
|
|
publicTypebotWithMatchedCondition ??
|
2023-08-29 10:01:28 +02:00
|
|
|
botsWithWhatsAppEnabled.find(
|
2023-10-03 10:13:25 +02:00
|
|
|
(publicTypebot) => !publicTypebot.settings.whatsApp?.startCondition
|
|
|
|
)
|
2023-08-29 10:01:28 +02:00
|
|
|
|
2023-10-25 14:26:10 +02:00
|
|
|
if (isNotDefined(publicTypebot))
|
|
|
|
return botsWithWhatsAppEnabled.length > 0
|
|
|
|
? { error: 'Message did not matched any condition' }
|
|
|
|
: { error: 'No public typebot with WhatsApp integration found' }
|
2023-08-29 10:01:28 +02:00
|
|
|
|
2023-09-26 09:50:20 +02:00
|
|
|
const sessionExpiryTimeoutHours =
|
|
|
|
publicTypebot.settings.whatsApp?.sessionExpiryTimeout ??
|
|
|
|
defaultSessionExpiryTimeout
|
|
|
|
|
2023-10-04 16:47:58 +02:00
|
|
|
return startSession({
|
2023-10-06 10:14:26 +02:00
|
|
|
version: 2,
|
2023-10-04 16:47:58 +02:00
|
|
|
message: incomingMessage,
|
2023-08-29 12:19:50 +02:00
|
|
|
startParams: {
|
2023-11-13 15:27:36 +01:00
|
|
|
type: 'live',
|
|
|
|
publicId: publicTypebot.typebot.publicId as string,
|
|
|
|
isOnlyRegistering: false,
|
2023-12-22 09:13:53 +01:00
|
|
|
isStreamEnabled: false,
|
2023-08-29 12:19:50 +02:00
|
|
|
},
|
2023-09-26 09:50:20 +02:00
|
|
|
initialSessionState: {
|
|
|
|
whatsApp: {
|
|
|
|
contact,
|
|
|
|
},
|
|
|
|
expiryTimeout: sessionExpiryTimeoutHours * 60 * 60 * 1000,
|
|
|
|
},
|
2023-08-29 10:01:28 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
export const messageMatchStartCondition = (
|
2024-01-30 08:02:10 +01:00
|
|
|
message: Reply,
|
2023-08-29 10:01:28 +02:00
|
|
|
startCondition: NonNullable<Settings['whatsApp']>['startCondition']
|
|
|
|
) => {
|
|
|
|
if (!startCondition) return true
|
2024-01-30 08:02:10 +01:00
|
|
|
if (typeof message !== 'string') return false
|
2023-08-29 10:01:28 +02:00
|
|
|
return startCondition.logicalOperator === LogicalOperator.AND
|
|
|
|
? startCondition.comparisons.every((comparison) =>
|
|
|
|
matchComparison(
|
|
|
|
message,
|
|
|
|
comparison.comparisonOperator,
|
|
|
|
comparison.value
|
|
|
|
)
|
|
|
|
)
|
|
|
|
: startCondition.comparisons.some((comparison) =>
|
|
|
|
matchComparison(
|
|
|
|
message,
|
|
|
|
comparison.comparisonOperator,
|
|
|
|
comparison.value
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const matchComparison = (
|
|
|
|
inputValue: string,
|
|
|
|
comparisonOperator?: ComparisonOperators,
|
|
|
|
value?: string
|
|
|
|
): boolean | undefined => {
|
|
|
|
if (!comparisonOperator) return false
|
|
|
|
switch (comparisonOperator) {
|
|
|
|
case ComparisonOperators.CONTAINS: {
|
|
|
|
if (!value) return false
|
|
|
|
return inputValue
|
|
|
|
.trim()
|
|
|
|
.toLowerCase()
|
|
|
|
.includes(value.trim().toLowerCase())
|
|
|
|
}
|
|
|
|
case ComparisonOperators.EQUAL: {
|
|
|
|
return inputValue === value
|
|
|
|
}
|
|
|
|
case ComparisonOperators.NOT_EQUAL: {
|
|
|
|
return inputValue !== value
|
|
|
|
}
|
|
|
|
case ComparisonOperators.GREATER: {
|
|
|
|
if (!value) return false
|
|
|
|
return parseFloat(inputValue) > parseFloat(value)
|
|
|
|
}
|
|
|
|
case ComparisonOperators.LESS: {
|
|
|
|
if (!value) return false
|
|
|
|
return parseFloat(inputValue) < parseFloat(value)
|
|
|
|
}
|
|
|
|
case ComparisonOperators.IS_SET: {
|
|
|
|
return inputValue.length > 0
|
|
|
|
}
|
|
|
|
case ComparisonOperators.IS_EMPTY: {
|
|
|
|
return inputValue.length === 0
|
|
|
|
}
|
|
|
|
case ComparisonOperators.STARTS_WITH: {
|
|
|
|
if (!value) return false
|
|
|
|
return inputValue.toLowerCase().startsWith(value.toLowerCase())
|
|
|
|
}
|
|
|
|
case ComparisonOperators.ENDS_WITH: {
|
|
|
|
if (!value) return false
|
|
|
|
return inputValue.toLowerCase().endsWith(value.toLowerCase())
|
|
|
|
}
|
|
|
|
case ComparisonOperators.NOT_CONTAINS: {
|
|
|
|
if (!value) return false
|
|
|
|
return !inputValue
|
|
|
|
.trim()
|
|
|
|
.toLowerCase()
|
|
|
|
.includes(value.trim().toLowerCase())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|