Skip to content

Commit 7fd4b33

Browse files
committed
🔧 Refactor email campaign script
1 parent e1f7565 commit 7fd4b33

File tree

5 files changed

+63
-56
lines changed

5 files changed

+63
-56
lines changed

apps/viewer/src/pages/old/[[...publicId]].tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as Sentry from "@sentry/nextjs";
12
import { isDefined, isNotDefined, omit } from "@typebot.io/lib/utils";
23
import prisma from "@typebot.io/prisma";
34
import type { IncomingMessage } from "http";
@@ -44,10 +45,16 @@ const getTypebotFromPublicId = async (
4445
const publishedTypebot = await prisma.publicTypebot.findFirst({
4546
where: { typebot: { publicId: publicId ?? "" } },
4647
include: {
47-
typebot: { select: { name: true, isClosed: true, isArchived: true } },
48+
typebot: {
49+
select: { name: true, isClosed: true, isArchived: true },
50+
},
4851
},
4952
});
5053
if (isNotDefined(publishedTypebot)) return null;
54+
if (!publishedTypebot.version) {
55+
Sentry.setTag("typebotId", publishedTypebot?.typebotId);
56+
Sentry.captureMessage("Is using TypebotPageV2");
57+
}
5158
return omit(
5259
publishedTypebot,
5360
"createdAt",

packages/scripts/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"name": "@typebot.io/scripts",
33
"scripts": {
4-
"playground": "SKIP_ENV_CHECK=true dotenv -e ./.env.local -- tsx src/playground.ts",
4+
"start": "SKIP_ENV_CHECK=true dotenv -e ./.env.local -- tsx src/main.ts",
5+
"start:prod": "cd ../prisma && bun db:generate:mysql && cd ../scripts && SKIP_ENV_CHECK=true dotenv -e ./.env.production -- tsx src/main.ts",
56
"db:backup": "tsx src/backupDatabase.ts",
67
"db:restore": "tsx src/restoreDatabase.ts",
78
"db:setCustomPlan": "tsx src/setCustomPlan.ts",

packages/scripts/src/main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { sendEmailCampaign } from "./sendEmailCampaign";
2+
3+
(async () => {
4+
await sendEmailCampaign();
5+
})();

packages/scripts/src/playground.ts

Lines changed: 0 additions & 9 deletions
This file was deleted.

packages/scripts/src/sendEmailCampaign.ts

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,55 +38,30 @@ Please republish before ${DEADLINE} to make sure your bots keep running smoothly
3838
3939
Thanks for keeping your bots up to date! 🧡`;
4040

41-
const DRY_RUN = process.env.DRY_RUN === "1";
41+
const DRY_RUN = false;
4242
const CONCURRENCY = Number(process.env.CONCURRENCY ?? 5); // email send concurrency
4343
const MAX_RETRIES = 3;
4444

45-
async function parseCsv(filePath: string): Promise<string[]> {
46-
const raw = await fs.readFile(filePath, "utf8");
47-
const parsed = Papa.parse<CsvRow>(raw, {
48-
header: true,
49-
skipEmptyLines: true,
50-
});
51-
52-
if (parsed.errors.length) {
53-
const preview = parsed.errors
54-
.slice(0, 3)
55-
.map((e) => `${e.type}: ${e.message} @ row ${e.row}`)
56-
.join(" | ");
57-
console.warn(
58-
`[CSV] Encountered ${parsed.errors.length} parse errors. First: ${preview}`,
59-
);
60-
}
61-
62-
const ids = new Set<string>();
63-
for (const row of parsed.data) {
64-
const safe = CSV_SCHEMA.safeParse(row);
65-
if (!safe.success) {
66-
console.warn(
67-
"[CSV] Skipping invalid row:",
68-
row,
69-
safe.error.flatten().fieldErrors,
70-
);
71-
continue;
72-
}
73-
ids.add(safe.data.typebotId.trim());
74-
}
75-
return Array.from(ids);
76-
}
77-
78-
async function main() {
79-
const typebotIds = await parseCsv("./inputs/typebotIds.csv");
45+
export async function sendEmailCampaign() {
46+
const newTypebotIds = await parseCsv("./inputs/new.csv");
47+
const existingTypebotIds = await parseCsv("./inputs/existing.csv");
48+
const typebotIds = newTypebotIds.filter(
49+
(id) => !existingTypebotIds.includes(id),
50+
);
8051
if (typebotIds.length === 0) {
8152
console.log("No valid typebot IDs found in CSV. Exiting.");
8253
return;
8354
}
84-
console.log(`Found ${typebotIds.length} unique typebot IDs.`);
55+
console.log(`Extracted ${typebotIds.length} typebot IDs from CSV.`);
8556

86-
const typebots = await prisma.typebot.findMany({
87-
where: { id: { in: typebotIds } },
88-
select: { id: true, workspaceId: true },
89-
});
57+
const typebots = (
58+
await prisma.typebot.findMany({
59+
where: { id: { in: typebotIds } },
60+
select: { id: true, workspaceId: true, version: true },
61+
})
62+
)?.filter((typebot) => !typebot.version);
63+
64+
console.log(`Found ${typebots.length} typebots that need to be republished.`);
9065

9166
if (typebots.length === 0) {
9267
throw new Error("None of the provided typebot IDs exist.");
@@ -185,7 +160,35 @@ async function main() {
185160
}
186161
}
187162

188-
main().catch((err) => {
189-
console.error("Unhandled error:", err);
190-
process.exitCode = 1;
191-
});
163+
async function parseCsv(filePath: string): Promise<string[]> {
164+
const raw = await fs.readFile(filePath, "utf8");
165+
const parsed = Papa.parse<CsvRow>(raw, {
166+
header: true,
167+
skipEmptyLines: true,
168+
});
169+
170+
if (parsed.errors.length) {
171+
const preview = parsed.errors
172+
.slice(0, 3)
173+
.map((e) => `${e.type}: ${e.message} @ row ${e.row}`)
174+
.join(" | ");
175+
console.warn(
176+
`[CSV] Encountered ${parsed.errors.length} parse errors. First: ${preview}`,
177+
);
178+
}
179+
180+
const ids = new Set<string>();
181+
for (const row of parsed.data) {
182+
const safe = CSV_SCHEMA.safeParse(row);
183+
if (!safe.success) {
184+
console.warn(
185+
"[CSV] Skipping invalid row:",
186+
row,
187+
safe.error.flatten().fieldErrors,
188+
);
189+
continue;
190+
}
191+
ids.add(safe.data.typebotId.trim());
192+
}
193+
return Array.from(ids);
194+
}

0 commit comments

Comments
 (0)