@acadix/setup
Version:
Acadix Learning Management System backend application project setup
609 lines • 25.5 kB
JavaScript
;
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