diff --git a/packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx b/packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx index 385d5d52d..e8afc9d91 100644 --- a/packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx +++ b/packages/emails/src/emails/AlmostReachedChatsLimitEmail.tsx @@ -13,6 +13,7 @@ import { SendMailOptions } from 'nodemailer' import { sendEmail } from '../sendEmail' type AlmostReachedChatsLimitEmailProps = { + usagePercent: number chatsLimit: number url: string } @@ -26,6 +27,7 @@ const readableResetDate = firstDayOfNextMonth .join(' ') export const AlmostReachedChatsLimitEmail = ({ + usagePercent, chatsLimit, url, }: AlmostReachedChatsLimitEmailProps) => { @@ -45,7 +47,8 @@ export const AlmostReachedChatsLimitEmail = ({ Your bots are chatting a lot. That's amazing. 💙 This means you've almost reached your monthly chats limit. - You currently reached 80% of {readableChatsLimit} chats. + You currently reached {usagePercent}% of {readableChatsLimit}{' '} + chats. This limit will be reset on {readableResetDate}. diff --git a/packages/emails/src/emails/AlmostReachedStorageLimitEmail.tsx b/packages/emails/src/emails/AlmostReachedStorageLimitEmail.tsx deleted file mode 100644 index e3394a16f..000000000 --- a/packages/emails/src/emails/AlmostReachedStorageLimitEmail.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { ComponentProps } from 'react' -import { - Mjml, - MjmlBody, - MjmlSection, - MjmlColumn, - MjmlSpacer, -} from '@faire/mjml-react' -import { render } from '@faire/mjml-react/utils/render' -import { Button, Head, HeroImage, Text } from '../components' -import { SendMailOptions } from 'nodemailer' -import { sendEmail } from '../sendEmail' - -type AlmostReachedStorageLimitEmailProps = { - storageLimit: number - url: string -} - -export const AlmostReachedStorageLimitEmail = ({ - storageLimit, - url, -}: AlmostReachedStorageLimitEmailProps) => { - const readableStorageLimit = `${storageLimit} GB` - - return ( - - - - - - - - - - - Your bots are working a lot. That's amazing. 🤖 - - This means you've almost reached your storage limit. You - currently reached 80% of your {readableStorageLimit} storage - limit. - - - Upon this limit your bots will still continue to collect new - files, but we ask you kindly to upgrade your storage limit or - delete existing results to free up space. - - - - - - - - ) -} - -export const sendAlmostReachedStorageLimitEmail = ({ - to, - ...props -}: Pick & - ComponentProps) => - sendEmail({ - to, - subject: "You're close to your storage limit", - html: render().html, - }) diff --git a/packages/emails/src/emails/ReachedChatsLimitEmail.tsx b/packages/emails/src/emails/ReachedChatsLimitEmail.tsx deleted file mode 100644 index 7986f2ebc..000000000 --- a/packages/emails/src/emails/ReachedChatsLimitEmail.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { ComponentProps } from 'react' -import { - Mjml, - MjmlBody, - MjmlSection, - MjmlColumn, - MjmlSpacer, -} from '@faire/mjml-react' -import { render } from '@faire/mjml-react/utils/render' -import { Button, Head, HeroImage, Text } from '../components' -import { parseNumberWithCommas } from '@typebot.io/lib' -import { SendMailOptions } from 'nodemailer' -import { sendEmail } from '../sendEmail' - -type ReachedChatsLimitEmailProps = { - chatsLimit: number - url: string -} - -export const ReachedChatsLimitEmail = ({ - chatsLimit, - url, -}: ReachedChatsLimitEmailProps) => { - const readableChatsLimit = parseNumberWithCommas(chatsLimit) - - return ( - - - - - - - - - - - - It just happened, you've reached your monthly{' '} - {readableChatsLimit} chats limit 😮 - - - If you'd like your bots to continue chatting with your users - this month, then you need to upgrade your plan. 🚀 - - - - - - - - - ) -} - -export const sendReachedChatsLimitEmail = ({ - to, - ...props -}: Pick & - ComponentProps) => - sendEmail({ - to, - subject: "You've reached your chats limit", - html: render().html, - }) diff --git a/packages/emails/src/emails/ReachedStorageLimitEmail.tsx b/packages/emails/src/emails/ReachedStorageLimitEmail.tsx deleted file mode 100644 index 0872725d1..000000000 --- a/packages/emails/src/emails/ReachedStorageLimitEmail.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React, { ComponentProps } from 'react' -import { - Mjml, - MjmlBody, - MjmlSection, - MjmlColumn, - MjmlSpacer, -} from '@faire/mjml-react' -import { render } from '@faire/mjml-react/utils/render' -import { Button, Head, HeroImage, Text } from '../components' -import { SendMailOptions } from 'nodemailer' -import { sendEmail } from '../sendEmail' - -type ReachedStorageLimitEmailProps = { - storageLimit: number - url: string -} - -export const ReachedStorageLimitEmail = ({ - storageLimit, - url, -}: ReachedStorageLimitEmailProps) => { - const readableStorageLimit = `${storageLimit} GB` - - return ( - - - - - - - - - - - - It just happened, you've reached your {readableStorageLimit}{' '} - storage limit 😮 - - - If you'd like to continue collecting files, then you need to - upgrade your plan or remove existing results to free up space. 🚀 - - - - - - - - - ) -} - -export const sendReachedStorageLimitEmail = ({ - to, - ...props -}: Pick & - ComponentProps) => - sendEmail({ - to, - subject: "You've reached your storage limit", - html: render().html, - }) diff --git a/packages/emails/src/emails/index.ts b/packages/emails/src/emails/index.ts index b2a863cb8..29742a26d 100644 --- a/packages/emails/src/emails/index.ts +++ b/packages/emails/src/emails/index.ts @@ -1,8 +1,5 @@ export * from './AlmostReachedChatsLimitEmail' -export * from './AlmostReachedStorageLimitEmail' export * from './DefaultBotNotificationEmail' export * from './GuestInvitationEmail' -export * from './ReachedChatsLimitEmail' -export * from './ReachedStorageLimitEmail' export * from './WorkspaceMemberInvitationEmail' export * from './MagicLinkEmail' diff --git a/packages/emails/src/preview.tsx b/packages/emails/src/preview.tsx index a09126107..36704c411 100644 --- a/packages/emails/src/preview.tsx +++ b/packages/emails/src/preview.tsx @@ -3,11 +3,8 @@ import fs from 'fs' import path from 'path' import { AlmostReachedChatsLimitEmail, - AlmostReachedStorageLimitEmail, DefaultBotNotificationEmail, GuestInvitationEmail, - ReachedChatsLimitEmail, - ReachedStorageLimitEmail, WorkspaceMemberInvitation, } from './emails' import { MagicLinkEmail } from './emails/MagicLinkEmail' @@ -47,38 +44,12 @@ const createHtmlFile = () => { path.resolve(__dirname, 'dist', 'almostReachedChatsLimit.html'), render( ).html ) - fs.writeFileSync( - path.resolve(__dirname, 'dist', 'almostReachedStorageLimit.html'), - render( - - ).html - ) - fs.writeFileSync( - path.resolve(__dirname, 'dist', 'reachedChatsLimit.html'), - render( - - ).html - ) - fs.writeFileSync( - path.resolve(__dirname, 'dist', 'reachedStorageLimit.html'), - render( - - ).html - ) fs.writeFileSync( path.resolve(__dirname, 'dist', 'defaultBotNotification.html'), render( diff --git a/packages/scripts/cleanDatabase.ts b/packages/scripts/cleanDatabase.ts index 7d57ecd89..f8be14450 100644 --- a/packages/scripts/cleanDatabase.ts +++ b/packages/scripts/cleanDatabase.ts @@ -14,6 +14,7 @@ export const cleanDatabase = async () => { if (isFirstOfMonth) { await deleteArchivedResults() await deleteArchivedTypebots() + await resetQuarantinedWorkspaces() } console.log('Done!') } @@ -118,4 +119,14 @@ const deleteExpiredVerificationTokens = async () => { console.log(`Deleted ${count} expired verifiations tokens.`) } +const resetQuarantinedWorkspaces = async () => + prisma.workspace.updateMany({ + where: { + isQuarantined: true, + }, + data: { + isQuarantined: false, + }, + }) + cleanDatabase().then() diff --git a/packages/scripts/sendTotalResultsDigest.ts b/packages/scripts/sendTotalResultsDigest.ts index b6d057871..bda3583b7 100644 --- a/packages/scripts/sendTotalResultsDigest.ts +++ b/packages/scripts/sendTotalResultsDigest.ts @@ -11,6 +11,22 @@ import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEven import { Workspace } from '@typebot.io/schemas' const prisma = new PrismaClient() +const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8 + +type WorkspaceForDigest = Pick< + Workspace, + | 'id' + | 'plan' + | 'customChatsLimit' + | 'customStorageLimit' + | 'additionalChatsIndex' + | 'additionalStorageIndex' + | 'isQuarantined' +> & { + members: (Pick & { + user: { id: string; email: string | null } + })[] +} export const sendTotalResultsDigest = async () => { await promptAndSetEnvironment('production') @@ -53,12 +69,15 @@ export const sendTotalResultsDigest = async () => { select: { id: true, typebots: { select: { id: true } }, - members: { select: { userId: true, role: true } }, + members: { + select: { user: { select: { id: true, email: true } }, role: true }, + }, additionalChatsIndex: true, additionalStorageIndex: true, customChatsLimit: true, customStorageLimit: true, plan: true, + isQuarantined: true, }, }) @@ -71,7 +90,7 @@ export const sendTotalResultsDigest = async () => { return workspace.members .filter((member) => member.role !== WorkspaceRole.GUEST) .map((member, memberIndex) => ({ - userId: member.userId, + userId: member.user.id, workspace: workspace, typebotId: result.typebotId, totalResultsYesterday: result._count._all, @@ -115,20 +134,13 @@ export const sendTotalResultsDigest = async () => { } const sendAlertIfLimitReached = async ( - workspaces: (Pick< - Workspace, - | 'id' - | 'plan' - | 'customChatsLimit' - | 'customStorageLimit' - | 'additionalChatsIndex' - | 'additionalStorageIndex' - > & { members: Pick[] })[] + workspaces: WorkspaceForDigest[] ): Promise => { const events: TelemetryEvent[] = [] const taggedWorkspaces: string[] = [] for (const workspace of workspaces) { - if (taggedWorkspaces.includes(workspace.id)) continue + if (taggedWorkspaces.includes(workspace.id) || workspace.isQuarantined) + continue taggedWorkspaces.push(workspace.id) const { totalChatsUsed, totalStorageUsed } = await getUsage(workspace.id) const totalStorageUsedInGb = totalStorageUsed / 1024 / 1024 / 1024 @@ -145,7 +157,7 @@ const sendAlertIfLimitReached = async ( (member) => ({ name: 'Workspace limit reached', - userId: member.userId, + userId: member.user.id, workspaceId: workspace.id, data: { totalChatsUsed, @@ -156,7 +168,20 @@ const sendAlertIfLimitReached = async ( } satisfies TelemetryEvent) ) ) + continue } + // if ( + // chatsLimit > 0 && + // totalChatsUsed >= chatsLimit * LIMIT_EMAIL_TRIGGER_PERCENT + // ) + // await sendAlmostReachedChatsLimitEmail({ + // to: workspace.members + // .map((member) => member.user.email) + // .filter(isDefined), + // usagePercent: Math.round((totalChatsUsed / chatsLimit) * 100), + // chatsLimit, + // url: `https://app.typebot.io/typebots?workspaceId=${workspace.id}`, + // }) } return events }