Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT_POSTHOG #1960

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
61 changes: 61 additions & 0 deletions apps/docs/editor/blocks/integrations/posthog.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: Posthog
---

With the Posthog block, you can send events to Posthog and trigger your Posthog workflows. It uses the Posthog Node.js library to send server side events to Posthog, so that it works on non web browser devices.

## How to find my `Project API Key` and `API Host`?

To find your `Project API Key`, you need to go to your Posthog Settings page and click on the `General` link. Then scroll down to the Web Snippet and copy the api key and host info from the snippet;

<Frame>
<img
src="/images/blocks/integrations/posthog/posthog-keys.png"
alt="Posthog Keys"
/>
</Frame>

## Identify User

This action allows you to identify a user in Posthog. It requires the `User ID` and can optionally take a set of `Properties` that will be added to the user profile.

<Frame>
<img
src="/images/blocks/integrations/posthog/identify.png"
alt="Identify User"
/>
</Frame>

## Identify Group

This action allows you to identify a group in Posthog. It requires the `Group Type` and `Group Key`, and can optionally take `User ID` and a set of `Properties` that will be added to the group profile.

<Frame>
<img
src="/images/blocks/integrations/posthog/group-identify.png"
alt="Identify Group"
/>
</Frame>

## Capture

This action allows you to capture a custom event, or native Posthog events, such as a page view, a screen view, etc. It requires the `Event Name` to be set. It can optionally take a `User ID` set of `Properties`, as well as Group information.

<Frame>
<img
src="/images/blocks/integrations/posthog/capture.png"
alt="Capture Event"
/>
</Frame>


## Get Flag

This action allows you to get a flag from Posthog. It requires the `Flag Name` and `User ID`. You can choose between `Boolean` and `Multivariate` flags. If the flag is enabled for the user, the action will retrieve the flag payload and store it in the `Flag Payload` variable that you configure.

<Frame>
<img
src="/images/blocks/integrations/posthog/flags.png"
alt="Get Flag"
/>
</Frame>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
"editor/blocks/integrations/anthropic",
"editor/blocks/integrations/dify-ai",
"editor/blocks/integrations/nocodb",
"editor/blocks/integrations/posthog",
"editor/blocks/integrations/segment",
"editor/blocks/integrations/zendesk"
]
Expand Down
Binary file modified bun.lockb
Binary file not shown.
17 changes: 17 additions & 0 deletions packages/forge/blocks/posthog/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@typebot.io/posthog-block",
"version": "1.0.0",
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts",
"./schemas": "./src/schemas.ts"
},
"devDependencies": {
"@typebot.io/forge": "workspace:*",
"@typebot.io/tsconfig": "workspace:*"
},
"dependencies": {
"posthog-node": "4.3.2"
}
}
112 changes: 112 additions & 0 deletions packages/forge/blocks/posthog/src/actions/capture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { createAction, option } from "@typebot.io/forge";
import { auth } from "../auth";
import { createClient } from "../helpers/createClient";
import { createProperties } from "../helpers/createProperties";

interface EventPayload {
distinctId: string;
event: string;
properties: {};
groups?: {};
}

export const capture = createAction({
auth,
name: "Capture",
options: option.object({
name: option.string.layout({
label: "Event Name",
placeholder: "Enter event name...",
helperText:
"You can use a custom event name or a PostHog event name, such as: $pageview, $screen, survey sent, etc.",
isRequired: true,
}),
userId: option.string.layout({
label: "User ID",
isRequired: false,
}),
anonymous: option.boolean.layout({
label: "Anonymous User",
isRequired: false,
defaultValue: false,
}),
properties: option
.array(
option.object({
key: option.string.layout({
label: "Key",
isRequired: true,
}),
value: option.string.layout({
label: "Value",
isRequired: true,
}),
}),
)
.layout({
itemLabel: "property",
accordion: "Event properties",
}),
groupKey: option.string.layout({
label: "Group Key",
isRequired: false,
accordion: "Group settings",
}),
groupType: option.string.layout({
label: "Group Type",
isRequired: false,
accordion: "Group settings",
}),
}),
run: {
server: async ({
credentials: { apiKey, host },
options: { name, userId, anonymous, groupKey, groupType, properties },
}) => {
if (
name === undefined ||
name.length === 0 ||
!userId ||
userId.length === 0 ||
apiKey === undefined ||
host === undefined
)
return;

const posthog = createClient(apiKey, host);

let eventPayload: EventPayload = {
distinctId: userId,
event: name,
properties: {},
};

if (properties !== undefined && properties.length > 0) {
eventPayload.properties = createProperties(properties);
}

if (
groupType !== undefined &&
groupType.length > 0 &&
groupKey !== undefined &&
groupKey.length > 0
) {
eventPayload = {
...eventPayload,
groups: { [`${groupType}`]: groupKey },
};
}

if (anonymous) {
eventPayload.properties = {
...eventPayload.properties,
$process_person_profile: false,
};
}

await posthog.capture(eventPayload);

await posthog.shutdown();
},
},
});
114 changes: 114 additions & 0 deletions packages/forge/blocks/posthog/src/actions/getFlag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { createAction, option } from "@typebot.io/forge";
import { isDefined, isEmpty } from "@typebot.io/lib/utils";
import { auth } from "../auth";
import { createClient } from "../helpers/createClient";
import { createProperties } from "../helpers/createProperties";

