This article describes how to send template messages from a Salesforce object to multiple recipients at once. For instance to send WeChat Template messages to CampaignMembers linked to a certain Campaign. For this guide, it is assumed the template messages and the Social25 environments are already configured and set-up. The trigger in this example will be a button press event on a Lightning Component, but this might also be a trigger, a process builder or a flow.
Consent is managed through the platform through which the messages are sent. In the case of WeChat, subscribing to your account. In the case of WhatsApp, consent is not technical but a legal requirement and pre-approved templates have to be used. |
Social25 has functionality to send pre-defined template message to a single or a collection of users. Marketing users may configure the template and the related merge fields, while an admin or a developer takes care of collecting the necessary info and providing this to the Social25 Post_Template_Message interface. Social25 then takes care of merging the fields and sending the template to the provided contacts (using the Heroku Conversation Id). This can be done using Process Builder and Flow (through the Invocable Method) or through Apex.
As mentioned Social25 does the heavy lifting of preparing the template and sending the messages. Salesforce still needs to tell Social25 what template to send, with what information, to whom, and when. The documentation below explains these steps.
This guide contains code snippets and examples that may be used. There are other ways to implement this, the best way is left to the digression of the reader. Code snippets are provided as-is, without any warranty or guarantee.
To send template messages, Social25 has a built in call message:
global static List<Post_Message.PostMessageResponse> Social25.Post_Template_Message.call(List<Social25.Post_Template_Message.PostTemplateMessageRequest>) |
This method accepts a list of PostTemplateMessageRequest objects, a Social25 custom class. The method returns a list of PostMessageResponses containing the individual request status. These are void when called from an asynchronous context.
The class TemplateMessageRequest contains the following accessible properties:
Name | DataType | Description |
---|---|---|
asynchronous | Boolean | Sets whether the messages should be sent asynchronously or not. |
conversation_id | String | The Social25 Heroku Conversation Id. Found on the Heroku Message object, and on the related Case, Opportunity or Lead. |
template_name | String | The Name of the Template defined. Used to fetch the Template definition. |
record_id | Id | The record Id to use when merging. |
The packaged Template functionality in Social25 consists of two parts; the Template object and the Template Field object, following the principle of the Integration Definition and the Integration Field objects used in some implementations. The Template defines the overall structure of the template, the Template Fields define the merging of the fields.
The Template consists of two relevant fields:
Optional fields can be added to customize the template. For instance (in the below example) a Query Filter is added that is used to exclude certain records when sending messages (see Filter Criteria below).
The merge fields consist of four relevant fields:
The below example shows the Merge Field setup for the given example template. Fields are linked to the corresponding values in the template.
Note that all field destination values are taken from the triggering object (in this example, Campaign). Simple cross object definitions up to one level deep can also be used (example: Owner.Name, or Location__r.Description__c, but not Primary_Contact__r.Account.Name). This limitation can be mitigated by using for instance cross-object formula fields.
Filter criteria may be used to exclude certain messages when sending. For instance, internal or external users may be excluded, or consent may be kept on the related record level and filtered in the criteria. This filter is appended to the SOQL query that is executed on trigger. See code snippet for more information.
Sending the actual messages in this example will be done based on a button push on a custom Lightning Component. This component consists of the Component, JS Controller and Helper, and Apex controller. The component lists the available templates and sends all messages (except those excluded by the filter criteria) the defined template message by triggering a Batch.
@AuraEnabled public static void sendWeChatTemplateMessages(Id templateId, Id campaignId) { //Query the selected template to retrieve the name Social25__Template__c selectedTemplate = [SELECT Name FROM Social25__Template__c WHERE Id = :templateId]; //Start the job to send the template messages to the campaign members. CampaingId is sent to the batch constructor. Id jobId = Database.executeBatch(new G25_Batch_SendS25TemplateMessages(selectedTemplate.Name, campaignId), 99); //Do something with the jobId for logging purposes and such... } |
This starts a batch, here we show the start() and execute() methods (some functionality such as logging omitted for brevity):
public Database.QueryLocator start(Database.BatchableContext bc) { Social25__Template__c template = [SELECT Query_Filter__c FROM Social25__Template__c WHERE Name = 'Meeting Event']; //this.campaignId is set by the batch constructor, as input from the Lightning controller. String queryString = 'SELECT Id, Conversation_ID__c FROM CampaignMember WHERE CampaignId = :this.campaignId AND Conversation_ID__c != NULL'; //append a query filter to the query, if(String.isNotBlank(template.Query_Filter__c)) { queryString = String.join(new List<String>{queryString, template.Query_Filter__c}, ' AND '); } return Database.getQueryLocator(queryString); } |
public void execute(Database.BatchableContext bc, List<CampaignMember> campaignMembers) { //Keep a mapping of the Conversation Id to a specific Campaign Member. The conversation Id is set on the //campaign member using logic out of scope for this guide. Map<String, CampaignMember> weChatIdToCampaignMember = new Map<String, CampaignMember>(); for (CampaignMember member : campaignMembers) { weChatIdToCampaignMember.put(member.Conversation_ID__c, member); } //Keep a list of the requests that needs to be send out List<Social25.Post_Template_Message.PostTemplateMessageRequest> postTemplateMessageRequests = new List<Social25.Post_Template_Message.PostTemplateMessageRequest>(); //Loop over the campaign members of current batch for (CampaignMember relatedCampaignMember : campaignMembers) { //Create a request object and fill in the required attributes Social25.Post_Template_Message.PostTemplateMessageRequest templateMessageRequest = new Social25.Post_Template_Message.PostTemplateMessageRequest(); templateMessageRequest.asynchronous = false; templateMessageRequest.conversation_id = relatedCampaignMember.Conversation_ID__c; templateMessageRequest.template_name = 'Meeting Event'; templateMessageRequest.record_id = campaignId; postTemplateMessageRequests.add(templateMessageRequest); } List<Social25.Post_Message.PostMessageResponse> responses = new List<Social25.Post_Message.PostMessageResponse>(); //do callouts. errors are handled by the package, so no try{} catch(){}. //results are stored in the request object (postTemplateMessageRequests). Social25.Post_Template_Message.call(postTemplateMessageRequests); } |
When called from a non synchronous context, use the PostMessageResponse object to check and handle any errors. Social25 is set up to handle and report back most errors in this way. As handling is different in each Salesforce org, no further documentation is provided here.
Related articles appear here based on the labels you select. Click to edit the macro and add or change labels.
|