visual-game-admin-analytics.js 5.07 KB
import * as v from 'valibot'

export const VisualGameAdminAnalyticsMetrics = v.object({
  active_players: v.number(),
  interactions: v.number(),
  say_turns: v.number(),
  act_turns: v.number(),
  credits_spent: v.number(),
  credits_granted: v.number(),
  runs_started: v.number(),
  runs_completed: v.number(),
  seasons_started: v.number(),
  seasons_completed: v.number(),
  paying_players: v.number(),
})

export const VisualGameAdminAnalyticsDailyItem = v.object({
  date: v.string(),
  active_players: v.number(),
  interactions: v.number(),
})

export const VisualGameAdminAnalyticsOverviewResponse = v.object({
  generated_at: v.string(),
  since: v.string(),
  until: v.string(),
  metrics: VisualGameAdminAnalyticsMetrics,
  daily: v.array(VisualGameAdminAnalyticsDailyItem),
})

export const VisualGameAdminAnalyticsDrilldownMetric = v.picklist([
  'active_players',
  'interactions',
  'say_turns',
  'act_turns',
  'credits_spent',
  'credits_granted',
  'runs_started',
  'runs_completed',
  'seasons_started',
  'seasons_completed',
  'paying_players',
])

export const VisualGameAdminAnalyticsDrilldownItem = v.object({
  user_id: v.string(),
  display_name: v.nullable(v.string()),
  account_type: v.picklist(['registered', 'guest']),
  interactions: v.number(),
  say_turns: v.number(),
  act_turns: v.number(),
  credits_spent: v.number(),
  credits_granted: v.number(),
  runs_started: v.number(),
  runs_completed: v.number(),
  seasons_started: v.number(),
  seasons_completed: v.number(),
  paid_orders: v.number(),
  last_interaction_at: v.nullable(v.string()),
})

export const VisualGameAdminAnalyticsDrilldownResponse = v.object({
  generated_at: v.string(),
  since: v.string(),
  until: v.string(),
  metric: VisualGameAdminAnalyticsDrilldownMetric,
  total: v.number(),
  items: v.array(VisualGameAdminAnalyticsDrilldownItem),
})

export const VisualGameAdminPlayerInteractionItem = v.object({
  id: v.string(),
  turn_no: v.number(),
  mode: v.picklist(['say', 'act']),
  input_text: v.string(),
  status: v.literal('completed'),
  created_at: v.string(),
  scenario_id: v.string(),
  scenario_title: v.nullable(v.string()),
  series_title: v.nullable(v.string()),
  season_no: v.nullable(v.number()),
  episode_no: v.nullable(v.number()),
})

export const VisualGameAdminPlayerInteractionsResponse = v.object({
  generated_at: v.string(),
  since: v.string(),
  until: v.string(),
  user_id: v.string(),
  total: v.number(),
  items: v.array(VisualGameAdminPlayerInteractionItem),
})

export const VisualGameAdminContentAnalyticsItem = v.object({
  scenario_id: v.string(),
  scenario_title: v.string(),
  series_id: v.nullable(v.string()),
  series_title: v.nullable(v.string()),
  episode_no: v.number(),
  players: v.number(),
  active_players: v.number(),
  runs_started: v.number(),
  runs_completed: v.number(),
  interactions: v.number(),
  say_turns: v.number(),
  act_turns: v.number(),
  completion_rate: v.nullable(v.number()),
})

export const VisualGameAdminContentAnalyticsResponse = v.object({
  generated_at: v.string(),
  since: v.string(),
  until: v.string(),
  items: v.array(VisualGameAdminContentAnalyticsItem),
})

const Ga4Count = v.pipe(v.number(), v.integer(), v.minValue(0))
const Ga4Summary = v.object({
  unique_visitors: Ga4Count,
  active_users: Ga4Count,
  page_views: Ga4Count,
  sessions: Ga4Count,
  video_viewers: Ga4Count,
  video_starts: Ga4Count,
  video_completions: Ga4Count,
  ui_clicks: Ga4Count,
  scene_clicks: Ga4Count,
  typed_inputs: Ga4Count,
  selected_inputs: Ga4Count,
})

export const VisualGameAdminGa4BehaviorResponse = v.object({
  configured: v.boolean(),
  generated_at: v.string(),
  since: v.string(),
  until: v.string(),
  summary: Ga4Summary,
  traffic_daily: v.array(v.object({ date: v.string(), unique_visitors: Ga4Count, page_views: Ga4Count })),
  sources: v.array(v.object({ channel: v.string(), source_medium: v.string(), campaign: v.string(), note_key: v.picklist(['x_referral', 'facebook_referral', 'instagram_referral', 'youtube_referral', 'tiktok_referral', 'direct', 'unassigned', 'organic_social', 'organic_search', 'referral', 'paid_social', 'paid_search', 'other']), sessions: Ga4Count, users: Ga4Count })),
  pages: v.array(v.object({ path: v.string(), title: v.string(), views: Ga4Count, users: Ga4Count })),
  flows: v.array(v.object({ from: v.string(), to: v.string(), views: Ga4Count, users: Ga4Count })),
  devices: v.array(v.object({ category: v.string(), operating_system: v.string(), browser: v.string(), users: Ga4Count, sessions: Ga4Count })),
  videos: v.array(v.object({ video_id: v.string(), video_title: v.nullable(v.string()), video_url: v.nullable(v.string()), placement: v.string(), starts: Ga4Count, progress_events: Ga4Count, completions: Ga4Count, users: Ga4Count })),
  clicks: v.array(v.object({ element_id: v.string(), surface: v.string(), clicks: Ga4Count, users: Ga4Count })),
  scene_misses: v.array(v.object({ scene_id: v.string(), grid_x: v.string(), grid_y: v.string(), pointer_type: v.string(), clicks: Ga4Count, users: Ga4Count })),
  inputs: v.array(v.object({ input_method: v.string(), mode: v.string(), submissions: Ga4Count, users: Ga4Count })),
})