UNPKG

@acadix/setup

Version:

Acadix Learning Management System backend application project setup

609 lines 25.5 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = __importDefault(require("axios")); const config_1 = require("../../config"); const logger_1 = require("../../logger"); class WhatsappCloud { constructor(env) { this.env = env; this.whatsappConfig = (0, config_1.WhatsappConfig)(Object.assign({}, this.env.WHATSAPP)); this.logger = (0, logger_1.Logger)(this.env); this.baseUrl = `https://graph.facebook.com/${this.whatsappConfig.graphAPIVersion}/${this.whatsappConfig.senderPhoneNumberId}`; } apiRequest({ url, method, body }) { return __awaiter(this, void 0, void 0, function* () { const headers = { "Content-Type": "application/json", "Accept-Language": "en_US", Authorization: `Bearer ${this.whatsappConfig.accessToken}`, }; const config = { method, url: this.baseUrl + url, headers, data: body, }; const result = yield (0, axios_1.default)(config); return result.data; }); } parseMessage(requestBody) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; // THIS FUNCTION IS NOT YET OPTIMIZED FOR PERFORMANCE. IT IS ONLY MADE AS A TEMPORARY SOLUTION. if (!requestBody) { throw new Error('"requestBody" is required'); } const WABA_ID = (_a = requestBody.entry[0]) === null || _a === void 0 ? void 0 : _a.id; // extract the business ID from the webhook payload if (WABA_ID == 0) { this.logger.warn("WABA_ID is 0. You seem to be testing with Meta test subscription. This is not really a valid WABA_ID. I recommend you to send an actual message from an actual whatsapp customer's number."); } if (!WABA_ID || WABA_ID !== this.whatsappConfig.whatsappBusinessId) { throw new Error("WABA_ID is not valid. Hint: the message is not intended for this Whatsapp Business Account."); } //first check if the message is a whatsapp message if (!requestBody.object || requestBody.object !== "whatsapp_business_account") { throw new Error('requestBody is not a valid whatsapp message. Hint: check the "object" property'); } if (!requestBody.entry || !((_b = requestBody.entry) === null || _b === void 0 ? void 0 : _b.length)) { throw new Error('requestBody is not a valid whatsapp message. Hint: check the "entry" property'); } if (!((_c = requestBody.entry[0].changes) === null || _c === void 0 ? void 0 : _c.length) || requestBody.entry[0].changes[0].field !== "messages") { throw new Error('requestBody is not a valid whatsapp message. Hint: check the "changes" property'); } const metadata = requestBody.entry[0].changes[0].value.metadata; const contacts = ((_d = requestBody.entry[0].changes[0].value.contacts) === null || _d === void 0 ? void 0 : _d.length) ? requestBody.entry[0].changes[0].value.contacts[0] : null; // Messages vs Notifications const message = ((_f = (_e = requestBody.entry[0].changes[0].value) === null || _e === void 0 ? void 0 : _e.messages) === null || _f === void 0 ? void 0 : _f.length) ? requestBody.entry[0].changes[0].value.messages[0] : null; let notificationMessage = ((_h = (_g = requestBody.entry[0].changes[0].value) === null || _g === void 0 ? void 0 : _g.statuses) === null || _h === void 0 ? void 0 : _h.length) ? requestBody.entry[0].changes[0].value.statuses[0] : null; const output = { metadata, contacts, WABA_ID, }; if (notificationMessage) { output["isNotificationMessage"] = true; output["isMessage"] = false; output["notificationType"] = notificationMessage.status; notificationMessage["from"] = { name: null, phone: notificationMessage.recipient_id, }; output["notificationMessage"] = notificationMessage; } else if (message) { output["isNotificationMessage"] = false; output["isMessage"] = true; let msgType; if (message.type === "text" && message.referral) { msgType = "ad_message"; } else if (message.type === "text") { msgType = "text_message"; } else if (message.type === "sticker") { msgType = "sticker_message"; } else if (message.type === "image") { msgType = "media_message"; } else if (message.location) { msgType = "location_message"; } else if (message.contacts) { msgType = "contact_message"; } else if (message.type === "button") { msgType = "quick_reply_message"; } else if (message.type === "interactive") { if (((_j = message.interactive) === null || _j === void 0 ? void 0 : _j.type) === "list_reply") { msgType = "radio_button_message"; message["list_reply"] = message.interactive.list_reply; } else if (((_k = message.interactive) === null || _k === void 0 ? void 0 : _k.type) === "button_reply") { msgType = "simple_button_message"; message["button_reply"] = message.interactive.button_reply; } } else if (message.type === "unsupported") { msgType = "unknown_message"; if ((_l = message.errors) === null || _l === void 0 ? void 0 : _l.length) { console.log({ Q: message.errors }); output["isNotificationMessage"] = true; output["isMessage"] = false; notificationMessage = { errors: message.errors, }; } } message["type"] = msgType; message["from"] = { name: contacts.profile.name, phone: message === null || message === void 0 ? void 0 : message.from, }; if (output.isMessage) { let thread = null; if (message.context) { thread = { from: { name: null, phone: (_m = message.context) === null || _m === void 0 ? void 0 : _m.from, message_id: (_o = message.context) === null || _o === void 0 ? void 0 : _o.id, }, }; } output["message"] = Object.assign(Object.assign({}, message), { thread, message_id: message.id || null }); delete output.message.id; //keep the data light delete output.context; //keep the data light } } else { console.warn("An unidentified."); } return output; } sendText({ message, recipient }) { return __awaiter(this, void 0, void 0, function* () { const body = JSON.stringify({ messaging_product: "whatsapp", preview_url: false, recipient_type: "individual", to: recipient, type: "text", text: { body: message, }, }); const response = yield this.apiRequest({ url: "/messages", body, method: "post", }); return response; }); } markMessageAsRead(messageId) { return __awaiter(this, void 0, void 0, function* () { const response = yield this.apiRequest({ url: `/messages`, method: "post", body: { messaging_product: "whatsapp", status: "read", messageId, }, }); return response; }); } sendSimpleButtons({ recipient, message, listOfButtons, }) { return __awaiter(this, void 0, void 0, function* () { const validButtons = listOfButtons .map((button) => { if (!button.title) { throw new Error('"title" is required in making a request.'); } if (button.title.length > 20) { throw new Error("The button title must be between 1 and 20 characters long."); } if (!button.id) { throw new Error('"id" is required in making a request.'); } if (button.id.length > 256) { throw new Error("The button id must be between 1 and 256 characters long."); } return { type: "reply", reply: { title: button.title, id: button.id, }, }; }) .filter(Boolean); if (validButtons.length === 0) { throw new Error('"listOfButtons" is required in making a request'); } const body = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "interactive", interactive: { type: "button", body: { text: message, }, action: { buttons: validButtons, }, }, }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return response; }); } sendRadioButtons({ recipient, headerText, bodyText, footerText, listOfSections, }) { return __awaiter(this, void 0, void 0, function* () { if (!bodyText) throw new Error('"bodyText" is required in making a request'); if (!headerText) throw new Error('"headerText" is required in making a request'); if (!footerText) throw new Error('"footerText" is required in making a request'); let totalNumberOfItems = 0; const validSections = listOfSections .map((section) => { var _a; const title = section.title; const rows = (_a = section.rows) === null || _a === void 0 ? void 0 : _a.map((row) => { if (!row.id) { throw new Error('"row.id" of an item is required in list of radio buttons.'); } if (row.id.length > 200) { throw new Error("The row id must be between 1 and 200 characters long."); } if (!row.title) { throw new Error('"row.title" of an item is required in list of radio buttons.'); } if (row.title.length > 24) { throw new Error("The row title must be between 1 and 24 characters long."); } if (!row.description) { throw new Error('"row.description" of an item is required in list of radio buttons.'); } if (row.description.length > 72) { throw new Error("The row description must be between 1 and 72 characters long."); } totalNumberOfItems += 1; return { id: row.id, title: row.title, description: row.description, }; }); if (!title) { throw new Error('"title" of a section is required in list of radio buttons.'); } return { title, rows, }; }) .filter(Boolean); if (totalNumberOfItems > 10) { throw new Error("The total number of items in the rows must be equal or less than 10."); } const samples = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "interactive", interactive: { type: "list", header: { type: "text", text: headerText, }, body: { text: bodyText, }, footer: { text: footerText, }, action: { button: headerText, sections: validSections, }, }, }; if (validSections.length === 0) { throw new Error('"listOfSections" is required in making a request'); } const response = yield this.apiRequest({ url: "/messages", method: "post", body: samples, }); return response; }); } sendImage({ recipient, caption, url }) { return __awaiter(this, void 0, void 0, function* () { if (!url) { throw new Error('You must send an image in a publicly available "url". Provide "url".'); } const body = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "image", image: { caption: caption || "Image", link: url, }, }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return { response, body, }; }); } sendVideo({ recipient, caption, url }) { return __awaiter(this, void 0, void 0, function* () { if (!url) { throw new Error('You must send a video in a publicly available "url". Provide "url".'); } const body = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "video", video: { caption: caption || "video", link: url, }, }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return { response, body, }; }); } sendAudio({ recipient, caption, url }) { return __awaiter(this, void 0, void 0, function* () { if (!url) { throw new Error('You must send an audio in a publicly available "url". Provide "url".'); } const body = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "audio", audio: { caption: caption || "", link: url, }, }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return { response, body, }; }); } sendDocument({ recipient, caption, url }) { return __awaiter(this, void 0, void 0, function* () { if (!url) { throw new Error('You must send a document that is in a publicly available "url". Provide "url".'); } if (!caption) { throw new Error('"caption" is required when sending a document'); } const body = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "document", document: { caption: caption || "Document File", link: url, }, }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return { response, body, }; }); } sendLocation({ recipient, latitude, longitude, name, address, }) { return __awaiter(this, void 0, void 0, function* () { if (!latitude || !longitude) { throw new Error('"latitude" and "longitude" are required in making a request'); } if (!name || !address) { throw new Error('"name" and "address" are required in making a request'); } const body = { messaging_product: "whatsapp", recipient_type: "individual", to: recipient, type: "location", location: { latitude, longitude, name, address, }, }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return response; }); } sendContact({ recipient, contactProfile }) { return __awaiter(this, void 0, void 0, function* () { const format_address = (address) => { const address_obj = { street: address.street || null, city: address.city || null, state: address.state || null, zip: address.zip || null, country: address.country || null, country_code: address.country_code || null, type: address.type || null, }; return address_obj; }; const format_email = (email) => { if (!email || !(email === null || email === void 0 ? void 0 : email.email)) { return {}; } const email_obj = { email: email.email || null, type: email.type || null, }; return email_obj; }; const format_name = (name) => { if (!name || !(name === null || name === void 0 ? void 0 : name.first_name) || !(name === null || name === void 0 ? void 0 : name.last_name)) { throw new Error('Provide both "name.first_name" and "name.last_name".'); } const name_obj = { formatted_name: name.formatted_name || null, first_name: name.first_name || null, last_name: name.last_name || null, middle_name: name.middle_name || null, suffix: name.suffix || null, prefix: name.prefix || null, }; if (!name_obj.formatted_name && name_obj.first_name && name_obj.last_name) { name_obj.formatted_name = `${name_obj.first_name} ${name_obj.last_name}`; } return name_obj; }; const format_org = (org) => { if (!org || !(org === null || org === void 0 ? void 0 : org.company)) { return {}; } const org_obj = { company: org.company || null, department: org.department || null, title: org.title || null, }; return org_obj; }; const format_phone = (phone) => { if (!phone || !(phone === null || phone === void 0 ? void 0 : phone.phone)) { return {}; } const phone_obj = { phone: phone.phone || null, type: phone.type || null, wa_id: phone.wa_id || null, }; return phone_obj; }; const format_url = (url) => { if (!url || !(url === null || url === void 0 ? void 0 : url.url)) { return {}; } const url_obj = { url: url.url || null, type: url.type || null, }; return url_obj; }; const format_birthday = (birthday) => { if (!birthday) { return null; } else { //ensure it is a valid date function isValidDate(dateObject) { return new Date(dateObject).toString() !== "Invalid Date"; } if (!isValidDate(birthday)) { throw new Error('Provide a valid date in format: "YYYY-MM-DD"'); } return birthday; } }; const format_contact = ({ addresses, emails, name, org, phones, urls, birthday, }) => { const fields = { addresses: [], emails: [], name: {}, org: {}, phones: [], urls: [], birthday: "", }; if (addresses && Array.isArray(addresses) && addresses.length > 0) { fields.addresses = addresses.map(format_address); } if (emails && Array.isArray(emails) && emails.length > 0) { fields.emails = emails.map(format_email); } if (name && typeof name === "object") { fields.name = format_name(name); } if (org && typeof org === "object") { fields.org = format_org(org); } if (phones && Array.isArray(phones) && phones.length > 0) { fields.phones = phones.map(format_phone); } if (urls && Array.isArray(urls) && urls.length > 0) { fields.urls = urls.map(format_url); } if (birthday) { fields.birthday = format_birthday(birthday); } return fields; }; const body = { messaging_product: "whatsapp", to: recipient, type: "contacts", contacts: [format_contact(contactProfile)], }; const response = yield this.apiRequest({ url: "/messages", method: "post", body, }); return response; }); } } exports.default = WhatsappCloud; //# sourceMappingURL=whatsappCloud.js.map