Last active
February 28, 2024 11:10
-
-
Save amitastreait/421a15f4f4c43a5593d0a436fc018c63 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<lightning-card> | |
<lightning-button if:true={showMessages} label="Chat with another Customer" | |
onclick={handleAnotherChat} | |
title="Chat with another Customer" variant="brand" slot="actions"> | |
</lightning-button> | |
<lightning-spinner variant="brand" alternative-text="loading.." if:true={isSpinner}></lightning-spinner> | |
<div class="slds-m-around_small" if:false={showMessages}> | |
<p> | |
<lightning-input required message-when-value-missing="Please provide the phone of the customer" type="text" | |
placeholder="Enter Customer Phone Number and Click Next...." | |
label="Send Message" variant="label-hidden" | |
onchange={handlePhoneChange}> | |
</lightning-input> | |
</p> | |
<p class="slds-m-top_medium"> | |
<lightning-button variant="brand" label="Chat with Customer" title="Chat with Customer" onclick={handleChat}></lightning-button> | |
</p> | |
</div> | |
<section if:true={showMessages} role="log" class="chatArea slds-chat slds-scrollable" style="height: 400px;"> | |
<ul class="slds-chat-list"> | |
<template for:each={messages} for:item="message" for:index="index"> | |
<li if:false={message.Outgoing__c} key={message.Id} class="slds-chat-listitem slds-chat-listitem_outbound"> | |
<div class="slds-chat-message"> | |
<div class="slds-chat-message__body"> | |
<div class="slds-chat-message__text slds-chat-message__text_outbound"> | |
<span> | |
<lightning-formatted-rich-text value={message.MessageContent__c}></lightning-formatted-rich-text> | |
</span> | |
</div> | |
<div class="slds-chat-message__meta" aria-label="said Amber Cann at 5:23 PM">{message.CustomerName__c}</div> | |
</div> | |
</div> | |
</li> | |
<li if:true={message.Outgoing__c} key={message.Id} class="slds-chat-listitem slds-chat-listitem_inbound"> | |
<div class="slds-chat-message"> | |
<span aria-hidden="true" class="slds-avatar slds-avatar_circle slds-chat-avatar"> | |
<abbr class="slds-avatar__initials slds-avatar__initials_inverse" title="Andy Martinez">AM</abbr> | |
</span> | |
<div class="slds-chat-message__body"> | |
<div class="slds-chat-message__text slds-chat-message__text_inbound"> | |
<span> | |
<lightning-formatted-rich-text value={message.MessageContent__c}></lightning-formatted-rich-text> | |
</span> | |
</div> | |
<div class="slds-chat-message__meta" aria-label="said Andy Martinez at 5:29 PM">{message.AgentName__c}</div> | |
</div> | |
</div> | |
</li> | |
</template> | |
</ul> | |
<div if:true={showMessages} class="slds-m-around_small"> | |
<lightning-input class="chat-input" | |
type="text" | |
value={messageText} | |
placeholder="type here....." | |
message-when-value-missing="Please provide a valid message for the customer to be sent!" | |
required | |
label="Send Message" variant="label-hidden" onchange={handleChange}> | |
</lightning-input> | |
</div> | |
</section> | |
</lightning-card> | |
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { LightningElement, track, wire } from 'lwc'; | |
import listAllMessages from '@salesforce/apex/WhatsAppLWCService.listAllMessages'; | |
import sendTextMessage from '@salesforce/apex/WhatsAppLWCService.sendTextMessage' | |
import getSingleMessage from '@salesforce/apex/WhatsAppLWCService.getSingleMessage'; | |
import { subscribe, unsubscribe, onError } from 'lightning/empApi'; | |
export default class Chatcomponent extends LightningElement { | |
@track messages; | |
@track errorDetails; | |
showMessages = false; | |
isSpinner = false; | |
phone; | |
messageText; | |
eventName = '/event/WA_Message_Event__e' //PE | |
subscription; | |
connectedCallback() { | |
this.handleErrorRegister(); | |
this.handleSubscribe(); | |
} | |
disconnectedCallback() { | |
this.handleUnSubscribe(); | |
} | |
handleUnSubscribe() { | |
//unsubscribe(this.subscription) | |
} | |
handleSubscribe() { | |
subscribe(this.eventName, -1, this.handleSubscribeResponse.bind(this)).then((response) => { | |
this.subscription = response; | |
console.log('Subscribed to channel ', JSON.stringify(response)); | |
}); | |
} | |
handleSubscribeResponse(response) { | |
console.log('Response from WhatsApp Webhook ', JSON.stringify(response)); | |
let data = response.data.payload; | |
let messageId = data.Message_Id__c; | |
let customerPhone = data.Customer_Phone__c; | |
if (this.phone === customerPhone) { | |
// Make the Apex Class Call to get the message details | |
getSingleMessage({ | |
recordId: messageId, | |
customerPhone: customerPhone | |
}) | |
.then((response) => { | |
this.messages.push(response); | |
}) | |
.catch((error) => { | |
console.error('Error While Recieving the Platform Event Message') | |
}) | |
.finally(() => { | |
let chatArea = this.template.querySelector('.chatArea'); | |
if (chatArea) { | |
chatArea.scrollTop = chatArea.scrollHeight; | |
} | |
}) | |
} | |
} | |
handleErrorRegister() { | |
onError((error) => { | |
console.error('Received error from server: ', JSON.stringify(error)); | |
// Error contains the server-side error | |
}); | |
} | |
handlePhoneChange(event) { | |
event.preventDefault(); | |
this.phone = event.target.value; | |
console.log(this.phone); | |
} | |
handleChat(event) { | |
event.preventDefault(); | |
console.log(this.phone); | |
if (this.handleValidate()) { | |
// make a call to Salesforce Apex to get the list of message | |
this.isSpinner = true; | |
listAllMessages({ | |
customerPhone: this.phone | |
}) | |
.then((result) => { | |
this.messages = result; | |
this.showMessages = true; | |
}) | |
.catch((errors) => { | |
this.errorDetails = errors; | |
this.showMessages = false; | |
}) | |
.finally(() => { | |
// | |
let chatArea = this.template.querySelector('.chatArea'); | |
if (chatArea) { | |
chatArea.scrollTop = chatArea.scrollHeight; | |
} | |
this.isSpinner = false; | |
this.setUpChatMessage(); | |
}) | |
} else { | |
return; | |
} | |
} | |
setUpChatMessage() { | |
let chatInput = this.template.querySelector(".chat-input"); | |
if (chatInput) { | |
chatInput.addEventListener("keydown", (event) => { | |
if (event.key === "Enter") { | |
this.handleSendMessage(); | |
} | |
}); | |
} | |
} | |
handleSendMessage() { | |
let allValid = this.handleValidate(); | |
if (allValid) { | |
this.isSpinner = true; | |
sendTextMessage({ | |
messageContent: this.messageText, | |
toPhone: this.phone | |
}) | |
.then((result) => { | |
this.messages.push(result); | |
}) | |
.catch((errors) => { | |
this.errorDetails = errors; | |
this.showMessages = false; | |
}) | |
.finally(() => { | |
let chatArea = this.template.querySelector('.chatArea'); | |
if (chatArea) { | |
chatArea.scrollTop = chatArea.scrollHeight; | |
} | |
this.isSpinner = false; | |
this.messageText = ''; | |
}) | |
} | |
} | |
handleChange(event) { | |
event.preventDefault(); | |
this.messageText = event.target.value; | |
} | |
handleAnotherChat() { | |
this.messageText = ''; | |
this.showMessages = false; | |
this.messages = undefined; | |
} | |
handleValidate() { | |
const allValid = [ | |
...this.template.querySelectorAll('lightning-input'), | |
].reduce((validSoFar, inputCmp) => { | |
inputCmp.reportValidity(); | |
return validSoFar && inputCmp.checkValidity(); | |
}, true); | |
return allValid; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class WhatsAppLWCService { | |
@AuraEnabled//(cacheable=true) | |
public static List<WAMessage__c> listAllMessages(String customerPhone){ | |
List<WAMessage__c> messages = new List<WAMessage__c>(); | |
messages = [SELECT Id, Name, MessageContent__c, MessageType__c, CustomerName__c, AgentName__c, Outgoing__c, CreatedDate | |
FROM WAMessage__c | |
WHERE CustomerPhone__c =: customerPhone | |
Order By CreatedDate ASC | |
]; | |
return messages; | |
} | |
@AuraEnabled | |
public static WAMessage__c getSingleMessage(String recordId, String customerPhone){ | |
return [SELECT Id, Name, MessageContent__c, MessageType__c, CustomerName__c, AgentName__c, Outgoing__c, CreatedDate | |
FROM WAMessage__c | |
WHERE Id =: recordId AND | |
CustomerPhone__c =: customerPhone | |
Order By CreatedDate ASC | |
]; | |
} | |
@AuraEnabled | |
public static WAMessage__c sendTextMessage(String messageContent, String toPhone){ | |
WAMessage__c message = WhatsAppUtils.sendTextMessage(messageContent, toPhone); | |
return [SELECT Id, Name, MessageContent__c, MessageType__c, CustomerName__c, AgentName__c, Outgoing__c, CreatedDate | |
FROM WAMessage__c | |
WHERE Id =: message.Id | |
Order By CreatedDate ASC | |
]; | |
} | |
@AuraEnabled(cacheable=true) | |
public static List<WAMessage__c> listAllMessageByCustomer(String customerPhone){ | |
List<WAMessage__c> messages = new List<WAMessage__c>(); | |
messages = [SELECT Id, Name, MessageContent__c, MessageType__c, CustomerName__c, AgentName__c, Outgoing__c, CreatedDate | |
FROM WAMessage__c | |
WHERE CustomerPhone__c =: customerPhone | |
Order By CreatedDate ASC | |
]; | |
return messages; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class WhatsAppUtils { | |
public static List<WAMessage__c> listAllMessageByCustomer(String customerPhone){ | |
List<WAMessage__c> messages = new List<WAMessage__c>(); | |
messages = [SELECT Id, Name, MessageContent__c, MessageType__c, Outgoing__c FROM WAMessage__c WHERE CustomerPhone__c =: customerPhone]; | |
return messages; | |
} | |
public static WAMessage__c sendTextMessage(String messageContent, String toPhone){ | |
HttpRequest httpReq = new HttpRequest(); | |
httpReq.setEndpoint('https://graph.facebook.com/v13.0/<YOUR_ACCOUNT_ID>/messages'); | |
httpReq.setMethod('POST'); | |
httpReq.setHeader('Content-Type', 'application/json'); | |
httpReq.setHeader('Authorization', 'Bearer '+System.Label.WHATSAPPACCESSTOKEN); | |
String messageBody = '{'+ | |
' "messaging_product": "whatsapp",'+ | |
' "recipient_type": "individual",'+ | |
' "to": "'+toPhone+'",'+ | |
' "type": "text",'+ | |
' "text": {'+ | |
' "preview_url": false,'+ | |
' "body": "'+messageContent+'"'+ | |
' }'+ | |
'}'; | |
httpReq.setBody(messageBody); | |
Http http = new Http(); | |
WAMessage__c salesforceMessage = new WAMessage__c(); | |
try{ | |
HttpResponse response = http.send(httpReq); | |
if( response.getStatusCode() == 200 ){ | |
// Parse & Create Message Record | |
System.debug('Successful!'); | |
WhatsAppUtils responseFromWA = (WhatsAppUtils) JSON.deserialize( response.getBody() , WhatsAppUtils.class); | |
salesforceMessage.MessageContent__c = messageContent; | |
salesforceMessage.CustomerPhone__c = toPhone; | |
salesforceMessage.MessageID__c = responseFromWA.messages.get(0).id; | |
salesforceMessage.MessageType__c = 'text'; | |
salesforceMessage.Outgoing__c = True; | |
salesforceMessage.AgentName__c = UserInfo.getFirstName()+' '+ UserInfo.getLastName(); | |
upsert salesforceMessage MessageID__c; | |
} | |
}catch(System.CalloutException ex){ | |
System.debug(' CalloutException Executed '+ ex.getStackTraceString() ); | |
System.debug(' CalloutException Executed '+ ex.getMessage() ); | |
}catch(System.Exception ex){ | |
System.debug(' System.Exception Executed '+ ex.getStackTraceString() ); | |
} | |
return salesforceMessage; | |
} | |
public String messaging_product; | |
public contacts[] contacts; | |
public messages[] messages; | |
public class contacts { | |
public String input; | |
public String wa_id; | |
} | |
public class messages { | |
public String id; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RestResource(urlMapping='/whatsapp/webhooks/v1/*') | |
global without sharing class WhatsAppWebhook { | |
private static Final String SIGNATURE_VALID_MESSAGE = 'Signature Verified'; | |
private static Final String SIGNATURE_NOT_VALID_MESSAGE = 'Signature could not be verified'; | |
@HttpGet // GET | |
global static void doGet() { | |
RestResponse response = RestContext.response; | |
RestRequest request = RestContext.request; | |
if(request.params.get('hub.verify_token') == 'WHATSAPPTOKEN'){ | |
response.responseBody = Blob.valueOf( request.params.get('hub.challenge') ); | |
} | |
} | |
@HttpPost // POST | |
global static void doPost() { | |
RestResponse response = RestContext.response; | |
response.addHeader('Content-type','application/json'); | |
String responseString = RestContext.request.requestBody.toString(); | |
Map<String, String> headers = RestContext.request.headers; | |
String responseValid = validateWhatsAppSignature(RestContext.request, responseString); | |
if(responseValid == SIGNATURE_VALID_MESSAGE){ | |
System.debug(System.LoggingLevel.DEBUG, ' Headers Response From WhatsApp \n '+ JSON.serialize(headers) ); | |
System.debug(System.LoggingLevel.DEBUG, ' Response From WhatsApp \n '+ responseString); | |
String finalResponseString = responseString.replace('type', 'typex'); | |
WhatsAppMessage parentMessage = (WhatsAppMessage)JSON.deserialize( finalResponseString, WhatsAppMessage.class); | |
List<WhatsAppMessage.entry> messageEntries = parentMessage.entry; | |
if(messageEntries != null && messageEntries.size() > 0){ | |
WhatsAppMessage.entry entryMessage = messageEntries.get(0); | |
List<WhatsAppMessage.changes> changeMessages = entryMessage.changes; | |
if(changeMessages != null && changeMessages.size() > 0){ | |
WhatsAppMessage.changes changeMessage = changeMessages.get(0); | |
List<WhatsAppMessage.contacts> contactList = changeMessage.value.contacts; | |
List<WhatsAppMessage.messages> messageList = changeMessage.value.messages; | |
WhatsAppMessage.metadata metadata = changeMessage.value.metadata; | |
/* Create record into Salesforce */ | |
WAMessage__c salesforceMessage = new WAMessage__c(); | |
salesforceMessage.BusinessPhoneNumber__c = metadata != null ? metadata.display_phone_number : null; | |
if(contactList != null && contactList.size() > 0){ | |
WhatsAppMessage.contacts contact = contactList.get(0); | |
salesforceMessage.CustomerPhone__c = contact.wa_id; | |
salesforceMessage.CustomerName__c = contact.profile.name; | |
} | |
if(messageList != null && messageList.size() > 0){ | |
/* Simple Message */ | |
WhatsAppMessage.messages message = messageList.get(0); | |
salesforceMessage.MessageID__c = message.id; | |
salesforceMessage.MessageType__c = message.typex; | |
salesforceMessage.MessageSentTime__c = System.now(); | |
salesforceMessage.MessageContent__c = message.text != null? message.text.body : null; | |
/* If message is reaction */ | |
salesforceMessage.Reaction__c = message.reaction != null ? message.reaction.emoji : null; | |
salesforceMessage.ParentMessageID__c = message.reaction != null ? message.reaction.message_id : null; | |
/* If message is Image */ | |
salesforceMessage.ImageID__c = message.image != null ? message.image.id : null; | |
salesforceMessage.ImageType__c = message.image != null ? message.image.mime_type : null; | |
salesforceMessage.ImageSHA256__c = message.image != null ? message.image.sha256 : null; | |
/* If message is Video */ | |
salesforceMessage.VideoId__c = message.video != null ? message.video.id : null; | |
salesforceMessage.VideoType__c = message.video != null ? message.video.mime_type : null; | |
salesforceMessage.VideoSHA256__c = message.video != null ? message.video.sha256 : null; | |
/* If the message is reply to another message */ | |
salesforceMessage.ParentMessageID__c = message.context != null ? message.context.id : null; | |
upsert salesforceMessage MessageID__c; | |
/* Publish the Platform Event to be listened by LWC */ | |
WA_Message_Event__e platformEvent = new WA_Message_Event__e(); | |
platformEvent.Message_Id__c = salesforceMessage.Id; | |
platformEvent.Customer_Phone__c = salesforceMessage.CustomerPhone__c; | |
Eventbus.publish( platformEvent ); | |
} | |
} | |
} | |
}else{ | |
response.responseBody = Blob.valueOf('{success:false, event:"Unknown","message:"'+responseValid+'"}'); | |
response.statusCode = 401; | |
return; | |
} | |
response.statusCode = 200; | |
response.responseBody = Blob.valueOf('{success:true, event:"success"}'); | |
} | |
private static String validateWhatsAppSignature(RestRequest request, String responseString) { | |
// Validate Stripe signature Start | |
Map<String, String> headers = request.headers; | |
String whatsAppSignature = headers.get('X-Hub-Signature-256'); | |
String whatsAppPayload = RestContext.request.requestBody.toString(); | |
// Verify the signature using 'hmacSHA256'. I have the Webhook key stored in a Custom Label | |
String whatsAppSecret = System.Label.WHATSAPPSECRET; // Facebook Application Secret Key | |
Blob signedPayload = Crypto.generateMac('hmacSHA256', Blob.valueOf(whatsAppPayload), Blob.valueOf( whatsAppSecret )); | |
String encodedPayload = 'sha256='+EncodingUtil.convertToHex(signedPayload); | |
// Return status code based on whether signed payload matches or not | |
String response = (encodedPayload == whatsAppSignature)? SIGNATURE_VALID_MESSAGE : SIGNATURE_NOT_VALID_MESSAGE; | |
return response; | |
// Validate Stripe signature End | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment