diff --git a/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx b/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx index 2b2b66fd5..2894208c2 100644 --- a/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx +++ b/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx @@ -200,14 +200,24 @@ const SetVariableValue = ({ ) } + case 'Now': + case 'Yesterday': + case 'Tomorrow': { + return ( + onOptionsChange({ ...options, timeZone })} + defaultValue={options.timeZone} + placeholder="Europe/Paris" + /> + ) + } case 'Contact name': case 'Phone number': case 'Random ID': - case 'Now': - case 'Today': - case 'Tomorrow': case 'User ID': - case 'Yesterday': + case 'Today': case 'Empty': return null } diff --git a/apps/docs/openapi/builder.json b/apps/docs/openapi/builder.json index 0bb80f40d..71d4bd06d 100644 --- a/apps/docs/openapi/builder.json +++ b/apps/docs/openapi/builder.json @@ -554,10 +554,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", @@ -4926,10 +4948,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", @@ -8349,10 +8393,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", @@ -16835,10 +16901,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", @@ -20325,10 +20413,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", @@ -23122,10 +23232,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", diff --git a/apps/docs/openapi/viewer.json b/apps/docs/openapi/viewer.json index 8e9d1fe14..3d77d6a7e 100644 --- a/apps/docs/openapi/viewer.json +++ b/apps/docs/openapi/viewer.json @@ -3621,10 +3621,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", @@ -7403,10 +7425,32 @@ "type": { "type": "string", "enum": [ - "Today", "Now", "Yesterday", - "Tomorrow", + "Tomorrow" + ] + }, + "timeZone": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "variableId": { + "type": "string" + }, + "isExecutedOnClient": { + "type": "boolean" + }, + "type": { + "type": "string", + "enum": [ + "Today", "Moment of the day", "Empty", "Environment name", diff --git a/packages/bot-engine/blocks/logic/setVariable/executeSetVariable.ts b/packages/bot-engine/blocks/logic/setVariable/executeSetVariable.ts index b6c75a2ca..99a6ec085 100644 --- a/packages/bot-engine/blocks/logic/setVariable/executeSetVariable.ts +++ b/packages/bot-engine/blocks/logic/setVariable/executeSetVariable.ts @@ -1,11 +1,12 @@ import { SessionState, SetVariableBlock, Variable } from '@typebot.io/schemas' -import { byId } from '@typebot.io/lib' +import { byId, isEmpty } from '@typebot.io/lib' import { ExecuteLogicResponse } from '../../../types' import { parseScriptToExecuteClientSideAction } from '../script/executeScript' import { parseGuessedValueType } from '@typebot.io/variables/parseGuessedValueType' import { parseVariables } from '@typebot.io/variables/parseVariables' import { updateVariablesInSession } from '@typebot.io/variables/updateVariablesInSession' import { createId } from '@paralleldrive/cuid2' +import { utcToZonedTime, format as tzFormat } from 'date-fns-tz' export const executeSetVariable = ( state: SessionState, @@ -87,13 +88,19 @@ const getExpressionToEvaluate = return phoneNumber ? `"${state.whatsApp?.contact.phoneNumber}"` : null } case 'Now': + if (isEmpty(options.timeZone)) return 'new Date().toISOString()' + return toISOWithTz(new Date(), options.timeZone) case 'Today': return 'new Date().toISOString()' case 'Tomorrow': { - return 'new Date(Date.now() + 86400000).toISOString()' + if (isEmpty(options.timeZone)) + return 'new Date(Date.now() + 86400000).toISOString()' + return toISOWithTz(new Date(Date.now() + 86400000), options.timeZone) } case 'Yesterday': { - return 'new Date(Date.now() - 86400000).toISOString()' + if (isEmpty(options.timeZone)) + return 'new Date(Date.now() - 86400000).toISOString()' + return toISOWithTz(new Date(Date.now() - 86400000), options.timeZone) } case 'Random ID': { return `"${createId()}"` @@ -130,3 +137,8 @@ const getExpressionToEvaluate = } } } + +const toISOWithTz = (date: Date, timeZone: string) => { + const zonedDate = utcToZonedTime(date, timeZone) + return tzFormat(zonedDate, "yyyy-MM-dd'T'HH:mm:ssXXX", { timeZone }) +} diff --git a/packages/bot-engine/continueBotFlow.ts b/packages/bot-engine/continueBotFlow.ts index 1ccd48d04..1bc997c7d 100644 --- a/packages/bot-engine/continueBotFlow.ts +++ b/packages/bot-engine/continueBotFlow.ts @@ -477,7 +477,11 @@ const parseReply = ? { status: 'fail' } : { status: 'skip' } const urls = reply.split(', ') - const status = urls.some((url) => isURL(url)) ? 'success' : 'fail' + const status = urls.some((url) => + isURL(url, { require_tld: env.S3_ENDPOINT !== 'localhost' }) + ) + ? 'success' + : 'fail' return { status, reply: reply } } case InputBlockType.PAYMENT: { diff --git a/packages/bot-engine/package.json b/packages/bot-engine/package.json index 0869770e3..2b4653d03 100644 --- a/packages/bot-engine/package.json +++ b/packages/bot-engine/package.json @@ -19,8 +19,9 @@ "@typebot.io/variables": "workspace:*", "@udecode/plate-common": "21.1.5", "ai": "2.2.33", - "chrono-node": "2.7.0", + "chrono-node": "2.7.5", "date-fns": "2.30.0", + "date-fns-tz": "2.0.0", "google-auth-library": "8.9.0", "google-spreadsheet": "4.1.1", "got": "12.6.0", diff --git a/packages/forge/blocks/difyAi/auth.ts b/packages/forge/blocks/difyAi/auth.ts index 1db1abac1..095b81d5f 100644 --- a/packages/forge/blocks/difyAi/auth.ts +++ b/packages/forge/blocks/difyAi/auth.ts @@ -10,7 +10,7 @@ const extractBaseUrl = (val: string | undefined) => { export const auth = { type: 'encryptedCredentials', - name: 'Dify.AI account', + name: 'Dify.AI assistant', schema: option.object({ apiEndpoint: option.string .layout({ diff --git a/packages/schemas/features/blocks/logic/setVariable/schema.ts b/packages/schemas/features/blocks/logic/setVariable/schema.ts index 9eeebfff7..047addd46 100644 --- a/packages/schemas/features/blocks/logic/setVariable/schema.ts +++ b/packages/schemas/features/blocks/logic/setVariable/schema.ts @@ -13,9 +13,6 @@ const baseOptions = z.object({ const basicSetVariableOptionsSchema = baseOptions.extend({ type: z.enum([ 'Today', - 'Now', - 'Yesterday', - 'Tomorrow', 'Moment of the day', 'Empty', 'Environment name', @@ -26,6 +23,11 @@ const basicSetVariableOptionsSchema = baseOptions.extend({ ]), }) +const dateSetVariableOptionsSchema = baseOptions.extend({ + type: z.enum(['Now', 'Yesterday', 'Tomorrow']), + timeZone: z.string().optional(), +}) + const initialSetVariableOptionsSchema = baseOptions.extend({ type: z.undefined().openapi({ type: 'string' }), expressionToEvaluate: z.string().optional(), @@ -56,6 +58,7 @@ const appendItemToListOptionsSchema = baseOptions.extend({ export const setVariableOptionsSchema = z.discriminatedUnion('type', [ initialSetVariableOptionsSchema, + dateSetVariableOptionsSchema, basicSetVariableOptionsSchema, customSetVariableOptionsSchema, mapListItemsOptionsSchema, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1450c7076..10a9e3615 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -660,11 +660,14 @@ importers: specifier: 2.2.33 version: 2.2.33(react@18.2.0)(solid-js@1.7.8)(svelte@4.2.9)(vue@3.4.15) chrono-node: - specifier: 2.7.0 - version: 2.7.0 + specifier: 2.7.5 + version: 2.7.5 date-fns: specifier: 2.30.0 version: 2.30.0 + date-fns-tz: + specifier: 2.0.0 + version: 2.0.0(date-fns@2.30.0) google-auth-library: specifier: 8.9.0 version: 8.9.0 @@ -12563,8 +12566,8 @@ packages: engines: {node: '>=6.0'} dev: false - /chrono-node@2.7.0: - resolution: {integrity: sha512-0s2vv89LmsbgoibV0AIVgNnGqlU8N5yCCVZXvc3mRCjnmlG/gJw1hCYOmNwjB+AIuwZQdKTXfwvsHDRTs6pwcg==} + /chrono-node@2.7.5: + resolution: {integrity: sha512-VJWqFN5rWmXVvXAxOD4i0jX8Tb4cLswaslyaAFhxM45zNXPsZleygPbgiaYBD7ORb9fj07zBgJb0Q6eKL+0iJg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: dayjs: 1.11.10 @@ -13245,6 +13248,14 @@ packages: whatwg-url: 11.0.0 dev: true + /date-fns-tz@2.0.0(date-fns@2.30.0): + resolution: {integrity: sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ==} + peerDependencies: + date-fns: '>=2.0.0' + dependencies: + date-fns: 2.30.0 + dev: false + /date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'}