mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
feat: implement fetching product catalog + order details on MD
This commit is contained in:
150
src/Socket/business.ts
Normal file
150
src/Socket/business.ts
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import { SocketConfig } from '../Types'
|
||||||
|
import { parseCatalogNode, parseCollectionsNode, parseOrderDetailsNode } from '../Utils/business'
|
||||||
|
import { jidNormalizedUser, S_WHATSAPP_NET } from '../WABinary'
|
||||||
|
import { makeMessagesRecvSocket } from './messages-recv'
|
||||||
|
|
||||||
|
export const makeBusinessSocket = (config: SocketConfig) => {
|
||||||
|
const { logger } = config
|
||||||
|
const sock = makeMessagesRecvSocket(config)
|
||||||
|
const {
|
||||||
|
authState,
|
||||||
|
query
|
||||||
|
} = sock
|
||||||
|
|
||||||
|
const getCatalog = async(jid?: string, limit = 10) => {
|
||||||
|
jid = jidNormalizedUser(jid || authState.creds.me?.id)
|
||||||
|
const result = await query({
|
||||||
|
tag: 'iq',
|
||||||
|
attrs: {
|
||||||
|
to: S_WHATSAPP_NET,
|
||||||
|
type: 'get',
|
||||||
|
xmlns: 'w:biz:catalog'
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'product_catalog',
|
||||||
|
attrs: {
|
||||||
|
jid,
|
||||||
|
allow_shop_source: 'true'
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'limit',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48 ])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'width',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48, 48 ])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'height',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48, 48 ])
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
return parseCatalogNode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCollections = async(jid?: string, limit = 51) => {
|
||||||
|
jid = jidNormalizedUser(jid || authState.creds.me?.id)
|
||||||
|
const result = await query({
|
||||||
|
tag: 'iq',
|
||||||
|
attrs: {
|
||||||
|
to: S_WHATSAPP_NET,
|
||||||
|
type: 'get',
|
||||||
|
xmlns: 'w:biz:catalog',
|
||||||
|
smax_id: '35'
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'collections',
|
||||||
|
attrs: {
|
||||||
|
biz_jid: jid,
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'collection_limit',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48 ])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'item_limit',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ limit ])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'width',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48, 48 ])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'height',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48, 48 ])
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return parseCollectionsNode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOrderDetails = async(orderId: string, tokenBase64: string) => {
|
||||||
|
const result = await query({
|
||||||
|
tag: 'iq',
|
||||||
|
attrs: {
|
||||||
|
to: S_WHATSAPP_NET,
|
||||||
|
type: 'get',
|
||||||
|
xmlns: 'fb:thrift_iq',
|
||||||
|
smax_id: '5'
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'order',
|
||||||
|
attrs: {
|
||||||
|
op: 'get',
|
||||||
|
id: orderId
|
||||||
|
},
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'image_dimensions',
|
||||||
|
attrs: { },
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
tag: 'width',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48, 48 ])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'height',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from([ 49, 48, 48 ])
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tag: 'token',
|
||||||
|
attrs: { },
|
||||||
|
content: Buffer.from(tokenBase64, 'base64')
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
return parseOrderDetailsNode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...sock,
|
||||||
|
getCatalog,
|
||||||
|
getCollections,
|
||||||
|
getOrderDetails,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults'
|
import { DEFAULT_CONNECTION_CONFIG } from '../Defaults'
|
||||||
import { SocketConfig } from '../Types'
|
import { SocketConfig } from '../Types'
|
||||||
import { makeMessagesRecvSocket as _makeSocket } from './messages-recv'
|
import { makeBusinessSocket as _makeSocket } from './business'
|
||||||
|
|
||||||
// export the last socket layer
|
// export the last socket layer
|
||||||
const makeWASocket = (config: Partial<SocketConfig>) => (
|
const makeWASocket = (config: Partial<SocketConfig>) => (
|
||||||
|
|||||||
@@ -10,6 +10,19 @@ export type ProductCreateResult = {
|
|||||||
data: { product: any }
|
data: { product: any }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CatalogStatus = {
|
||||||
|
status: string
|
||||||
|
canAppeal: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CatalogCollection = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
products: Product[]
|
||||||
|
|
||||||
|
status: CatalogStatus
|
||||||
|
}
|
||||||
|
|
||||||
export type ProductAvailability = 'in stock'
|
export type ProductAvailability = 'in stock'
|
||||||
|
|
||||||
export type ProductBase = {
|
export type ProductBase = {
|
||||||
|
|||||||
107
src/Utils/business.ts
Normal file
107
src/Utils/business.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { CatalogCollection, CatalogStatus, OrderDetails, OrderProduct, Product } from '../Types'
|
||||||
|
import { BinaryNode, getBinaryNodeChild, getBinaryNodeChildBuffer, getBinaryNodeChildren, getBinaryNodeChildString } from '../WABinary'
|
||||||
|
|
||||||
|
export const parseCatalogNode = (node: BinaryNode) => {
|
||||||
|
const catalogNode = getBinaryNodeChild(node, 'product_catalog')
|
||||||
|
const products = getBinaryNodeChildren(catalogNode, 'product').map(parseProductNode)
|
||||||
|
return { products }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseCollectionsNode = (node: BinaryNode) => {
|
||||||
|
const collectionsNode = getBinaryNodeChild(node, 'collections')
|
||||||
|
const collections = getBinaryNodeChildren(collectionsNode, 'collection').map<CatalogCollection>(
|
||||||
|
collectionNode => {
|
||||||
|
const id = parseCatalogId(collectionNode)
|
||||||
|
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: BinaryNode) => {
|
||||||
|
const orderNode = getBinaryNodeChild(node, 'order')
|
||||||
|
const products = getBinaryNodeChildren(orderNode, 'product').map<OrderProduct>(
|
||||||
|
productNode => {
|
||||||
|
const imageNode = getBinaryNodeChild(productNode, 'image')
|
||||||
|
return {
|
||||||
|
id: parseCatalogId(productNode),
|
||||||
|
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: OrderDetails = {
|
||||||
|
price: {
|
||||||
|
total: +getBinaryNodeChildString(priceNode, 'total'),
|
||||||
|
currency: getBinaryNodeChildString(priceNode, 'currency'),
|
||||||
|
},
|
||||||
|
products
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseProductNode = (productNode: BinaryNode) => {
|
||||||
|
const isHidden = productNode.attrs.is_hidden === 'true'
|
||||||
|
const id = parseCatalogId(productNode)
|
||||||
|
|
||||||
|
const mediaNode = getBinaryNodeChild(productNode, 'media')
|
||||||
|
const statusInfoNode = getBinaryNodeChild(productNode, 'status_info')
|
||||||
|
|
||||||
|
const product: 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
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseImageUrls = (mediaNode: BinaryNode) => {
|
||||||
|
const imgNode = getBinaryNodeChild(mediaNode, 'image')
|
||||||
|
return {
|
||||||
|
requested: getBinaryNodeChildString(imgNode, 'request_image_url'),
|
||||||
|
original: getBinaryNodeChildString(imgNode, 'original_image_url')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseStatusInfo = (mediaNode: BinaryNode): CatalogStatus => {
|
||||||
|
const node = getBinaryNodeChild(mediaNode, 'status_info')
|
||||||
|
return {
|
||||||
|
status: getBinaryNodeChildString(node, 'status'),
|
||||||
|
canAppeal: getBinaryNodeChildString(node, 'can_appeal') === 'true',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseCatalogId = (node: BinaryNode) => {
|
||||||
|
const idNode = getBinaryNodeChildBuffer(node, 'id')
|
||||||
|
const id = Buffer.from(idNode).readBigUInt64LE().toString()
|
||||||
|
return id
|
||||||
|
}
|
||||||
@@ -33,6 +33,15 @@ export const getBinaryNodeChildBuffer = (node: BinaryNode, childTag: string) =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getBinaryNodeChildString = (node: BinaryNode, childTag: string) => {
|
||||||
|
const child = getBinaryNodeChild(node, childTag)?.content
|
||||||
|
if(Buffer.isBuffer(child) || child instanceof Uint8Array) {
|
||||||
|
return Buffer.from(child).toString('utf-8')
|
||||||
|
} else if(typeof child === 'string') {
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getBinaryNodeChildUInt = (node: BinaryNode, childTag: string, length: number) => {
|
export const getBinaryNodeChildUInt = (node: BinaryNode, childTag: string, length: number) => {
|
||||||
const buff = getBinaryNodeChildBuffer(node, childTag)
|
const buff = getBinaryNodeChildBuffer(node, childTag)
|
||||||
if(buff) {
|
if(buff) {
|
||||||
|
|||||||
Reference in New Issue
Block a user