Integrating Audience Targeting with your Apps – Part III: Channels

What a coincidence (or maybe not...) that Doc and Marty landed in the future on October 21, 2015, the date of the Liferay Spain Symposium. Maybe we didn’t see any overboards or self-tying shoes, but this event was indeed a window to the future of Web Experience. With Liferay 7 just around the corner, it was the best chance to learn about all its new features first hand from our developers.
Welcome to the Future
As in other events, Audience Targeting was one of the trending topics. In the expert roundtables we had the chance to get some valuable feedback from some of the first users and developers working with Audience Targeting. From these lines we want to thank all the attendees and we’re looking forward to seeing them next year.
 

Where were we…?

Oh, yes, our series of blog entries about how to integrate Audience Targeting with your apps! Last time you learned how to classify your users based on information from external applications by creating custom rules. This time we’ll focus on the second stage of the Audience Targeting machine: 
Audience Targeting - Targeting Stage
At this point is where targeted content is delivered to the classified users through campaigns. By definition, a marketing campaign is a specific, defined series of activities designed to promote a product, service or business within a time-frame. Since Audience Targeting 2.0, those activities are called “Promotions” and they can be performed in through different means or “Channels”.
 
Audience Targeting - Campaign Promotions
 
Any medium that may be used to reach our audience can be considered as a potential channel. Email, SMS or mobile notifications are just a few examples of channels. Could you list them all? Well, we just couldn’t so that’s why we let you add your own custom channels in Audience Targeting, just as with rules.  
 

Integrating your app in a multi-channel campaign

Channels are a very powerful integration point in Audience Targeting. You can virtually send targeted content or perform targeted actions through any app that exposes an API. And it works all the way round: you custom channel can expose its own remote API that is consumible by any app.
 
This means that your apps can be easily integrated as channels in your marketing campaigns, increasing your audience and the opportunities to reach your goals. We’ll show you how with an example.
 

Targeting content to Twitter influencers

Do you remember our custom rule to classify users based on their influence in Twitter? Basically we consumed Twitter API to find out the number of followers of the given user and if it was greater than a certain configurable threshold, then the user was considered an “influencer”.
 
Now we want to increase our company’s brand awareness with a campaign targeted to the most influent users in Twitter.  As one of our promotions, we will send them a message via Twitter including a link to our brand new website. In other words, in addition to using Twitter for user classification, we’ll use it as a channel.
 

Creating the channel project

Adding a new channel type for Audience Targeting is pretty much the same as adding a new rule type. Simply use this command:
at-sdk-dir>./create_channel.sh twitter-sample “Twitter Sample” 
You should now find a channel-twitter-sample folder in your SDK. It contains the skeleton of our channel project. Move to that directory and execute this command:
at-sdk-dir/channel-twitter-sample>ant deploy
Now your channel has been deployed in your Liferay bundle and you will find it in the list of channels when editing a promotion in a campaign.
 

Customizing the channel

As we did with our Twitter Sample Rule, once we have created our basic Twitter Sample Channel  it’s time to implement the targeting mechanism based on Twitter messages. Let’s start by changing the icon:
 
TwitterSampleChannel.java
	@Override
	public String getIcon() {
		return "icon-twitter";
	}
 
Now we need a UI that allows our marketing team to enter the message that will be delivered to the influent users:
 
ct_channel.ftl
<#assign aui = PortletJspTagLibs["/META-INF/aui.tld"] />
<#assign liferay_ui = PortletJspTagLibs["/META-INF/liferay-ui.tld"] />

<#setting number_format="computer">

<@aui["input"] label="message" name="{ct_field_guid}message" type="textarea" value=message>
	<@aui["validator"] name="required" />
</@>
Don’t forget to add the keys and translations in the Languages*.properties files.
 
As you may have noticed, so far the changes are very similar to the ones we did to customize our rule. 
 

Searching for classified users

The first step to send the messages is to find the recipients. When a marketer creates a promotion, the targeted user segments are chosen. Therefore, in our channel we have to obtain a list with the users that have been classified into the selected user segments. In Audience Targeting this can be achieved very easily with the AnonymousUserUserSegment service:
 
TwitterSampleChannel.java
	private List<String> _getTwitterUsers(PortletRequest request)
		throws Exception {

		long campaignId = ParamUtil.getLong(request, "campaignId");

		Campaign campaign = _campaignLocalService.fetchCampaign(campaignId);

		if (!campaign.isActive()) {
			return Collections.emptyList();
		}

		long tacticId = ParamUtil.getLong(request, "tacticId");

		List<UserSegment> tacticUserSegments =
			_userSegmentLocalService.getTacticUserSegments(tacticId);

		List<User> users = _userLocalService.getGroupUsers(
			campaign.getGroupId());

		List<String> twitterUsers = new ArrayList<String>();

		for (User user : users) {
			Contact contact = user.getContact();

			String twitterScreenName = contact.getTwitterSn();

			if (Validator.isNull(twitterScreenName)) {
				continue;
			}

			List<UserSegment> userSegments =
				_anonymousUserUserSegmentLocalService.getUserSegmentsByUserId(
					user.getUserId(), true);

			if (Collections.disjoint(userSegments, tacticUserSegments)) {
				continue;
			}

			twitterUsers.add(twitterScreenName);
		}

		return twitterUsers;
	}

