⚡ (whatsapp) Improve whatsApp management and media collection
Closes #796
This commit is contained in:
@@ -31,14 +31,16 @@ export const createCredentials = authenticatedProcedure
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
credentials: z.discriminatedUnion('type', [
|
||||
stripeCredentialsSchema.pick(inputShape),
|
||||
smtpCredentialsSchema.pick(inputShape),
|
||||
googleSheetsCredentialsSchema.pick(inputShape),
|
||||
openAICredentialsSchema.pick(inputShape),
|
||||
whatsAppCredentialsSchema.pick(inputShape),
|
||||
zemanticAiCredentialsSchema.pick(inputShape),
|
||||
]),
|
||||
credentials: z
|
||||
.discriminatedUnion('type', [
|
||||
stripeCredentialsSchema.pick(inputShape),
|
||||
smtpCredentialsSchema.pick(inputShape),
|
||||
googleSheetsCredentialsSchema.pick(inputShape),
|
||||
openAICredentialsSchema.pick(inputShape),
|
||||
whatsAppCredentialsSchema.pick(inputShape),
|
||||
zemanticAiCredentialsSchema.pick(inputShape),
|
||||
])
|
||||
.and(z.object({ id: z.string().cuid2().optional() })),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
|
||||
@@ -39,7 +39,7 @@ type UpdateTypebotPayload = Partial<
|
||||
| 'customDomain'
|
||||
| 'resultsTablePreferences'
|
||||
| 'isClosed'
|
||||
| 'whatsAppPhoneNumberId'
|
||||
| 'whatsAppCredentialsId'
|
||||
>
|
||||
>
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import { env } from '@typebot.io/env'
|
||||
import { isEmpty, isNotEmpty } from '@typebot.io/lib/utils'
|
||||
import { getViewerUrl } from '@typebot.io/lib/getViewerUrl'
|
||||
import React, { useState } from 'react'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
|
||||
const steps = [
|
||||
{ title: 'Requirements' },
|
||||
@@ -57,6 +58,8 @@ type Props = {
|
||||
onNewCredentials: (id: string) => void
|
||||
}
|
||||
|
||||
const credentialsId = createId()
|
||||
|
||||
export const WhatsAppCredentialsModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
@@ -115,6 +118,7 @@ export const WhatsAppCredentialsModal = ({
|
||||
if (!workspace) return
|
||||
mutate({
|
||||
credentials: {
|
||||
id: credentialsId,
|
||||
type: 'whatsApp',
|
||||
workspaceId: workspace.id,
|
||||
name: phoneNumberName,
|
||||
@@ -269,7 +273,7 @@ export const WhatsAppCredentialsModal = ({
|
||||
<Webhook
|
||||
appId={tokenInfoData?.appId}
|
||||
verificationToken={verificationToken}
|
||||
phoneNumberId={phoneNumberId}
|
||||
credentialsId={credentialsId}
|
||||
/>
|
||||
)}
|
||||
</ModalBody>
|
||||
@@ -442,18 +446,16 @@ const PhoneNumber = ({
|
||||
const Webhook = ({
|
||||
appId,
|
||||
verificationToken,
|
||||
phoneNumberId,
|
||||
credentialsId,
|
||||
}: {
|
||||
appId?: string
|
||||
verificationToken: string
|
||||
phoneNumberId: string
|
||||
credentialsId: string
|
||||
}) => {
|
||||
const { workspace } = useWorkspace()
|
||||
const webhookUrl = `${
|
||||
env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ?? getViewerUrl()
|
||||
}/api/v1/workspaces/${
|
||||
workspace?.id
|
||||
}/whatsapp/phoneNumbers/${phoneNumberId}/webhook`
|
||||
}/api/v1/workspaces/${workspace?.id}/whatsapp/${credentialsId}/webhook`
|
||||
|
||||
return (
|
||||
<Stack spacing={6}>
|
||||
|
||||
@@ -30,7 +30,6 @@ import { PublishButton } from '../../../PublishButton'
|
||||
import { useParentModal } from '@/features/graph/providers/ParentModalProvider'
|
||||
import { trpc } from '@/lib/trpc'
|
||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||
import { isDefined } from '@typebot.io/lib/utils'
|
||||
import { TableList } from '@/components/TableList'
|
||||
import { Comparison, LogicalOperator } from '@typebot.io/schemas'
|
||||
import { DropdownList } from '@/components/DropdownList'
|
||||
@@ -51,18 +50,25 @@ export const WhatsAppModal = ({ isOpen, onClose }: ModalProps): JSX.Element => {
|
||||
|
||||
const { data: phoneNumberData } = trpc.whatsApp.getPhoneNumber.useQuery(
|
||||
{
|
||||
credentialsId: whatsAppSettings?.credentialsId as string,
|
||||
credentialsId: typebot?.whatsAppCredentialsId as string,
|
||||
},
|
||||
{
|
||||
enabled: !!whatsAppSettings?.credentialsId,
|
||||
enabled: !!typebot?.whatsAppCredentialsId,
|
||||
}
|
||||
)
|
||||
|
||||
const toggleEnableWhatsApp = (isChecked: boolean) => {
|
||||
if (!phoneNumberData?.id) return
|
||||
if (!phoneNumberData?.id || !typebot) return
|
||||
updateTypebot({
|
||||
updates: { whatsAppPhoneNumberId: isChecked ? phoneNumberData.id : null },
|
||||
save: true,
|
||||
updates: {
|
||||
settings: {
|
||||
...typebot.settings,
|
||||
whatsApp: {
|
||||
...typebot.settings.whatsApp,
|
||||
isEnabled: isChecked,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -70,13 +76,7 @@ export const WhatsAppModal = ({ isOpen, onClose }: ModalProps): JSX.Element => {
|
||||
if (!typebot) return
|
||||
updateTypebot({
|
||||
updates: {
|
||||
settings: {
|
||||
...typebot.settings,
|
||||
whatsApp: {
|
||||
...typebot.settings.whatsApp,
|
||||
credentialsId,
|
||||
},
|
||||
},
|
||||
whatsAppCredentialsId: credentialsId,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -148,7 +148,9 @@ export const WhatsAppModal = ({ isOpen, onClose }: ModalProps): JSX.Element => {
|
||||
<CredentialsDropdown
|
||||
type="whatsApp"
|
||||
workspaceId={workspace.id}
|
||||
currentCredentialsId={whatsAppSettings?.credentialsId}
|
||||
currentCredentialsId={
|
||||
typebot?.whatsAppCredentialsId ?? undefined
|
||||
}
|
||||
onCredentialsSelect={updateCredentialsId}
|
||||
onCreateNewClick={onOpen}
|
||||
credentialsName="WA phone number"
|
||||
@@ -158,7 +160,7 @@ export const WhatsAppModal = ({ isOpen, onClose }: ModalProps): JSX.Element => {
|
||||
)}
|
||||
</HStack>
|
||||
</ListItem>
|
||||
{typebot?.settings.whatsApp?.credentialsId && (
|
||||
{typebot?.whatsAppCredentialsId && (
|
||||
<>
|
||||
<ListItem>
|
||||
<Accordion allowToggle>
|
||||
@@ -196,22 +198,22 @@ export const WhatsAppModal = ({ isOpen, onClose }: ModalProps): JSX.Element => {
|
||||
</Accordion>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<HStack>
|
||||
<Text>Publish your bot:</Text>
|
||||
<PublishButton size="sm" isMoreMenuDisabled />
|
||||
</HStack>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<SwitchWithLabel
|
||||
label="Enable WhatsApp integration"
|
||||
initialValue={
|
||||
isDefined(typebot?.whatsAppPhoneNumberId) ? true : false
|
||||
typebot?.settings.whatsApp?.isEnabled ?? false
|
||||
}
|
||||
onCheckChange={toggleEnableWhatsApp}
|
||||
justifyContent="flex-start"
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<HStack>
|
||||
<Text>Publish your bot:</Text>
|
||||
<PublishButton size="sm" isMoreMenuDisabled />
|
||||
</HStack>
|
||||
</ListItem>
|
||||
{phoneNumberData?.id && (
|
||||
<ListItem>
|
||||
<TextLink
|
||||
|
||||
@@ -24,4 +24,5 @@ export const convertPublicTypebotToTypebot = (
|
||||
resultsTablePreferences: existingTypebot.resultsTablePreferences,
|
||||
selectedThemeTemplateId: existingTypebot.selectedThemeTemplateId,
|
||||
whatsAppPhoneNumberId: existingTypebot.whatsAppPhoneNumberId,
|
||||
whatsAppCredentialsId: existingTypebot.whatsAppCredentialsId,
|
||||
})
|
||||
|
||||
@@ -31,6 +31,7 @@ export const updateTypebot = authenticatedProcedure
|
||||
.pick({
|
||||
isClosed: true,
|
||||
whatsAppPhoneNumberId: true,
|
||||
whatsAppCredentialsId: true,
|
||||
})
|
||||
.partial()
|
||||
),
|
||||
@@ -151,6 +152,7 @@ export const updateTypebot = authenticatedProcedure
|
||||
typebot.customDomain === null ? null : typebot.customDomain,
|
||||
isClosed: typebot.isClosed,
|
||||
whatsAppPhoneNumberId: typebot.whatsAppPhoneNumberId ?? undefined,
|
||||
whatsAppCredentialsId: typebot.whatsAppCredentialsId ?? undefined,
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import prisma from '@typebot.io/lib/prisma'
|
||||
import { decrypt } from '@typebot.io/lib/api'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
||||
import { parsePhoneNumber } from 'libphonenumber-js'
|
||||
|
||||
const inputSchema = z.object({
|
||||
credentialsId: z.string().optional(),
|
||||
@@ -46,18 +45,13 @@ export const getPhoneNumber = authenticatedProcedure
|
||||
display_phone_number: string
|
||||
}
|
||||
|
||||
const parsedPhoneNumber = parsePhoneNumber(display_phone_number)
|
||||
|
||||
if (!parsedPhoneNumber.isValid())
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message:
|
||||
"Phone number is not valid. Make sure you don't provide a WhatsApp test number.",
|
||||
})
|
||||
const formattedPhoneNumber = `${
|
||||
display_phone_number.startsWith('+') ? '' : '+'
|
||||
}${display_phone_number.replace(/\s-/g, '')}`
|
||||
|
||||
return {
|
||||
id: credentials.phoneNumberId,
|
||||
name: parsedPhoneNumber.formatInternational().replace(/\s/g, ''),
|
||||
name: formattedPhoneNumber,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ export const receiveMessagePreview = publicProcedure
|
||||
return resumeWhatsAppFlow({
|
||||
receivedMessage,
|
||||
sessionId: `wa-${receivedMessage.from}-preview`,
|
||||
phoneNumberId: env.WHATSAPP_PREVIEW_FROM_PHONE_NUMBER_ID,
|
||||
contact: {
|
||||
name: contactName,
|
||||
phoneNumber: contactPhoneNumber,
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import prisma from '@typebot.io/lib/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import {
|
||||
decrypt,
|
||||
methodNotAllowed,
|
||||
notAuthenticated,
|
||||
notFound,
|
||||
} from '@typebot.io/lib/api'
|
||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
||||
import got from 'got'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
if (req.method === 'GET') {
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
if (!user) return notAuthenticated(res)
|
||||
|
||||
const typebotId = req.query.typebotId as string
|
||||
|
||||
const typebot = await prisma.typebot.findFirst({
|
||||
where: {
|
||||
id: typebotId,
|
||||
},
|
||||
select: {
|
||||
whatsAppCredentialsId: true,
|
||||
workspace: {
|
||||
select: {
|
||||
credentials: {
|
||||
where: {
|
||||
type: 'whatsApp',
|
||||
},
|
||||
},
|
||||
members: {
|
||||
select: {
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (!typebot?.workspace || isReadWorkspaceFobidden(typebot.workspace, user))
|
||||
return notFound(res, 'Workspace not found')
|
||||
|
||||
if (!typebot) return notFound(res, 'Typebot not found')
|
||||
|
||||
const mediaId = req.query.mediaId as string
|
||||
const credentialsId = typebot.whatsAppCredentialsId
|
||||
|
||||
const credentials = typebot.workspace.credentials.find(
|
||||
(credential) => credential.id === credentialsId
|
||||
)
|
||||
|
||||
if (!credentials) return notFound(res, 'Credentials not found')
|
||||
|
||||
const credentialsData = (await decrypt(
|
||||
credentials.data,
|
||||
credentials.iv
|
||||
)) as WhatsAppCredentials['data']
|
||||
|
||||
const { body } = await got.get({
|
||||
url: `https://graph.facebook.com/v17.0/${mediaId}`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${credentialsData.systemUserAccessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
const parsedBody = JSON.parse(body) as { url: string; mime_type: string }
|
||||
|
||||
const buffer = await got(parsedBody.url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${credentialsData.systemUserAccessToken}`,
|
||||
},
|
||||
}).buffer()
|
||||
|
||||
res.setHeader('Content-Type', parsedBody.mime_type)
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400')
|
||||
|
||||
return res.send(buffer)
|
||||
}
|
||||
return methodNotAllowed(res)
|
||||
}
|
||||
|
||||
export default handler
|
||||
@@ -8633,8 +8633,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -12745,8 +12745,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -12877,6 +12877,10 @@
|
||||
"whatsAppPhoneNumberId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"whatsAppCredentialsId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -12899,7 +12903,8 @@
|
||||
"resultsTablePreferences",
|
||||
"isArchived",
|
||||
"isClosed",
|
||||
"whatsAppPhoneNumberId"
|
||||
"whatsAppPhoneNumberId",
|
||||
"whatsAppCredentialsId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -16819,8 +16824,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -17018,6 +17023,10 @@
|
||||
"whatsAppPhoneNumberId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"whatsAppCredentialsId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -20951,8 +20960,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -21083,6 +21092,10 @@
|
||||
"whatsAppPhoneNumberId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"whatsAppCredentialsId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -21105,7 +21118,8 @@
|
||||
"resultsTablePreferences",
|
||||
"isArchived",
|
||||
"isClosed",
|
||||
"whatsAppPhoneNumberId"
|
||||
"whatsAppPhoneNumberId",
|
||||
"whatsAppCredentialsId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -25049,8 +25063,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -25181,6 +25195,10 @@
|
||||
"whatsAppPhoneNumberId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"whatsAppCredentialsId": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -25203,7 +25221,8 @@
|
||||
"resultsTablePreferences",
|
||||
"isArchived",
|
||||
"isClosed",
|
||||
"whatsAppPhoneNumberId"
|
||||
"whatsAppPhoneNumberId",
|
||||
"whatsAppCredentialsId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
@@ -29206,8 +29225,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -30849,294 +30868,307 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentials": {
|
||||
"anyOf": [
|
||||
"allOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"live": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secretKey": {
|
||||
"live": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secretKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secretKey",
|
||||
"publicKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"test": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secretKey": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"live",
|
||||
"test"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"stripe"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"isTlsEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"port": {
|
||||
"type": "number"
|
||||
},
|
||||
"from": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"port",
|
||||
"from"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"smtp"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"expiry_date": {
|
||||
"type": "number",
|
||||
"nullable": true
|
||||
},
|
||||
"access_token": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"token_type": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"id_token": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"google sheets"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secretKey",
|
||||
"publicKey"
|
||||
"apiKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"test": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secretKey": {
|
||||
"systemUserAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicKey": {
|
||||
"phoneNumberId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"systemUserAccessToken",
|
||||
"phoneNumberId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"whatsApp"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"live",
|
||||
"test"
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"stripe"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"isTlsEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"port": {
|
||||
"type": "number"
|
||||
},
|
||||
"from": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"port",
|
||||
"from"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"smtp"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
"enum": [
|
||||
"zemanticAi"
|
||||
]
|
||||
},
|
||||
"expiry_date": {
|
||||
"type": "number",
|
||||
"nullable": true
|
||||
},
|
||||
"access_token": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"token_type": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"id_token": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"scope": {
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"google sheets"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiKey"
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"systemUserAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumberId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"systemUserAccessToken",
|
||||
"phoneNumberId"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"whatsApp"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
"pattern": "^[a-z][a-z0-9]*$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"apiKey": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"apiKey"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"zemanticAi"
|
||||
]
|
||||
},
|
||||
"workspaceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data",
|
||||
"type",
|
||||
"workspaceId",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3761,8 +3761,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -6172,8 +6172,8 @@
|
||||
"whatsApp": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"credentialsId": {
|
||||
"type": "string"
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"startCondition": {
|
||||
"type": "object",
|
||||
@@ -6350,17 +6350,12 @@
|
||||
"presignedUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"formData": {
|
||||
"type": "object",
|
||||
"additionalProperties": {}
|
||||
},
|
||||
"hasReachedStorageLimit": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"presignedUrl",
|
||||
"formData",
|
||||
"hasReachedStorageLimit"
|
||||
],
|
||||
"additionalProperties": false
|
||||
@@ -6508,7 +6503,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaces/{workspaceId}/whatsapp/phoneNumbers/{phoneNumberId}/webhook": {
|
||||
"/workspaces/{workspaceId}/whatsapp/{credentialsId}/webhook": {
|
||||
"get": {
|
||||
"operationId": "whatsAppRouter-subscribeWebhook",
|
||||
"summary": "Subscribe webhook",
|
||||
@@ -6530,7 +6525,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "phoneNumberId",
|
||||
"name": "credentialsId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
@@ -6932,7 +6927,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "phoneNumberId",
|
||||
"name": "credentialsId",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
|
||||
@@ -8,14 +8,14 @@ export const receiveMessage = publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'POST',
|
||||
path: '/workspaces/{workspaceId}/whatsapp/phoneNumbers/{phoneNumberId}/webhook',
|
||||
path: '/workspaces/{workspaceId}/whatsapp/{credentialsId}/webhook',
|
||||
summary: 'Message webhook',
|
||||
tags: ['WhatsApp'],
|
||||
},
|
||||
})
|
||||
.input(
|
||||
z
|
||||
.object({ workspaceId: z.string(), phoneNumberId: z.string() })
|
||||
.object({ workspaceId: z.string(), credentialsId: z.string() })
|
||||
.merge(whatsAppWebhookRequestBodySchema)
|
||||
)
|
||||
.output(
|
||||
@@ -23,7 +23,7 @@ export const receiveMessage = publicProcedure
|
||||
message: z.string(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input: { entry, workspaceId, phoneNumberId } }) => {
|
||||
.mutation(async ({ input: { entry, workspaceId, credentialsId } }) => {
|
||||
const receivedMessage = entry.at(0)?.changes.at(0)?.value.messages?.at(0)
|
||||
if (isNotDefined(receivedMessage)) return { message: 'No message found' }
|
||||
const contactName =
|
||||
@@ -32,8 +32,8 @@ export const receiveMessage = publicProcedure
|
||||
entry.at(0)?.changes.at(0)?.value?.messages?.at(0)?.from ?? ''
|
||||
return resumeWhatsAppFlow({
|
||||
receivedMessage,
|
||||
sessionId: `wa-${phoneNumberId}-${receivedMessage.from}`,
|
||||
phoneNumberId,
|
||||
sessionId: `wa-${credentialsId}-${receivedMessage.from}`,
|
||||
credentialsId,
|
||||
workspaceId,
|
||||
contact: {
|
||||
name: contactName,
|
||||
|
||||
@@ -7,7 +7,7 @@ export const subscribeWebhook = publicProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/workspaces/{workspaceId}/whatsapp/phoneNumbers/{phoneNumberId}/webhook',
|
||||
path: '/workspaces/{workspaceId}/whatsapp/{credentialsId}/webhook',
|
||||
summary: 'Subscribe webhook',
|
||||
tags: ['WhatsApp'],
|
||||
protect: true,
|
||||
@@ -16,7 +16,7 @@ export const subscribeWebhook = publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
workspaceId: z.string(),
|
||||
phoneNumberId: z.string(),
|
||||
credentialsId: z.string(),
|
||||
'hub.challenge': z.string(),
|
||||
'hub.verify_token': z.string(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user