UNPKG

whatsapp-api-js

Version:

A TypeScript server agnostic Whatsapp's Official API framework

709 lines (708 loc) 18 kB
import { ClientMessage, ClientLimitedMessageComponent } from "../types.js"; class Template extends ClientMessage { /** * The name of the template */ name; /** * The language of the template */ language; /** * The components of the template */ components; /** * @override * @internal */ get _type() { return "template"; } /** * Create a Template object for the API * * @example * ```ts * import { * Template, * HeaderComponent, * HeaderParameter, * BodyComponent, * BodyParameter, * Currency, * DateTime * } from "whatsapp-api-js/messages"; * * const template_variables_message = new Template( * "template_name", * "en", * new HeaderComponent( * new HeaderParameter("Hello"), * new HeaderParameter(new Currency(1.5 * 1000, "USD", "U$1.5")), * new HeaderParameter(new DateTime("01/01/2023")) * ), * new BodyComponent( * new BodyParameter("Hello"), * new BodyParameter(new Currency(1.5 * 1000, "USD", "U$1.5")), * new BodyParameter(new DateTime("01/01/2023")) * ) * ); * ``` * * @param name - Name of the template * @param language - The code of the language or locale to use. Accepts both language and language_locale formats (e.g., en and en_US). * @param components - Components objects containing the parameters of the message. For text-based templates, the only supported component is {@link BodyComponent}. * @throws If the template isn't text-based (only one {@link BodyComponent} is given) and one of the parameters is a string and it's over 1024 characters. */ constructor(name, language, ...components) { super(); this.name = name; this.language = typeof language === "string" ? new Language(language) : language; if (components.length) { const pointers = { theres_only_body: components.length === 1 && components[0] instanceof BodyComponent, button_counter: 0 }; this.components = components.map((cmpt) => cmpt._build(pointers)).filter((e) => !!e); } } /** * OTP Template generator * * @example * ```ts * import { Template } from "whatsapp-api-js/messages"; * * const template_otp_message = Template.OTP("template_name", "en", "123456"); * ``` * * @param name - Name of the template * @param language - The code of the language or locale to use. Accepts both language and language_locale formats (e.g., en and en_US). * @param code - The one time password to be sent * @returns A Template object for the API */ static OTP(name, language, code) { return new Template( name, language, new BodyComponent(new BodyParameter(code)), new URLComponent(code) ); } } class Language { /** * The code of the language or locale to use. Accepts both language and language_locale formats (e.g., en and en_US). */ code; /** * The language policy */ policy; /** * Create a Language component for a Template message * * @param code - The code of the language or locale to use. Accepts both language and language_locale formats (e.g., en and en_US). * @param policy - The language policy the message should follow. The only supported option is 'deterministic'. */ constructor(code, policy = "deterministic") { this.policy = policy; this.code = code; } } class Currency { /** * The amount of the currency by 1000 */ amount_1000; /** * The currency code */ code; /** * The fallback value */ fallback_value; /** * @override * @internal */ get _type() { return "currency"; } /** * Builds a currency object for a Parameter * * @param amount_1000 - Amount multiplied by 1000 * @param code - Currency code as defined in ISO 4217 * @param fallback_value - Default text if localization fails * @throws If amount_1000 is not greater than 0 */ constructor(amount_1000, code, fallback_value) { if (amount_1000 <= 0) throw new Error("Currency must have an amount_1000 greater than 0"); this.amount_1000 = amount_1000; this.code = code; this.fallback_value = fallback_value; } } class DateTime { /** * The fallback value */ fallback_value; /** * @override * @internal */ get _type() { return "date_time"; } /** * Builds a date_time object for a Parameter * * @param fallback_value - Default text. For Cloud API, we always use the fallback value, and we do not attempt to localize using other optional fields. */ constructor(fallback_value) { this.fallback_value = fallback_value; } } class ButtonComponent { /** * The type of the component */ type = "button"; /** * The subtype of the component */ sub_type; /** * The parameter of the component */ parameters; /** * The index of the component (assigned after calling _build) */ index = NaN; /** * Builds a button component for a Template message. * The index of each component is defined by the order they are sent to the Template's constructor. * * @internal * @param sub_type - The type of button component to create. * @param parameter - The parameter for the component. The index of each component is defined by the order they are sent to the Template's constructor. */ constructor(sub_type, parameter) { this.sub_type = sub_type; this.parameters = [parameter]; } /** * @override * @internal */ _build(pointers) { this.index = pointers.button_counter++; return this; } } class URLComponent extends ButtonComponent { /** * Creates a button component for a Template message with call to action buttons. * * @param parameter - The variable for the url button. * @throws If parameter is an empty string. */ constructor(parameter) { super("url", new URLComponent.Button(parameter)); } /** * @internal */ static Button = class { type = "text"; text; /** * Creates a parameter for a Template message with call to action buttons. * * @param text - The text of the button * @throws If text is an empty string */ constructor(text) { if (!text.length) { throw new Error("Button parameter can't be an empty string"); } this.text = text; } }; } class PayloadComponent extends ButtonComponent { /** * Creates a button component for a Template message with quick reply buttons. * * @param parameter - Parameter for the component. * @throws If parameter is an empty string. */ constructor(parameter) { super("quick_reply", new PayloadComponent.Button(parameter)); } /** * @internal */ static Button = class { type = "payload"; payload; /** * Creates a parameter for a Template message with quick reply buttons. * * @param payload - The id of the button. * @throws If payload is an empty string. */ constructor(payload) { if (!payload.length) { throw new Error("Button parameter can't be an empty string"); } this.payload = payload; } }; } class CatalogComponent extends ButtonComponent { /** * Creates a button component for a Template catalog button. * * @param thumbnail - The product to use as thumbnail. */ constructor(thumbnail) { super("catalog", new CatalogComponent.Action(thumbnail)); } /** * @internal */ static Action = class { type = "action"; action; /** * Creates a parameter for a Template message with a catalog button. * * @param thumbnail - The product to use as thumbnail. */ constructor(thumbnail) { this.action = { thumbnail_product_retailer_id: thumbnail.product_retailer_id }; } }; } class MPMComponent extends ButtonComponent { /** * Creates a button component for a MPM Template. * * @param thumbnail - The product to use as thumbnail. * @param sections - The sections of the MPM. Must have between 1 and 10 sections. Must have less than 30 products *across* all sections. * @throws If sections is over 10 elements. * @throws If sections is over 1 element and one of the sections doesn't have a title. */ constructor(thumbnail, ...sections) { super("mpm", new MPMComponent.Action(thumbnail, sections)); } /** * @internal */ static Action = class extends ClientLimitedMessageComponent { type = "action"; action; /** * Creates a parameter for a MPM Template. * * @param thumbnail - The product to use as thumbnail. * @param sections - The sections of the MPM. Must have between 1 and 10 sections. * @throws If sections is over 10 elements. * @throws If sections is over 1 element and one of the sections doesn't have a title. */ constructor(thumbnail, sections) { super("MPMComponent", "sections", sections, 10); if (sections.length > 1) { if (!sections.every((s) => !!s.title)) { throw new Error( "All sections must have a title if more than 1 section is provided" ); } } this.action = { thumbnail_product_retailer_id: thumbnail.product_retailer_id, sections }; } }; } class CopyComponent extends ButtonComponent { /** * Creates a button component for a Template message with copy coupon button. * * @param parameter - The coupon's code of the button to copy. * @throws If parameter is an empty string. */ constructor(parameter) { super("copy_code", new CopyComponent.Action(parameter)); } /** * @internal */ static Action = class { type = "coupon_code"; coupon_code; /** * Creates a parameter for a Template message with copy coupon button. * * @param coupon_code - The coupon's code of the button. * @throws If coupon_code is an empty string. */ constructor(coupon_code) { if (!coupon_code.length) { throw new Error("Action coupon_code can't be an empty string"); } this.coupon_code = coupon_code; } }; } class FlowComponent extends ButtonComponent { /** * Creates a button component for a Template message with flow button. * * @param flow_token - Honestly, I don't know what this is, the documentation only says this might be "FLOW_TOKEN" and defaults to "unused". * @param flow_action_data - JSON object with the data payload for the first screen. */ constructor(flow_token, flow_action_data) { super("flow", new FlowComponent.Action(flow_token, flow_action_data)); } /** * @internal */ static Action = class { type = "action"; action; /** * Creates a parameter for a Template message with flow button. * * @param flow_token - Idk, some string. * @param flow_action_data - JSON object with the data payload for the first screen. */ constructor(flow_token, flow_action_data) { this.action = { flow_token, flow_action_data }; } }; } class SkipButtonComponent extends ButtonComponent { /** * Skips a button component index for a Template message. */ constructor() { super(); } /** * @override * @internal */ _build(pointers) { pointers.button_counter++; return null; } } class HeaderComponent { /** * The type of the component */ type; /** * The parameters of the component */ parameters; /** * Builds a header component for a Template message * * @param parameters - Parameters of the body component */ constructor(...parameters) { this.type = "header"; this.parameters = parameters; } /** * @override * @internal */ _build() { return this; } } class HeaderParameter { /** * The type of the parameter */ type; /** * The text of the parameter */ text; /** * The currency of the parameter */ currency; /** * The datetime of the parameter */ date_time; /** * The image of the parameter */ image; /** * The document of the parameter */ document; /** * The video of the parameter */ video; /** * The location of the parameter */ location; /** * The product of the parameter */ product; /** * Builds a parameter object for a HeaderComponent. * For text parameter, the character limit is 60. * For Document parameter, only PDF documents are supported for document-based message templates (not checked). * For Location parameter, the location must have a name and address. * * @param parameter - The parameter to be used in the template's header * @throws If parameter is a string and it's over 60 characters * @throws If parameter is a Location and it doesn't have a name and address */ constructor(parameter) { if (typeof parameter === "string") { if (parameter.length > 60) throw new Error("Header text must be 60 characters or less"); this.type = "text"; } else { if (parameter._type === "location" && !(parameter.name && parameter.address)) { throw new Error("Header location must have a name and address"); } this.type = parameter._type; } Object.defineProperty(this, this.type, { value: parameter, enumerable: true }); } } class BodyComponent { /** * The type of the component */ type; /** * The parameters of the component */ parameters; /** * Builds a body component for a Template message * * @param parameters - Parameters of the body component */ constructor(...parameters) { this.type = "body"; this.parameters = parameters; } /** * @override * @internal * @throws If theres_only_body is false and one of the parameters is a string and it's over 1024 characters */ _build({ theres_only_body }) { if (!theres_only_body) { for (const param of this.parameters) { if (param.text && param.text?.length > 1024) { throw new Error( "Body text must be 1024 characters or less" ); } } } return this; } } class BodyParameter { /** * The type of the parameter */ type; /** * The text of the parameter */ text; /** * The currency of the parameter */ currency; /** * The datetime of the parameter */ date_time; /** * Builds a parameter object for a BodyComponent. * For text parameter, the character limit is 32768 if only one BodyComponent is used for the Template, else it's 1024. * * @param parameter - The parameter to be used in the template * @throws If parameter is a string and it's over 32768 characters * @throws If parameter is a string, there are other components in the Template and it's over 1024 characters * @see {@link BodyComponent._build} The method that checks the 1024 character limit */ constructor(parameter) { if (typeof parameter === "string") { if (parameter.length > 32768) throw new Error("Body text must be 32768 characters or less"); this.type = "text"; } else { this.type = parameter._type; } Object.defineProperty(this, this.type, { value: parameter, enumerable: true }); } } class CarouselComponent extends ClientLimitedMessageComponent { /** * The type of the component */ type = "carousel"; /** * The cards of the component */ cards; /** * Builds a carousel component for a Template message * * @param cards - The cards of the component * @throws If cards is over 10 elements */ constructor(...cards) { super("CarouselComponent", "CarouselCard", cards, 10); const pointers = { counter: 0 }; this.cards = cards.map((cmpt) => cmpt._build(pointers)); } /** * @override * @internal */ _build() { return this; } } class CarouselCard { /** * The index of the card (assigned after calling _build) */ card_index = NaN; /** * The components of the card */ components; /** * Builds a carousel card for a CarouselComponent. * * @remarks * If this looks odly similar to Template constructor's signature, it's because it is. * * @param header - The header parameter for the card * @param components - The other components for the card */ constructor(header, ...components) { const tmp = new Template( "", "", new HeaderComponent(new HeaderParameter(header)), ...components ); this.components = tmp.components; } /** * @override * @internal */ _build(ptr) { this.card_index = ptr.counter++; return this; } } class LTOComponent { /** * The type of the component */ type = "limited_time_offer"; /** * The parameters of the component */ parameters; /** * Builds a limited-time offer component for a Template message * * @param expiration - Offer code expiration time as a UNIX timestamp in milliseconds * @throws If expiration is negative */ constructor(expiration) { if (expiration < 0) { throw new Error( "Expiration time must be a positive Unix timestamp" ); } this.parameters = [ { type: "limited_time_offer", limited_time_offer: { expiration_time_ms: expiration } } ]; } /** * @override * @internal */ _build() { return this; } } export { BodyComponent, BodyParameter, ButtonComponent, CarouselCard, CarouselComponent, CatalogComponent, CopyComponent, Currency, DateTime, FlowComponent, HeaderComponent, HeaderParameter, LTOComponent, Language, MPMComponent, PayloadComponent, SkipButtonComponent, Template, URLComponent }; //# sourceMappingURL=template.js.map