Fixes SMS not sending for country codes not supporting alphanumeric sender IDs (#5969)

* don't use alphanumeric sender ID for not support country codes

* fix import

* rename file

* fix spelling mistakes

* add twilio phone number to .env file

* add SENDER_ID to env variable and create function to get sender id

* add NEXT_PUBLIC_SENDER_ID env variable

* remove not needed Cal fallback

* fix readme

* code clean up

* code clean up

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
Carina Wollendorfer
2022-12-12 12:00:15 +01:00
committed by GitHub
co-authored by CarinaWolli
parent 56c07ec351
commit c1bbb9b139
13 changed files with 133 additions and 32 deletions
+2
View File
@@ -85,6 +85,8 @@ SENDGRID_EMAIL=
TWILIO_SID=
TWILIO_TOKEN=
TWILIO_MESSAGING_SID=
TWILIO_PHONE_NUMBER=
NEXT_PUBLIC_SENDER_ID=
# This is used so we can bypass emails in auth flows for E2E testing
# Set it to "1" if you need to run E2E tests locally
+10 -8
View File
@@ -419,14 +419,16 @@ following
2. Click ‘Get a Twilio phone number’
3. Copy Account SID to your .env file into the TWILIO_SID field
4. Copy Auth Token to your .env file into the TWILIO_TOKEN field
5. Create a messaging service (Develop -> Messaging -> Services)
6. Choose any name for the messaging service
7. Click 'Add Senders'
8. Choose phone number as sender type
9. Add the listed phone number
10. Leave all other fields as they are
11. Complete setup and click ‘View my new Messaging Service’
12. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
5. Copy your Twilio phone number to your .env file into the TWILIO_PHONE_NUMBER field
6. Add your own sender id to the .env file into the NEXT_PUBLIC_SENDER_ID field (fallback is Cal)
7. Create a messaging service (Develop -> Messaging -> Services)
8. Choose any name for the messaging service
9. Click 'Add Senders'
10. Choose phone number as sender type
11. Add the listed phone number
12. Leave all other fields as they are
13. Complete setup and click ‘View my new Messaging Service’
14. Copy Messaging Service SID to your .env file into the TWILIO_MESSAGING_SID field
<!-- LICENSE -->
@@ -6,6 +6,7 @@ import dayjs from "@calcom/dayjs";
import { defaultHandler } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
import { getSenderId } from "../lib/alphanumericSenderIdSupport";
import * as twilio from "../lib/reminders/smsProviders/twilioProvider";
import customTemplate, { VariablesType } from "../lib/reminders/templates/customTemplate";
import smsReminderTemplate from "../lib/reminders/templates/smsReminderTemplate";
@@ -72,6 +73,8 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
? reminder.booking?.attendees[0].timeZone
: reminder.booking?.user?.timeZone;
const senderID = getSenderId(sendTo, reminder.workflowStep.sender);
let message: string | null = reminder.workflowStep.reminderBody;
switch (reminder.workflowStep.template) {
case WorkflowTemplates.REMINDER:
@@ -105,12 +108,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
break;
}
if (message?.length && message?.length > 0 && sendTo) {
const scheduledSMS = await twilio.scheduleSMS(
sendTo,
message,
reminder.scheduledDate,
reminder.workflowStep.sender || "Cal"
);
const scheduledSMS = await twilio.scheduleSMS(sendTo, message, reminder.scheduledDate, senderID);
await prisma.workflowReminder.update({
where: {
@@ -5,6 +5,7 @@ import { Dispatch, SetStateAction, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import { SENDER_ID } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import {
Button,
@@ -68,7 +69,7 @@ export const AddActionDialog = (props: IAddActionDialog) => {
mode: "onSubmit",
defaultValues: {
action: WorkflowActions.EMAIL_HOST,
sender: "Cal",
sender: SENDER_ID,
},
resolver: zodResolver(formSchema),
});
@@ -166,7 +167,7 @@ export const AddActionDialog = (props: IAddActionDialog) => {
<TextField
label={t("sender_id")}
type="text"
placeholder="Cal"
placeholder={SENDER_ID}
maxLength={11}
{...form.register(`sender`)}
/>
@@ -3,6 +3,7 @@ import { useRouter } from "next/router";
import { Dispatch, SetStateAction, useMemo, useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form";
import { SENDER_ID } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
@@ -73,7 +74,7 @@ export default function WorkflowDetailsPage(props: Props) {
emailSubject: null,
template: WorkflowTemplates.CUSTOM,
numberRequired: numberRequired || false,
sender: sender || "Cal",
sender: sender || SENDER_ID,
};
steps?.push(step);
form.setValue("steps", steps);
@@ -9,6 +9,7 @@ import { Dispatch, SetStateAction, useRef, useState } from "react";
import { Controller, UseFormReturn } from "react-hook-form";
import "react-phone-number-input/style.css";
import { SENDER_ID } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
import { trpc } from "@calcom/trpc/react";
@@ -366,7 +367,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
<TextField
label={t("sender_id")}
type="text"
placeholder="Cal"
placeholder={SENDER_ID}
maxLength={11}
{...form.register(`steps.${step.stepNumber - 1}.sender`)}
/>
@@ -557,7 +558,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
emailSubject,
reminderBody,
template: step.template,
sender: step.sender || "Cal",
sender: step.sender || SENDER_ID,
});
} else {
const isNumberValid =
@@ -596,6 +597,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
reminderBody: reminderBody || "",
template: step.template,
sendTo: step.sendTo || "",
sender: step.sender || SENDER_ID,
});
setConfirmationDialogOpen(false);
}}>
@@ -0,0 +1,87 @@
import { SENDER_ID } from "@calcom/lib/constants";
export function getSenderId(phoneNumber?: string | null, sender?: string | null) {
const isAlphanumericSenderIdSupported = !noAlphanumericSenderIdSupport.find(
(code) => code === phoneNumber?.substring(0, code.length)
);
const senderID = isAlphanumericSenderIdSupported ? sender || SENDER_ID : "";
return senderID;
}
const noAlphanumericSenderIdSupport = [
"+93",
"+54",
"+374",
"+1",
"+375",
"+32",
"+229",
"+55",
"+237",
"+56",
"+86",
"+57",
"+243",
"+506",
"+53",
"+42",
"+593",
"+20",
"+503",
"+251",
"+594",
"+233",
"+224",
"+245",
"+852",
"+36",
"+91",
"+62",
"+98",
"+972",
"+225",
"+962",
"+7",
"+254",
"+965",
"+996",
"+231",
"+60",
"+52",
"+212",
"+95",
"+674",
"+977",
"+64",
"+505",
"+234",
"+968",
"+507",
"+595",
"+51",
"+63",
"+974",
"+7",
"+250",
"+966",
"+27",
"+82",
"+211",
"+94",
"+249",
"+268",
"+963",
"+886",
"+255",
"+66",
"+216",
"+90",
"+256",
"+971",
"+598",
"+58",
"+84",
"+260",
];
@@ -6,6 +6,7 @@ import {
WorkflowTriggerEvents,
} from "@prisma/client";
import { SENDER_ID } from "@calcom/lib/constants";
import type { CalendarEvent } from "@calcom/types/Calendar";
import { scheduleEmailReminder } from "./emailReminderManager";
@@ -51,7 +52,7 @@ export const scheduleWorkflowReminders = async (
step.reminderBody || "",
step.id,
step.template,
step.sender || "Cal"
step.sender || SENDER_ID
);
} else if (
step.action === WorkflowActions.EMAIL_ATTENDEE ||
@@ -120,7 +121,7 @@ export const sendCancelledReminders = async (
step.reminderBody || "",
step.id,
step.template,
step.sender || "Cal"
step.sender || SENDER_ID
);
} else if (
step.action === WorkflowActions.EMAIL_ATTENDEE ||
@@ -25,7 +25,7 @@ export const sendSMS = async (phoneNumber: string, body: string, sender: string)
body: body,
messagingServiceSid: process.env.TWILIO_MESSAGING_SID,
to: phoneNumber,
from: sender,
from: sender ? sender : process.env.TWILIO_PHONE_NUMBER,
});
return response;
@@ -39,7 +39,7 @@ export const scheduleSMS = async (phoneNumber: string, body: string, scheduledDa
to: phoneNumber,
scheduleType: "fixed",
sendAt: scheduledDate,
from: sender,
from: sender ? sender : process.env.TWILIO_PHONE_NUMBER,
});
return response;
@@ -10,6 +10,7 @@ import dayjs from "@calcom/dayjs";
import prisma from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import { getSenderId } from "../alphanumericSenderIdSupport";
import * as twilio from "./smsProviders/twilioProvider";
import customTemplate, { VariablesType } from "./templates/customTemplate";
import smsReminderTemplate from "./templates/smsReminderTemplate";
@@ -57,6 +58,8 @@ export const scheduleSMSReminder = async (
const timeUnit: timeUnitLowerCase | undefined = timeSpan.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
let scheduledDate = null;
const senderID = getSenderId(reminderPhone, sender);
if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT) {
scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
} else if (triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) {
@@ -98,7 +101,7 @@ export const scheduleSMSReminder = async (
triggerEvent === WorkflowTriggerEvents.RESCHEDULE_EVENT
) {
try {
await twilio.sendSMS(reminderPhone, message, sender);
await twilio.sendSMS(reminderPhone, message, senderID);
} catch (error) {
console.log(`Error sending SMS with error ${error}`);
}
@@ -117,7 +120,7 @@ export const scheduleSMSReminder = async (
reminderPhone,
message,
scheduledDate.toDate(),
sender
senderID
);
await prisma.workflowReminder.create({
+1
View File
@@ -9,6 +9,7 @@ export const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL || "https://cal.c
export const APP_NAME = process.env.NEXT_PUBLIC_APP_NAME || "Cal.com";
export const SUPPORT_MAIL_ADDRESS = process.env.NEXT_PUBLIC_SUPPORT_MAIL_ADDRESS || "help@cal.com";
export const COMPANY_NAME = process.env.NEXT_PUBLIC_COMPANY_NAME || "Cal.com, Inc.";
export const SENDER_ID = process.env.NEXT_PUBLIC_SENDER_ID || "Cal";
// This is the URL from which all Cal Links and their assets are served.
// Use website URL to make links shorter(cal.com and not app.cal.com)
@@ -26,6 +26,7 @@ import {
deleteScheduledSMSReminder,
scheduleSMSReminder,
} from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
import { SENDER_ID } from "@calcom/lib/constants";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { TRPCError } from "@trpc/server";
@@ -154,7 +155,7 @@ export const workflowsRouter = router({
action: WorkflowActions.EMAIL_HOST,
template: WorkflowTemplates.REMINDER,
workflowId: workflow.id,
sender: "Cal",
sender: SENDER_ID,
},
});
return { workflow };
@@ -469,7 +470,7 @@ export const workflowsRouter = router({
step.reminderBody || "",
step.id,
step.template,
step.sender || "Cal"
step.sender || SENDER_ID
);
}
});
@@ -535,7 +536,7 @@ export const workflowsRouter = router({
emailSubject: newStep.template === WorkflowTemplates.CUSTOM ? newStep.emailSubject : null,
template: newStep.template,
numberRequired: newStep.numberRequired,
sender: newStep.sender || "Cal",
sender: newStep.sender || SENDER_ID,
},
});
//cancel all reminders of step and create new ones (not for newEventTypes)
@@ -647,7 +648,7 @@ export const workflowsRouter = router({
newStep.reminderBody || "",
newStep.id || 0,
newStep.template,
newStep.sender || "Cal"
newStep.sender || SENDER_ID
);
}
});
@@ -671,7 +672,7 @@ export const workflowsRouter = router({
addedSteps.forEach(async (step) => {
if (step) {
const newStep = step;
newStep.sender = step.sender || "Cal";
newStep.sender = step.sender || SENDER_ID;
const createdStep = await ctx.prisma.workflowStep.create({
data: step,
});
@@ -760,7 +761,7 @@ export const workflowsRouter = router({
step.reminderBody || "",
createdStep.id,
step.template,
step.sender || "Cal"
step.sender || SENDER_ID
);
}
});
@@ -897,7 +898,7 @@ export const workflowsRouter = router({
reminderBody,
0,
template,
sender || "Cal"
sender || SENDER_ID
);
return { message: "Notification sent" };
}
+2
View File
@@ -183,6 +183,7 @@
"$TWILIO_TOKEN",
"$TWILIO_SID",
"$TWILIO_MESSAGING_SID",
"$TWILIO_PHONE_NUMBER",
"$CRON_API_KEY",
"$DAILY_API_KEY",
"$DAILY_SCALE_PLAN",
@@ -221,6 +222,7 @@
"$NEXT_PUBLIC_APP_NAME",
"$NEXT_PUBLIC_SUPPORT_MAIL_ADDRESS",
"$NEXT_PUBLIC_COMPANY_NAME",
"$NEXT_PUBLIC_SENDER_ID",
"$NEXTAUTH_COOKIE_DOMAIN",
"$NEXTAUTH_SECRET",
"$NEXTAUTH_URL",