naruyaizumi
Version:
A WebSockets library for interacting with WhatsApp Web
245 lines (244 loc) • 8.59 kB
JavaScript
import { Boom } from "@hapi/boom";
import { createHash } from "crypto";
import { createWriteStream, promises as fs } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import {
getBinaryNodeChild,
getBinaryNodeChildren,
getBinaryNodeChildString,
} from "../WABinary/index.js";
import { generateMessageIDV2 } from "./generics.js";
import { getStream, getUrlFromDirectPath } from "./messages-media.js";
export const parseCatalogNode = (node) => {
const catalogNode = getBinaryNodeChild(node, "product_catalog");
const products = getBinaryNodeChildren(catalogNode, "product").map(parseProductNode);
const paging = getBinaryNodeChild(catalogNode, "paging");
return {
products,
nextPageCursor: paging ? getBinaryNodeChildString(paging, "after") : undefined,
};
};
export const parseCollectionsNode = (node) => {
const collectionsNode = getBinaryNodeChild(node, "collections");
const collections = getBinaryNodeChildren(collectionsNode, "collection").map(
(collectionNode) => {
const id = getBinaryNodeChildString(collectionNode, "id");
const name = getBinaryNodeChildString(collectionNode, "name");
const products = getBinaryNodeChildren(collectionNode, "product").map(parseProductNode);
return {
id,
name,
products,
status: parseStatusInfo(collectionNode),
};
}
);
return {
collections,
};
};
export const parseOrderDetailsNode = (node) => {
const orderNode = getBinaryNodeChild(node, "order");
const products = getBinaryNodeChildren(orderNode, "product").map((productNode) => {
const imageNode = getBinaryNodeChild(productNode, "image");
return {
id: getBinaryNodeChildString(productNode, "id"),
name: getBinaryNodeChildString(productNode, "name"),
imageUrl: getBinaryNodeChildString(imageNode, "url"),
price: +getBinaryNodeChildString(productNode, "price"),
currency: getBinaryNodeChildString(productNode, "currency"),
quantity: +getBinaryNodeChildString(productNode, "quantity"),
};
});
const priceNode = getBinaryNodeChild(orderNode, "price");
const orderDetails = {
price: {
total: +getBinaryNodeChildString(priceNode, "total"),
currency: getBinaryNodeChildString(priceNode, "currency"),
},
products,
};
return orderDetails;
};
export const toProductNode = (productId, product) => {
const attrs = {};
const content = [];
if (typeof productId !== "undefined") {
content.push({
tag: "id",
attrs: {},
content: Buffer.from(productId),
});
}
if (typeof product.name !== "undefined") {
content.push({
tag: "name",
attrs: {},
content: Buffer.from(product.name),
});
}
if (typeof product.description !== "undefined") {
content.push({
tag: "description",
attrs: {},
content: Buffer.from(product.description),
});
}
if (typeof product.retailerId !== "undefined") {
content.push({
tag: "retailer_id",
attrs: {},
content: Buffer.from(product.retailerId),
});
}
if (product.images.length) {
content.push({
tag: "media",
attrs: {},
content: product.images.map((img) => {
if (!("url" in img)) {
throw new Boom("Expected img for product to already be uploaded", {
statusCode: 400,
});
}
return {
tag: "image",
attrs: {},
content: [
{
tag: "url",
attrs: {},
content: Buffer.from(img.url.toString()),
},
],
};
}),
});
}
if (typeof product.price !== "undefined") {
content.push({
tag: "price",
attrs: {},
content: Buffer.from(product.price.toString()),
});
}
if (typeof product.currency !== "undefined") {
content.push({
tag: "currency",
attrs: {},
content: Buffer.from(product.currency),
});
}
if ("originCountryCode" in product) {
if (typeof product.originCountryCode === "undefined") {
attrs["compliance_category"] = "COUNTRY_ORIGIN_EXEMPT";
} else {
content.push({
tag: "compliance_info",
attrs: {},
content: [
{
tag: "country_code_origin",
attrs: {},
content: Buffer.from(product.originCountryCode),
},
],
});
}
}
if (typeof product.isHidden !== "undefined") {
attrs["is_hidden"] = product.isHidden.toString();
}
const node = {
tag: "product",
attrs,
content,
};
return node;
};
export const parseProductNode = (productNode) => {
const isHidden = productNode.attrs.is_hidden === "true";
const id = getBinaryNodeChildString(productNode, "id");
const mediaNode = getBinaryNodeChild(productNode, "media");
const statusInfoNode = getBinaryNodeChild(productNode, "status_info");
const product = {
id,
imageUrls: parseImageUrls(mediaNode),
reviewStatus: {
whatsapp: getBinaryNodeChildString(statusInfoNode, "status"),
},
availability: "in stock",
name: getBinaryNodeChildString(productNode, "name"),
retailerId: getBinaryNodeChildString(productNode, "retailer_id"),
url: getBinaryNodeChildString(productNode, "url"),
description: getBinaryNodeChildString(productNode, "description"),
price: +getBinaryNodeChildString(productNode, "price"),
currency: getBinaryNodeChildString(productNode, "currency"),
isHidden,
};
return product;
};
/**
* Uploads images not already uploaded to WA's servers
*/
export async function uploadingNecessaryImagesOfProduct(
product,
waUploadToServer,
timeoutMs = 30000
) {
product = {
...product,
images: product.images
? await uploadingNecessaryImages(product.images, waUploadToServer, timeoutMs)
: product.images,
};
return product;
}
/**
* Uploads images not already uploaded to WA's servers
*/
export const uploadingNecessaryImages = async (images, waUploadToServer, timeoutMs = 30000) => {
const results = await Promise.all(
images.map(async (img) => {
if ("url" in img) {
const url = img.url.toString();
if (url.includes(".whatsapp.net")) {
return { url };
}
}
const { stream } = await getStream(img);
const hasher = createHash("sha256");
const filePath = join(tmpdir(), "img" + generateMessageIDV2());
const encFileWriteStream = createWriteStream(filePath);
for await (const block of stream) {
hasher.update(block);
encFileWriteStream.write(block);
}
const sha = hasher.digest("base64");
const { directPath } = await waUploadToServer(filePath, {
mediaType: "product-catalog-image",
fileEncSha256B64: sha,
timeoutMs,
});
await fs.unlink(filePath).catch((err) => console.log("Error deleting temp file ", err));
return { url: getUrlFromDirectPath(directPath) };
})
);
return results;
};
const parseImageUrls = (mediaNode) => {
const imgNode = getBinaryNodeChild(mediaNode, "image");
return {
requested: getBinaryNodeChildString(imgNode, "request_image_url"),
original: getBinaryNodeChildString(imgNode, "original_image_url"),
};
};
const parseStatusInfo = (mediaNode) => {
const node = getBinaryNodeChild(mediaNode, "status_info");
return {
status: getBinaryNodeChildString(node, "status"),
canAppeal: getBinaryNodeChildString(node, "can_appeal") === "true",
};
};
//# sourceMappingURL=business.js.map