diff --git a/App/FeatureSet/Notification/Services/SmsService.ts b/App/FeatureSet/Notification/Services/SmsService.ts index 0a73109a9a7..d5d85d18fd9 100644 --- a/App/FeatureSet/Notification/Services/SmsService.ts +++ b/App/FeatureSet/Notification/Services/SmsService.ts @@ -37,58 +37,52 @@ export default class SmsService { const smsLog: SmsLog = new SmsLog(); try { - // check number of sms to send for this entire messages to send. Each sms can have 160 characters. + // Calculate the number of SMS segments, each SMS can contain 160 characters. const smsSegments: number = Math.ceil(message.length / 160); + // Trim unnecessary lines from the message message = Text.trimLines(message); let smsCost: number = 0; + // Determine if billing is enabled and no custom Twilio config is used const shouldChargeForSMS: boolean = IsBillingEnabled && !options.customTwilioConfig; if (shouldChargeForSMS) { smsCost = SMSDefaultCostInCents / 100; + // Check if the phone number is high-risk, adjust cost accordingly if (isHighRiskPhoneNumber(to)) { smsCost = SMSHighRiskCostInCents / 100; } } + // Multiply cost by the number of message segments if it exceeds one if (smsSegments > 1) { smsCost = smsCost * smsSegments; } - smsLog.toNumber = to; - - smsLog.smsText = - options && options.isSensitive - ? "This message is sensitive and is not logged" - : message; - smsLog.smsCostInUSDCents = 0; - - if (options.projectId) { - smsLog.projectId = options.projectId; - } - const twilioConfig: TwilioConfig | null = options.customTwilioConfig || (await getTwilioConfig()); + // Check if Twilio configuration is available if (!twilioConfig) { throw new BadDataException("Twilio Config not found"); } + // Initialize Twilio client with account credentials const client: Twilio.Twilio = Twilio( twilioConfig.accountSid, twilioConfig.authToken, ); + // Set the sender phone number in the SMS log smsLog.fromNumber = twilioConfig.phoneNumber; let project: Project | null = null; - // make sure project has enough balance. - + // Ensure the project has sufficient balance for SMS notifications if (options.projectId) { project = await ProjectService.findOneById({ id: options.projectId, @@ -104,6 +98,7 @@ export default class SmsService { }, }); + // Check if the specified project exists if (!project) { smsLog.status = SmsStatus.Error; smsLog.statusMessage = `Project ${options.projectId.toString()} not found.`; @@ -114,20 +109,28 @@ export default class SmsService { isRoot: true, }, }); - return; + return; // Exit if project not found } + // Verify if SMS notifications are enabled for the project if (!project.enableSmsNotifications) { smsLog.status = SmsStatus.Error; + smsLog.statusMessage = `SMS notifications are not enabled for this project. Please enable SMS notifications in Project Settings.`; + logger.error(smsLog.statusMessage); + + // Create an SMS log entry in the database await SmsLogService.create({ data: smsLog, props: { isRoot: true, }, }); + + // Check if the notification about disabled SMS has not been sent to project owners if (!project.notEnabledSmsOrCallNotificationSentToOwners) { + // Update the project to indicate the notification has been sent await ProjectService.updateOneById({ data: { notEnabledSmsOrCallNotificationSentToOwners: true, @@ -137,17 +140,21 @@ export default class SmsService { isRoot: true, }, }); + + // Send an email to project owners notifying them that SMS notifications are not enabled await ProjectService.sendEmailToProjectOwners( project.id!, "SMS notifications not enabled for " + (project.name || ""), `We tried to send an SMS to ${to.toString()} with message:

${message}

This SMS was not sent because SMS notifications are not enabled for this project. Please enable SMS notifications in Project Settings.`, ); } + return; } + // Check if SMS sending should incur a charge if (shouldChargeForSMS) { - // check if auto recharge is enabled and current balance is low. + // Initialize updated balance with the current SMS or call balance let updatedBalance: number = project.smsOrCallCurrentBalanceInUSDCents!; try { @@ -155,15 +162,22 @@ export default class SmsService { project.id!, ); } catch (err) { + // Log an error if recharging fails logger.error(err); } + // Update the project's current balance with the new balance project.smsOrCallCurrentBalanceInUSDCents = updatedBalance; + // Check if the updated balance is zero or undefined if (!project.smsOrCallCurrentBalanceInUSDCents) { + // Set the SMS log status to indicate low balance smsLog.status = SmsStatus.LowBalance; + // Log an error message indicating insufficient balance smsLog.statusMessage = `Project ${options.projectId.toString()} does not have enough SMS balance.`; logger.error(smsLog.statusMessage); + + // Create a log entry for the low balance situation await SmsLogService.create({ data: smsLog, props: { @@ -171,7 +185,9 @@ export default class SmsService { }, }); + // Check if a low balance notification has been sent to project owners if (!project.lowCallAndSMSBalanceNotificationSentToOwners) { + // Update the project to reflect that the notification has been sent await ProjectService.updateOneById({ data: { lowCallAndSMSBalanceNotificationSentToOwners: true, @@ -181,9 +197,12 @@ export default class SmsService { isRoot: true, }, }); + + // Send an email to project owners about the low balance await ProjectService.sendEmailToProjectOwners( project.id!, "Low SMS and Call Balance for " + (project.name || ""), + `We tried to send an SMS to ${to.toString()} with message:

${message}
This SMS was not sent because project does not have enough balance to send SMS. Current balance is ${ (project.smsOrCallCurrentBalanceInUSDCents || 0) / 100 } USD cents. Required balance to send this SMS should is ${smsCost} USD. Please enable auto recharge or recharge manually.`, @@ -250,6 +269,7 @@ export default class SmsService { data: { smsOrCallCurrentBalanceInUSDCents: project.smsOrCallCurrentBalanceInUSDCents, + notEnabledSmsOrCallNotificationSentToOwners: false, // reset this flag }, id: project.id!,