Skip to content

Commit

Permalink
mixpanel-destination: send page title and click ids, filter bot traff…
Browse files Browse the repository at this point in the history
…ic setting, send page events toggle.

Fixes
  • Loading branch information
absorbb committed Dec 21, 2023
1 parent 3d57a1b commit 628977a
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 23 deletions.
108 changes: 87 additions & 21 deletions libs/core-functions/src/functions/mixpanel-destination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const specialProperties = [
"unsubscribed",
];

const CLICK_IDS = ["dclid", "fbclid", "gclid", "ko_click_id", "li_fat_id", "msclkid", "ttclid", "twclid", "wbraid"];

export type HttpRequest = {
id: string;
method?: string;
Expand All @@ -41,6 +43,37 @@ function evict(obj: Record<string, any>, key: string) {
return val;
}

function getQueryParam(url: string, param: string) {
param = param.replace(/[[]/, "\\[").replace(/[\]]/, "\\]");
const regexS = "[\\?&]" + param + "=([^&#]*)",
regex = new RegExp(regexS),
results = regex.exec(url);
if (results === null || (results && typeof results[1] !== "string" && results[1]["length"])) {
return "";
} else {
let result = results[1];
try {
result = decodeURIComponent(result);
} catch (err) {}
return result.replace(/\+/g, " ");
}
}

function clickParams(url: string) {
if (!url) {
return {};
}
const params: any = {};
CLICK_IDS.forEach(idkey => {
const id = getQueryParam(url, idkey);
if (id.length) {
params[idkey] = id;
}
});

return params;
}

function trackEvent(
ctx: FullContext,
deviceId: string,
Expand Down Expand Up @@ -68,6 +101,7 @@ function trackEvent(
...(groupId ? { [groupKey]: groupId } : {}),
userAgent: event.context?.userAgent,
};
const pageUrl = evict(customProperties, "url");
return {
id: randomId(),
url: `https://api.mixpanel.com/import?strict=1&project_id=${opts.projectId}`,
Expand All @@ -89,7 +123,9 @@ function trackEvent(
$browser: ctx.ua?.browser?.name,
$browser_version: ctx.ua?.browser?.version,
$os: ctx.ua?.os?.name,
$current_url: evict(customProperties, "url"),
$current_url: pageUrl,
...clickParams(pageUrl),
current_page_title: evict(customProperties, "title"),
$referrer: evict(customProperties, "referrer"),
$referring_domain: evict(customProperties, "referring_domain"),
$session_id: event.context?.sessionId,
Expand Down Expand Up @@ -118,33 +154,54 @@ function setProfileMessage(ctx: FullContext, distinctId: string, event: Analytic
const groupId = event.context?.groupId || traits.groupId;
delete traits.groupId;

const setPayload: any = {
$token: opts.projectToken,
$distinct_id: distinctId,
$ip: event.context?.ip || event.requestIp,
$latitude: event.context?.geo?.location?.latitude,
$longitude: event.context?.geo?.location?.longitude,
$set: {
...traits,
$initial_referrer: event.context?.page?.referrer,
$initial_referring_domain: event.context?.page?.referring_domain,
$browser: ctx.ua?.browser?.name,
$browser_version: ctx.ua?.browser?.version,
$os: ctx.ua?.os?.name,
},
};

const reqs = [
const reqs: HttpRequest[] = [
{
id: randomId(),
url: "https://api.mixpanel.com/engage?verbose=1#profile-set",
headers: {
"Content-type": "application/json",
Accept: "text-plain",
},
payload: [setPayload],
payload: [
{
$token: opts.projectToken,
$distinct_id: distinctId,
$ip: event.context?.ip || event.requestIp,
$latitude: event.context?.geo?.location?.latitude,
$longitude: event.context?.geo?.location?.longitude,
$set: {
...traits,
$browser: ctx.ua?.browser?.name,
$browser_version: ctx.ua?.browser?.version,
$os: ctx.ua?.os?.name,
},
},
],
},
];
if (event.context?.page?.referrer || event.context?.page?.referring_domain) {
reqs.push({
id: randomId(),
url: "https://api.mixpanel.com/engage?verbose=1#profile-set-once",
headers: {
"Content-type": "application/json",
Accept: "text-plain",
},
payload: [
{
$token: opts.projectToken,
$distinct_id: distinctId,
$ip: event.context?.ip || event.requestIp,
$latitude: event.context?.geo?.location?.latitude,
$longitude: event.context?.geo?.location?.longitude,
$set_once: {
$initial_referrer: event.context?.page?.referrer,
$initial_referring_domain: event.context?.page?.referring_domain,
},
},
],
});
}
if (groupId) {
const groupKey = opts.groupKey || "$group_id";
const unionPayload: any = {
Expand Down Expand Up @@ -299,6 +356,13 @@ function getDeviceId(ctx: FullContext, event: AnalyticsServerEvent) {

const MixpanelDestination: JitsuFunction<AnalyticsServerEvent, MixpanelCredentials> = async (event, ctx) => {
ctx.log.debug(`Mixpanel destination (props=${JSON.stringify(ctx.props)}) received event ${JSON.stringify(event)}`);
if (typeof ctx.props.filterBotTraffic === "undefined" || ctx.props.filterBotTraffic) {
if (ctx.ua?.bot) {
ctx.log.debug(`Skipping bot traffic`);
return;
}
}
const trackPageView = typeof ctx.props.sendPageEvents === "undefined" || ctx.props.sendPageEvents;
const deviceId = getDeviceId(ctx, event);
if (!deviceId) {
ctx.log.warn(
Expand Down Expand Up @@ -334,8 +398,10 @@ const MixpanelDestination: JitsuFunction<AnalyticsServerEvent, MixpanelCredentia
messages.push(setGroupMessage(event, ctx.props));
} else if (event.type === "track") {
messages.push(trackEvent(ctx, deviceId, distinctId, event.event as string, event));
} else if (event.type === "page") {
messages.push(trackEvent(ctx, deviceId, distinctId, "Page View", event));
} else if (event.type === "page" && trackPageView) {
messages.push(trackEvent(ctx, deviceId, distinctId, "$mp_web_page_view", event));
} else if (event.type === "screen") {
messages.push(trackEvent(ctx, deviceId, distinctId, "Screen", event));
}
}
for (const message of messages) {
Expand Down
6 changes: 6 additions & 0 deletions libs/core-functions/src/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ export const MixpanelCredentials = z.object({
//apiSecret: z.string(),
serviceAccountUserName: z.string().describe(MixpanelServiceAccountDocumentation),
serviceAccountPassword: z.string().describe(MixpanelServiceAccountDocumentation),
sendPageEvents: z
.boolean()
.optional()
.default(true)
.describe("If enabled, all page view events will be sent to Mixpanel."),
sendIdentifyEvents: z
.boolean()
.optional()
Expand All @@ -79,6 +84,7 @@ export const MixpanelCredentials = z.object({
.describe(
"Mixpanel Group Analytics allows behavioral data analysis at a customized group level. Group Analytics is available as an add-on package to customers on <a href='https://mixpanel.com/pricing/' target='_blank' rel='noreferrer noopener'>Growth and Enterprise plans.</a>"
),
filterBotTraffic: z.boolean().optional().default(true).describe("Don't send traffic from known bots to Mixpanel"),
groupKey: z
.string()
.optional()
Expand Down
2 changes: 1 addition & 1 deletion webapps/console/components/DataView/DataView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function DataView() {
setState({ ...state, activeView: activeView as DataViewState["activeView"] });

const patchQueryStringState = (key: string, value: any) => {
if (state.viewState[state.activeView][key] === value) return;
if (state.viewState[state.activeView]?.[key] === value) return;
if (value === null) {
const newState = { ...state };
delete newState[key];
Expand Down
2 changes: 1 addition & 1 deletion webapps/console/components/DataView/EventsBrowser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ export const EventsBrowser = ({
<div key={"left"}>
<div className={"flex flex-row gap-4"}>
<div>
<span>{entityType == "stream" ? "Sites: " : "Connection: "}</span>
<span>{entityType == "stream" ? "Site: " : "Connection: "}</span>
<Select
dropdownMatchSelectWidth={false}
notFoundContent={
Expand Down

1 comment on commit 628977a

@vercel
Copy link

@vercel vercel bot commented on 628977a Dec 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

new-jitsu – ./webapps/console

ag.ru
logu.au
ozon.ru
sse.ere
erxes.io
baidu.dom
ilmiya.io
sambla.se
bobsec.com
sambla.com
agro4u.life
bluetick.ai
myilmiya.io
protontv.eu
t.quenti.io
alicesec.com
d.askloan.tw
dev.aclis.io
docs.dh19.de
docs.dh19.eu
hunterbi.com
joseviso.com
mydomain.dom
t.democo.dev
t.shoppub.io
t2.jitsu.com
timeplus.com
zoopsign.com
*.d.jitsu.com
beta.mitzu.io
d.versatus.io
data.light.so
data.loudy.co
data.schej.it
dog.jitsu.com
imusician.app
imusician.pro
jitsu.logu.au
jitsu.www1.ru
t.thequack.ai
thinkr.com.br
use.jitsu.com
usepolygon.io
www.sambla.se
ajewellers.com
data.uselog.io
gpt.whatfa.com
sidetrekai.com
t.papermark.io
t.saasmonk.app
use2.jitsu.com
w.d2.jitsu.com
www.kellen.top
*.dataspecc.com
app.bluetick.ai
caddy.jitsu.com
data.askloan.tw
enterticket.com
events.mitzu.io
ildar.jitsu.com
jitsu.efeer.com
jitsu.ivve.tech
test.bigfootproof.com
teste.fazcomex.com.br
analytics.dev.knekt.io
loraboutiquedental.com
notion.twelftree.co.uk
dev-portal.zoopsign.com
event.tradejobsnz.co.nz
investing-poc.jitsu.dev
savvy-replay.jitsu.tech
data.analytics-smart.com
data.handelsregister.app
event.clickncruise.co.uk
jt.fairhopeweb.github.io
savvy-replay2.jitsu.tech
savvy-replay3.jitsu.tech
savvy-replay4.jitsu.tech
track.alquimiaweb.com.br
track.pressance-group.jp
track.uniquecafes.com.br
colectha.agenciavoolu.com
kolectha.agenciavoolu.com
lp.loraboutiquedental.com
stage-portal.zoopsign.com
new-jitsu-jitsu.vercel.app
lodercom-colectha.voolu.shop
warehouse1.trendstyle.com.au
d0.livingdesignsfurniture.com
ingest-load-testing.jitsu.dev
jitsu.precisaosistemas.com.br
analytics.inspiresolutions.app
betteruptime-monitoring.jitsu.dev
canvas.livingdesignsfurniture.com
analytics.dev.inspiresolutions.app
cl9vt45z50001znkunc6v8fmm.d.jitsu.com
clm2jikrm00002v6r5l6niws3.d.jitsu.com
new-jitsu-git-newjitsu-jitsu.vercel.app
3000-rajaraodv-customerdemo-nmpsqwflswt.ws-us102.gitpod.io
new.jitsu.dev

Please sign in to comment.