SAP Web IDE, SAPUI5

WhatsApp Integration with SAPUI5 Application using Twilio and SAP Open Connectors

In this blog I am going to explain about Whatsapp Integration with SAPUI5 by using Open Connectors and Twilio. By this, we can send and receive whatsapp messages from UI5 application. Now let us see how i achieved this.

Also Read: SAP ERP Certifications

Pre-requisities for our requirement:

  1. TWILIO setup
  2. Setup the SAP Open Connectors
  3. Create UI5 application

TWILIO SETUP

Step – 1:

Before developing the application, first we need to create an account in Twilio.

For that, open this link https://www.twilio.com and register as shown below.

Give the required credentials and check the checkbox to accept the terms and conditions.

Now your account in Twilio is created and you will see a dashboard.

Step – 2:

In the dashboard, go to create project and select a template.

Here I choose “SMS Chatbot” template. After choosing, you will navigate to the following screen.

Enter the project name and click on continue. Now your project is successfully created.

Step – 3:

Go to project dashboard.

For every project created, an unique Account SID and Auth Token will be generated. Take a note of it further use.

Step – 4:

Goto Programmable SMS and select “WhatsApp Beta” to setup the testing sandbox.

Then the following popup will be displayed.

Agree to the terms of service by checking the check box and then click on confirm to activate the sandbox.

Step – 5:

Now goto Learn tab in WhatsApp Beta, we can see a code generated for our project.

From your mobile send a WhatsApp message with the given code (join anyone-dead) to the designated number (+1 415 523 8886) to register our mobile number in Twilio as shown below.

Step – 6:

We can also check our whatsapp numbers in sandbox participants as shown below.

If multiple users registered, then it show multiple numbers.

Step – 7:

Twilio setup is done. Now our next step is to setup the SAP Open Connectors.

SETUP THE SAP OPEN CONNECTORS

Step-1:

Login to SAP Webide.

Step-2:

Goto Services and search for “Open Connectors” and enable it.

Step-3:

Click on the Open Connectors service after enabling it. Then you will navigate to the following screen.

Step-4:

Goto Connectors and search for Twilio.

Step-5:

Choose “Authenticate”.

Step-6:

Now, we have to create instance. For that, we need to give the required credentials as shown in the screenshot.

Here, I gave

Name as “Whatsapp Integration with UI5”

Phone Number

Create the Instance using Test Credentials as “false”

Account SID and Auth Token (which are generated in Twilio when the project created)

After giving all the credentials, click on “Create Instance” button.

Step-7:

We will see a success message along with some tiles after creating Twilio Connector Instance. Select “Test in the API docs” to test the API.

Step-8:

Let us test our Twilio API by sending or receiving messages from whatsapp. For that, follow the below steps.

POST

First we will check with “POST” to send messages. Select Messages and Click on “POST”.

Select Project instance and click on “Try it out” button.

Authorization will be generated automatically for our project. Please take a note of it for future use. Now replace the body with our code and click “Execute” button.

{
  "Body": "Hi Friends!",
  "From": "whatsapp:+14155238886",
  "MediaUrl": "https://genuineprogrammer.com/wp-content/uploads/2017/11/sapui5.jpg",
  "To": "whatsapp:xxxxxxxxxx"
}

We can see a success message as shown in below screenshot and the message is delivered to the mentioned whatsapp number.

GET

Now we will see how to get messages from registered whatsapp number.

Select Messages and click on “GET”.

Select Project instance and click on “Try it out” button.

Now click the “Execute” button after sending reply message from your registered mobile number. Here, I am sending “Hi…How are you??”

Let us see whether we will get this message or not. Just click on “Execute” button. If there are no errors, we will see the message.

Yes, we got the message.

In this way, we can send or receive messages by using Twilio and Open Connectors.

CREATING AN UI5 APPLICATION:

Now, let us see how to integrate this with UI5 application.

Step-1:

We need to create a project as shown below.

Select SAPUI5 Template and click on “Next” button.

In Basic Information tab, give Project Name & Namespace and then click on “Next” button to continue.

Select View type and give View name in Template Customization tab.

Click on “Finish” button.

Now the project is created in our workspace as shown below.

Step-2:

Copy the below code in your view.xml file (In this view, I kept a button “Whatsapp Me”. By clicking that button, a popup will be opened where we can do chat).

