{"version":3,"sources":["../../src/server/reddit-conversions-api.ts"],"sourcesContent":["import { fetch } from '@shware/utils';\nimport { IGNORED_EVENTS } from '../third-parties/ignored-events';\nimport { type ServerStandardEvent, mapRDTEvent, mapServerStandardEvent } from '../track/rdt';\nimport type { TrackEvent, UserProvidedData } from '../track/types';\nimport { getFirst } from '../utils/field';\n\n/**\n * https://ads-api.reddit.com/docs/v3/operations/Post%20Conversion%20Events\n * https://business.reddithelp.com/s/article/map-a-catalog-to-a-signal-source\n */\nexport interface RedditEvent {\n  /** Match keys: Share user identifiers to match conversions to a Reddit ad engagement. */\n  click_id?: string;\n\n  /** Unix epoch timestamp in milliseconds, event_at can't be older than seven days. */\n  event_at: number;\n\n  action_source: 'WEBSITE' | 'APP' | string;\n\n  type: {\n    tracking_type: ServerStandardEvent | 'CUSTOM';\n    custom_event_name?: string;\n  };\n\n  /**\n   * Event metadata\n   * Share as much additional information about your conversion event as you'd like. If you're\n   * using the Conversions API with the pixel, conversion_id is required for deduplication.\n   */\n  metadata?: {\n    conversion_id?: string;\n    currency?: string; // ISO 4217 3-letter currency code\n    item_count?: number;\n    value?: number;\n    products?: { id: string; name?: string; category?: string }[];\n  };\n\n  user?: {\n    email?: string;\n    external_id?: string;\n    ip_address?: string;\n    phone_number?: string;\n    user_agent?: string;\n\n    /** The Identifier for Advertisers (IDFA) of the user's Apple device. */\n    idfa?: string;\n\n    /** The Android Advertising ID (AAID) of the user's Android device. */\n    aaid?: string;\n    /**\n     * The value from the first-party Pixel _rdt_uuid cookie on your domain. Note that it is in\n     * the {timestamp}.{uuid} format. You may use the full value or just the UUID portion.\n     * Example: 1684189007728.7c73f2ae-a433-4d7b-9838-f467da98f48e\n     */\n    uuid?: string;\n\n    screen_dimensions?: { width: number; height: number };\n\n    /**\n     * A structure of data processing options to specify the processing type for the event\n     * https://business.reddithelp.com/s/article/Limited-Data-Use\n     */\n    data_processing_options?: {\n      country: string;\n      region: string;\n      modes: string[] | ['LDU'];\n    };\n  };\n}\n\nexport interface CreateRedditEventDTO {\n  data: { test_id?: string; events: RedditEvent[] };\n}\n\nexport function getServerEvent(\n  // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n  event: TrackEvent<any>,\n  data: UserProvidedData\n): RedditEvent {\n  const { id, name, properties, tags } = event;\n  const [type, params] = mapRDTEvent(name, properties, id);\n\n  return {\n    click_id: tags.rdt_cid,\n    event_at: Date.now(),\n    action_source: tags.source === 'web' ? 'WEBSITE' : tags.source === 'app' ? 'APP' : 'UNKNOWN',\n    type: {\n      tracking_type: type === 'Custom' ? 'CUSTOM' : mapServerStandardEvent(type),\n      custom_event_name: type === 'Custom' ? params.customEventName : undefined,\n    },\n    metadata: {\n      conversion_id: id,\n      currency:\n        'currency' in params && typeof params.currency === 'string'\n          ? params.currency.toUpperCase()\n          : undefined,\n      item_count:\n        'itemCount' in params && typeof params.itemCount === 'number'\n          ? params.itemCount\n          : undefined,\n      value: 'value' in params && typeof params.value === 'number' ? params.value : undefined,\n      products:\n        'products' in params && Array.isArray(params.products) && params.products.length > 0\n          ? params.products\n          : undefined,\n    },\n    user: {\n      email: getFirst(data.email),\n      external_id: data.user_id,\n      ip_address: data.ip_address,\n      phone_number: getFirst(data.phone_number),\n      user_agent: data.user_agent,\n      idfa: tags.platform === 'ios' ? tags.advertising_id : undefined,\n      aaid: tags.platform === 'android' ? tags.advertising_id : undefined,\n      uuid: tags.rdt_uuid,\n      screen_dimensions:\n        tags.screen_width && tags.screen_height\n          ? { width: tags.screen_width, height: tags.screen_height }\n          : undefined,\n    },\n  };\n}\n\nexport async function sendEvents(\n  accessToken: string,\n  pixelId: string,\n  // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n  events: TrackEvent<any>[],\n  data: UserProvidedData = {},\n  testId?: string\n) {\n  const dto: CreateRedditEventDTO = {\n    data: {\n      test_id: testId,\n      events: events\n        .filter((event) => !IGNORED_EVENTS.includes(event.name))\n        .map((event) => getServerEvent(event, data)),\n    },\n  };\n\n  if (dto.data.events.length === 0) return;\n\n  try {\n    const response = await fetch(\n      `https://ads-api.reddit.com/api/v3/pixels/${pixelId}/conversion_events`,\n      {\n        method: 'POST',\n        headers: {\n          'Content-Type': 'application/json',\n          Accept: 'application/json',\n          Authorization: `Bearer ${accessToken}`,\n        },\n        body: JSON.stringify(dto),\n      }\n    );\n    if (response.ok) return;\n    const { status } = response;\n    const message = await response.text();\n    console.error(`Failed to send Reddit conversion, status: ${status}, body: ${message}`);\n  } catch (error) {\n    console.error('Failed to send Reddit conversion, network error:', error);\n  }\n}\n"],"mappings":";AAAA,SAAS,aAAa;AACtB,SAAS,sBAAsB;AAC/B,SAAmC,aAAa,8BAA8B;AAE9E,SAAS,gBAAgB;AAsElB,SAAS,eAEd,OACA,MACa;AACb,QAAM,EAAE,IAAI,MAAM,YAAY,KAAK,IAAI;AACvC,QAAM,CAAC,MAAM,MAAM,IAAI,YAAY,MAAM,YAAY,EAAE;AAEvD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK,IAAI;AAAA,IACnB,eAAe,KAAK,WAAW,QAAQ,YAAY,KAAK,WAAW,QAAQ,QAAQ;AAAA,IACnF,MAAM;AAAA,MACJ,eAAe,SAAS,WAAW,WAAW,uBAAuB,IAAI;AAAA,MACzE,mBAAmB,SAAS,WAAW,OAAO,kBAAkB;AAAA,IAClE;AAAA,IACA,UAAU;AAAA,MACR,eAAe;AAAA,MACf,UACE,cAAc,UAAU,OAAO,OAAO,aAAa,WAC/C,OAAO,SAAS,YAAY,IAC5B;AAAA,MACN,YACE,eAAe,UAAU,OAAO,OAAO,cAAc,WACjD,OAAO,YACP;AAAA,MACN,OAAO,WAAW,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,MAC9E,UACE,cAAc,UAAU,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,SAAS,IAC/E,OAAO,WACP;AAAA,IACR;AAAA,IACA,MAAM;AAAA,MACJ,OAAO,SAAS,KAAK,KAAK;AAAA,MAC1B,aAAa,KAAK;AAAA,MAClB,YAAY,KAAK;AAAA,MACjB,cAAc,SAAS,KAAK,YAAY;AAAA,MACxC,YAAY,KAAK;AAAA,MACjB,MAAM,KAAK,aAAa,QAAQ,KAAK,iBAAiB;AAAA,MACtD,MAAM,KAAK,aAAa,YAAY,KAAK,iBAAiB;AAAA,MAC1D,MAAM,KAAK;AAAA,MACX,mBACE,KAAK,gBAAgB,KAAK,gBACtB,EAAE,OAAO,KAAK,cAAc,QAAQ,KAAK,cAAc,IACvD;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,WACpB,aACA,SAEA,QACA,OAAyB,CAAC,GAC1B,QACA;AACA,QAAM,MAA4B;AAAA,IAChC,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,QAAQ,OACL,OAAO,CAAC,UAAU,CAAC,eAAe,SAAS,MAAM,IAAI,CAAC,EACtD,IAAI,CAAC,UAAU,eAAe,OAAO,IAAI,CAAC;AAAA,IAC/C;AAAA,EACF;AAEA,MAAI,IAAI,KAAK,OAAO,WAAW,EAAG;AAElC,MAAI;AACF,UAAM,WAAW,MAAM;AAAA,MACrB,4CAA4C,OAAO;AAAA,MACnD;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe,UAAU,WAAW;AAAA,QACtC;AAAA,QACA,MAAM,KAAK,UAAU,GAAG;AAAA,MAC1B;AAAA,IACF;AACA,QAAI,SAAS,GAAI;AACjB,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,YAAQ,MAAM,6CAA6C,MAAM,WAAW,OAAO,EAAE;AAAA,EACvF,SAAS,OAAO;AACd,YAAQ,MAAM,oDAAoD,KAAK;AAAA,EACzE;AACF;","names":[]}