UNPKG

whatsapp-api-js

Version:

A TypeScript server agnostic Whatsapp's Official API framework

372 lines 15 kB
/** @module WhatsAppAPI */ import type { WhatsAppAPIConstructorArguments, PostData, GetParams, ClientMessage, ServerMessageResponse, ServerMarkAsReadResponse, ServerCreateQRResponse, ServerRetrieveQRResponse, ServerUpdateQRResponse, ServerDeleteQRResponse, ServerMediaRetrieveResponse, ServerMediaUploadResponse, ServerMediaDeleteResponse } from "./types"; import type { OnMessage, OnSent, OnStatus } from "./emitters"; /** * The main API Class */ export default class WhatsAppAPI { /** * The API token */ private token; /** * The app secret */ private appSecret?; /** * The webhook verify token */ private webhookVerifyToken?; /** * The API version to use */ private v; /** * The fetch function for the requests */ private fetch; /** * The CryptoSubtle library for checking the signatures */ private subtle?; /** * If true, API operations will return the fetch promise instead. Intended for low level debugging. */ private parsed; /** * If false, the user functions won't be offloaded from the main event loop. * Intended for Serverless Environments where the process might be killed after the main function finished. */ private offload_functions; /** * If false, the API will be used in a less secure way, reducing the need for appSecret. Defaults to true. */ private secure; /** * The callbacks for the events (message, sent, status) * * @example * ```ts * const Whatsapp = new WhatsAppAPI({ * token: "my-token", * appSecret: "my-app-secret" * }); * * // Set the callback * Whatsapp.on.message = ({ from, phoneID }) => console.log(`Message from ${from} to bot ${phoneID}`); * * // Remove the callback * Whatsapp.on.message = undefined; * ``` */ on: { message?: OnMessage; sent?: OnSent; status?: OnStatus; }; /** * Main entry point for the API. * * It's highly recommended reading the named parameters docs at * {@link types.TheBasicConstructorArguments}, * at least for `token`, `appSecret` and `webhookVerifyToken` properties, * which are the most common in normal usage. * * The other parameters are used for fine tunning the framework, * such as `ponyfill`, which allows the code to execute on platforms * that are missing standard APIs such as fetch and crypto. * * @throws If fetch is not defined in the enviroment and the provided ponyfill isn't a function * @throws If secure is true, crypto.subtle is not defined in the enviroment and the provided ponyfill isn't an object */ constructor({ token, appSecret, webhookVerifyToken, v, parsed, offload_functions, secure, ponyfill }: WhatsAppAPIConstructorArguments); /** * Send a Whatsapp message * * @example * ```ts * import WhatsAppAPI from "whatsapp-api-js"; * import Text from "whatsapp-api-js/messages/text"; * * const Whatsapp = new WhatsAppAPI({ * token: "YOUR_TOKEN", * appSecret: "YOUR_APP_SECRET" * }); * * Whatsapp.sendMessage( * "BOT_PHONE_ID", * "USER_PHONE", * new Text("Hello World") * ).then(console.log); * ``` * * @param phoneID - The bot's phone ID * @param to - The user's phone number * @param message - A Whatsapp message, built using the corresponding module for each type of message. * @param context - The message ID of the message to reply to * @param biz_opaque_callback_data - An arbitrary 512B string, useful for tracking (length not checked by the framework) * @returns The server response */ sendMessage(phoneID: string, to: string, message: ClientMessage, context?: string, biz_opaque_callback_data?: string): Promise<ServerMessageResponse | Response>; /** * Send the same Whatsapp message to multiple phone numbers. * * In order to avoid reaching the * [API rate limit](https://developers.facebook.com/docs/whatsapp/cloud-api/overview?locale=en_US#throughput), * this method will send the messages in batches of 50 per second by default, * but this can be changed using the `batch_size` and `delay` parameters. * * The API rate limit can be increased by contacting Facebook as explained * [here](https://developers.facebook.com/docs/whatsapp/cloud-api/overview?locale=en_US#throughput). * * @example * ```ts * import WhatsAppAPI from "whatsapp-api-js"; * import Text from "whatsapp-api-js/messages/text"; * * const Whatsapp = new WhatsAppAPI({ * token: "YOUR_TOKEN", * appSecret: "YOUR_APP_SECRET" * }); * * const phoneID = "YOUR_BOT_NUMBER"; * const users = ["YOUR_USER1_NUMBER", "YOUR_USER2_NUMBER"]; * const message = new Text("Hello World"); * * const responses = Whatsapp.broadcastMessage(phoneID, users, message); * * Promise.all(responses).then(console.log); * ``` * * @param phoneID - The bot's phone ID * @param to - The users' phone numbers * @param message - A Whatsapp message, built using the corresponding module for each type of message. * @param batch_size - The number of messages to send per batch * @param delay - The delay between each batch of messages in milliseconds * @returns The server response * @throws if batch_size is lower than 1 * @throws if delay is lower than 0 */ broadcastMessage(phoneID: string, to: string[], message: ClientMessage, batch_size?: number, delay?: number): Promise<Array<ReturnType<typeof this.sendMessage>>>; /** * Mark a message as read * * @param phoneID - The bot's phone ID * @param messageId - The message ID * @returns The server response */ markAsRead(phoneID: string, messageId: string): Promise<ServerMarkAsReadResponse | Response>; /** * Generate a QR code for sharing the bot * * @param phoneID - The bot's phone ID * @param message - The quick message on the QR code * @param format - The format of the QR code * @returns The server response */ createQR(phoneID: string, message: string, format?: "png" | "svg"): Promise<ServerCreateQRResponse | Response>; /** * Get one or many QR codes of the bot * * @param phoneID - The bot's phone ID * @param id - The QR's id to find. If not specified, all QRs will be returned * @returns The server response */ retrieveQR(phoneID: string, id?: string): Promise<ServerRetrieveQRResponse | Response>; /** * Update a QR code of the bot * * @param phoneID - The bot's phone ID * @param id - The QR's id to edit * @param message - The new quick message for the QR code * @returns The server response */ updateQR(phoneID: string, id: string, message: string): Promise<ServerUpdateQRResponse | Response>; /** * Delete a QR code of the bot * * @param phoneID - The bot's phone ID * @param id - The QR's id to delete * @returns The server response */ deleteQR(phoneID: string, id: string): Promise<ServerDeleteQRResponse | Response>; /** * Get a Media object data with an ID * * @param id - The Media's ID * @param phoneID - Business phone number ID. If included, the operation will only be processed if the ID matches the ID of the business phone number that the media was uploaded on. * @returns The server response */ retrieveMedia(id: string, phoneID?: string): Promise<ServerMediaRetrieveResponse | Response>; /** * Upload a Media to the server * * @param phoneID - The bot's phone ID * @param form - The Media's FormData. Must have a 'file' property with the file to upload as a blob and a valid mime-type in the 'type' field of the blob. Example for Node ^18: new FormData().set("file", new Blob([stringOrFileBuffer], "image/png")); Previous versions of Node will need an external FormData, such as undici's. To use non spec complaints versions of FormData (eg: form-data) or Blob set the 'check' parameter to false. * @param check - If the FormData should be checked before uploading. The FormData must have the method .get("name") to work with the checks. If it doesn't (for example, using the module "form-data"), set this to false. * @returns The server response * @throws If check is set to true and form is not a FormData * @throws If check is set to true and the form doesn't have valid required properties (file, type) * @throws If check is set to true and the form file is too big for the file type * @example * ```ts * import WhatsAppAPI from "whatsapp-api-js"; * * const token = "token"; * const appSecret = "appSecret"; * * const Whatsapp = new WhatsAppAPI({ token, appSecret }); * * // If required: * // import FormData from "undici"; * // import { Blob } from "node:buffer"; * * const form = new FormData(); * * // If you don't mind reading the whole file into memory: * form.set("file", new Blob([fs.readFileSync("image.png")], "image/png")); * * // If you do, you will need to use streams. The module "form-data", * // although not spec compliant (hence needing to set check to false), * // has an easy way to do this: * // form.append("file", fs.createReadStream("image.png"), { contentType: "image/png" }); * * console.log(await Whatsapp.uploadMedia("phoneID", form)); * // Expected output: { id: "mediaID" } * ``` */ uploadMedia(phoneID: string, form: unknown, check?: boolean): Promise<ServerMediaUploadResponse | Response>; /** * Get a Media fetch from an url. * When using this method, be sure to pass a trusted url, since the request will be authenticated with the token. * * @param url - The Media's url * @returns The fetch raw response * @throws If url is not a valid url * @example * ```ts * import WhatsAppAPI from "whatsapp-api-js"; * * const token = "token"; * const appSecret = "appSecret"; * * const Whatsapp = new WhatsAppAPI({ token, appSecret }); * * const id = "mediaID"; * const { url } = await Whatsapp.retrieveMedia(id); * const response = Whatsapp.fetchMedia(url); * ``` */ fetchMedia(url: string): Promise<Response>; /** * Delete a Media object with an ID * * @param id - The Media's ID * @param phoneID - Business phone number ID. If included, the operation will only be processed if the ID matches the ID of the business phone number that the media was uploaded on. * @returns The server response */ deleteMedia(id: string, phoneID?: string): Promise<ServerMediaDeleteResponse | Response>; /** * POST helper, must be called inside the post function of your code. * When setting up the webhook, only subscribe to messages. Other subscritions support might be added later. * * @example * ```ts * // author arivanbastos on issue #114 * // Simple http example implementation with Whatsapp.post() on Node@^19 * import WhatsAppAPI from "whatsapp-api-js"; * import { NodeNext } from "whatsapp-api-js/setup/node"; * * import { createServer } from "http"; * * const token = "token"; * const appSecret = "appSecret"; * const Whatsapp = new WhatsAppAPI(NodeNext({ token, appSecret })); * * function handler(req, res) { * if (req.method == "POST") { * const chunks = []; * req.on("data", (chunk) => chunks.push(chunk)); * * req.on("end", async () => { * const body = Buffer.concat(chunks).toString(); * * try { * const response = await Whatsapp.post(JSON.parse(body), body, req.headers["x-hub-signature-256"]); * res.writeHead(response); * } catch (err) { * res.writeHead(err); * } * * res.end(); * }); * } else res.writeHead(501).end(); * }; * * Whatsapp.on.message = ({ phoneID, from, message, name }) => { * console.log(`User ${name} (${from}) sent to bot ${phoneID} a(n) ${message.type}`); * }; * * const server = createServer(handler); * server.listen(3000); * ``` * * @param data - The POSTed data object sent by Whatsapp * @param raw_body - The raw body of the POST request * @param signature - The x-hub-signature-256 (all lowercase) header signature sent by Whatsapp * @returns 200, it's the expected http/s response code * @throws 500 if secure and the appSecret isn't specified * @throws 501 if secure and crypto.subtle or ponyfill isn't available * @throws 400 if secure and the raw body is missing * @throws 401 if secure and the signature is missing * @throws 401 if secure and the signature doesn't match the hash * @throws 400 if the POSTed data is not a valid Whatsapp API request */ post(data: PostData, raw_body?: string, signature?: string): Promise<200>; /** * GET helper, must be called inside the get function of your code. * Used once at the first webhook setup. * * @param params - The request object sent by Whatsapp * @returns The challenge string, it must be the http response body * @throws 500 if webhookVerifyToken is not specified * @throws 400 if the request is missing data * @throws 403 if the verification tokens don't match */ get(params: GetParams): string; /** * Make an authenticated request to any url. * When using this method, be sure to pass a trusted url, since the request will be authenticated with the token. * * @internal * @param url - The url to request to * @returns The fetch response * @throws If url is not specified */ _authenicatedRequest(url: string | URL | Request): Promise<Response>; /** * Get the body of a fetch response * * @internal * @param promise - The fetch response * @returns The json body parsed */ private getBody; /** * Call a user function, offloading it from the main thread if needed * * @internal * @param f - The user function to call * @param a - The arguments to pass to the function */ private user_function; /** * Offload a function to the next tick of the event loop * * @internal * @param f - The function to offload from the main thread * @param a - The arguments to pass to the function */ private offload; } //# sourceMappingURL=index.d.ts.map