diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd3f86..946cd12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [1.4.0] - 2022-12-09 +### Added +- Add missing models to send WhatsApp interactive messages +- Add missing models to send Apple Messages for Business listpicker messages. + ## [1.3.6] - 2022-12-05 ### Added - Support for the Telegram channel diff --git a/README.md b/README.md index 9a9520c..ee6d374 100644 --- a/README.md +++ b/README.md @@ -96,5 +96,54 @@ response.then((result) => { }); ``` +or send whatsapp interactive messages using the message builder +```javascript +const whatsAppInteractiveContent = { + type: 'list', + header: { + type: "text", + text: "your-header-content" + }, + body: { + text: "your-text-message-content" + }, + footer: { + text: "your-footer-content" + }, + action: { + button: "cta-button-content", + sections: [{ + title: "your-section-title1", + rows: [{ + id: "unique-row-identifier1", + title: "row-title-content", + description: "row-description-content" + }] + }, + { + title: "your-section-title2", + rows: [{ + id: "unique-row-identifier2", + title: "row-title-content", + description: "row-description-content" + }] + } + ] + } +}; + +const response = client.createMessage() + .setMessage(["00316012345678"], "TestSender", "Hello world?!") + .setAllowedChannels(["WhatsApp"]) + .setInteractive(whatsAppInteractiveContent) + .send(); + +response.then((result) => { + console.log(result); +}).catch((error) => { + console.log(error); +}); +``` + ### License @cmdotcom/text-sdk is under the MIT license. See LICENSE file. diff --git a/lib/MessageApiClient.ts b/lib/MessageApiClient.ts index 3a70a3f..a93559f 100644 --- a/lib/MessageApiClient.ts +++ b/lib/MessageApiClient.ts @@ -1,12 +1,12 @@ import * as CMTypes from "../typescript-node-client/api"; import http = require('http'); -export type Channel = "SMS" | "Viber" | "RCS" | "Apple Business Chat" | "WhatsApp" | "Telegram Messenger" | "Twitter" | "MobilePush" | "Facebook Messenger" | "Google Business Messages" | "Instagram"; +export type Channel = "SMS" | "Viber" | "RCS" | "Apple Messages for Business" | "WhatsApp" | "Telegram Messenger" | "Twitter" | "MobilePush" | "Facebook Messenger" | "Google Business Messages" | "Instagram"; export type RichMessage = CMTypes.RichMessage; export type Suggestion = CMTypes.Suggestion; export type Template = CMTypes.Template; export type MessagesResponse = CMTypes.MessagesResponse; - +export type WhatsAppInteractive = CMTypes.WhatsAppInteractive; /** * Message client for the CM.com Platform */ @@ -142,6 +142,15 @@ export class Message extends CMTypes.MessageEnvelope { return this; } + /** + * Sets the WhatsAppInteractive Message + * @param template template definition and usage object + */ + public setInteractive(interactive: WhatsAppInteractive): Message { + this.getRichContent().conversation = [{ interactive: interactive }]; + return this; + } + /** * Sends the message to the CM.com Platform */ diff --git a/package-lock.json b/package-lock.json index 687115f..2a2bb17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@cmdotcom/text-sdk", - "version": "1.3.6", + "version": "1.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@cmdotcom/text-sdk", - "version": "1.3.6", + "version": "1.4.0", "license": "MIT", "dependencies": { "bluebird": "~3.7.2", diff --git a/package.json b/package.json index b05643f..dc2593d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cmdotcom/text-sdk", - "version": "1.3.6", + "version": "1.4.0", "description": "Package to make it very easy to send text messages with CM.com", "keywords": [ "cm", diff --git a/spec/api.spec.ts b/spec/api.spec.ts index 4426508..0353e11 100644 --- a/spec/api.spec.ts +++ b/spec/api.spec.ts @@ -170,4 +170,103 @@ describe("MessageApiClient+MessageBuilder", () => { return response.body.details === "Created 1 message(s)"; }); }); + + const whatsAppInteractiveContent = { + type: 'list', + header: { + type: "text", + text: "your-header-content" + }, + body: { + text: "your-text-message-content" + }, + footer: { + text: "your-footer-content" + }, + action: { + button: "cta-button-content", + sections: [{ + title: "your-section-title1", + rows: [{ + id: "unique-row-identifier1", + title: "row-title-content", + description: "row-description-content" + }] + }, + { + title: "your-section-title2", + rows: [{ + id: "unique-row-identifier2", + title: "row-title-content", + description: "row-description-content" + }] + } + ] + } + }; + + it("should create a valid http(s) request, when using the message-builder with a interactive WhatsApp message", () => { + const yourProductToken = "dddd"; + const client = new MessageApiClient(yourProductToken); + + const response = client.createMessage() + .setMessage(["00316012345678"], "TestSender", "Hello world?!") + .setAllowedChannels(["WhatsApp"]) + .setInteractive(whatsAppInteractiveContent) + .send(); + + expect(response).to.be.eventually.fulfilled.and.to.satisfy((response) => { + return response.body.details === "Created 1 message(s)"; + }); + }); + + const appleListPickerrichMessage: RichMessage = { + text: "Check out my image", + listPicker: { + label: "Please, pick a card", + media: { + "mediaUri": "https://static.thenounproject.com/png/393234-200.png" + }, + options: [{ + label: "Ace of Hearts", + media: { + mediaUri: "https://proxy.duckduckgo.com/iu/?u=https%3A%2F%2Ftse1.mm.bing.net%2Fth%3Fid%3DOIP.N7zZqoCvjxZZvwp2Zi1UVwHaH6%26pid%3D15.1&f=1" + } + }, + { + label: "Ace of Spades", + media: { + mediaUri: "https://proxy.duckduckgo.com/iu/?u=https%3A%2F%2Fcdn.pixabay.com%2Fphoto%2F2013%2F07%2F12%2F12%2F01%2Fsuit-of-spades-145116_960_720.png&f=1" + } + }, + { + label: "Ace of Diamonds", + media: { + mediaUri: "https://proxy.duckduckgo.com/iu/?u=https%3A%2F%2Fcdn.pixabay.com%2Fphoto%2F2012%2F05%2F07%2F18%2F37%2Fsuit-48941_960_720.png&f=1" + } + }, + { + label: "Ace of Clubs", + media: { + mediaUri: "https://proxy.duckduckgo.com/iu/?u=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F8%2F8a%2FSuitClubs.svg%2F709px-SuitClubs.svg.png&f=1" + } + } + ] + } + }; + + it("should create a valid http(s) request, when using the message-builder with a Apple for Business listpicker", () => { + const yourProductToken = "dddd"; + const client = new MessageApiClient(yourProductToken); + + const response = client.createMessage() + .setMessage(["00316012345678"], "TestSender", "Hello world?!") + .setAllowedChannels(["Apple Messages for Business"]) + .setConversation([appleListPickerrichMessage]) + .send(); + + expect(response).to.be.eventually.fulfilled.and.to.satisfy((response) => { + return response.body.details === "Created 1 message(s)"; + }); + }); }); \ No newline at end of file diff --git a/typescript-node-client/api.ts b/typescript-node-client/api.ts index 99695ce..188055b 100644 --- a/typescript-node-client/api.ts +++ b/typescript-node-client/api.ts @@ -136,6 +136,446 @@ class ObjectSerializer { } } +/** +* Contains information for a listpicker +*/ +export class ListPicker { + /** + * The label which will be shown to the end user to describe the contents of the list picker. + */ + 'label'?: string; + + /** + * An image, which will be shown to the end user to show information about the list picker + */ + 'media'?: Media; + + /** + * The items which the end users can choose + */ + 'options'?: Array; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [ + { + "name": "label", + "baseName": "label", + "type": "string" + }, + { + "name": "media", + "baseName": "media", + "type": "media" + }, + { + "name": "options", + "baseName": "options", + "type": "Array" + }]; + + static getAttributeTypeMap() { + return ListPicker.attributeTypeMap; + } +} + +/** +* Describes one item in a ListPicker/>. +*/ +export class ListItem { + /** + * The label which will be shown to the end user to describe the item. + */ + 'label'?: string; + + /** + * An image, which will be shown to the end user to show information about the list item + */ + 'media'?: Media + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "label", + "baseName": "label", + "type": "string" + }, + { + "name": "media", + "baseName": "media", + "type": "media" + }]; + + static getAttributeTypeMap() { + return ListItem.attributeTypeMap; + } +} + +/** +* WhatsApp Interactive Messages />. +*/ +export class WhatsAppInteractive { + /** + * The Type that will be used, + * either list or button + */ + 'type'?: string; + + /** + * Your message’s header. + */ + 'header'?: InteractiveHeader + + /** + * Required Your message’s body. + */ + 'body'?: InteractiveBody + + /** + * Required Your message’s footer. + */ + 'footer'?: InteractiveFooter + + /** + * Required. Inside action, you must nest: + * a button field with your button’s content, and + * at least one section object (maximum of 10). + */ + 'action'?: InteractiveAction + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "type", + "baseName": "type", + "type": "string" + }, + { + "name": "header", + "baseName": "header", + "type": "InteractiveHeader" + }, + { + "name": "footer", + "baseName": "footer", + "type": "InteractiveBody" + }, + { + "name": "header", + "baseName": "header", + "type": "InteractiveFooter" + }, + { + "name": "action", + "baseName": "action", + "type": "InteractiveAction" + }]; + + static getAttributeTypeMap() { + return WhatsAppInteractive.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class InteractiveHeader { + /** + * Required. The header type you would like to use.Supported values are: + * text: Used for List Messages and Reply Buttons. + */ + 'type'?: string; + + /** + * Required if type is set to text. + * Text for the header.Formatting allows emojis, but not markdown. + */ + 'text'?: string; + + /** + * Required if type is set to text. + * Text for the header.Formatting allows emojis, but not markdown. + */ + 'media'?: Media; + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "type", + "baseName": "type", + "type": "string" + }, + { + "name": "text", + "baseName": "text", + "type": "string" + }, + { + "name": "media", + "baseName": "media", + "type": "media" + }]; + + static getAttributeTypeMap() { + return InteractiveHeader.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class InteractiveBody { + /** + * The body content of the message. + * Emojis and markdown are supported. Links are supported. + */ + 'text'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "text", + "baseName": "text", + "type": "string" + }]; + + static getAttributeTypeMap() { + return InteractiveBody.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class InteractiveFooter { + /** + * The footer content. Emojis and markdown are supported. Links are supported. + * Maximum length: 60 characters + */ + 'text'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "text", + "baseName": "text", + "type": "string" + }]; + static getAttributeTypeMap() { + return InteractiveFooter.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class InteractiveAction { + /** + * Required for List Messages. + * Button content. It cannot be an empty string and must be unique within the message + * Does not allow emojis or markdown. + */ + 'button'?: string; + /** + * Required for Reply Button Messages. + */ + 'buttons'?: Array; + /** + * Required for List Messages. + */ + 'sections'?: Array; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "button", + "baseName": "button", + "type": "button" + }, + { + "name": "buttons", + "baseName": "buttons", + "type": "Array" + }, + { + "name": "sections", + "baseName": "sections", + "type": "Array" + }]; + + static getAttributeTypeMap() { + return InteractiveAction.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class InteractiveButton { + /** + * type: only supported type is reply (for Reply Button Messages). + */ + 'type'?: string; + + /** + * Button title.It cannot be an empty string and must be unique within the message. + * Does not allow emojis or markdown. Maximum length: 20 characters. + */ + 'title'?: string; + + /** + * id: Unique identifier for your button. + * This ID is returned in the webhook when the button is clicked by the user. + */ + 'id'?: string; + + /** + * Reply Message for your button. + */ + 'reply'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "type", + "baseName": "type", + "type": "string" + }, + { + "name": "title", + "baseName": "title", + "type": "string" + }, + { + "name": "id", + "baseName": "id", + "type": "string" + }, + { + "name": "reply", + "baseName": "reply", + "type": "string" + }]; + + static getAttributeTypeMap() { + return InteractiveButton.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class ReplyMessage { + /** + * The options to select. + */ + 'id'?: string; + + /** + * The options to select. + */ + 'title'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "id", + "baseName": "id", + "type": "string" + }, + { + "name": "title", + "baseName": "title", + "type": "string" + }]; + + static getAttributeTypeMap() { + return ReplyMessage.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class InteractiveSection { + /** + * Title of the row.. + */ + 'title'?: string; + + /** + * Contains a list of rows. + */ + 'rows'?: Array; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + "name": "title", + "baseName": "title", + "type": "string" + }, + { + "name": "rows", + "baseName": "rows", + "type": "Array" + }]; + + static getAttributeTypeMap() { + return InteractiveSection.attributeTypeMap; + } +} + +/** +* Part of WhatsApp interactive mesage +*/ +export class Rows { + /** + * Title of the row. . + */ + 'title'?: string; + + /** + * Id of the row.. + */ + 'id'?: string; + /** + * Description of the row. + */ + 'description'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [ + { + "name": "title", + "baseName": "title", + "type": "string" + }, + { + "name": "id", + "baseName": "id", + "type": "string" + }, + { + "name": "description", + "baseName": "description", + "type": "string" + }]; + + static getAttributeTypeMap() { + return Rows.attributeTypeMap; + } +} + /** * Contains information for a {CM.Messaging.RCSModels.Models.Suggestion.Calendar} (RCS). */ @@ -989,6 +1429,16 @@ export class RichMessage { */ 'oauth2'?: OAuthMessage; + /** + * Used to send an WhatsApp interactive message. + */ + 'interactive'?: WhatsAppInteractive; + + /** + * Used to send an Apple Messages for Business listpicker message. + */ + 'listPicker'?: ListPicker; + static discriminator: string | undefined = undefined; static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [ @@ -1036,7 +1486,17 @@ export class RichMessage { "name": "oauth2", "baseName": "oauth2", "type": "OAuthMessage" - } ]; + }, + { + "name": "interactive", + "baseName": "interactive", + "type": "WhatsAppInteractive" + } , + { + "name": "listPicker", + "baseName": "listPicker", + "type": "ListPicker" + } ]; static getAttributeTypeMap() { return RichMessage.attributeTypeMap; @@ -1781,6 +2241,17 @@ let typeMap: {[index: string]: any} = { "Messages": Messages, "MessagesResponse": MessagesResponse, "Recipient": Recipient, + "ListPicker": ListPicker, + "ListItem": ListItem, + "WhatsAppInteractive": WhatsAppInteractive, + "InteractiveHeader": InteractiveHeader, + "InteractiveBody": InteractiveBody, + "InteractiveFooter": InteractiveFooter, + "InteractiveAction": InteractiveAction, + "ReplyMessage": ReplyMessage, + "InteractiveSection": InteractiveSection, + "InteractiveButton": InteractiveButton, + "Rows": Rows } export interface Authentication {