UNPKG

@swrve/smarttv-sdk

Version:

Swrve marketing engagement platform SDK for SmartTV OTT devices

561 lines (560 loc) 29.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CampaignManager = void 0; const StorageManager_1 = require("../Storage/StorageManager"); const SwrveConstants_1 = require("../utils/SwrveConstants"); const SwrveMessageDisplayManager_1 = require("./Messages/SwrveMessageDisplayManager"); const AssetManager_1 = require("./AssetManager"); const SwrveLogger_1 = __importDefault(require("../utils/SwrveLogger")); const SwrveButton_1 = require("../UIElements/SwrveButton"); const SwrveImage_1 = require("../UIElements/SwrveImage"); const DateHelper_1 = __importDefault(require("../utils/DateHelper")); class CampaignManager { constructor(profileManager, platform, config, resourceManager, personalizationProperties) { this.profileManager = profileManager; this.campaigns = []; this.campaignState = {}; this.onMessageListener = null; this.onEmbeddedListener = null; //global rules this._maxMessagesPerSession = 99999; this._minDelay = 55; this._delayFirstMessage = 150; this.MAX_MESSAGES_PER_SESSION = 99999; this.MIN_DELAY = 55; this.DELAY_FIRST_MESSAGE = 150; //session counter this.messagesShownCount = 0; this.lastShownMessageTime = 0; this.assetManager = new AssetManager_1.AssetManager(); this.loadStoredCampaigns(this.profileManager.currentUser.userId); this.messageDisplayManager = new SwrveMessageDisplayManager_1.SwrveMessageDisplayManager(platform, config, resourceManager); this.platform = platform; } storeCampaigns(response, personalizationProperties, onAssetsLoaded) { if (response === undefined) { return; } if (response.campaigns) { this.handleCDN(response); if (response.campaigns.campaigns) { this.campaigns = this.filterUnsupportedCampaigns(response); } else { this.campaigns = []; } this.parseGlobalRules(response.campaigns); this.synchronizeCampaignState(); this.handleAssets(personalizationProperties, onAssetsLoaded); StorageManager_1.StorageManager.saveData(SwrveConstants_1.CAMPAIGNS + this.profileManager.currentUser.userId, JSON.stringify(response)); } } refreshAssets(personalizationProperties, onAssetsLoaded) { this.handleAssets(personalizationProperties, onAssetsLoaded); } resetCampaignState(clearCampaigns = true) { if (clearCampaigns) { this.campaigns = []; } this.campaignState = {}; this.messagesShownCount = 0; this.lastShownMessageTime = 0; } //this is called when the SDK is initialized and when the user is switched loadStoredCampaigns(userId) { SwrveLogger_1.default.debug("Load stored campaigns: " + userId); // dont clear campaigns array, they will be replaced below, if in storage for user. this.resetCampaignState(false); const storedCampaigns = StorageManager_1.StorageManager.getData(SwrveConstants_1.CAMPAIGNS + userId); if (storedCampaigns) { const response = JSON.parse(storedCampaigns); //check it has expected structure if (response && response.campaigns && response.campaigns.campaigns !== undefined) { this.handleCDN(response); if (response.campaigns.campaigns) { this.campaigns = this.filterUnsupportedCampaigns(response); } else { this.campaigns = []; } if (this.campaigns) { this.parseGlobalRules(response.campaigns); } } else { SwrveLogger_1.default.debug("Campaign JSON empty, no campaigns downloaded"); } } else { SwrveLogger_1.default.debug("Unable to load stored campaigns"); } const campaignState = StorageManager_1.StorageManager.getData(SwrveConstants_1.CAMPAIGN_STATE + userId); if (campaignState) { this.campaignState = JSON.parse(campaignState); } } synchronizeCampaignState() { // only keep state for campaigns that are sent down const origCampaignState = this.campaignState; this.campaignState = {}; this.campaigns.forEach(({ id }) => { // ensure new campaigns have a default state this.campaignState[id] = origCampaignState[id] || { status: SwrveConstants_1.SWRVE_CAMPAIGN_STATUS_UNSEEN, impressions: 0, next: 0, lastShownTime: 0, }; }); StorageManager_1.StorageManager.saveData(SwrveConstants_1.CAMPAIGN_STATE + this.profileManager.currentUser.userId, JSON.stringify(this.campaignState)); } getCampaignIDs() { return this.campaigns .map(campaign => ({ id: campaign.id, type: campaign.messages ? "iam" : "unknown", variant_id: this.getCampaignVariantID(campaign), })); } getCampaignVariantID(campaign) { return campaign.messages && campaign.messages[0] ? campaign.messages[0].id : 0; } getSentPageViewEvents() { return this.messageDisplayManager.getSentPageViewEvents(); } getSentNavigationEvents() { return this.messageDisplayManager.getSentNavigationEvents(); } getCampaignState(campaignId) { return this.campaignState[campaignId]; } onEmbeddedMessage(onEmbeddedMessageListener) { this.onEmbeddedListener = onEmbeddedMessageListener; } onMessage(onMessageListener) { this.onMessageListener = onMessageListener; } onPageViewed(callback) { this.messageDisplayManager.onPageViewed(callback); } onButtonClicked(callback) { this.messageDisplayManager.onButtonClicked(callback); } onBackButtonClicked(callback) { this.messageDisplayManager.onBackButtonClicked(callback); } showCampaign(campaign, personalizationProperties, impressionCallback) { if (campaign.messages && campaign.messages.length > 0) { const message = campaign.messages[0]; message.parentCampaign = campaign.id; this.messageDisplayManager.showMessage(campaign.messages[0], campaign, this.assetManager.ImagesCDN, this.platform, personalizationProperties); if (impressionCallback) { impressionCallback(message); } this.updateCampaignState(message); return true; } else if (campaign.embedded_message && campaign.embedded_message.data) { const embeddedMessage = campaign.embedded_message; embeddedMessage.parentCampaign = campaign.id; if (this.onEmbeddedListener) { this.onEmbeddedListener(campaign.embedded_message, personalizationProperties); } return true; } else { return false; } } checkTriggers(triggerName, payload, impressionCallback, qa = false, personalizationProperties) { const matchingMessages = []; let globalStatus = this.applyGlobalRules(triggerName); let campaignStatus; const campaignStatuses = []; function logCampaignTriggerStatus(id, displayed, reason, code) { SwrveLogger_1.default.debug("Campaign trigger status: " + reason + " Displayed " + displayed); campaignStatus = { status: code, message: reason }; campaignStatuses.push({ id, displayed, type: "iam", reason }); } if (globalStatus.status === SwrveConstants_1.CAMPAIGN_MATCH) { //for each campaign, see if there is a matching trigger and pull out the messages if there is this.campaigns.forEach(campaign => { if (campaign.triggers && campaign.triggers.length > 0) { for (const trigger of campaign.triggers) { const canTrigger = trigger.event_name === triggerName && this.canTriggerWithPayload(trigger, payload); if (!canTrigger && qa) { const reason = "Campaign [" + campaign.id + "], Trigger [" + trigger.event_name + "], " + "does not match eventName[" + triggerName + "] & payload[" + JSON.stringify(payload) + "]. Skipping this trigger."; logCampaignTriggerStatus(campaign.id, "false", reason, SwrveConstants_1.CAMPAIGN_NO_MATCH); } if (canTrigger) { if (campaign.messages) { campaign.messages.forEach(message => (matchingMessages.push(Object.assign({ parentCampaign: campaign.id }, message)))); } else if (campaign.embedded_message) { matchingMessages.push(Object.assign({ parentCampaign: campaign.id }, campaign.embedded_message)); } break; } } } else { if (qa) { const reason = "Campaign [" + campaign.id + "], no triggers " + "(could be message centre). Skipping this campaign"; logCampaignTriggerStatus(campaign.id, "false", reason, SwrveConstants_1.CAMPAIGN_ERROR_INVALID_TRIGGERS); } } }); if (matchingMessages.length > 0) { matchingMessages.sort((a, b) => a.priority - b.priority); let passedAllRules = matchingMessages.filter(message => { for (const campaign of this.campaigns) { if (campaign.id === message.parentCampaign) { const { status, message: reason } = this.applyCampaignRules(triggerName, campaign, personalizationProperties); const ok = status === SwrveConstants_1.CAMPAIGN_MATCH; SwrveLogger_1.default.debug(reason); if (qa && !ok) { logCampaignTriggerStatus(campaign.id, "false", reason, status); } return ok; } } return false; }); if (passedAllRules.length > 1) { const minPriority = passedAllRules.reduce((min, msg) => msg.priority < min ? msg.priority : min, passedAllRules[0].priority); passedAllRules = passedAllRules.filter((message) => { return message.priority <= minPriority; }); } if (passedAllRules.length > 0) { const randomPick = passedAllRules.length > 1 ? Math.floor(Math.random() * passedAllRules.length) : 0; const selectedMessage = passedAllRules[randomPick]; if (qa) { const pickedCampaign = passedAllRules[randomPick]; const logNotPicked = passedAllRules.filter((message, index) => index !== randomPick); logNotPicked.forEach(message => { const reason = "Campaign " + pickedCampaign.id + " was selected for display ahead of this campaign."; logCampaignTriggerStatus(message.parentCampaign, "false", reason, SwrveConstants_1.CAMPAIGN_ELIGIBLE_BUT_OTHER_CHOSEN); }); const reason = "Campaign [" + pickedCampaign.id + "], Trigger [" + triggerName + "], matches " + triggerName + " & payload " + JSON.stringify(payload) + "."; logCampaignTriggerStatus(pickedCampaign.parentCampaign, "true", reason, SwrveConstants_1.CAMPAIGN_MATCH); } this.lastShownMessageTime = this.getNow(); if (!this.messageDisplayManager.isIAMShowing()) { if (this.onMessageListener) { this.onMessageListener(selectedMessage); } else { for (const campaign of this.campaigns) { if (campaign.id === selectedMessage.parentCampaign) { if (campaign.messages && campaign.messages.length > 0) { this.showMessage(selectedMessage, campaign, personalizationProperties); this.updateCampaignState(selectedMessage); if (impressionCallback) { impressionCallback(selectedMessage); } break; } if (campaign.embedded_message && campaign.embedded_message.data) { if (this.onEmbeddedListener) { this.onEmbeddedListener(selectedMessage, personalizationProperties); } break; } } } } } } } else if (qa) { globalStatus = { status: SwrveConstants_1.CAMPAIGN_NO_MATCH, message: "No matching campaigns." }; } } const campaignFailCode = globalStatus.status !== SwrveConstants_1.CAMPAIGN_MATCH || !campaignStatus ? globalStatus.status : campaignStatus.status; return { globalStatus, campaignStatus, campaignFailCode, campaigns: campaignStatuses }; } showMessage(message, campaign, personalizationProperties) { this.messageDisplayManager.showMessage(message, campaign, this.assetManager.ImagesCDN, this.platform, personalizationProperties); } closeMessage() { this.messageDisplayManager.closeMessage(); } canTriggerWithPayload(trigger, payload) { if (typeof trigger !== "object" || !trigger) { return false; } return this.hasFulfilledCondition(trigger.conditions || {}, payload || {}); } hasFulfilledCondition(condition, payload) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; switch (condition.op) { case "eq": const payloadValue = (_b = (_a = payload[condition.key]) === null || _a === void 0 ? void 0 : _a.toString()) === null || _b === void 0 ? void 0 : _b.toLowerCase(); const conditionValue = (_d = (_c = condition.value) === null || _c === void 0 ? void 0 : _c.toString()) === null || _d === void 0 ? void 0 : _d.toLowerCase(); return payloadValue === conditionValue; case "contains": const payloadValueLowercase = (_g = (_f = (_e = payload[condition.key]) === null || _e === void 0 ? void 0 : _e.toString()) === null || _f === void 0 ? void 0 : _f.toLowerCase()) !== null && _g !== void 0 ? _g : ""; const conditionValueLowercase = (_j = (_h = condition.value) === null || _h === void 0 ? void 0 : _h.toString()) === null || _j === void 0 ? void 0 : _j.toLowerCase(); return payloadValueLowercase.includes(conditionValueLowercase); case "and": for (const arg of condition.args) { if (!this.hasFulfilledCondition(arg, payload)) { return false; } } return true; case "or": for (const arg of condition.args) { if (this.hasFulfilledCondition(arg, payload)) { return true; } } return false; default: if (Object.keys(condition).length === 0) { return true; } return false; } } handleCDN(response) { this.assetManager.clearCDN(); if (response.campaigns && response.campaigns.cdn_root) { this.assetManager.ImagesCDN = response.campaigns.cdn_root; } else if (response.campaigns && response.campaigns.cdn_paths) { const paths = response.campaigns.cdn_paths; this.assetManager.ImagesCDN = paths.message_images; this.assetManager.FontsCDN = paths.message_fonts; } } getAssetManager() { return this.assetManager; } getMessageCenterCampaigns(personalizationProperties) { return this.campaigns .filter(campaign => { var _a; return (campaign.message_center && campaign.messages && campaign.messages.length > 0) || (campaign.message_center && campaign.embedded_message && campaign.embedded_message.data.length > 0) || (campaign.message_center && ((_a = this.campaignState[campaign.id]) === null || _a === void 0 ? void 0 : _a.status) !== SwrveConstants_1.SWRVE_CAMPAIGN_STATUS_DELETED && this.canCampaignRender(campaign, personalizationProperties)); }) .map(campaign => (Object.assign({}, campaign))); } updateCampaignState(message) { for (const parentCampaign of this.campaigns) { if (parentCampaign.id === message.parentCampaign) { const campaignState = this.campaignState[parentCampaign.id]; if (campaignState) { campaignState.impressions++; campaignState.status = "seen"; campaignState.lastShownTime = this.getNow(); this.campaignState[parentCampaign.id] = campaignState; StorageManager_1.StorageManager.saveData(SwrveConstants_1.CAMPAIGN_STATE + this.profileManager.currentUser.userId, JSON.stringify(this.campaignState)); } else { SwrveLogger_1.default.error("Campaign state not found for campaign " + parentCampaign.id); } this.messagesShownCount++; } } } markCampaignAsSeen(campaign) { this.setCampaignStatus(campaign, SwrveConstants_1.SWRVE_CAMPAIGN_STATUS_SEEN); } removeMessageCenterCampaign(campaign) { this.setCampaignStatus(campaign, SwrveConstants_1.SWRVE_CAMPAIGN_STATUS_DELETED); } isMessageShowing() { var _a, _b; return (_b = (_a = this.messageDisplayManager) === null || _a === void 0 ? void 0 : _a.isIAMShowing()) !== null && _b !== void 0 ? _b : false; } setCampaignStatus(campaign, newStatus) { if (campaign) { const campaignState = this.campaignState[campaign.id]; if (campaignState) { campaignState.status = newStatus; this.campaignState[campaign.id] = campaignState; StorageManager_1.StorageManager.saveData(SwrveConstants_1.CAMPAIGN_STATE + this.profileManager.currentUser.userId, JSON.stringify(this.campaignState)); } else { SwrveLogger_1.default.error("Campaign state not found for campaign " + campaign.id); } } } applyGlobalRules(triggerName) { if (this.messagesShownCount >= this._maxMessagesPerSession) { return { status: SwrveConstants_1.GLOBAL_CAMPAIGN_THROTTLE_MAX_IMPRESSIONS, message: "{App Throttle limit} Too many messages shown.", }; } const timeSinceStartup = this.profileManager.currentUser.sessionStart + (this._delayFirstMessage * 1000); SwrveLogger_1.default.info("delay first message " + this._delayFirstMessage); if (triggerName !== SwrveConstants_1.SWRVE_AUTOSHOW_AT_SESSION_START_TRIGGER && timeSinceStartup > this.getNow()) { return { status: SwrveConstants_1.GLOBAL_CAMPAIGN_THROTTLE_LAUNCH_TIME, message: "{App Throttle limit} Too soon after launch. Wait until " + timeSinceStartup, }; } const lastDisplay = this.lastShownMessageTime + (this._minDelay * 1000); if (this.lastShownMessageTime !== 0 && lastDisplay > this.getNow()) { return { status: SwrveConstants_1.GLOBAL_CAMPAIGN_THROTTLE_RECENT, message: "{App Throttle limit} Too soon after last message. Wait until " + lastDisplay, }; } return { status: SwrveConstants_1.CAMPAIGN_MATCH, message: "Global display rules passing.", }; } applyCampaignRules(triggerName, parentCampaign, personalizationProperties) { const rules = parentCampaign.rules; const campaignState = this.campaignState[parentCampaign.id]; if (parentCampaign.start_date > this.getNow() || parentCampaign.end_date < this.getNow()) { return { status: SwrveConstants_1.CAMPAIGN_NOT_ACTIVE, message: "Campaign " + parentCampaign.id + "not active.", }; } const timeSinceStart = this.profileManager.currentUser.sessionStart + (rules.delay_first_message * 1000); if (triggerName !== SwrveConstants_1.SWRVE_AUTOSHOW_AT_SESSION_START_TRIGGER && timeSinceStart > this.getNow()) { return { status: SwrveConstants_1.CAMPAIGN_THROTTLE_LAUNCH_TIME, message: "{Campaign throttle limit} Too soon after launch. Wait until " + timeSinceStart, }; } if (rules.hasOwnProperty("dismiss_after_views") && (campaignState === null || campaignState === void 0 ? void 0 : campaignState.impressions) >= rules.dismiss_after_views) { const message = "{Campaign throttle limit} Campaign " + parentCampaign.id + " has been shown " + parentCampaign.rules.dismiss_after_views + " times already"; return { status: SwrveConstants_1.CAMPAIGN_THROTTLE_MAX_IMPRESSIONS, message, }; } const lastShown = (campaignState === null || campaignState === void 0 ? void 0 : campaignState.lastShownTime) + (rules.min_delay_between_messages * 1000); if ((campaignState === null || campaignState === void 0 ? void 0 : campaignState.lastShownTime) !== 0 && lastShown > this.getNow()) { const message = "{Campaign throttle limit} Too soon after last campaign. Wait until " + (lastShown + rules.min_delay_between_messages); return { status: SwrveConstants_1.CAMPAIGN_THROTTLE_RECENT, message, }; } const assetsForTrigger = CampaignManager.getAllAssets([parentCampaign], this.assetManager.ImagesCDN, personalizationProperties); if (!assetsForTrigger.every((asset) => asset.canRender())) { return { status: SwrveConstants_1.CAMPAIGN_COULD_NOT_PERSONALIZE, message: "Personalization failed for campaign " + parentCampaign, }; } if (!this.assetManager.checkAssetsForCampaign(assetsForTrigger)) { return { status: SwrveConstants_1.CAMPAIGN_NOT_DOWNLOADED, message: "Assets not loaded for Campaign " + parentCampaign, }; } return { status: SwrveConstants_1.CAMPAIGN_MATCH, message: "Campaign " + parentCampaign.id + "passes display rules", }; } parseGlobalRules(campaigns) { if (campaigns && campaigns.rules) { const rules = campaigns.rules; this._maxMessagesPerSession = rules.hasOwnProperty("max_messages_per_session") ? rules.max_messages_per_session : this.MAX_MESSAGES_PER_SESSION; this._minDelay = rules.hasOwnProperty("min_delay_between_messages") ? rules.min_delay_between_messages : this.MIN_DELAY; this._delayFirstMessage = rules.hasOwnProperty("delay_first_message") ? rules.delay_first_message : this.DELAY_FIRST_MESSAGE; } SwrveLogger_1.default.info("Global Rules: max_messages_per_session:" + this._maxMessagesPerSession + " min_delay:" + this._minDelay + " delay_first_message:" + this._delayFirstMessage); } handleAssets(personalizationProperties, onAssetsLoaded) { const assets = CampaignManager.getAllAssets(this.campaigns, this.assetManager.ImagesCDN, personalizationProperties); this.assetManager.manageAssets(assets) .then(() => { SwrveLogger_1.default.info("CampaignManager: asset download complete"); onAssetsLoaded(); }) .catch(error => { SwrveLogger_1.default.info("Error downloading assets " + error); onAssetsLoaded(error); }); } static getAllAssets(campaigns, cdn, personalizationProperties) { const assets = []; for (const campaign of campaigns) { for (const message of campaign.messages || []) { const formats = (message.template && message.template.formats) || []; for (const format of formats) { // Check images and buttons directly under format if any. for (const button of format.buttons || []) { assets.push(new SwrveButton_1.SwrveButton(button, cdn, personalizationProperties)); } for (const image of format.images || []) { assets.push(new SwrveImage_1.SwrveImage(image, cdn, personalizationProperties)); } // Check images and buttons in each page within the format if any for (const page of format.pages || []) { for (const button of page.buttons || []) { assets.push(new SwrveButton_1.SwrveButton(button, cdn, personalizationProperties)); } for (const image of page.images || []) { assets.push(new SwrveImage_1.SwrveImage(image, cdn, personalizationProperties)); } } } } } return assets; } canCampaignRender(campaign, personalizationProperties) { const assets = CampaignManager.getAllAssets([campaign], this.assetManager.ImagesCDN, personalizationProperties); return assets.every((asset) => asset.canRender()); } getNow() { return DateHelper_1.default.nowInUtcTime(); } filterUnsupportedCampaigns(response) { return response.campaigns.campaigns.filter((campaign) => { var _a; return !((_a = campaign.messages) === null || _a === void 0 ? void 0 : _a.some((message) => { var _a; return (_a = message.template.formats) === null || _a === void 0 ? void 0 : _a.some((format) => { // each check needs to verify legacy json format (which has no concept of pages) and new format (which has pages) const hasUnsupportedFeature = this.hasMultilineTextFeature(format) || this.hasPlainTextButtonFeature(format) || this.hasCopyToClipboardFeature(format); if (hasUnsupportedFeature) { SwrveLogger_1.default.warn("Campaign " + campaign.id + " contains unsupported feature. Skipping."); } return hasUnsupportedFeature; }); })); }); } hasMultilineTextFeature(format) { var _a, _b; const isMultilineImage = (image) => 'multiline_text' in image; return ((_a = format.images) === null || _a === void 0 ? void 0 : _a.some(isMultilineImage)) || ((_b = format.pages) === null || _b === void 0 ? void 0 : _b.some(page => { var _a; return (_a = page.images) === null || _a === void 0 ? void 0 : _a.some(isMultilineImage); })); } hasPlainTextButtonFeature(format) { var _a, _b; const isTextButton = (button) => 'text' in button; return ((_a = format.buttons) === null || _a === void 0 ? void 0 : _a.some(isTextButton)) || ((_b = format.pages) === null || _b === void 0 ? void 0 : _b.some(page => { var _a; return (_a = page.buttons) === null || _a === void 0 ? void 0 : _a.some(isTextButton); })); } hasCopyToClipboardFeature(format) { var _a, _b; const isCopyToClipboardButton = (button) => { var _a; return ((_a = button.type) === null || _a === void 0 ? void 0 : _a.value) === SwrveConstants_1.COPY_TO_CLIPBOARD; }; return ((_a = format.buttons) === null || _a === void 0 ? void 0 : _a.some(isCopyToClipboardButton)) || ((_b = format.pages) === null || _b === void 0 ? void 0 : _b.some(page => { var _a; return (_a = page.buttons) === null || _a === void 0 ? void 0 : _a.some(isCopyToClipboardButton); })); } } exports.CampaignManager = CampaignManager;