diff --git a/backend/controllers/followups.ts b/backend/controllers/followups.ts index d5e1c0a42e..4a5c486c41 100644 --- a/backend/controllers/followups.ts +++ b/backend/controllers/followups.ts @@ -13,6 +13,8 @@ import Request from "../types/express.d.js" import { phoneNumberValidation } from "../../lib/phone-number.js" import config from "../config/index.js" import { sendSimulationResultsEmail } from "../lib/messaging/email/email-service.js" +import { sendSimulationResultsSms } from "../lib/messaging/sms/sms-service.js" +import dayjs from "dayjs" export function followup( req: Request, @@ -68,7 +70,7 @@ export async function persist(req: Request, res: Response) { config.smsService.internationalDiallingCodes ) ) { - await followup.sendSimulationResultsSms() + await sendSimulationResultsSms(followup) } else { return res.status(422).send("Unsupported phone number format") } @@ -200,25 +202,27 @@ async function updateSurveyInFollowup(req: Request) { await followup.updateSurvey(SurveyType.TousABordNotification) break default: - throw new Error("Unknown survey type") + throw new Error(`Unknown survey type: ${surveyType}`) } } async function getRedirectUrl(req: Request) { const { surveyType } = req.params const { followup } = req - switch (surveyType) { case SurveyType.TrackClickOnSimulationUsefulnessEmail: case SurveyType.TrackClickOnBenefitActionEmail: + case SurveyType.TrackClickOnBenefitActionSms: { await followup.addSurveyIfMissing(SurveyType.BenefitAction) + const surveyOpened = await followup.addSurveyIfMissing(surveyType) + surveyOpened.openedAt = dayjs().toDate() await followup.save() - return followup.surveyPath + } case SurveyType.TousABordNotification: return "https://www.tadao.fr/713-Demandeur-d-emploi.html" default: - throw new Error("Unknown survey type") + throw new Error(`Unknown survey type: ${surveyType}`) } } @@ -233,3 +237,14 @@ export async function logSurveyLinkClick(req: Request, res: Response) { return res.sendStatus(404) } } + +export async function smsSurveyLinkClick(req: Request, res: Response) { + try { + req.params.surveyType = SurveyType.TrackClickOnBenefitActionSms + const redirectUrl = await getRedirectUrl(req) + res.redirect(redirectUrl) + } catch (error) { + console.error("error", error) + return res.sendStatus(404) + } +} diff --git a/backend/lib/messaging/sending.ts b/backend/lib/messaging/sending.ts index 0f23bf510b..4be1b94aff 100644 --- a/backend/lib/messaging/sending.ts +++ b/backend/lib/messaging/sending.ts @@ -1,6 +1,6 @@ import dayjs from "dayjs" -import { EmailType } from "../../../lib/enums/messaging.js" +import { EmailType, SmsType } from "../../../lib/enums/messaging.js" import { SurveyType } from "../../../lib/enums/survey.js" import Followups from "../../models/followup.js" import { Followup } from "../../../lib/types/followup.js" @@ -8,8 +8,14 @@ import { sendSimulationResultsEmail, sendSurveyEmail, } from "../messaging/email/email-service.js" +import { + sendSimulationResultsSms, + sendSurveyBySms, +} from "../messaging/sms/sms-service.js" +import { Survey } from "../../../lib/types/survey.js" -const DaysBeforeInitialEmail = 6 +const DaysBeforeInitialSurvey = 6 +const DelayAfterInitialSurveyEmail = 3 async function sendMultipleEmails(emailType: EmailType, limit: number) { switch (emailType) { @@ -37,7 +43,7 @@ async function sendMultipleInitialEmails(limit: number) { }, }, sentAt: { - $lt: dayjs().subtract(DaysBeforeInitialEmail, "day").toDate(), + $lt: dayjs().subtract(DaysBeforeInitialSurvey, "day").toDate(), }, surveyOptin: true, }) @@ -95,3 +101,122 @@ export async function processSendEmails( throw new Error("Missing followup id or multiple") } } + +function getEmailSurvey(followup: Followup): Survey | undefined { + return followup.surveys.find((survey) => + [ + SurveyType.TrackClickOnBenefitActionEmail, + SurveyType.TrackClickOnSimulationUsefulnessEmail, + ].includes(survey.type) + ) +} + +export function shouldSendSurveyBySms(followup: Followup): boolean { + const hasPhone = !!followup.phone + const hasEmail = !!followup.email + const emailSurvey = getEmailSurvey(followup) + + if (hasPhone && !hasEmail) { + return true + } + + if (hasPhone && hasEmail && emailSurvey && emailSurvey.answers.length === 0) { + const surveyEmailCreatedAtWithDelay = dayjs(emailSurvey.createdAt).add( + DelayAfterInitialSurveyEmail, + "day" + ) + return dayjs().isAfter(surveyEmailCreatedAtWithDelay) + } + + return false +} + +function initialSurveySmsMongooseCriteria(): any { + const getDaysBeforeInitialSurveyDate = (): Date => + dayjs().subtract(DaysBeforeInitialSurvey, "day").toDate() + return { + phone: { + $exists: true, + }, + surveys: { + $not: { + $elemMatch: { + type: { + $in: [ + SurveyType.BenefitAction, // TODO: + // - remove this line 10 days after this comment added in production + // - add a condition on createdAt : { $gt: "this comment added in production date" } + SurveyType.TrackClickOnBenefitActionSms, + ], + }, + }, + }, + }, + smsSentAt: { + $lt: getDaysBeforeInitialSurveyDate(), + }, + surveyOptin: true, + } +} + +export async function filterInitialSurveySms( + followups: any[], + limit: number +): Promise { + return followups + .slice(0, limit) + .filter((followup) => shouldSendSurveyBySms(followup)) +} + +export async function sendMultipleInitialSms(limit: number) { + const mongooseFollowups = await Followups.find( + initialSurveySmsMongooseCriteria() + ).sort({ createdAt: 1 }) + const followupsToSendSms: any[] = await filterInitialSurveySms( + mongooseFollowups, + limit + ) + + const results = await Promise.all( + followupsToSendSms.map(async (followup: Followup) => { + try { + const survey = await sendSurveyBySms(followup) + return { "Survey sent": survey._id } + } catch (error) { + return { ko: error } + } + }) + ) + console.log(results) +} + +async function processSingleSms(smsType: SmsType, followupId: string) { + const followup: Followup | null = await Followups.findById(followupId) + if (!followup) { + throw new Error("Followup not found") + } + switch (smsType) { + case SmsType.SimulationResults: + await sendSimulationResultsSms(followup) + break + case SmsType.InitialSurvey: + await sendSurveyBySms(followup) + break + default: + throw new Error(`Unknown sms category: ${SmsType}`) + } +} + +export async function processSendSms( + SmsType: SmsType, + followupId: string, + multiple: number | null +) { + if (followupId) { + await processSingleSms(SmsType, followupId) + } else if (multiple) { + await sendMultipleInitialSms(multiple) + } else { + throw new Error("Missing followup id or multiple") + } +} diff --git a/backend/lib/messaging/sms/sms-service.ts b/backend/lib/messaging/sms/sms-service.ts new file mode 100644 index 0000000000..9aabeac480 --- /dev/null +++ b/backend/lib/messaging/sms/sms-service.ts @@ -0,0 +1,120 @@ +import axios from "axios" +import config from "../../../config/index.js" +import { phoneNumberFormatting } from "./../../../../lib/phone-number.js" +import { SmsType } from "../../../../lib/enums/messaging.js" +import { Followup } from "../../../../lib/types/followup.js" +import { Survey } from "../../../../lib/types/survey.d.js" +import { SurveyType } from "../../../../lib/enums/survey.js" +import dayjs from "dayjs" + +async function getSMSConfig() { + const { username, password } = config.smsService + + if (!username || !password) { + throw new Error("Missing SMS service credentials") + } + + return { username, password } +} + +async function createAxiosInstance() { + const instance = axios.create({ + timeout: 10000, + }) + instance.interceptors.response.use( + (response) => { + const { data, status } = response + if (status !== 200 || data.responseCode !== 0) { + throw new Error(`SMS request failed. Body: ${JSON.stringify(data)}`) + } + return response + }, + (error) => { + throw error + } + ) + + return instance +} + +function buildSmsUrl({ accessToken, phone, username, password, smsType }) { + const { baseURL } = config + const { url } = config.smsService + const formattedPhone = phoneNumberFormatting( + phone, + config.smsService.internationalDiallingCodes + ) + + let text, surveyLink + switch (smsType) { + case SmsType.SimulationResults: + text = `Bonjour\nRetrouvez les résultats de votre simulation ici ${baseURL}/api/sms/${accessToken}\n1jeune1solution` + break + case SmsType.InitialSurvey: + surveyLink = `${baseURL}/api/r/${accessToken}` + text = `Bonjour\nVotre simulation sur 1jeune1solution.gouv.fr vous a-t-elle été utile?\nVoici un court sondage : ${surveyLink}\n1jeune1solution` + break + default: + throw new Error(`Unknown SMS type: ${smsType}`) + } + + const encodedText = encodeURIComponent(text) + return `${url}?&originatorTON=1&originatingAddress=SIMUL 1J1S&destinationAddress=${formattedPhone}&messageText=${encodedText}&username=${username}&password=${password}` +} + +export async function sendSimulationResultsSms( + followup: Followup +): Promise { + try { + if (!followup.phone) { + throw new Error("Missing followup phone") + } + + const { username, password } = await getSMSConfig() + const { phone, accessToken } = followup + const smsUrl = buildSmsUrl({ + accessToken, + phone, + username, + password, + smsType: SmsType.SimulationResults, + }) + const axiosInstance = await createAxiosInstance() + const { data } = await axiosInstance.get(smsUrl) + followup.smsError = undefined + if (!followup.surveyOptin) { + followup.phone = undefined + } + followup.smsSentAt = dayjs().toDate() + followup.smsMessageId = data.messageIds[0] + return await followup.save() + } catch (err) { + followup.smsError = JSON.stringify(err, null, 2) + throw err + } +} + +export async function sendSurveyBySms(followup: Followup): Promise { + if (!followup.phone) { + throw new Error("Missing followup phone") + } + const survey = await followup.addSurveyIfMissing( + SurveyType.TrackClickOnBenefitActionSms + ) + const { username, password } = await getSMSConfig() + const { phone, accessToken } = followup + const smsUrl = buildSmsUrl({ + accessToken, + phone, + username, + password, + smsType: SmsType.InitialSurvey, + }) + const axiosInstance = await createAxiosInstance() + const { data } = await axiosInstance.get(smsUrl) + survey.messageId = data.messageIds[0] + survey.error = undefined + survey.smsSentAt = dayjs().toDate() + await followup.save() + return survey +} diff --git a/backend/models/followup-schema.ts b/backend/models/followup-schema.ts index 569eef5d7c..160f417582 100644 --- a/backend/models/followup-schema.ts +++ b/backend/models/followup-schema.ts @@ -28,10 +28,9 @@ const FollowupSchema = new mongoose.Schema( }, createdAt: { type: Date, default: Date.now }, sentAt: { type: Date }, - smsSentAt: { type: Date }, messageId: { type: String }, smsMessageId: { type: String }, - surveySentAt: { type: Date }, + smsSentAt: { type: Date }, benefits: { type: Object }, surveyOptin: { type: Boolean, default: false }, surveys: { diff --git a/backend/models/followup.ts b/backend/models/followup.ts index 2877224404..14f8efb9c1 100644 --- a/backend/models/followup.ts +++ b/backend/models/followup.ts @@ -1,67 +1,14 @@ import mongoose from "mongoose" -import axios from "axios" import { Survey } from "../../lib/types/survey.js" import { SurveyType } from "../../lib/enums/survey.js" -import config from "../config/index.js" import { Followup } from "../../lib/types/followup.d.js" import { FollowupModel } from "../types/models.d.js" -import { phoneNumberFormatting } from "../../lib/phone-number.js" import FollowupSchema from "./followup-schema.js" FollowupSchema.static("findByEmail", function (email: string) { return this.find({ email }) }) -FollowupSchema.method("postSimulationResultsSms", function (messageId) { - this.smsSentAt = Date.now() - this.smsMessageId = messageId - if (!this.surveyOptin) { - this.phone = undefined - } - this.smsError = undefined - return this.save() -}) - -FollowupSchema.method( - "renderSimulationResultsSmsUrl", - function (username: string, password: string) { - const { baseURL } = config - const { url } = config.smsService - const { accessToken, phone } = this - const formattedPhone = phoneNumberFormatting( - phone, - config.smsService.internationalDiallingCodes - ) - - const text = `Bonjour\nRetrouvez les résultats de votre simulation ici ${baseURL}/api/sms/${accessToken}\n1jeune1solution` - const encodedText = encodeURIComponent(text) - - return `${url}?&originatorTON=1&originatingAddress=SIMUL 1J1S&destinationAddress=${formattedPhone}&messageText=${encodedText}&username=${username}&password=${password}` - } -) - -FollowupSchema.method("sendSimulationResultsSms", async function () { - try { - const username = config.smsService.username - const password = config.smsService.password - if (!username || !password) { - throw new Error("Missing SMS service credentials") - } - const renderUrl = this.renderSimulationResultsSmsUrl(username, password) - const axiosInstance = axios.create({ - timeout: 10000, - }) - const { data, status } = await axiosInstance.get(renderUrl) - if (status !== 200 || data.responseCode !== 0) { - throw new Error(`SMS request failed. Body: ${JSON.stringify(data)}`) - } - return this.postSimulationResultsSms(data.messageIds[0]) - } catch (err) { - this.smsError = JSON.stringify(err, null, 2) - throw err - } -}) - FollowupSchema.method( "addSurveyIfMissing", async function (surveyType: SurveyType): Promise { diff --git a/backend/models/survey-schema.ts b/backend/models/survey-schema.ts index f30d4ab4ce..5f7be770ca 100644 --- a/backend/models/survey-schema.ts +++ b/backend/models/survey-schema.ts @@ -9,6 +9,8 @@ const SurveySchema = new mongoose.Schema( createdAt: { type: Date, default: Date.now }, messageId: { type: String }, repliedAt: { type: Date }, + openedAt: { type: Date }, + smsSentAt: { type: Date }, error: { type: Object }, answers: [ { diff --git a/backend/routes/sms.ts b/backend/routes/sms.ts index a5bcc483b0..ccac1a3cae 100644 --- a/backend/routes/sms.ts +++ b/backend/routes/sms.ts @@ -1,4 +1,7 @@ -import { followupByAccessToken } from "../controllers/followups.js" +import { + followupByAccessToken, + smsSurveyLinkClick, +} from "../controllers/followups.js" import simulation from "../controllers/support.js" import simulationController from "../controllers/simulation.js" import { Express } from "express" @@ -8,5 +11,6 @@ export default function (api: Express) { const simulationId = req.followup?.simulation._id.toString() simulationController.simulation(req, res, next, simulationId) }, simulation) + api.route("/r/:accessToken").get(smsSurveyLinkClick) // r = redirection (sms needs short message length) api.param("accessToken", followupByAccessToken) } diff --git a/lib/enums/messaging.ts b/lib/enums/messaging.ts index fdb4f4d092..57b72cc997 100644 --- a/lib/enums/messaging.ts +++ b/lib/enums/messaging.ts @@ -4,3 +4,8 @@ export enum EmailType { SimulationUsefulness = "simulation-usefulness", InitialSurvey = "initial-survey", } + +export enum SmsType { + SimulationResults = "simulation-results", + InitialSurvey = "initial-survey", +} diff --git a/lib/enums/survey.ts b/lib/enums/survey.ts index ae3252d32b..5b39fb0fac 100644 --- a/lib/enums/survey.ts +++ b/lib/enums/survey.ts @@ -2,5 +2,6 @@ export enum SurveyType { BenefitAction = "benefit-action", TrackClickOnSimulationUsefulnessEmail = "track-click-on-simulation-usefulness-email", TrackClickOnBenefitActionEmail = "track-click-on-benefit-action-email", + TrackClickOnBenefitActionSms = "track-click-on-benefit-action-sms", TousABordNotification = "tous-a-bord-notification", } diff --git a/lib/types/followup.d.ts b/lib/types/followup.d.ts index b627904b62..006e9349c0 100644 --- a/lib/types/followup.d.ts +++ b/lib/types/followup.d.ts @@ -12,22 +12,20 @@ interface FollowupAttributes { smsSentAt: Date messageId: string smsMessageId: string - surveySentAt: Date + smsSentAt: Date benefits: any surveyOptin: boolean surveys: Survey[] version: number error: any smsError: any + smsSurveyError: any accessToken: string tousABordNotificationEmail: any __v: number } interface FollowupMethods { - postSimulationResultsSms(messageId: string): void - renderSimulationResultsSms(): any - sendSimulationResultsSms(): Promise addSurveyIfMissing(surveyType: SurveyType): Promise updateSurvey(action: SurveyType, data?: any) } diff --git a/lib/types/survey.d.ts b/lib/types/survey.d.ts index e518edaebe..a418d89cc8 100644 --- a/lib/types/survey.d.ts +++ b/lib/types/survey.d.ts @@ -21,6 +21,9 @@ export interface Survey { messageId?: string error?: string | any type: SurveyType + createdAt?: Date + openedAt?: Date + smsSentAt?: Date } interface FetchSurvey { diff --git a/package.json b/package.json index 5a87bb8bdb..702a2bb88c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "tools:institutions-logo-check": "ts-node ./tools/institutions-logo-check", "tools:serve-mail": "ts-node ./tools/mjml", "tools:send-initial-survey-email": "ts-node ./tools/email-sending-tool send initial-survey --multiple 1000", + "tools:send-initial-survey-sms": "ts-node ./tools/sms-sending-tool send initial-survey --multiple 2", "tools:test-benefits-geographical-constraint-consistency": "ts-node ./tools/test-benefits-geographical-constraint-consistency", "tools:test-definition-periods": "ts-node ./tools/test-definition-periods", "tools:test-iframe-generation": "./tools/iframe-build-control.sh", diff --git a/tests/unit/lib/state/messaging/messaging-sms.spec.ts b/tests/unit/lib/state/messaging/messaging-sms.spec.ts new file mode 100644 index 0000000000..da06385222 --- /dev/null +++ b/tests/unit/lib/state/messaging/messaging-sms.spec.ts @@ -0,0 +1,192 @@ +import { expect, jest } from "@jest/globals" +import { + shouldSendSurveyBySms, + filterInitialSurveySms, +} from "@backend/lib/messaging/sending.js" +import dayjs from "dayjs" +import utc from "dayjs/plugin/utc" +dayjs.extend(utc) + +describe("shouldSendSurveyBySms condition tests", () => { + let followup + beforeEach(() => { + followup = { + surveys: [], + phone: "0600000000", + email: "test@test.fr", + sentAt: dayjs("2023-11-17"), + smsSentAt: dayjs("2023-11-17"), + createdAt: dayjs("2023-11-17"), + } + jest.useFakeTimers().setSystemTime(new Date("2023-11-19")) + }) + + afterEach(() => { + jest.useRealTimers() + }) + + it("should not sent sms without phone field inside the followup", async () => { + followup.phone = undefined + expect(shouldSendSurveyBySms(followup)).toBe(false) + }) + + it("should sent sms without email field inside the followup", async () => { + followup.email = undefined + expect(shouldSendSurveyBySms(followup)).toBe(true) + }) + + it("should not send sms if followup has phone and already track-click-on-benefit-action-sms survey", async () => { + followup.surveys = [ + { + type: "track-click-on-benefit-action-sms", + createdAt: dayjs("2023-11-16"), + answers: [], + }, + ] + expect(shouldSendSurveyBySms(followup)).toBe(false) + }) + + it("should not send sms if followup has both phone and email sended 2 days ago without answers", async () => { + followup.surveys = [ + { + type: "track-click-on-simulation-usefulness-email", + createdAt: dayjs("2023-11-17"), + answers: [], + }, + ] + expect(shouldSendSurveyBySms(followup)).toBe(false) + }) + + it("should send sms if followup has both phone and email sended more than 3 days ago without answers", async () => { + followup.surveys = [ + { + type: "track-click-on-simulation-usefulness-email", + createdAt: dayjs("2023-11-16").subtract(1, "hour"), + answers: [], + }, + ] + expect(shouldSendSurveyBySms(followup)).toBe(true) + }) + + it("should not send sms if followup has both phone and email sended more than 3 days ago and email survey answers", async () => { + followup.surveys = [ + { + type: "track-click-on-simulation-usefulness-email", + createdAt: dayjs("2023-11-16").subtract(1, "hour"), + answers: [ + { + question: "mock-question", + value: "mock-value", + }, + ], + }, + ] + expect(shouldSendSurveyBySms(followup)).toBe(false) + }) +}) + +describe("filterInitialSurveySms tests", () => { + let mockFollowups + const limit = 10 + + beforeEach(() => { + jest.useFakeTimers().setSystemTime(new Date("2023-11-25")) + + mockFollowups = [ + { + // should not be included because it has already a sms survey + _id: "mock-0", + surveys: [ + { + type: "track-click-on-simulation-usefulness-sms", + createdAt: dayjs("2023-11-17").toDate(), + answers: [], + }, + ], + phone: "0600000000", + email: "test@test.fr", + createdAt: dayjs("2023-11-06").toDate(), + sentAt: dayjs("2023-11-14").toDate(), + smsSentAt: dayjs("2023-11-17").toDate(), + }, + { + // should be included because it has no sms survey and has a phone and respect the email delay + _id: "mock-1", + surveys: [ + { + type: "track-click-on-simulation-usefulness-email", + createdAt: dayjs("2023-11-21").toDate(), + answers: [], + }, + ], + phone: "0600000000", + email: "test@test.fr", + createdAt: dayjs("2023-11-15").toDate(), + sentAt: dayjs("2023-11-15").toDate(), + smsSentAt: dayjs("2023-11-15").toDate(), + }, + { + // should not be included because it has no sms survey and has a phone + // but does not respect the email delay + _id: "mock-2", + surveys: [], + phone: "0600000000", + email: "test@test.fr", + createdAt: dayjs("2023-11-21").toDate(), + sentAt: dayjs("2023-11-21").toDate(), + smsSentAt: dayjs("2023-11-24").toDate(), + }, + { + // should not be included because it has no phone + _id: "mock-3", + surveys: [], + email: "test@test.fr", + createdAt: dayjs("2023-11-17").toDate(), + sentAt: dayjs("2023-11-17").toDate(), + }, + { + // should be included because it has a phone, no sms survey and no email + _id: "mock-4", + surveys: [], + phone: "0600000000", + createdAt: dayjs("2023-11-17").toDate(), + sentAt: dayjs("2023-11-17").toDate(), + }, + ] + }) + + afterEach(() => { + jest.useRealTimers() + }) + + it("should not include followup with sms survey", async () => { + const result = await filterInitialSurveySms([mockFollowups[0]], limit) + expect(result).not.toContainEqual( + expect.objectContaining({ _id: "mock-0" }) + ) + }) + + it("should include followup with no sms survey and has a phone and respects the email delay", async () => { + const result = await filterInitialSurveySms([mockFollowups[1]], limit) + expect(result).toContainEqual(expect.objectContaining({ _id: "mock-1" })) + }) + + it("should not include followup with no sms survey and has a phone but does not respect the email delay", async () => { + const result = await filterInitialSurveySms([mockFollowups[2]], limit) + expect(result).not.toContainEqual( + expect.objectContaining({ _id: "mock-2" }) + ) + }) + + it("should not include followup without a phone", async () => { + const result = await filterInitialSurveySms([mockFollowups[3]], limit) + expect(result).not.toContainEqual( + expect.objectContaining({ _id: "mock-3" }) + ) + }) + + it("should include followup with a phone, no sms survey, and no email", async () => { + const result = await filterInitialSurveySms([mockFollowups[4]], limit) + expect(result).toContainEqual(expect.objectContaining({ _id: "mock-4" })) + }) +}) diff --git a/tools/sms-sending-tool.ts b/tools/sms-sending-tool.ts new file mode 100644 index 0000000000..4a0c2ac859 --- /dev/null +++ b/tools/sms-sending-tool.ts @@ -0,0 +1,62 @@ +import { ArgumentParser } from "argparse" +import config from "../backend/config/index.js" +import { SmsType } from "../lib/enums/messaging.js" +import mongoose from "mongoose" +import mongooseConfig from "../backend/config/mongoose.js" +import { processSendSms } from "../backend/lib/messaging/sending.js" + +mongooseConfig(mongoose, config) + +const parser = new ArgumentParser({ + add_help: true, + description: "Outil d'envoi des sms de suivi", +}) + +const subparsers = parser.add_subparsers({ + title: "Commandes", + dest: "command", +}) + +const send = subparsers.add_parser("send") +const send_types = send.add_subparsers({ + title: "Type", + dest: "type", +}) + +const initial_survey_parser = send_types.add_parser(SmsType.InitialSurvey) +const simulation_results_parser = send_types.add_parser( + SmsType.SimulationResults +) + +const single_sms_id_parsers = [initial_survey_parser, simulation_results_parser] + +single_sms_id_parsers.forEach((parser) => { + parser.add_argument("--id", { + help: "Followup Id", + }) +}) + +initial_survey_parser.add_argument("--multiple", { + help: "Maximum number of sms to send", +}) + +async function main() { + try { + const args = parser.parse_args() + const { type, id, multiple } = args + + if (args.command == "send") { + await processSendSms(type, id, multiple) + } else { + parser.print_help() + } + } catch (error) { + console.error("Error:", error) + parser.print_help() + } finally { + await mongoose.connection.close() + console.log("DB disconnected") + } +} + +await main()