<mvc:View controllerName="pfe.bot.controller.App" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mvc="sap.ui.core.mvc" xmlns:controls="pfe.bot.controls" displayBlock="true" xmlns="sap.m">
    <App id="idAppControl">
        <pages>
            <Page title="{i18n>title}">
                <footer>
                    <Bar>
                        <contentRight>
                            <ToolbarSpacer width="" />
                            <Button text="Whatsapp Me" icon="sap-icon://discussion" tooltip="{i18n>Clear}" press="onChatPress" />
                            <controls:ChatDialog id="botchat" title="Whatsapp ChatBot!" height="600px" width="350px" showCloseButton="false" send="onSendPressed" initialMessage="Hi! Start chatting with me" placeHolder="Write a reply" userIcon="https://cdn.recast.ai/webchat/user.png" robotIcon="images/whatsapp.png" buttonIcon="sap-icon://discussion"></controls:ChatDialog>
                        </contentRight>
                    </Bar>
                </footer>
            </Page>
        </pages>
    </App>
</mvc:View>

Step-3:

Code for ChatDailog.js file

Here, I am using a control “ChatDailog” for whatsapp chatbot. For that, create a folder “control” and create a ChatDailog.js file under that folder. Replace the following code in your .js file.

sap.ui.define(
  [	"sap/ui/core/Control",
	"sap/m/Button",
	"sap/ui/core/IconPool",
	"sap/m/Dialog",
	"sap/m/List",
	"sap/m/FeedListItem",
	"sap/m/FeedInput",
	"sap/m/ResponsivePopover",
	"sap/m/VBox",
	"sap/m/ScrollContainer",
	"sap/m/Bar",
	"sap/m/Title",
	"sap/ui/core/ResizeHandler"
  ],
  function(Control, Button, IconPool, Dialog, List, FeedListItem, FeedInput, ResponsivePopover, VBox, ScrollContainer, Bar, Title, ResizeHandler) {
	var ChatDialog = Control.extend("pfe.bot.controls.ChatDialog",{
		
		metadata : {
			properties : {
				title: {type: "string", group: "Appearance", defaultValue: null},
			    
			    width: {type: "sap.ui.core.CSSSize", group: "Dimension", defaultValue: null},
				height: {type: "sap.ui.core.CSSSize", group: "Dimension", defaultValue: null},
			    
			    buttonIcon: {type: "sap.ui.core.URI", group: "Appearance", defaultValue: null},
				robotIcon: {type: "sap.ui.core.URI", group: "Appearance", defaultValue: null},
				userIcon: {type: "sap.ui.core.URI", group: "Appearance", defaultValue: null},
				
				initialMessage: {type: "string", group: "Appearance", defaultValue: "Hello, How can I help?"},
				placeHolder: {type: "string", group: "Appearance", defaultValue: "Post something here"}
			
			},
			aggregations : {
				_chatButton:  {type: "sap.m.Button", multiple: false},
				_popover: {type: "sap.m.ResponsivePopover", multiple: false}
				
			},
			events : {
				send: {
            		parameters : {
						text : {type : "string"}
					}
            	}
			}
		},
    	

    	init : function () {
    		
    		//initialisation code, in this case, ensure css is imported
	        var libraryPath = jQuery.sap.getModulePath("pfe.bot"); 
	        jQuery.sap.includeStyleSheet(libraryPath + "/css/bkChat.css"); 

			
			var oBtn = new Button(this.getId() + "-bkChatButton", {
				press: this._onOpenChat.bind(this)
			});
			this.setAggregation("_chatButton", oBtn);
			
			var oHeader = new Bar({
				contentLeft: new Button({
					icon: "sap-icon://sys-cancel",
					press: this._toggleClose.bind(this),
					tooltip: "Clear chat"
				}),
				contentMiddle: new Title(this.getId() + "-bkChatTitle", {}),
				contentRight: new Button({
					icon: "sap-icon://pushpin-off",
					press: this._toggleAutoClose.bind(this),
					tooltip: "Toggle"
				})
			});
			
			var oRpop = new ResponsivePopover(this.getId() + "-bkChatPop", {
				customHeader: oHeader,
				placement: "Top",
				showHeader: true,
				resizable: true,
				horizontalScrolling: false,
				verticalScrolling: false,
				beforeClose: function(e){
					ResizeHandler.deregister(this.sResizeHandleId);
				}.bind(this),
				afterOpen: function(e){
					this.sResizeHandleId = ResizeHandler.register(sap.ui.getCore().byId(this.getId() + "-bkChatPop"), this._saveDimensions.bind(this));
				}.bind(this),
			}).addStyleClass("sapUiTinyMargin");
			
			
			this.setAggregation("_popover", oRpop);
			
			var oFeedIn = new FeedInput(this.getId() + "-bkChatInput", {
				post: this._onPost.bind(this),
				showicon: true
			});
			
			oFeedIn.addEventDelegate({
    			onsapenter: function(oEvent) {
    				
    				oEvent.preventDefault();
    				
    				var sTxt = oFeedIn.getValue();
    				if(sTxt.length > 0){
	    				oFeedIn.fireEvent("post", {
							value: sTxt
						}, true, false);
						oFeedIn.setValue(null); 
    				}
			    }
			});
			
			var oFeedList = new List(this.getId() + "-bkChatList", {
				showSeparators: "None",
				showNoData: false
			});
			
			var oInitialFeedListItem = new FeedListItem(this.getId() + "-bkChatInitial", {
				showicon: true,
				text: "Hello I'm Ro Bot, how can i help you?"
			});
			oInitialFeedListItem.addStyleClass("bkRobotInput");
			oFeedList.addItem(oInitialFeedListItem);

			
			var oScroll = new ScrollContainer(this.getId() + "-bkChatScroll", {				
				horizontal: false,
				vertical: true,
				focusable: true
			});			
			oScroll.insertContent(oFeedList);

			
			var oStatusBar = new sap.m.Label(this.getId() + "-bkChatStatusBar", { text : ""
			}).addStyleClass("sapUiTinyMargin");

			var oVBox = new VBox({				
				items: [oScroll, oStatusBar, oFeedIn],
				fitContainer: true,
				justifyContent : "End",
            	alignItems : "Stretch"
			});
			
			oRpop.insertContent(oVBox, 0);
		},
    	
    	renderer  : function(oRm, oControl) {
    		
			var oChatBtn = oControl.getAggregation("_chatButton");
			var oPop = oControl.getAggregation("_popover");

			oRm.write("<div ");
			//oRm.addClass("bkChatButton");
			//oRm.writeClasses();
			oRm.write(">");
			
			oRm.renderControl(oChatBtn);
			oRm.renderControl(oPop);
			oRm.write("</div>");
		
		},
		
		onAfterRendering: function(args) {
            if(sap.ui.core.Control.prototype.onAfterRendering) {
             sap.ui.core.Control.prototype.onAfterRendering.apply(this,args);
            }
        },
        
        setTitle: function(sTitle){
        	this.setProperty("title", sTitle, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatTitle").setText(sTitle);
        },
                
        setHeight: function(sHeight){
        	this.setProperty("height", sHeight, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatPop").setContentHeight(sHeight);
        	
        	var iScrollHeight = sHeight.substring(0, sHeight.length - 2) - "96px".substring(0, "96px".length - 2);
        	sap.ui.getCore().byId(this.getId() + "-bkChatScroll").setHeight(iScrollHeight + "px");
        },
        
        setWidth: function(sWidth){
        	this.setProperty("width", sWidth, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatPop").setContentWidth(sWidth);
        },
        
        
        setUserIcon: function(sUserIcon){
        	this.setProperty("userIcon", sUserIcon, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatInput").setIcon(sUserIcon);
        },
        
        setRobotIcon: function(sRobotIcon){
        	this.setProperty("robotIcon", sRobotIcon, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatInitial").setIcon(sRobotIcon);
        },
        
        setButtonIcon: function(sButtonIcon){
        	//this.setProperty("buttonIcon", sButtonIcon, true);
        	//sap.ui.getCore().byId(this.getId() + "-bkChatButton").setIcon(sButtonIcon);
        },
        
        setInitialMessage: function(sText){
        	this.setProperty("initialMessage", sText, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatInitial").setText(sText);
        },
        
        setPlaceHolder: function(sText){
        	this.setProperty("placeHolder", sText, true);
        	sap.ui.getCore().byId(this.getId() + "-bkChatInput").setPlaceholder(sText);
        },
        
        _onPost: function(oEvent){     
			var this_ = this;
			setTimeout(function () {
				this_.botStartTyping();
			}, 1000);

			var sText = oEvent.getSource().getValue();
			this.addChatItem(sText, true);
			this.fireEvent("send", {
				text: sText
			}, false, true);        	
        },
        
        _onOpenChat: function(oEvent){
        	this.getAggregation("_popover").openBy(this.getAggregation("_chatButton"));
        	this.getAggregation("_popover").setContentHeight(this.getProperty("height"));
			this.getAggregation("_popover").setContentWidth(this.getProperty("width"));
        },
        
        _saveDimensions: function(oEvent){
			//console.log(sap.ui.getCore().byId(this.getId() + "-bkChatPop").getContentHeight() + ", " + oEvent.size.height);
        	this.setProperty("height", oEvent.size.height + "px", true);
        	this.setProperty("width", oEvent.size.width + "px", true);
        },
        
        _toggleAutoClose: function(oEvent){
       
        	var bAuto = this.getAggregation("_popover").getAggregation("_popup").oPopup.getAutoClose();
        	if(bAuto){
        		oEvent.getSource().setProperty("icon", "sap-icon://pushpin-on");
        		this.getAggregation("_popover").getAggregation("_popup").oPopup.setAutoClose(false);
        	}else {
        		oEvent.getSource().setProperty("icon", "sap-icon://pushpin-off");
        		this.getAggregation("_popover").getAggregation("_popup").oPopup.setAutoClose(true);
        	}
        },

		_toggleClose: function(){
			 sap.ui.getCore().byId(this.getId() + "-bkChatList").removeAllItems();
			 this.getAggregation("_popover").close();
		},

		botStartTyping: function() {
			sap.ui.getCore().byId(this.getId() + "-bkChatStatusBar").setText("Bot is typing...");
		},

		botFinishTyping: function() {
			sap.ui.getCore().byId(this.getId() + "-bkChatStatusBar").setText("");
		},

        addChatItem: function(sText, bUser){        	
        	var oFeedListItem = new FeedListItem({
				showicon: true,
				text: sText
			});

			if(bUser){
				oFeedListItem.setIcon(this.getUserIcon());
				oFeedListItem.addStyleClass("bkUserInput");
				sap.ui.getCore().byId(this.getId() + "-bkChatList").addItem(oFeedListItem, 0);
			} else {
				oFeedListItem.setIcon(this.getRobotIcon());
				oFeedListItem.addStyleClass("bkRobotInput");
				sap.ui.getCore().byId(this.getId() + "-bkChatList").addItem(oFeedListItem, 0);
				
			}
			var oScroll = sap.ui.getCore().byId(this.getId() + "-bkChatScroll");
			setTimeout(function(){ 
				oScroll.scrollTo(0, 1000, 0);
			}, 0);	
        }
	});
	  
	return ChatDialog;
});

Step-3:

Controller code

And our controller code is as follows. Here, we used two Ajax calls – one for posting or sending the message and the other one is for getting or receiving the message.

sap.ui.define([
	"sap/ui/core/mvc/Controller"
], function (Controller) {
	"use strict";
	var _id;

	return Controller.extend("pfe.bot.controller.App", {

		onChatPress: function () {
			var chatbot = this.getView().byId("botchat");
			chatbot._onOpenChat();

			var this_ = this;
			var headers = {
				'Authorization': 'User xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=, Organization xxxxxxxxxxxxxxxxxxxxxxxxxxxx=, Element xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=',
				'Content-Type': 'application/json'
			};

			var ajax_get_response = function () {
				jQuery.ajax({
					url: "https://api.openconnectors.ext.hanatrial.ondemand.com/elements/api-v2/messages?pageSize=1",
					cache: false,
					type: "GET",
					headers: headers,
					async: true,
					success: function (sData) {
						//console.log('[GET] /discover-dialog', sData);
						localStorage.setItem("chatId", sData[0].date_created);

						if (sData[0].direction == 'inbound') {
							if (localStorage.getItem("chatId") != this_._id) {
								this_._id = localStorage.getItem("chatId");
								// chatbot.addChatItem(sData[0].from, false);
								chatbot.addChatItem(sData[0].from + "  -  " + sData[0].body);
								chatbot.botFinishTyping();
							}
						}
					},
					error: function (sError) {
						chatbot.addChatItem("Something error!", false);
					}
				});
			};

			var interval = 2000;
			setInterval(ajax_get_response, interval);
		},

		onSendPressed: function (oEvent) {

			var chatbot = this.getView().byId("botchat");
			var question = oEvent.getParameter("text");
			var this_ = this;

			console.log(question);

				var data = '{"Body": "' + question + '", "From": "whatsapp:+14155238886", "To": "whatsapp:XXXXXXXXXXX"}';

				var headers = {
					'Authorization': 'User xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=, Organization xxxxxxxxxxxxxxxxxxxxxxxxxx, Element xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=',
					'Content-Type': 'application/json'
				};

				jQuery.ajax({
					//Post Message
					url: "https://api.openconnectors.ext.hanatrial.ondemand.com/elements/api-v2/messages",
					cache: false,
					type: "POST",
					headers: headers,
					data: data,
					async: true,
					success: function (sData) {
						localStorage.setItem("chatId", sData.date_created);
						chatbot.botFinishTyping();
					},
					error: function (sError) {
						chatbot.addChatItem("Something error!", false);
					}
				});
			}

	});
});

Here in the payload, we have to give Authorization(user, organization, element) which are generated while creating open connector instance. Otherwise the app will not work.

Step-4:

Now our app is ready and after running the app, the output is as follows.

When we click on “Whatsapp Me” button, a popup will be opened where we can send and receive messages.

Leave a Reply

Your email address will not be published. Required fields are marked *