___TERMS_OF_SERVICE___ By creating or modifying this file you agree to Google Tag Manager's Community Template Gallery Developer Terms of Service available at https://developers.google.com/tag-manager/gallery-tos (or such other URL as Google may provide), as modified from time to time. ___INFO___ { "type": "TAG", "id": "cvt_temp_public_id", "version": 1, "securityGroups": [], "displayName": "Telltale Bot", "categories": [ "ANALYTICS", "UTILITY" ], "brand": { "id": "brand_dummy", "displayName": "Telltale Bot", "thumbnail": "data:image/png;base64,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" }, "description": "Tells people, AI agents and bots apart. Loads telltale.js and pushes each pageview's verdict to the dataLayer as telltale.verdict. Fire on Initialization - All Pages. https://telltale.bot/install.html", "containerContexts": [ "WEB" ] } ___TEMPLATE_PARAMETERS___ [ { "type": "LABEL", "name": "intro", "displayName": "The values are your site's, from your tag on Site settings > Installation in Telltale Bot. Fire this tag on Initialization - All Pages, once per page." }, { "type": "TEXT", "name": "scriptUrl", "displayName": "Script URL", "simpleValueType": true, "defaultValue": "https://js.ttbcdn.net/v2/telltale.js", "help": "The tag's script. If your site uses its own script hostname, add it under this template's Permissions (Injects scripts) too, or the tag fails.", "valueValidators": [ { "type": "NON_EMPTY" }, { "type": "REGEX", "args": [ "^https://.+" ] } ] }, { "type": "TEXT", "name": "server", "displayName": "Server", "simpleValueType": true, "defaultValue": "https://in.ttbcdn.net", "help": "The tag's server: where this page's reports go.", "valueValidators": [ { "type": "NON_EMPTY" }, { "type": "REGEX", "args": [ "^https://[^/]+/?$" ] } ] }, { "type": "TEXT", "name": "path", "displayName": "Path", "simpleValueType": true, "help": "The tag's path, e.g. /c/abc123.", "valueValidators": [ { "type": "NON_EMPTY" }, { "type": "REGEX", "args": [ "^/.*" ] } ] }, { "type": "TEXT", "name": "key", "displayName": "Site key", "simpleValueType": true, "help": "The tag's key.", "valueValidators": [ { "type": "NON_EMPTY" } ] }, { "type": "GROUP", "name": "dataLayerGroup", "displayName": "dataLayer", "groupStyle": "ZIPPY_OPEN_ON_PARAM", "subParams": [ { "type": "CHECKBOX", "name": "pushDataLayer", "checkboxText": "Push the verdict to the dataLayer (telltale.verdict, telltale.verdict-update)", "simpleValueType": true, "defaultValue": true }, { "type": "TEXT", "name": "dataLayerName", "displayName": "dataLayer name", "simpleValueType": true, "defaultValue": "dataLayer", "help": "Change it only if this container uses a renamed dataLayer.", "enablingConditions": [ { "paramName": "pushDataLayer", "paramValue": true, "type": "EQUALS" } ], "valueValidators": [ { "type": "NON_EMPTY" } ] } ] }, { "type": "GROUP", "name": "accountGroup", "displayName": "Signed-in members (Member accounts tab)", "groupStyle": "ZIPPY_OPEN_ON_PARAM", "subParams": [ { "type": "RADIO", "name": "accountSource", "displayName": "Member ID", "radioItems": [ { "value": "none", "displayValue": "None" }, { "value": "cookie", "displayValue": "From a cookie", "help": "Read at every report, so a sign-in partway through a page still counts. The cookie must not be HttpOnly." }, { "value": "value", "displayValue": "From a GTM variable", "help": "Read once, when the tag fires. If your site keeps the ID in JavaScript, first make a GTM variable that gives it (a JavaScript Variable, or a Custom JavaScript variable for anything longer)." } ], "simpleValueType": true, "defaultValue": "none", "help": "Your site's own ID for the signed-in member: up to 128 characters, no spaces, not an email address." }, { "type": "TEXT", "name": "accountCookie", "displayName": "Cookie name", "simpleValueType": true, "enablingConditions": [ { "paramName": "accountSource", "paramValue": "cookie", "type": "EQUALS" } ], "valueValidators": [ { "type": "NON_EMPTY" } ] }, { "type": "TEXT", "name": "accountValue", "displayName": "GTM variable", "help": "Choose the variable with the button beside the field. Empty for a visitor who is not signed in: no member ID is sent.", "simpleValueType": true, "enablingConditions": [ { "paramName": "accountSource", "paramValue": "value", "type": "EQUALS" } ] } ] } ] ___SANDBOXED_JS_FOR_WEB_TEMPLATE___ // Telltale Bot: the GTM template form of gtm/telltale-tag.html. Loads telltale.js and // calls Telltale.create with the same options the Custom HTML tag passes. const callInWindow = require('callInWindow'); const copyFromWindow = require('copyFromWindow'); const injectScript = require('injectScript'); const logToConsole = require('logToConsole'); const makeString = require('makeString'); const queryPermission = require('queryPermission'); const setInWindow = require('setInWindow'); const scriptUrl = data.scriptUrl; if (copyFromWindow('__telltaleTag')) { // One collector per page: the in-page tag, or an earlier firing of this one, ran. data.gtmOnSuccess(); } else if (!queryPermission('inject_script', scriptUrl)) { logToConsole('Telltale Bot: ' + scriptUrl + ' is not allowed. Add its host under this template\'s Permissions (Injects scripts).'); data.gtmOnFailure(); } else { setInWindow('__telltaleTag', true, true); let server = data.server; while (server.length > 0 && server.charAt(server.length - 1) === '/') { server = server.slice(0, server.length - 1); } const opts = { reportUrl: server + data.path + '/ingest', serverKey: data.key, dataLayer: data.pushDataLayer ? (data.dataLayerName || 'dataLayer') : null }; if (data.accountSource === 'cookie' && data.accountCookie) { opts.account = {cookie: data.accountCookie}; } else if (data.accountSource === 'value' && data.accountValue !== undefined && data.accountValue !== null) { const id = makeString(data.accountValue); if (id !== '') opts.account = id; // an empty variable: no member signed in } injectScript(scriptUrl, () => { callInWindow('Telltale.create', opts); data.gtmOnSuccess(); }, () => { logToConsole('Telltale Bot: could not load ' + scriptUrl); data.gtmOnFailure(); }, 'telltale'); } ___WEB_PERMISSIONS___ [ { "instance": { "key": { "publicId": "inject_script", "versionId": "1" }, "param": [ { "key": "urls", "value": { "type": 2, "listItem": [ { "type": 1, "string": "https://js.ttbcdn.net/" } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "access_globals", "versionId": "1" }, "param": [ { "key": "keys", "value": { "type": 2, "listItem": [ { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "__telltaleTag" }, { "type": 8, "boolean": true }, { "type": 8, "boolean": true }, { "type": 8, "boolean": false } ] }, { "type": 3, "mapKey": [ { "type": 1, "string": "key" }, { "type": 1, "string": "read" }, { "type": 1, "string": "write" }, { "type": 1, "string": "execute" } ], "mapValue": [ { "type": 1, "string": "Telltale.create" }, { "type": 8, "boolean": false }, { "type": 8, "boolean": false }, { "type": 8, "boolean": true } ] } ] } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true }, { "instance": { "key": { "publicId": "logging", "versionId": "1" }, "param": [ { "key": "environments", "value": { "type": 1, "string": "debug" } } ] }, "clientAnnotations": { "isEditedByUser": true }, "isRequired": true } ] ___TESTS___ scenarios: - name: Loads the script and creates the collector code: |- let injected; mock('injectScript', (url, onSuccess) => { injected = url; onSuccess(); }); mock('callInWindow', () => {}); runCode(mockData); assertThat(injected).isEqualTo('https://js.ttbcdn.net/v2/telltale.js'); assertApi('callInWindow').wasCalledWith('Telltale.create', { reportUrl: 'https://in.ttbcdn.net/c/abc123/ingest', serverKey: 'k-test', dataLayer: 'dataLayer' }); assertApi('setInWindow').wasCalledWith('__telltaleTag', true, true); assertApi('gtmOnSuccess').wasCalled(); - name: Runs once per page code: |- mock('copyFromWindow', (name) => name === '__telltaleTag'); runCode(mockData); assertApi('injectScript').wasNotCalled(); assertApi('gtmOnSuccess').wasCalled(); - name: Member ID from a cookie, no dataLayer, trailing slash on the server code: |- mock('injectScript', (url, onSuccess) => { onSuccess(); }); mock('callInWindow', () => {}); mockData.server = 'https://in.ttbcdn.net/'; mockData.pushDataLayer = false; mockData.accountSource = 'cookie'; mockData.accountCookie = 'memberid'; runCode(mockData); assertApi('callInWindow').wasCalledWith('Telltale.create', { reportUrl: 'https://in.ttbcdn.net/c/abc123/ingest', serverKey: 'k-test', dataLayer: null, account: {cookie: 'memberid'} }); - name: Member ID from a GTM variable code: |- mock('injectScript', (url, onSuccess) => { onSuccess(); }); mock('callInWindow', () => {}); mockData.accountSource = 'value'; mockData.accountValue = 12345; runCode(mockData); assertApi('callInWindow').wasCalledWith('Telltale.create', { reportUrl: 'https://in.ttbcdn.net/c/abc123/ingest', serverKey: 'k-test', dataLayer: 'dataLayer', account: '12345' }); - name: An empty member ID sends none code: |- mock('injectScript', (url, onSuccess) => { onSuccess(); }); mock('callInWindow', () => {}); mockData.accountSource = 'value'; mockData.accountValue = ''; runCode(mockData); assertApi('callInWindow').wasCalledWith('Telltale.create', { reportUrl: 'https://in.ttbcdn.net/c/abc123/ingest', serverKey: 'k-test', dataLayer: 'dataLayer' }); - name: Refuses a script host the permissions do not allow code: |- mockData.scriptUrl = 'https://cdn.example.com/v2/telltale.js'; runCode(mockData); assertApi('injectScript').wasNotCalled(); assertApi('gtmOnFailure').wasCalled(); - name: Fails when the script cannot load code: |- mock('injectScript', (url, onSuccess, onFailure) => { onFailure(); }); runCode(mockData); assertApi('callInWindow').wasNotCalled(); assertApi('gtmOnFailure').wasCalled(); setup: |- // Scenarios share one window: keep the once-per-page guard out of it. mock('copyFromWindow', () => undefined); mock('setInWindow', () => true); const mockData = { scriptUrl: 'https://js.ttbcdn.net/v2/telltale.js', server: 'https://in.ttbcdn.net', path: '/c/abc123', key: 'k-test', pushDataLayer: true, dataLayerName: 'dataLayer', accountSource: 'none' }; ___NOTES___ Telltale Bot tag template. Source: gtm/telltale-bot.tpl in the Telltale Bot repo; the Custom HTML equivalent is gtm/telltale-tag.html. Import: Templates > Tag Templates > New > menu > Import.