whatsapp-api-js
Advanced tools
Comparing version 0.6.2 to 0.7.0-beta.0
89
index.js
@@ -183,45 +183,48 @@ // Most of these imports are here only for types checks | ||
* @namespace Exports | ||
* @property {WhatsAppAPI} WhatsAppAPI The main API object | ||
* @property {Object} Handlers The handlers object | ||
* @property {Function} Handlers.post The post handler | ||
* @property {Function} Handlers.get The get handler | ||
* @property {Object} Types The API types objects | ||
* @property {Object} Types.Contacts The Contacts module | ||
* @property {Contacts} Types.Contacts.Contacts The API Contacts type object | ||
* @property {Address} Types.Contacts.Address The API Address type object | ||
* @property {Birthday} Types.Contacts.Birthday The API Birthday type object | ||
* @property {Email} Types.Contacts.Email The API Email type object | ||
* @property {Name} Types.Contacts.Name The API Name type object | ||
* @property {Organization} Types.Contacts.Organization The API Organization type object | ||
* @property {Phone} Types.Contacts.Phone The API Phone type object | ||
* @property {Url} Types.Contacts.Url The API Url type object | ||
* @property {Object} Types.Interactive The Interactive module | ||
* @property {Interactive} Types.Interactive.Interactive The API Interactive type object | ||
* @property {Body} Types.Interactive.Body The API Body type object | ||
* @property {Footer} Types.Interactive.Footer The API Footer type object | ||
* @property {Header} Types.Interactive.Header The API Header type object | ||
* @property {ActionList} Types.Interactive.ActionList The API Action type object | ||
* @property {Section} Types.Interactive.Section The API Section type object | ||
* @property {Row} Types.Interactive.Row The API Row type object | ||
* @property {ActionButtons} Types.Interactive.ActionButtons The API Action type object | ||
* @property {Button} Types.Interactive.Button The API Button type object | ||
* @property {Location} Types.Location The API Location type object | ||
* @property {Object} Types.Media The Media module | ||
* @property {Media} Types.Media.Media Placeholder, don't use | ||
* @property {Audio} Types.Media.Audio The API Audio type object | ||
* @property {Document} Types.Media.Document The API Document type object | ||
* @property {Image} Types.Media.Image The API Image type object | ||
* @property {Sticker} Types.Media.Sticker The API Sticker type object | ||
* @property {Video} Types.Media.Video The API Video type object | ||
* @property {Object} Types.Template The Template module | ||
* @property {Template} Types.Template.Template The API Template type object | ||
* @property {Language} Types.Template.Language The API Language type object | ||
* @property {ButtonComponent} Types.Template.ButtonComponent The API ButtonComponent type object | ||
* @property {ButtonParameter} Types.Template.ButtonParameter The API ButtonParameter type object | ||
* @property {HeaderComponent} Types.Template.HeaderComponent The API HeaderComponent type object | ||
* @property {BodyComponent} Types.Template.BodyComponent The API BodyComponent type object | ||
* @property {Parameter} Types.Template.Parameter The API Parameter type object | ||
* @property {Currency} Types.Template.Currency The API Currency type object | ||
* @property {DateTime} Types.Template.DateTime The API DateTime type object | ||
* @property {Text} Types.Text The API Text type object | ||
* @property {WhatsAppAPI} WhatsAppAPI The main API object | ||
* @property {Object} Handlers The handlers object | ||
* @property {Function} Handlers.post The post handler | ||
* @property {Function} Handlers.get The get handler | ||
* @property {Object} Types The API types objects | ||
* @property {Object} Types.Contacts The Contacts module | ||
* @property {Contacts} Types.Contacts.Contacts The API Contacts type object | ||
* @property {Address} Types.Contacts.Address The API Address type object | ||
* @property {Birthday} Types.Contacts.Birthday The API Birthday type object | ||
* @property {Email} Types.Contacts.Email The API Email type object | ||
* @property {Name} Types.Contacts.Name The API Name type object | ||
* @property {Organization} Types.Contacts.Organization The API Organization type object | ||
* @property {Phone} Types.Contacts.Phone The API Phone type object | ||
* @property {Url} Types.Contacts.Url The API Url type object | ||
* @property {Object} Types.Interactive The Interactive module | ||
* @property {Interactive} Types.Interactive.Interactive The API Interactive type object | ||
* @property {Body} Types.Interactive.Body The API Body type object | ||
* @property {Footer} Types.Interactive.Footer The API Footer type object | ||
* @property {Header} Types.Interactive.Header The API Header type object | ||
* @property {ActionButtons} Types.Interactive.ActionButtons The API Action type object | ||
* @property {Button} Types.Interactive.Button The API Button type object | ||
* @property {ActionList} Types.Interactive.ActionList The API Action type object | ||
* @property {ListSection} Types.Interactive.ListSection The API Section type object | ||
* @property {Row} Types.Interactive.Row The API Row type object | ||
* @property {ActionCatalog} Types.Interactive.ActionCatalog The API Action type object | ||
* @property {ProductSection} Types.Interactive.ProductSection The API Section type object | ||
* @property {Product} Types.Interactive.Product The API Product type object | ||
* @property {Location} Types.Location The API Location type object | ||
* @property {Object} Types.Media The Media module | ||
* @property {Media} Types.Media.Media Placeholder, don't use | ||
* @property {Audio} Types.Media.Audio The API Audio type object | ||
* @property {Document} Types.Media.Document The API Document type object | ||
* @property {Image} Types.Media.Image The API Image type object | ||
* @property {Sticker} Types.Media.Sticker The API Sticker type object | ||
* @property {Video} Types.Media.Video The API Video type object | ||
* @property {Object} Types.Template The Template module | ||
* @property {Template} Types.Template.Template The API Template type object | ||
* @property {Language} Types.Template.Language The API Language type object | ||
* @property {ButtonComponent} Types.Template.ButtonComponent The API ButtonComponent type object | ||
* @property {ButtonParameter} Types.Template.ButtonParameter The API ButtonParameter type object | ||
* @property {HeaderComponent} Types.Template.HeaderComponent The API HeaderComponent type object | ||
* @property {BodyComponent} Types.Template.BodyComponent The API BodyComponent type object | ||
* @property {Parameter} Types.Template.Parameter The API Parameter type object | ||
* @property {Currency} Types.Template.Currency The API Currency type object | ||
* @property {DateTime} Types.Template.DateTime The API DateTime type object | ||
* @property {Text} Types.Text The API Text type object | ||
*/ | ||
@@ -228,0 +231,0 @@ module.exports = { |
{ | ||
"name": "whatsapp-api-js", | ||
"version": "0.6.2", | ||
"version": "0.7.0-beta.0", | ||
"author": "Secreto31126", | ||
"description": "A Whatsapp Official API helper for Node.js", | ||
"description": "A Whatsapp Official API framework for Node.js", | ||
"license": "MIT", | ||
@@ -7,0 +7,0 @@ "main": "index.js", |
# whatsapp-api-js | ||
A Whatsapp's Official API framework for Node.js [(and others)](#running-outside-of-nodejs) | ||
## List of contents | ||
- [Disclaimers](#disclaimers) | ||
- [Set up](#set-up) | ||
- [Running outside of Node.js](#running-outside-of-nodejs) | ||
- [Breaking changes](#breaking-changes) | ||
- [Documentation](#documentation) | ||
- [Beta Releases](#beta-releases) | ||
- [Comments](#comments) | ||
## Disclaimers | ||
@@ -28,2 +38,3 @@ | ||
```js | ||
// Read Running outside of Nodejs to see support for other engines | ||
const { WhatsAppAPI, Handlers, Types } = require("whatsapp-api-js"); | ||
@@ -42,3 +53,3 @@ const { Text, Media, Contacts } = Types; | ||
function onMessage(phoneID, phone, message, name, raw_data) { | ||
async function onMessage(phoneID, phone, message, name, raw_data) { | ||
console.log(`User ${phone} (${name}) sent to bot ${phoneID} ${JSON.stringify(message)}`); | ||
@@ -48,3 +59,3 @@ | ||
if (message.type === "text") promise = Whatsapp.sendMessage(phoneID, phone, new Text(`*${name}* said:\n\n${message.text.body}`)); | ||
if (message.type === "text") promise = Whatsapp.sendMessage(phoneID, phone, new Text(`*${name}* said:\n\n${message.text.body}`), message.id); | ||
@@ -68,3 +79,3 @@ if (message.type === "image") promise = Whatsapp.sendMessage(phoneID, phone, new Media.Image(message.image.id, true, `Nice photo, ${name}`)); | ||
if (promise) promise.then(console.log); | ||
console.log(await promise ?? "There are more types of messages, such as locations, templates/interactives replies and all the other medias types."); | ||
@@ -104,12 +115,16 @@ Whatsapp.markAsRead(phoneID, message.id); | ||
Personal suggestion, use [esm.sh](https://esm.sh/) to import the code directly from npm, works flawlessly with Deno. | ||
Bun seems to be more picky with the urls, but using ```bun install whatsapp-api-js``` works great for me. | ||
With the release of Deno 1.25.0, now you can import npm modules directly to Deno. It's really simple to use: | ||
Some examples: | ||
```js | ||
import { WhatsAppAPI } from "https://esm.sh/whatsapp-api-js"; | ||
import { WhatsAppAPI } from "npm:whatsapp-api-js"; | ||
const Whatsapp = new WhatsAppAPI("YOUR_TOKEN_HERE"); | ||
``` | ||
However, the npm support is still experimental and behind the --unstable flag. | ||
If you want to use prior versions of Deno, use [https://esm.sh/whatsapp-api-js](https://esm.sh/) to import the code. | ||
Bun also works by running ```bun install whatsapp-api-js```. | ||
HTML module example: | ||
```html | ||
@@ -123,4 +138,12 @@ <script type="module"> | ||
## Breaking changes in 0.6.0 | ||
## Breaking changes | ||
### 0.7.0 | ||
With the release of cart support for Cloud API, some naming changes where made within the interactive's classes. | ||
The Section class, which was a component of the ActionList, was renamed to ListSection, to avoid confusion with | ||
the new ProductSection. | ||
### 0.6.0 | ||
Since 0.6.0, the module will no longer return the raw fetch request, now it's internally parsed and returned. | ||
@@ -127,0 +150,0 @@ This change was made in order to improve the logSentMessages function, as it can now log the server response too. |
@@ -17,3 +17,3 @@ const Text = require("./text"); | ||
* | ||
* @param {(ActionList|ActionButtons)} action The action component of the interactive message | ||
* @param {(ActionList|ActionButtons|ActionCatalog)} action The action component of the interactive message | ||
* @param {Body} body The body component of the interactive message | ||
@@ -23,7 +23,11 @@ * @param {Header} [header] The header component of the interactive message | ||
* @throws {Error} If action is not provided | ||
* @throws {Error} If body is not provided | ||
* @throws {Error} If body is not provided, unless action is an ActionCatalog with a single product | ||
* @throws {Error} If header is provided for an ActionCatalog with a single product | ||
* @throws {Error} If header is not provided for an ActionCatalog with a product list | ||
*/ | ||
constructor(action, body, header, footer) { | ||
if (!action) throw new Error("Interactive must have an action component"); | ||
if (!body) throw new Error("Interactive must have a body component"); | ||
if (action._ !== "product" && !body) throw new Error("Interactive must have a body component"); | ||
if (action._ === "product" && header) throw new Error("Interactive must not have a header component if action is a single product"); | ||
if (action._ === "product_list" && !header) throw new Error("Interactive must have a header component if action is a product list"); | ||
@@ -34,3 +38,3 @@ this.type = action._; | ||
this.action = action; | ||
this.body = body; | ||
if (body) this.body = body; | ||
if (header) this.header = header; | ||
@@ -121,2 +125,66 @@ if (footer) this.footer = footer; | ||
* | ||
* @property {Array<Button>} buttons The buttons of the action | ||
* @property {String} _ The type of the action, for internal use only | ||
*/ | ||
class ActionButtons { | ||
/** | ||
* Builds a reply buttons component for an Interactive message | ||
* | ||
* @param {...Button} button Buttons to be used in the reply buttons. Each button title must be unique within the message. Emojis are supported, markdown is not. Must be between 1 and 3 buttons. | ||
* @throws {Error} If no buttons are provided or are over 3 | ||
* @throws {Error} If two or more buttons have the same id | ||
* @throws {Error} If two or more buttons have the same title | ||
*/ | ||
constructor(...button) { | ||
if (!button.length || button.length > 3) throw new Error("Reply buttons must have between 1 and 3 buttons"); | ||
// Find if there are duplicates in button.id | ||
const ids = button.map(b => b[b.type].id); | ||
if (ids.length !== new Set(ids).size) throw new Error("Reply buttons must have unique ids"); | ||
// Find if there are duplicates in button.title | ||
const titles = button.map(b => b[b.type].title); | ||
if (titles.length !== new Set(titles).size) throw new Error("Reply buttons must have unique titles"); | ||
this.buttons = button; | ||
this._ = "button"; | ||
} | ||
} | ||
/** | ||
* Button API object | ||
* | ||
* @property {String} type The type of the button | ||
* @property {String} reply.id The id of the row | ||
* @property {String} reply.title The title of the row | ||
*/ | ||
class Button { | ||
/** | ||
* Builds a button component for ActionButtons | ||
* | ||
* @param {String} id Unique identifier for your button. It cannot have leading or trailing spaces. This ID is returned in the webhook when the button is clicked by the user. Maximum length: 256 characters. | ||
* @param {String} title Button title. It cannot be an empty string and must be unique within the message. Emojis are supported, markdown is not. Maximum length: 20 characters. | ||
* @throws {Error} If id is not provided | ||
* @throws {Error} If id is over 256 characters | ||
* @throws {Error} If title is not provided | ||
* @throws {Error} If title is over 20 characters | ||
*/ | ||
constructor(id, title) { | ||
if (!id) throw new Error("Button must have an id"); | ||
if (id.length > 256) throw new Error("Button id must be 256 characters or less"); | ||
if (/^ | $/.test(id)) throw new Error("Button id cannot have leading or trailing spaces"); | ||
if (!title) throw new Error("Button must have a title"); | ||
if (title.length > 20) throw new Error("Button title must be 20 characters or less"); | ||
this.type = "reply"; | ||
this[this.type] = { | ||
title, | ||
id | ||
}; | ||
} | ||
} | ||
/** | ||
* Action API object | ||
* | ||
* @property {String} button The button text | ||
@@ -132,3 +200,3 @@ * @property {Array<Section>} sections The sections of the action | ||
* @param {String} button Button content. It cannot be an empty string and must be unique within the message. Emojis are supported, markdown is not. Maximum length: 20 characters. | ||
* @param {...Section} sections Sections of the list | ||
* @param {...ListSection} sections Sections of the list | ||
* @throws {Error} If button is not provided | ||
@@ -143,3 +211,3 @@ * @throws {Error} If button is over 20 characters | ||
if (!sections.length || sections.length > 10) throw new Error("Action must have between 1 and 10 sections"); | ||
if (sections.length > 1) sections.forEach(s => { if (!s.title) throw new Error("Sections must have a title if more than 1 section is provided") }); | ||
if (sections.length > 1 && !sections.every(obj => obj.hasOwnProperty("title"))) throw new Error("All sections must have a title if more than 1 section is provided"); | ||
@@ -155,6 +223,6 @@ this._ = "list"; | ||
* | ||
* @property {String} title The title of the section | ||
* @property {String} [title] The title of the section | ||
* @property {Array<Row>} rows The rows of the section | ||
*/ | ||
class Section { | ||
class ListSection { | ||
/** | ||
@@ -213,27 +281,59 @@ * Builds a section component for ActionList | ||
* | ||
* @property {Array<Button>} buttons The buttons of the action | ||
* @property {String} catalog_id The id of the catalog from where to get the products | ||
* @property {String} [product_retailer_id] The product to be added to the catalog | ||
* @property {Array<ProductSection>} [sections] The section to be added to the catalog | ||
* @property {String} _ The type of the action, for internal use only | ||
*/ | ||
class ActionButtons { | ||
class ActionCatalog { | ||
/** | ||
* Builds a reply buttons component for an Interactive message | ||
* Builds a catalog component for an Interactive message | ||
* | ||
* @param {...Button} button Buttons to be used in the reply buttons. Each button title must be unique within the message. Emojis are supported, markdown is not. Must be between 1 and 3 buttons. | ||
* @throws {Error} If no buttons are provided or are over 3 | ||
* @throws {Error} If two or more buttons have the same id | ||
* @throws {Error} If two or more buttons have the same title | ||
* @param {String} catalog_id The catalog id | ||
* @param {...(Product|ProductSection)} products The products to add to the catalog | ||
* @throws {Error} If catalog_id is not provided | ||
* @throws {Error} If products is not provided | ||
* @throws {Error} If products is a single product and more than 1 product is provided | ||
* @throws {Error} If products is a product list and more than 10 sections are provided | ||
* @throws {Error} If products is a product list with more than 1 section and at least one section is missing a title | ||
*/ | ||
constructor(...button) { | ||
if (!button.length || button.length > 3) throw new Error("Reply buttons must have between 1 and 3 buttons"); | ||
constructor(catalog_id, ...products) { | ||
if (!catalog_id) throw new Error("Catalog must have a catalog id"); | ||
if (!products.length) throw new Error("Catalog must have at least one product or product section"); | ||
const single_product = products[0].product_retailer_id; | ||
if (single_product && products.length > 1) throw new Error("Catalog must have only 1 product, use a ProductSection instead"); | ||
else { | ||
if (products.length > 10) throw new Error("Catalog must have between 1 and 10 product sections"); | ||
if (products.length > 1 && !products.every(obj => obj.hasOwnProperty("title"))) throw new Error("All sections must have a title if more than 1 section is provided"); | ||
} | ||
// Find if there are duplicates in button.id | ||
const ids = button.map(b => b[b.type].id); | ||
if (ids.length !== new Set(ids).size) throw new Error("Reply buttons must have unique ids"); | ||
this.catalog_id = catalog_id; | ||
if (single_product) this.product_retailer_id = single_product; | ||
else this.sections = products; | ||
this._ = single_product ? "product" : "product_list"; | ||
} | ||
} | ||
// Find if there are duplicates in button.title | ||
const titles = button.map(b => b[b.type].title); | ||
if (titles.length !== new Set(titles).size) throw new Error("Reply buttons must have unique titles"); | ||
/** | ||
* Section API object | ||
* | ||
* @property {String} [title] The title of the section | ||
* @property {Array<Product>} product_items The products of the section | ||
*/ | ||
class ProductSection { | ||
/** | ||
* Builds a product section component for an ActionCatalog | ||
* | ||
* @param {String} [title] The title of the product section | ||
* @param {...Product} products The products to add to the product section | ||
* @throws {Error} If title is over 24 characters if provided | ||
* @throws {Error} If no products are provided or are over 30 | ||
*/ | ||
constructor(title, ...products) { | ||
if (title && title.length > 24) throw new Error("Section title must be 24 characters or less"); | ||
if (!products.length || products.length > 30) throw new Error("Section must have between 1 and 30 products"); | ||
this.buttons = button; | ||
this._ = "button"; | ||
if (title) this.title = title; | ||
this.product_items = products; | ||
} | ||
@@ -243,31 +343,16 @@ } | ||
/** | ||
* Button API object | ||
* Product API object | ||
* | ||
* @property {String} type The type of the button | ||
* @property {String} reply.id The id of the row | ||
* @property {String} reply.title The title of the row | ||
* @property {String} product_retailer_id The id of the product | ||
*/ | ||
class Button { | ||
class Product { | ||
/** | ||
* Builds a button component for ActionButtons | ||
* Builds a product component for ActionCart and ProductSection | ||
* | ||
* @param {String} id Unique identifier for your button. It cannot have leading or trailing spaces. This ID is returned in the webhook when the button is clicked by the user. Maximum length: 256 characters. | ||
* @param {String} title Button title. It cannot be an empty string and must be unique within the message. Emojis are supported, markdown is not. Maximum length: 20 characters. | ||
* @throws {Error} If id is not provided | ||
* @throws {Error} If id is over 256 characters | ||
* @throws {Error} If title is not provided | ||
* @throws {Error} If title is over 20 characters | ||
* @param {String} product_retailer_id The id of the product | ||
* @throws {Error} If product_retailer_id is not provided | ||
*/ | ||
constructor(id, title) { | ||
if (!id) throw new Error("Button must have an id"); | ||
if (id.length > 256) throw new Error("Button id must be 256 characters or less"); | ||
if (/^ | $/.test(id)) throw new Error("Button id cannot have leading or trailing spaces"); | ||
if (!title) throw new Error("Button must have a title"); | ||
if (title.length > 20) throw new Error("Button title must be 20 characters or less"); | ||
this.type = "reply"; | ||
this[this.type] = { | ||
title, | ||
id | ||
}; | ||
constructor(product_retailer_id) { | ||
if (!product_retailer_id) throw new Error("Product must have a product_retailer_id"); | ||
this.product_retailer_id = product_retailer_id; | ||
} | ||
@@ -281,7 +366,10 @@ } | ||
Header, | ||
ActionButtons, | ||
Button, | ||
ActionList, | ||
Section, | ||
ListSection, | ||
Row, | ||
ActionButtons, | ||
Button | ||
ActionCatalog, | ||
ProductSection, | ||
Product, | ||
}; |
107477
2321
171