cal.pub0.org/lib/dailyVideoClient.ts

240 lines
6.9 KiB
TypeScript
Raw Normal View History

Daily video calls (#542) * :arrow_up: Bump tailwindcss from 2.2.14 to 2.2.15 Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 2.2.14 to 2.2.15. - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v2.2.14...v2.2.15) --- updated-dependencies: - dependency-name: tailwindcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * updating cal will provide a zoom meeting url * updating cal will provide a zoom meeting url * modifying how daily emails send * modifying how daily emails send * daily table * migration updates * daily table * rebasing updates * updating Daily references to a new table * updating internal notes * merge updates, adding Daily references to book/events.ts * updated video email templates to remove Daily specific references * updating the events.ts and refactoring in the event manager * removing the package-lock * changing calendso video powered by Daily.co to cal video powered by Daily.co * updating some of the internal Daily notes * added a modal for when the call/ link is invalid * removing handle errors raw from the Daily video client * prettier formatting fixes * Added the Daily location to calendar events and updated Cal video references to Daily.co video * updating references to create in event manager to check for Daily video * fixing spacing on the cancel booking modal and adding Daily references in the event manager * formatting fixes * updating the readme file * adding a daily interface in the event manager * adding daily to the location labels * added a note to cal event parser * resolving yarn merge conflicts * updating dailyReturn to DailyReturnType * removing prettier auto and refactoring integrations: daily in the event manager * removing changes to estlintrc.json * updating read me formatting * indent space for Daily ReadMe section * resolving the merge conflicts in the yarn file Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Lola-Ojabowale <lola.ojabowale@gmail.com>
2021-10-07 16:12:39 +00:00
import { Credential } from "@prisma/client";
import short from "short-uuid";
import { v5 as uuidv5 } from "uuid";
import CalEventParser from "@lib/CalEventParser";
import { AdditionInformation, EntryPoint } from "@lib/emails/EventMail";
import { getIntegrationName } from "@lib/emails/helpers";
import { EventResult } from "@lib/events/EventManager";
import logger from "@lib/logger";
import { CalendarEvent } from "./calendarClient";
import EventAttendeeRescheduledMail from "./emails/EventAttendeeRescheduledMail";
import EventOrganizerRescheduledMail from "./emails/EventOrganizerRescheduledMail";
import VideoEventAttendeeMail from "./emails/VideoEventAttendeeMail";
import VideoEventOrganizerMail from "./emails/VideoEventOrganizerMail";
const log = logger.getChildLogger({ prefix: ["[lib] dailyVideoClient"] });
const translator = short();
export interface DailyVideoCallData {
type: string;
id: string;
password: string;
url: string;
}
function handleErrorsJson(response) {
if (!response.ok) {
response.json().then(console.log);
throw Error(response.statusText);
}
return response.json();
}
const dailyCredential = process.env.DAILY_API_KEY;
interface DailyVideoApiAdapter {
dailyCreateMeeting(event: CalendarEvent): Promise<any>;
dailyUpdateMeeting(uid: string, event: CalendarEvent);
dailyDeleteMeeting(uid: string): Promise<unknown>;
getAvailability(dateFrom, dateTo): Promise<any>;
}
const DailyVideo = (credential): DailyVideoApiAdapter => {
const translateEvent = (event: CalendarEvent) => {
// Documentation at: https://docs.daily.co/reference#list-rooms
// added a 1 hour buffer for room expiration and room entry
const exp = Math.round(new Date(event.endTime).getTime() / 1000) + 60 * 60;
const nbf = Math.round(new Date(event.startTime).getTime() / 1000) - 60 * 60;
return {
privacy: "private",
properties: {
enable_new_call_ui: true,
enable_prejoin_ui: true,
enable_knocking: true,
enable_screenshare: true,
enable_chat: true,
exp: exp,
nbf: nbf,
},
};
};
return {
getAvailability: () => {
return credential;
},
dailyCreateMeeting: (event: CalendarEvent) =>
fetch("https://api.daily.co/v1/rooms", {
method: "POST",
headers: {
Authorization: "Bearer " + dailyCredential,
"Content-Type": "application/json",
},
body: JSON.stringify(translateEvent(event)),
}).then(handleErrorsJson),
dailyDeleteMeeting: (uid: string) =>
fetch("https://api.daily.co/v1/rooms/" + uid, {
method: "DELETE",
headers: {
Authorization: "Bearer " + dailyCredential,
},
}).then(handleErrorsJson),
dailyUpdateMeeting: (uid: string, event: CalendarEvent) =>
fetch("https://api.daily.co/v1/rooms/" + uid, {
method: "POST",
headers: {
Authorization: "Bearer " + dailyCredential,
"Content-Type": "application/json",
},
body: JSON.stringify(translateEvent(event)),
}).then(handleErrorsJson),
};
};
// factory
const videoIntegrations = (withCredentials): DailyVideoApiAdapter[] =>
withCredentials
.map((cred) => {
return DailyVideo(cred);
})
.filter(Boolean);
const getBusyVideoTimes: (withCredentials) => Promise<unknown[]> = (withCredentials) =>
Promise.all(videoIntegrations(withCredentials).map((c) => c.getAvailability())).then((results) =>
results.reduce((acc, availability) => acc.concat(availability), [])
);
const dailyCreateMeeting = async (
credential: Credential,
calEvent: CalendarEvent,
maybeUid: string = null
): Promise<EventResult> => {
const parser: CalEventParser = new CalEventParser(calEvent, maybeUid);
const uid: string = parser.getUid();
if (!credential) {
throw new Error(
"Credentials must be set! Video platforms are optional, so this method shouldn't even be called when no video credentials are set."
);
}
let success = true;
const creationResult = await videoIntegrations([credential])[0]
.dailyCreateMeeting(calEvent)
.catch((e) => {
log.error("createMeeting failed", e, calEvent);
success = false;
});
const currentRoute = process.env.BASE_URL;
const videoCallData: DailyVideoCallData = {
type: "Daily.co Video",
id: creationResult.name,
password: creationResult.password,
url: currentRoute + "/call/" + uid,
};
const entryPoint: EntryPoint = {
entryPointType: getIntegrationName(videoCallData),
uri: videoCallData.url,
label: "Enter Meeting",
pin: "",
};
const additionInformation: AdditionInformation = {
entryPoints: [entryPoint],
};
const organizerMail = new VideoEventOrganizerMail(calEvent, uid, videoCallData, additionInformation);
const attendeeMail = new VideoEventAttendeeMail(calEvent, uid, videoCallData, additionInformation);
try {
await organizerMail.sendEmail();
} catch (e) {
console.error("organizerMail.sendEmail failed", e);
}
if (!creationResult || !creationResult.disableConfirmationEmail) {
try {
await attendeeMail.sendEmail();
} catch (e) {
console.error("attendeeMail.sendEmail failed", e);
}
}
return {
type: "daily",
success,
uid,
createdEvent: creationResult,
originalEvent: calEvent,
};
};
const dailyUpdateMeeting = async (
credential: Credential,
uidToUpdate: string,
calEvent: CalendarEvent
): Promise<EventResult> => {
const newUid: string = translator.fromUUID(uuidv5(JSON.stringify(calEvent), uuidv5.URL));
if (!credential) {
throw new Error(
"Credentials must be set! Video platforms are optional, so this method shouldn't even be called when no video credentials are set."
);
}
let success = true;
const updateResult = credential
? await videoIntegrations([credential])[0]
.dailyUpdateMeeting(uidToUpdate, calEvent)
.catch((e) => {
log.error("updateMeeting failed", e, calEvent);
success = false;
})
: null;
const organizerMail = new EventOrganizerRescheduledMail(calEvent, newUid);
const attendeeMail = new EventAttendeeRescheduledMail(calEvent, newUid);
try {
await organizerMail.sendEmail();
} catch (e) {
console.error("organizerMail.sendEmail failed", e);
}
if (!updateResult || !updateResult.disableConfirmationEmail) {
try {
await attendeeMail.sendEmail();
} catch (e) {
console.error("attendeeMail.sendEmail failed", e);
}
}
return {
type: credential.type,
success,
uid: newUid,
updatedEvent: updateResult,
originalEvent: calEvent,
};
};
const dailyDeleteMeeting = (credential: Credential, uid: string): Promise<unknown> => {
if (credential) {
return videoIntegrations([credential])[0].dailyDeleteMeeting(uid);
}
return Promise.resolve({});
};
export { getBusyVideoTimes, dailyCreateMeeting, dailyUpdateMeeting, dailyDeleteMeeting };