syntax = "proto3";

import "google/protobuf/descriptor.proto";

import "google/protobuf/wrappers.proto";

import "google/protobuf/timestamp.proto";

package discord_protos.discord_experimentation.v1;

enum FilterCategory {
  FILTER_CATEGORY_UNSPECIFIED = 0;
  FILTER_CATEGORY_CLIENT = 1;
  FILTER_CATEGORY_GUILD = 2;
  FILTER_CATEGORY_USER = 3;
  FILTER_CATEGORY_UTILITY = 4;
}

extend google.protobuf.MessageOptions {
  FilterCategory filter_category = 50000;
}

message Experiment {
  enum UnitType {
    UNIT_TYPE_UNSPECIFIED = 0;
    UNIT_TYPE_USER = 1;
    UNIT_TYPE_INSTALLATION = 2;
    UNIT_TYPE_GUILD = 3;
    UNIT_TYPE_CUSTOM = 4;
  }

  enum Type {
    TYPE_UNSPECIFIED = 0;
    TYPE_ACTIVE = 1;
    TYPE_UNUSED = 2;
    TYPE_BURNED = 3;
    TYPE_PRESERVED = 4;
  }

  message Bucket {
    int32 start = 1;
    int32 stop = 2;
    Type type = 3;
  }

  message Variation {
    int32 id = 1;
    string label = 2;
    int32 target_allocation = 3;
    repeated Bucket buckets = 4;
    Type type = 5;
    optional google.protobuf.StringValue configuration = 6;
  }

  message PlatformVersionSpecifier {
    uint32 major = 1;
    optional google.protobuf.UInt32Value minor = 2;
    optional google.protobuf.UInt64Value build = 3;
  }

  message PlatformVersionRangeBound {
    optional PlatformVersionSpecifier version = 1;
    bool inclusive = 2;
  }

  message PlatformVersionRange {
    optional PlatformVersionRangeBound lower_bound = 1;
    optional PlatformVersionRangeBound upper_bound = 2;
  }

  message PlatformVersion {
    repeated PlatformVersionRange ranges = 1;
    bool work_around_pyoto_bug = 2;
  }

  message ClientRequiredChanges {
    repeated string commit_hashes = 1 [packed = false];
    repeated int32 pr_numbers = 2;
  }

  message ClientPlatform {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    optional PlatformVersion ios_version = 1;
    optional PlatformVersion android_version = 2;
    optional PlatformVersion web_version = 3;
    optional PlatformVersion native_version = 4;
    bool allow_non_native_web = 6;
    optional ClientRequiredChanges client_required_changes = 5;
  }

  message SDKVersionSpecifier {
    int32 version = 1;
  }

  message SDKVersionRangeBound {
    optional SDKVersionSpecifier version = 1;
    bool inclusive = 2;
  }

  message SDKVersionRange {
    optional SDKVersionRangeBound lower_bound = 1;
    optional SDKVersionRangeBound upper_bound = 2;
  }

  message SDKVersion {
    repeated SDKVersionRange ranges = 1;
    bool work_around_pyoto_bug = 2;
  }

  message ClientOperatingSystem {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    optional SDKVersion ios_version = 1;
    optional SDKVersion android_version = 2;
    optional SDKVersion macos_version = 3;
    optional SDKVersion windows_version = 4;
    optional SDKVersion playstation_version = 5;
    optional SDKVersion xbox_version = 6;
    optional SDKVersion linux_version = 7;
  }

  message StaffUsers {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    bool work_accounts = 1;
    bool personal_accounts = 2;
  }

  message UserInGuild {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    repeated fixed64 guild_ids = 1;
  }

  message UserIds {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    repeated fixed64 user_ids = 1;
  }

  message ClientLocale {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    repeated string locales = 1 [packed = false];
  }

  message ISORegion {
    string iso_country = 1;
    string iso_subdivision = 2;
  }

  message Place {
    string city = 1;
    string subdivision = 2;
    string country = 3;
  }

  message Location {
    oneof location {
      ISORegion iso_region = 1;
      bool is_eu = 2;
      Place place = 3;
    }
  }

  message ClientLocation {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    repeated Location locations = 1;
  }

  message ClientIP {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    repeated string blocks = 1 [packed = false];
  }

  message UserLocale {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    repeated string locales = 1 [packed = false];
  }

  message UserIsBot {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    bool is_bot = 1;
  }

  message UserAgeRange {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    optional google.protobuf.UInt32Value min_age_years = 1;
    optional google.protobuf.UInt32Value max_age_years = 2;
  }

  message Fixed64Value {
    fixed64 value = 1;
  }

  message UserIDRange {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    optional Fixed64Value min_id = 1;
    optional Fixed64Value max_id = 2;
  }

  message UserHasFlag {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    fixed64 mask = 1;
  }

  message UnitIdInRangeByHash {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_UTILITY;
    string hash_key = 1;
    uint32 stop_ring_position = 2;
    uint32 start_ring_position = 3;
  }

  message ClientReleaseChannel {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    repeated string release_channels = 1 [packed = false];
  }

  message Always {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_UTILITY;
    bool value = 1;
  }

  message ClientSystemLocale {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_CLIENT;
    repeated string locales = 1 [packed = false];
  }

  message UnitIdInExperiment {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_UTILITY;
    fixed64 experiment_id = 1;
    repeated int32 variation_ids = 2;
  }

  message UserPremiumType {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    repeated int32 premium_types = 1;
  }

  message UnitIdMatchesFilterSnapshot {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_UTILITY;
    string filter_snapshot_name = 1;
    repeated fixed64 target_filter_values = 2;
  }

  message GuildIds {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_GUILD;
    repeated fixed64 guild_ids = 1;
  }

  message GuildMemberCountRange {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_GUILD;
    optional google.protobuf.UInt32Value min_count = 1;
    optional google.protobuf.UInt32Value max_count = 2;
  }

  message GuildHasFeature {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_GUILD;
    repeated string features = 1 [packed = false];
  }

  message UserLocation {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    repeated Location locations = 1;
    bool prefer_client_ip = 2;
  }

  message UserIP {
    option (discord_protos.discord_experimentation.v1.filter_category) = FILTER_CATEGORY_USER;
    repeated string blocks = 1 [packed = false];
    bool prefer_client_ip = 2;
  }

  message Filter {
    oneof filter {
      ClientPlatform client_version = 2;
      ClientOperatingSystem client_os = 3;
      StaffUsers staff = 4;
      UserInGuild user_in_guild = 5;
      UserIds user_ids = 6;
      ClientLocale client_locale = 7;
      ClientLocation client_location = 8;
      ClientIP client_ip = 9;
      UserLocale user_locale = 10;
      UserIsBot bot = 11;
      UserAgeRange user_age_range = 12;
      UserIDRange user_id_range = 13;
      UserHasFlag user_has_flag = 14;
      UnitIdInRangeByHash unit_id_in_range_by_hash = 15;
      ClientReleaseChannel client_release_channel = 16;
      Always always = 17;
      ClientSystemLocale client_system_locale = 18;
      UnitIdInExperiment unit_id_in_experiment = 19;
      UserPremiumType user_premium_type = 20;
      UnitIdMatchesFilterSnapshot unit_id_matches_filter_snapshot = 21;
      GuildIds guild_ids = 22;
      GuildMemberCountRange guild_member_count_range = 25;
      GuildHasFeature guild_has_feature = 26;
      UserLocation user_location = 27;
      UserIP user_ip = 28;
    }
  }

  message Override {
    int32 variation_id = 1;
  }

  enum Subtype {
    SUBTYPE_REGULAR = 0;
    SUBTYPE_HOLDOUT = 1;
  }

  message Rule {
    Type type = 1;
    repeated Filter filters = 2;
    optional Override override = 3;
    bool is_sunset_rule = 4;
    Subtype subtype = 5;
  }

  enum Phase {
    PHASE_UNSPECIFIED = 0;
    PHASE_DRAFT = 1;
    PHASE_MEASUREMENT = 2;
    PHASE_ROLLING_OUT = 4;
    PHASE_ARCHIVED = 6;
    PHASE_AA_MODE = 7;
  }

  enum Surface {
    SURFACE_UNSPECIFIED = 0;
    SURFACE_API = 1;
    SURFACE_APP = 2;
    SURFACE_DEVELOPER_PORTAL = 3;
    SURFACE_ADMIN_PANEL = 4;
    SURFACE_ADS_BUDGET_AB = 5;
    SURFACE_AV_WORKER = 6;
    SURFACE_SEO = 7;
  }

  enum ExposureTracking {
    EXPOSURE_TRACKING_ENABLED = 0;
    EXPOSURE_TRACKING_DISABLED = 1;
  }

  enum AssignmentMode {
    ASSIGNMENT_MODE_FULL = 0;
    ASSIGNMENT_MODE_FORCE_CONTROL = 3;
    ASSIGNMENT_MODE_OVERRIDES_ONLY = 4;
    ASSIGNMENT_MODE_OFF = 5;
  }

  message DebugConfig {
    bool enable_decision_logging = 1;
    double metrics_sample_rate = 2;
    bool log_context_on_failure = 3;
    bool log_raw_headers = 4;
    bool tag_filter_metrics = 5;
    double decision_log_sample_rate = 6;
  }

  enum CustomUnitPrefix {
    CUSTOM_UNIT_PREFIX_UNSPECIFIED = 0;
    CUSTOM_UNIT_PREFIX_SEO_URL_SLUG = 1;
  }

  fixed64 id = 1;
  string name = 2;
  optional google.protobuf.Timestamp created_at = 3;
  fixed64 creator_id = 4;
  int32 version = 5;
  optional google.protobuf.Timestamp edited_at = 6;
  fixed64 editor_id = 7;
  string title = 8;
  string description = 9;
  optional google.protobuf.StringValue hypothesis = 10;
  optional google.protobuf.StringValue tech_spec_link = 11;
  int32 revision = 12;
  string hash_key = 13;
  UnitType unit_type = 14;
  repeated Variation variations = 15;
  repeated Rule rules = 16;
  Phase phase = 18;
  repeated Surface surfaces = 19;
  string owning_team_id = 20;
  fixed64 cached_notification_channel_id = 21;
  ExposureTracking exposure_tracking = 22;
  AssignmentMode assignment_mode = 25;
  bool enable_edit_raw_json_ui = 23;
  int32 winning_variation_id = 24;
  string extra_outcome_context = 34;
  Type type = 26;
  bool is_template = 27;
  repeated int32 field_numbers_to_copy = 28;
  repeated string engine_feature_flags = 29 [packed = false];
  optional DebugConfig debug_config = 30;
  optional google.protobuf.Timestamp expected_end_date = 31;
  bool is_automated_change = 32;
  optional google.protobuf.Timestamp archive_at = 33;
  optional google.protobuf.Int32Value guild_experiment_version = 35;
  CustomUnitPrefix custom_unit_prefix = 36;
  repeated string growthbook_tags = 37 [packed = false];
}
