Skip to content

Commit

Permalink
🔧 (scripts) Add typebot fix script
Browse files Browse the repository at this point in the history
Closes #192
  • Loading branch information
baptisteArno committed Dec 27, 2022
1 parent 8382fd7 commit ad72557
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ dump.tar

__env.js

invalidTypebots.json
typebotsToFix.json
**/scripts/logs
3 changes: 2 additions & 1 deletion apps/builder/public/templates/lead-gen.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@
"groupId": "o4SH1UtKANnW5N5D67oZUz",
"options": {
"labels": { "button": "Send", "placeholder": "Type your email..." },
"variableId": "v3VFChNVSCXQ2rXv4DrJ8Ah"
"variableId": "v3VFChNVSCXQ2rXv4DrJ8Ah",
"retryMessageContent": "This email doesn't seem to be valid. Can you type it again?"
},
"outgoingEdgeId": "w3MiN1Ct38jT5NykVsgmb5"
}
Expand Down
12 changes: 12 additions & 0 deletions apps/builder/src/pages/api/typebots/[typebotId].ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { canReadTypebots, canWriteTypebots } from '@/utils/api/dbRules'
import { methodNotAllowed, notAuthenticated } from 'utils/api'
import { getAuthenticatedUser } from '@/features/auth/api'
import { archiveResults } from '@/features/results/api'
import { typebotSchema } from 'models'
import { captureEvent } from '@sentry/nextjs'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req)
Expand Down Expand Up @@ -55,6 +57,16 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
}
if (req.method === 'PUT') {
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
const parser = typebotSchema.safeParse(data)
if ('error' in parser) {
captureEvent({
message: 'Typebot schema validation failed',
extra: {
typebotId: data.id,
error: parser.error,
},
})
}
const existingTypebot = await prisma.typebot.findFirst({
where: canReadTypebots(typebotId, user),
select: { updatedAt: true },
Expand Down
3 changes: 1 addition & 2 deletions packages/models/src/features/blocks/inputs/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export const emailInputOptionsSchema = optionBaseSchema
.and(textInputOptionsBaseSchema)
.and(
z.object({
// TODO: make it required once database migration is done
retryMessageContent: z.string().optional(),
retryMessageContent: z.string(),
})
)

Expand Down
195 changes: 195 additions & 0 deletions packages/scripts/fixTypebots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { PrismaClient } from 'db'
import { readFileSync, writeFileSync } from 'fs'
import {
Block,
BlockOptions,
BlockType,
defaultEmailInputOptions,
Group,
InputBlockType,
Theme,
Typebot,
typebotSchema,
} from 'models'
import { isNotDefined } from 'utils'
import { promptAndSetEnvironment } from './utils'
import { detailedDiff } from 'deep-object-diff'

const fixTypebot = (brokenTypebot: Typebot) =>
({
...brokenTypebot,
theme: fixTheme(brokenTypebot.theme),
groups: fixGroups(brokenTypebot.groups),
} satisfies Typebot)

const fixTheme = (brokenTheme: Theme) =>
({
...brokenTheme,
chat: {
...brokenTheme.chat,
hostAvatar: brokenTheme.chat.hostAvatar
? {
isEnabled: brokenTheme.chat.hostAvatar.isEnabled,
url: brokenTheme.chat.hostAvatar.url ?? undefined,
}
: undefined,
},
} satisfies Theme)

const fixGroups = (brokenGroups: Group[]) =>
brokenGroups.map(
(brokenGroup, index) =>
({
...brokenGroup,
graphCoordinates: {
...brokenGroup.graphCoordinates,
x: brokenGroup.graphCoordinates.x ?? 0,
y: brokenGroup.graphCoordinates.y ?? 0,
},
blocks: fixBlocks(brokenGroup.blocks, brokenGroup.id, index),
} satisfies Group)
)

const fixBlocks = (
brokenBlocks: Block[],
groupId: string,
groupIndex: number
) => {
if (groupIndex === 0 && brokenBlocks.length > 1) return [brokenBlocks[0]]
return brokenBlocks
.filter((block) => block && Object.keys(block).length > 0)
.map((brokenBlock) => {
return removeUndefinedFromObject({
...brokenBlock,
webhookId:
('webhookId' in brokenBlock ? brokenBlock.webhookId : undefined) ??
('webhook' in brokenBlock && brokenBlock.webhook
? //@ts-ignore
brokenBlock.webhook.id
: undefined),
webhook: undefined,
groupId: brokenBlock.groupId ?? groupId,
options:
brokenBlock && 'options' in brokenBlock && brokenBlock.options
? fixBrokenBlockOption(brokenBlock.options, brokenBlock.type)
: undefined,
})
}) as Block[]
}

const fixBrokenBlockOption = (option: BlockOptions, blockType: BlockType) =>
removeUndefinedFromObject({
...option,
sheetId:
'sheetId' in option && option.sheetId
? option.sheetId.toString()
: undefined,
step: 'step' in option && option.step ? option.step : undefined,
value: 'value' in option && option.value ? option.value : undefined,
retryMessageContent: fixRetryMessageContent(
//@ts-ignore
option.retryMessageContent,
blockType
),
}) as BlockOptions

const fixRetryMessageContent = (
retryMessageContent: string | undefined,
blockType: BlockType
) => {
if (isNotDefined(retryMessageContent) && blockType === InputBlockType.EMAIL)
return defaultEmailInputOptions.retryMessageContent
if (isNotDefined(retryMessageContent)) return undefined
return retryMessageContent
}

const removeUndefinedFromObject = (obj: any) => {
Object.keys(obj).forEach((key) => obj[key] === undefined && delete obj[key])
return obj
}

const resolve = (path: string, obj: object, separator = '.') => {
const properties = Array.isArray(path) ? path : path.split(separator)
//@ts-ignore
return properties.reduce((prev, curr) => prev?.[curr], obj)
}

const fixTypebots = async () => {
await promptAndSetEnvironment()
const prisma = new PrismaClient({
log: [{ emit: 'event', level: 'query' }, 'info', 'warn', 'error'],
})

prisma.$on('query', (e) => {
console.log(e.query)
console.log(e.params)
console.log(e.duration, 'ms')
})

const typebots = JSON.parse(readFileSync('typebots.json', 'utf-8')) as any[]

const total = typebots.length
let totalFixed = 0
let progress = 0
const fixedTypebots: Typebot[] = []
const diffs: any[] = []
for (const typebot of typebots) {
progress += 1
console.log(
`Progress: ${progress}/${total} (${Math.round(
(progress / total) * 100
)}%) (${totalFixed} fixed typebots)`
)
const parser = typebotSchema.safeParse({
...typebot,
updatedAt: new Date(typebot.updatedAt),
createdAt: new Date(typebot.createdAt),
})
if ('error' in parser) {
const fixedTypebot = {
...fixTypebot(typebot),
updatedAt: new Date(typebot.updatedAt),
createdAt: new Date(typebot.createdAt),
}
fixedTypebots.push(fixedTypebot)
totalFixed += 1
diffs.push({
id: typebot.id,
failedObject: resolve(
parser.error.issues[0].path.join('.'),
fixedTypebot
),
...detailedDiff(typebot, fixedTypebot),
})
}
}
writeFileSync('logs/fixedTypebots.json', JSON.stringify(fixedTypebots))
writeFileSync(
'logs/diffs.json',
JSON.stringify(diffs.reverse().slice(0, 100))
)
}

// export const parseZodError = (parser: any) => {
// if ('error' in parser) {
// console.log(
// parser.error.issues.map((issue) =>
// JSON.stringify({
// message: issue.message,
// path: issue.path,
// })
// )
// )
// writeFileSync(
// 'failedObject.json',
// JSON.stringify(
// resolve(parser.error.issues[0].path.join('.'), fixedTypebot)
// )
// )
// writeFileSync('failedTypebot.json', JSON.stringify(fixedTypebot))
// writeFileSync('issue.json', JSON.stringify(parser.error.issues))
// exit()
// }
// }

fixTypebots()
4 changes: 3 additions & 1 deletion packages/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
"db:backup": "tsx backupDatabase.ts",
"db:restore": "tsx restoreDatabase.ts",
"db:setCustomPlan": "tsx setCustomPlan.ts",
"db:bulkUpdate": "tsx bulkUpdate.ts"
"db:bulkUpdate": "tsx bulkUpdate.ts",
"db:fixTypebots": "tsx fixTypebots.ts"
},
"devDependencies": {
"@types/node": "18.11.17",
"@types/prompts": "^2.4.2",
"db": "workspace:*",
"deep-object-diff": "1.1.9",
"emails": "workspace:*",
"got": "12.5.3",
"models": "workspace:*",
Expand Down
3 changes: 2 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 comments on commit ad72557

@vercel
Copy link

@vercel vercel bot commented on ad72557 Dec 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on ad72557 Dec 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on ad72557 Dec 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs-typebot-io.vercel.app
docs.typebot.io

@vercel
Copy link

@vercel vercel bot commented on ad72557 Dec 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on ad72557 Dec 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2-alpha – ./apps/viewer

pant.maxbot.com.br
positivobra.com.br
survey.digienge.io
this-is-a-test.com
zap.techadviser.in
bot.digitalbled.com
bot.eventhub.com.au
bot.jepierre.com.br
bot.winglabs.com.br
carsalesenquiry.com
chatbot.repplai.com
demo.botscientis.us
forms.webisharp.com
kbsub.wpwakanda.com
live.botscientis.us
nutrisamirbayde.com
bot.truongnguyen.live
cdd.searchcube.com.sg
chat.missarkansas.org
chatbot.ownacademy.co
sbutton.wpwakanda.com
815639944.21000000.one
aplicacao.bmind.com.br
apply.ansuraniphone.my
bbutton.wpwwakanda.com
bot.louismarcondes.com
bot.t20worldcup.com.au
c23111azqw.nigerias.io
felipewelington.com.br
form.searchcube.com.sg
gcase.barrettamario.it
help.giversforgood.com
info.clickasuransi.com
kodawariab736.skeep.it
my.swamprecordsgnv.com
premium.kandabrand.com
report.gratirabbit.com
resume.gratirabbit.com
83242573.actualizar.xyz
bot.blackboxtips.com.br
bot.upgradesolutions.eu
help.comebackreward.com
mainmenu.diddancing.com
register.kandabrand.com
signup.hypemarketing.in
subfooter.wpwakanda.com
survey.hypemarketing.in
testbot.afterorigin.com
91181264.your-access.one
ai.chromebookstoreph.com
form.sergiolimajr.com.br
hunterbot.saleshunter.ai
link.cascadigital.com.br
onboarding.growthside.io
reward.onlinebotdemo.xyz
type.opaulovieira.com.br
aibot.angrybranding.co.uk
bot.aidigitalmarketing.kr
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
bot.fusionstarreviews.com
boyfriend-breakup.riku.ai
brigadeirosemdrama.com.br
chat.ertcrebateportal.com
chat.thisiscrushhouse.com
sellmyharleylouisiana.com
verfica.botmachine.com.br
configurator.bouclidom.com
help.atlasoutfittersk9.com
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
piazzatorre.barrettamario.it
requests.swamprecordsgnv.com
type.cookieacademyonline.com
bot.brigadeirosemdrama.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
yourfeedback.comebackreward.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
viewer-v2-alpha-typebot-io.vercel.app
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
viewer-v2-alpha-git-main-typebot-io.vercel.app

Please sign in to comment.