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
parent 56c07ec351
commit c1bbb9b139
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 133 additions and 32 deletions

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

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 -->

View File

@ -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: {

View File

@ -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`)}
/>

View File

@ -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);

View File

@ -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);
}}>

View File

@ -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",
];

View File

@ -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 ||

View File

@ -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;

View File

@ -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({

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)

View File

@ -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" };
}

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",