___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.