Files
calendar/packages/features/tasker/tasks/scanWorkflowBody.ts
T
GiridharandGitHub 381c462660 feat: email workflow templates translation (#20674)
* Add translations in respective files

* implement multilingual support for email templates

* fix: indentation in common.js & typos in common.js and update.handler.ts
2025-04-15 23:56:34 +05:30

155 lines
4.1 KiB
TypeScript

import { AkismetClient } from "akismet-api";
import type { Comment } from "akismet-api";
import z from "zod";
import { getTemplateBodyForAction } from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
import compareReminderBodyToTemplate from "@calcom/features/ee/workflows/lib/compareReminderBodyToTemplate";
import { lockUser, LockReason } from "@calcom/lib/autoLock";
import { WEBAPP_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import { getTranslation } from "@calcom/lib/server/i18n";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
import prisma from "@calcom/prisma";
import { scheduleWorkflowNotifications } from "@calcom/trpc/server/routers/viewer/workflows/util";
export const scanWorkflowBodySchema = z.object({
userId: z.number(),
workflowStepIds: z.array(z.number()),
});
const log = logger.getSubLogger({ prefix: ["[tasker] scanWorkflowBody"] });
export async function scanWorkflowBody(payload: string) {
if (!process.env.AKISMET_API_KEY) {
log.info("AKISMET_API_KEY not set, skipping scan");
return;
}
const { workflowStepIds, userId } = scanWorkflowBodySchema.parse(JSON.parse(payload));
const workflowSteps = await prisma.workflowStep.findMany({
where: {
id: {
in: workflowStepIds,
},
},
include: {
workflow: {
select: {
user: {
select: {
locale: true,
timeFormat: true,
},
},
},
},
},
});
const client = new AkismetClient({ key: process.env.AKISMET_API_KEY, blog: WEBAPP_URL });
for (const workflowStep of workflowSteps) {
if (!workflowStep.reminderBody) {
await prisma.workflowStep.update({
where: {
id: workflowStep.id,
},
data: {
verifiedAt: new Date(),
},
});
continue;
}
const timeFormat = getTimeFormatStringFromUserTimeFormat(workflowStep.workflow.user?.timeFormat);
// Determine if body is a template
const defaultTemplate = getTemplateBodyForAction({
action: workflowStep.action,
locale: workflowStep.workflow.user?.locale ?? "en",
t: await getTranslation(workflowStep.workflow.user?.locale ?? "en", "common"),
template: workflowStep.template,
timeFormat,
});
if (!defaultTemplate) {
log.error(`Template not found for action ${workflowStep.action}, template ${workflowStep.template}`);
continue;
}
if (
compareReminderBodyToTemplate({ reminderBody: workflowStep.reminderBody, template: defaultTemplate })
) {
await prisma.workflowStep.update({
where: {
id: workflowStep.id,
},
data: {
verifiedAt: new Date(),
},
});
continue;
}
const comment: Comment = {
user_ip: "127.0.0.1",
content: workflowStep.reminderBody,
};
const isSpam = await client.checkSpam(comment);
if (isSpam) {
// We won't delete the workflow step incase it is flagged as a false positive
log.warn(`Workflow step ${workflowStep.id} is spam with body ${workflowStep.reminderBody}`);
await lockUser("userId", userId.toString(), LockReason.SPAM_WORKFLOW_BODY);
// Return early if spam is detected
return;
} else {
await prisma.workflowStep.update({
where: {
id: workflowStep.id,
},
data: {
verifiedAt: new Date(),
},
});
}
}
const workflow = await prisma.workflow.findFirst({
where: {
steps: {
some: {
id: {
in: workflowStepIds,
},
},
},
},
include: {
activeOn: true,
team: true,
},
});
if (!workflow) {
log.warn(`Workflow with steps ${workflowStepIds} not found`);
return;
}
const isOrg = !!workflow?.team?.isOrganization;
await scheduleWorkflowNotifications({
activeOn: workflow.activeOn.map((activeOn) => activeOn.eventTypeId) ?? [],
isOrg,
workflowSteps,
time: workflow.time,
timeUnit: workflow.timeUnit,
trigger: workflow.trigger,
userId,
teamId: workflow.team?.id || null,
});
}