From bfb0f1dab770e3b88ae92dedcb92902d446e461a Mon Sep 17 00:00:00 2001 From: John Pham Date: Thu, 9 Jan 2025 13:21:56 -0800 Subject: [PATCH] Add analytics for tracking order buy --- src/index.ts | 11 ++++--- src/lib/buy/index.tsx | 30 ++++++++++++------- src/lib/posthog.ts | 68 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 91 insertions(+), 18 deletions(-) diff --git a/src/index.ts b/src/index.ts index f374c7c..772a824 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ import { registerDev } from "./lib/dev.ts"; import { registerLogin } from "./lib/login.ts"; import { registerMe } from "./lib/me.ts"; import { registerOrders } from "./lib/orders/index.tsx"; -import { IS_TRACKING_DISABLED, postHogClient } from "./lib/posthog.ts"; +import { IS_TRACKING_DISABLED, analytics } from "./lib/posthog.ts"; import { registerSell } from "./lib/sell.ts"; import { registerTokens } from "./lib/tokens.ts"; import { registerScale } from "./lib/updown.tsx"; @@ -102,9 +102,8 @@ const main = async () => { return acc; }, {}); - postHogClient.capture({ - distinctId: exchangeAccountId, - event: `cli_sf_${process.argv[2] || "unknown"}${process.argv[3] ? "_" + process.argv[3] : ""}`, + analytics.track({ + event: `${process.argv[2] || "unknown"}${process.argv[3] ? "_" + process.argv[3] : ""}`, properties: { ...args, shell: process.env.SHELL, @@ -116,11 +115,11 @@ const main = async () => { } try { - await postHogClient.shutdown(); + await analytics.shutdown(); const c = program.parse(process.argv); } catch (err) { console.log(err); - await postHogClient.shutdown(); + await analytics.shutdown(); process.exit(1); } } diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index db40fb6..6d8fdeb 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -1,27 +1,26 @@ +import { parseDate } from "chrono-node"; import type { Command } from "commander"; +import { Box, Text, render, useApp } from "ink"; +import Spinner from "ink-spinner"; +import ms from "ms"; import dayjs from "npm:dayjs@1.11.13"; import duration from "npm:dayjs@1.11.13/plugin/duration.js"; import relativeTime from "npm:dayjs@1.11.13/plugin/relativeTime.js"; +import parseDurationFromLibrary from "parse-duration"; +import React, { useCallback, useEffect, useRef, useState } from "react"; +import invariant from "tiny-invariant"; import { apiClient } from "../../apiClient.ts"; import { logAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; import { roundStartDate } from "../../helpers/units.ts"; -import parseDurationFromLibrary from "parse-duration"; -import { Box, render, useApp } from "ink"; -import { parseDate } from "chrono-node"; -import { GPUS_PER_NODE } from "../constants.ts"; +import ConfirmInput from "../ConfirmInput.tsx"; import type { Quote } from "../Quote.tsx"; import QuoteDisplay from "../Quote.tsx"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { Text } from "ink"; -import ConfirmInput from "../ConfirmInput.tsx"; -import React from "react"; import { Row } from "../Row.tsx"; -import ms from "ms"; -import Spinner from "ink-spinner"; -import invariant from "tiny-invariant"; +import { GPUS_PER_NODE } from "../constants.ts"; +import { analytics } from "../posthog.ts"; dayjs.extend(relativeTime); dayjs.extend(duration); @@ -352,15 +351,24 @@ function BuyOrder(props: BuyOrderProps) { const [resultMessage, setResultMessage] = useState(null); const handleSubmit = useCallback( (submitValue: boolean) => { + analytics.track({ + event: "buy_order_quoted", + }); if (submitValue === false) { setIsLoading(false); setResultMessage("Order not placed, use 'y' to confirm"); setTimeout(() => { + analytics.track({ + event: "buy_order_quoted_rejected", + }); exit(); }, 0); return; } + analytics.track({ + event: "buy_order_quoted_accepted", + }); submitOrder(); }, [exit, setIsLoading] diff --git a/src/lib/posthog.ts b/src/lib/posthog.ts index 4615895..05a1bdd 100644 --- a/src/lib/posthog.ts +++ b/src/lib/posthog.ts @@ -1,6 +1,8 @@ import { PostHog } from "posthog-node"; +import { loadConfig, saveConfig } from "../helpers/config.ts"; +import { getApiUrl } from "../helpers/urls.ts"; -export const postHogClient = new PostHog( +const postHogClient = new PostHog( "phc_ErsIQYNj6gPFTkHfupfuUGeKjabwtk3WTPdkTDktbU4", { host: "https://us.posthog.com", @@ -17,3 +19,67 @@ export const postHogClient = new PostHog( export const IS_TRACKING_DISABLED = process.env.SF_CLI_TELEMETRY_OPTOUT === "1" || process.env.SF_CLI_TELEMETRY_OPTOUT === "true"; + +/** + * Type copied from posthog-node because it's not exported + */ +interface IdentifyMessage { + distinctId: string; + // biome-ignore lint/suspicious/noExplicitAny: + properties?: Record; + disableGeoip?: boolean; +} + +/** + * Type copied from posthog-node because it's not exported + */ +interface EventMessage extends IdentifyMessage { + event: string; + groups?: Record; + sendFeatureFlags?: boolean; + timestamp?: Date; + uuid?: string; +} + +const trackEvent = ({ + properties, + event, + ...payload +}: Omit) => { + const runner = async () => { + const config = await loadConfig(); + let exchangeAccountId = config.account_id; + + if (!exchangeAccountId) { + const response = await fetch(await getApiUrl("me"), { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${config.auth_token}`, + }, + }); + + const data = await response.json(); + if (data.id) { + exchangeAccountId = data.id; + saveConfig({ ...config, account_id: data.id }); + } + } + + if (exchangeAccountId) { + postHogClient.capture({ + ...payload, + distinctId: exchangeAccountId, + event: `cli_sf_${event}`, + properties: { ...properties, source: "cli" }, + }); + } + }; + + runner(); +}; + +export const analytics = { + track: trackEvent, + shutdown: () => postHogClient.shutdown(), +};