Notice that we’re discarding users that do not have a Twitter account set on their profile and that we’re targeting only users who are members of the current site.
 

Consuming the Twitter API

The Twitter API provides services to send private messages to a given user but the recipient must either be following the sender or have their allow_dms_from account setting set to “all”.  Since we don’t want to assume that the influent users are following our account, we’ll instead generate a tweet mentioning the user. This is how to do it with the Twitter4j lib:
 
TwitterSampleChannel.java
	private void _sendMessage(List<String> twitterUsers, String message)
		throws Exception {

		ConfigurationBuilder cb = new ConfigurationBuilder();

		cb.setDebugEnabled(true);
		cb.setOAuthConsumerKey(_CONSUMER_KEY);
		cb.setOAuthConsumerSecret(_CONSUMER_SECRET);
		cb.setOAuthAccessToken(_ACCESS_KEY);
		cb.setOAuthAccessTokenSecret(_ACCESS_SECRET);

		TwitterFactory twitterFactory = new TwitterFactory(cb.build());

		Twitter twitter = twitterFactory.getInstance();

		for (String twitterUser : twitterUsers) {
			twitter.updateStatus("@" + twitterUser + " " + message);
		}
	}
 

Sending the messages

One of the decisions we have to make is when to send your messages. In a real campaign you’d want to schedule the messaging for a given date and time. You could do this by creating a new scheduled task in the processChannel method (i.e. when saving the changes in your promotion).
 
Having said that, in order to keep this educational example simple and focused on the Audience Targeting logic, we’ll send the message right after the promotion is saved. This is how the processChannel method remains:
	@Override
	public String processChannel(
			PortletRequest request, PortletResponse response, String id,
			Map<String, String> values)
		throws InvalidChannelException {

		String message = values.get("message");

		JSONObject jsonObj = JSONFactoryUtil.createJSONObject();

		jsonObj.put("message", message);

		try {
			List<String> twitterUsers = _getTwitterUsers(request);

			if (!twitterUsers.isEmpty()) {
				_sendMessage(twitterUsers, message);
			}
		}
		catch (Exception e) {
			throw new InvalidChannelException(
				"Cannot send twitter messages", e);
		}

		return jsonObj.toString();
	}
 

Setting channel dependencies

Finally, as we did for our rule we have to resolve our build-time and runtime dependencies:
 
ivy.xml
<ivy-module>
	...
	<dependencies defaultconf="default">
		...
		<dependency name="twitter4j-core" org="org.twitter4j" rev="4.0.4" />
	</dependencies>
</ivy-module>
bnd.bnd
...
Include-Resource:\
 	...,\
	@lib/twitter4j-core.jar
...

 

Testing our channel

Once we’ve performed the changes and re-deployed our channel we can test if the sending is working. Notice that the steps to test the channel are a variation of the steps to test the rule:
  1. Create a couple of users (A and B), each with a different Twitter profile. 
  2. Add a User Segment “Twitter Influencers” with the Twitter Sample Rule and set a number of followers that is greater than the number of followers of B but lower than the number of followers of A. 
  3. Log in with users A and B (at this moment they will be categorized as “Twitter Influencers”).
  4. Add a new campaign “Visit my website” targeted to "Twitter Influencers".
  5. Add a new promotion “Tweets for influencers” with the Twitter Sample Channel and enter the message you want to send.
  6. Save changes. In a few seconds, a tweet with your message mentioning user A should have been generated with your account.

Twitter Channel

Tweet

Take into account that the Twitter API limits the number of messages each 15 min.
 

Best practices and improvements

Apart from having the API limitations in mind, it’s a good practice to plan and optimize how and when your content is sent. You don’t want to bother your users with continuous messages!
 
And as we mentioned for rules,  in upcoming entries we’ll show you how to handle your API credentials more efficiently with the Consumer Manager.
 

And now what?

Probably you may want some feedback about how your app is performing as a channel in your campaign promotions. For instance, if you added a link to your message, it’d be very interesting to know how many users followed that link, right? This leads us to the next and final stage in the Audience Targeting machine: Monitoring. But unless you have a time machine, you’ll have to wait till our next entry!  cool
 
In the meantime, put into practice what you’ve learned about channels and try to create yours!