export const getFlag = createAction({
auth,
name: "Get Flag",
options: option.object({
userId: option.string.layout({
label: "User ID",
isRequired: true,
}),
flagKey: option.string.layout({
label: "Flag Key",
isRequired: true,
}),
responseMapping: option
.saveResponseArray(["Flag Payload"] as const)
.layout({
accordion: "Save payload",
}),
multivariate: option.boolean.layout({
label: "Multivariate",
isRequired: false,
defaultValue: false,
}),
properties: option
.array(
option.object({
key: option.string.layout({
label: "Key",
isRequired: true,
}),
value: option.string.layout({
label: "Value",
isRequired: true,
}),
}),
)
.layout({
itemLabel: "property",
}),
}),
getSetVariableIds: ({ responseMapping }) =>
responseMapping?.map((res) => res.variableId).filter(isDefined) ?? [],
run: {
server: async ({
credentials: { apiKey, host },
options: { userId, flagKey, responseMapping, multivariate, properties },
variables,
}) => {
if (
!userId ||
userId.length === 0 ||
!flagKey ||
flagKey.length === 0 ||
apiKey === undefined ||
host === undefined
)
return;

const posthog = createClient(apiKey, host);

let isFeatureFlagEnabled = false;
let matchedFlagPayload = null;

if (!multivariate) {
isFeatureFlagEnabled =
(await posthog.isFeatureEnabled(flagKey, userId)) ?? false;
if (isFeatureFlagEnabled) {
matchedFlagPayload = await posthog.getFeatureFlagPayload(
flagKey,
userId,
isFeatureFlagEnabled,
);
}
jwalsh-bdt marked this conversation as resolved.
Show resolved Hide resolved
} else {
let enabledVariant = null;

if (properties) {
const personProperties = createProperties(properties);
enabledVariant = await posthog.getFeatureFlag(
flagKey,
userId,
personProperties,
);
} else {
enabledVariant =
(await posthog.getFeatureFlag(flagKey, userId)) ?? false;
}
matchedFlagPayload = await posthog.getFeatureFlagPayload(
flagKey,
userId,
enabledVariant,
);
}

if (responseMapping) {
responseMapping?.forEach((mapping) => {
if (!mapping.variableId) return;
if (!mapping.item || mapping.item === "Flag Payload") {
variables.set([
{ id: mapping.variableId, value: matchedFlagPayload },
]);
}
});
}

await posthog.shutdown();
jwalsh-bdt marked this conversation as resolved.
Show resolved Hide resolved
},
},
});
60 changes: 60 additions & 0 deletions packages/forge/blocks/posthog/src/actions/identify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { createAction, option } from "@typebot.io/forge";
import { auth } from "../auth";
import { createClient } from "../helpers/createClient";
import { createProperties } from "../helpers/createProperties";

export const identify = createAction({
auth,
name: "Identify User",
options: option.object({
userId: option.string.layout({
label: "User ID",
isRequired: true,
}),
properties: option
.array(
option.object({
key: option.string.layout({
label: "Key",
isRequired: true,
}),
value: option.string.layout({
label: "Value",
isRequired: true,
}),
}),
)
.layout({
itemLabel: "property",
accordion: "User properties",
}),
}),
run: {
server: async ({
credentials: { apiKey, host },
options: { userId, properties },
}) => {
if (
!userId ||
userId.length === 0 ||
apiKey === undefined ||
host === undefined
)
return;

jwalsh-bdt marked this conversation as resolved.
Show resolved Hide resolved
const posthog = createClient(apiKey, host);

if (properties === undefined || properties.length === 0) {
await posthog.identify({
distinctId: userId,
});
} else {
await posthog.identify({
distinctId: userId,
properties: createProperties(properties),
});
}
await posthog.shutdown();
},
},
});
Loading