UNPKG

ws-cloud-api

Version:

WhatsApp Cloud API for NodeJS

385 lines (381 loc) 8.24 kB
import { uploadMedia } from '../media.mjs'; import { MessageTypes, InteractiveTypes } from '../index.mjs'; async function sendRequest({ id, body, path, query, method, config }) { const apiVersion = typeof process !== "undefined" ? process.env.WS_CA_VERSION ?? config?.apiVersion ?? "20.0" : config?.apiVersion ?? "20.0"; const phoneNumberId = typeof process !== "undefined" ? process.env.WS_PHONE_NUMBER_ID ?? config?.phoneNumberId : config?.phoneNumberId; const businessId = typeof process !== "undefined" ? process.env.WS_BUSINESS_ID ?? config?.businessId : config?.businessId; const token = typeof process !== "undefined" ? process.env.WS_TOKEN ?? config?.token : config?.token; const requestId = id === "phoneNumberId" ? phoneNumberId : businessId; if (requestId === void 0) { return { success: false, error: "Missing request ID" }; } if (token === void 0) { return { success: false, error: "Missing token" }; } try { const response = await fetch( `https://graph.facebook.com/v${apiVersion}/${requestId}/${path}${query !== void 0 ? `?${query}` : ""}`, { method, headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }, body } ); if (!response.ok) { return { success: false, error: response }; } return { success: true, response: await response.json() }; } catch (error) { console.error("Fetch failed:", error); return { success: false, error }; } } async function sendMessageRequest({ to, body, config }) { const postBody = JSON.stringify({ messaging_product: "whatsapp", to, ...body }); const requestResponse = await sendRequest({ id: "phoneNumberId", body: postBody, path: "messages", method: "POST", config }); if (!requestResponse.success) { console.error(`Failed to send ${body.type} message`); console.error(JSON.stringify(await requestResponse.error, null, 2)); } return requestResponse; } async function sendText({ to, message, previewUrl, config }) { return await sendMessageRequest({ to, body: { type: MessageTypes.Text, [MessageTypes.Text]: { preview_url: previewUrl, body: message } }, config }); } async function sendContact({ to, contacts, config }) { return await sendMessageRequest({ to, body: { type: MessageTypes.Contacts, [MessageTypes.Contacts]: contacts }, config }); } async function sendSimpleMedia({ to, type, link, filename, caption, config }) { return await sendMessageRequest({ to, // FIXME: Typescript not identifying the type // @ts-expect-error Typescript not identifying the type body: { type, [type]: { link, filename, caption } }, config }); } async function sendImage({ to, link, config }) { return await sendSimpleMedia({ to, type: MessageTypes.Image, link, config }); } async function sendVideo({ to, link, config }) { return await sendSimpleMedia({ to, type: MessageTypes.Video, link, config }); } async function sendDocument({ to, link, filename, caption, config }) { return await sendSimpleMedia({ to, type: MessageTypes.Document, link, filename, caption, config }); } async function sendAudio({ to, link, config }) { return await sendSimpleMedia({ to, type: MessageTypes.Audio, link, config }); } async function sendFile({ to, file, config }) { try { const mediaId = await uploadMedia({ media: file, config }); const mimeType = file.type.split("/")[0]; const type = mimeType === "text" || mimeType === "application" ? MessageTypes.Document : mimeType; return await sendMessageRequest({ to, // FIXME: Typescript not identifying the type // @ts-expect-error Typescript not identifying the type body: { type, [type]: { id: mediaId } }, config }); } catch (error) { console.error("Failed to send file"); console.error(error); return { success: false, error }; } } function generateInteractiveBody(input) { return { type: MessageTypes.Interactive, interactive: input }; } async function sendButtonMessage({ to, message, config }) { const body = { type: InteractiveTypes.Button, body: { text: message.text }, action: { buttons: [] } }; for (let i = 0; i < message.buttons.length; i++) { body.action.buttons.push({ type: "reply", reply: message.buttons[i] }); } return await sendMessageRequest({ to, body: generateInteractiveBody(body), config }); } async function sendCTAButtonMessage({ to, message, config }) { const body = { type: InteractiveTypes.CTAButton, body: { text: message.text }, action: { name: InteractiveTypes.CTAButton, parameters: { display_text: message.buttonText, url: message.url } } }; return await sendMessageRequest({ to, body: generateInteractiveBody(body), config }); } async function sendInteractiveListMessage({ to, list, config }) { const body = { type: InteractiveTypes.List, body: { text: list.text }, action: { button: list.buttonText, sections: [ { title: list.buttonText, rows: [] } ] } }; for (let i = 0; i < list.list.length; i++) { body.action.sections[0].rows.push({ id: list.list[i].description, ...list.list[i] }); } return await sendMessageRequest({ to, body: generateInteractiveBody(body), config }); } async function sendInteractiveSectionListMessage({ to, list, config }) { const body = { type: InteractiveTypes.List, body: { text: list.text }, action: { button: list.buttonText, sections: [] } }; for (let i = 0; i < list.sections.length; i++) { body.action.sections.push({ title: list.sections[i].sectionTitle, rows: [] }); for (let j = 0; j < list.sections[i].list.length; j++) { body.action.sections[i].rows.push({ id: list.sections[i].list[j].description, ...list.sections[i].list[j] }); } } return await sendMessageRequest({ to, body: generateInteractiveBody(body), config }); } async function sendFlowMessage({ to, flow, draft, config }) { const body = { type: InteractiveTypes.Flow, body: { text: flow.text }, action: { name: "flow", parameters: { mode: draft === true ? "draft" : "published", flow_message_version: "3", flow_action: flow.initDataExchange === true ? "data_exchange" : "navigate", flow_token: flow.token, flow_id: flow.id, flow_cta: flow.ctaText, flow_action_payload: flow.initDataExchange === true ? void 0 : { screen: flow.defaultScreen } } } }; return await sendMessageRequest({ to, body: generateInteractiveBody(body), config }); } async function sendTypingIndicator({ input, config }) { const postBody = JSON.stringify({ messaging_product: "whatsapp", status: "read", message_id: input.messageId, typing_indicator: { type: "text" } }); const requestResponse = await sendRequest({ id: "phoneNumberId", body: postBody, path: "messages", method: "POST", config }); if (!requestResponse.success) { console.error(`Failed to send typing indicator`); console.error(JSON.stringify(await requestResponse.error, null, 2)); } return requestResponse; } export { sendRequest as a, sendText as b, sendContact as c, sendImage as d, sendVideo as e, sendDocument as f, sendAudio as g, sendFile as h, sendButtonMessage as i, sendCTAButtonMessage as j, sendInteractiveListMessage as k, sendInteractiveSectionListMessage as l, sendFlowMessage as m, sendTypingIndicator as n, sendMessageRequest as s };