diff --git a/src/ChatClient.cs b/src/ChatClient.cs index f69a8c3..1c13954 100644 --- a/src/ChatClient.cs +++ b/src/ChatClient.cs @@ -506,6 +506,7 @@ public async Task> UpdateMemberParti // Sends new message to the specified channel // Sends events: + // - channel.visible // - message.new // - message.updated public async Task> SendMessageAsync(string type, string id, SendMessageRequest request, diff --git a/src/CommonClient.cs b/src/CommonClient.cs index eb81d2b..2e6b191 100644 --- a/src/CommonClient.cs +++ b/src/CommonClient.cs @@ -104,6 +104,21 @@ public async Task> CreateBlockListAsync( return result; } + public async Task> ImportBlockListAsync(string id, ImportBlockListRequest request, + CancellationToken cancellationToken = default) + { + var pathParams = new Dictionary + { + ["id"] = id, + }; + + var result = await MakeRequestAsync( + "POST", + "/api/v2/blocklists/{id}/import", null, request, pathParams, + cancellationToken); + + return result; + } public async Task> DeleteBlockListAsync(string name, object request = null, CancellationToken cancellationToken = default) { @@ -642,11 +657,10 @@ public async Task> GetPollOptionAsync(string ["poll_id"] = pollID, ["option_id"] = optionID, }; - var queryParams = ExtractQueryParams(request); var result = await MakeRequestAsync( "GET", - "/api/v2/polls/{poll_id}/options/{option_id}", queryParams, null, pathParams, + "/api/v2/polls/{poll_id}/options/{option_id}", null, null, pathParams, cancellationToken); return result; diff --git a/src/ModerationClient.cs b/src/ModerationClient.cs index af604df..9498b69 100644 --- a/src/ModerationClient.cs +++ b/src/ModerationClient.cs @@ -403,25 +403,33 @@ public async Task> UpsertModeration return result; } - public async Task> DeleteModerationRuleAsync(object request = null, + public async Task> DeleteModerationRuleAsync(string id, object request = null, CancellationToken cancellationToken = default) { + var pathParams = new Dictionary + { + ["id"] = id, + }; var queryParams = ExtractQueryParams(request); var result = await _client.MakeRequestAsync( "DELETE", - "/api/v2/moderation/moderation_rule/{id}", queryParams, null, null, + "/api/v2/moderation/moderation_rule/{id}", queryParams, null, pathParams, cancellationToken); return result; } - public async Task> GetModerationRuleAsync(object request = null, + public async Task> GetModerationRuleAsync(string id, object request = null, CancellationToken cancellationToken = default) { + var pathParams = new Dictionary + { + ["id"] = id, + }; var result = await _client.MakeRequestAsync( "GET", - "/api/v2/moderation/moderation_rule/{id}", null, null, null, + "/api/v2/moderation/moderation_rule/{id}", null, null, pathParams, cancellationToken); return result; @@ -448,13 +456,96 @@ public async Task> MuteAsync(MuteRequest request, return result; } + public async Task> GetPolicyTestRunAsync(string id, object request = null, + CancellationToken cancellationToken = default) + { + var pathParams = new Dictionary + { + ["id"] = id, + }; + + var result = await _client.MakeRequestAsync( + "GET", + "/api/v2/moderation/policy_tests/runs/{id}", null, null, pathParams, + cancellationToken); + + return result; + } + public async Task> ListPolicyTestSetsAsync(object request = null, + CancellationToken cancellationToken = default) + { + + var result = await _client.MakeRequestAsync( + "GET", + "/api/v2/moderation/policy_tests/sets", null, null, null, + cancellationToken); + + return result; + } + public async Task> CreatePolicyTestSetAsync(CreatePolicyTestSetRequest request, + CancellationToken cancellationToken = default) + { + + var result = await _client.MakeRequestAsync( + "POST", + "/api/v2/moderation/policy_tests/sets", null, request, null, + cancellationToken); + + return result; + } + public async Task> DeletePolicyTestSetAsync(string id, object request = null, + CancellationToken cancellationToken = default) + { + var pathParams = new Dictionary + { + ["id"] = id, + }; + + var result = await _client.MakeRequestAsync( + "DELETE", + "/api/v2/moderation/policy_tests/sets/{id}", null, null, pathParams, + cancellationToken); + + return result; + } + public async Task> GetPolicyTestSetAsync(string id, object request = null, + CancellationToken cancellationToken = default) + { + var pathParams = new Dictionary + { + ["id"] = id, + }; + + var result = await _client.MakeRequestAsync( + "GET", + "/api/v2/moderation/policy_tests/sets/{id}", null, null, pathParams, + cancellationToken); + + return result; + } + public async Task> StartPolicyTestRunAsync(string id, object request = null, + CancellationToken cancellationToken = default) + { + var pathParams = new Dictionary + { + ["id"] = id, + }; + + var result = await _client.MakeRequestAsync( + "POST", + "/api/v2/moderation/policy_tests/sets/{id}/runs", null, null, pathParams, + cancellationToken); + + return result; + } public async Task> ListQueuesAsync(object request = null, CancellationToken cancellationToken = default) { + var queryParams = ExtractQueryParams(request); var result = await _client.MakeRequestAsync( "GET", - "/api/v2/moderation/queues", null, null, null, + "/api/v2/moderation/queues", queryParams, null, null, cancellationToken); return result; diff --git a/src/Webhook.cs b/src/Webhook.cs index 30bfc18..ce1d6fc 100644 --- a/src/Webhook.cs +++ b/src/Webhook.cs @@ -152,6 +152,7 @@ public static class WebhookEventType public const string MessageUnblocked = "message.unblocked"; public const string MessageUndeleted = "message.undeleted"; public const string MessageUpdated = "message.updated"; + public const string ModerationAnalysisFailed = "moderation.analysis.failed"; public const string ModerationCustomAction = "moderation.custom_action"; public const string ModerationFlagged = "moderation.flagged"; public const string ModerationImageAnalysisComplete = "moderation.image_analysis.complete"; @@ -415,6 +416,7 @@ private static Type GetEventTypeClass(string eventType) "message.unblocked" => typeof(MessageUnblockedEvent), "message.undeleted" => typeof(MessageUndeletedEvent), "message.updated" => typeof(MessageUpdatedEvent), + "moderation.analysis.failed" => typeof(ModerationAnalysisFailedEvent), "moderation.custom_action" => typeof(ModerationCustomActionEvent), "moderation.flagged" => typeof(ModerationFlaggedEvent), "moderation.image_analysis.complete" => typeof(ModerationImageAnalysisCompleteEvent), diff --git a/src/models.cs b/src/models.cs index 82c14a1..18f5300 100644 --- a/src/models.cs +++ b/src/models.cs @@ -850,6 +850,11 @@ public class ActivityRequest [JsonPropertyName("collection_refs")] public List CollectionRefs { get; set; } /// + /// Collections to create or update as part of this request, so an activity and the collections it references can be written in one call. Their refs (name:id) are added to collection_refs automatically; you do not need to restate them, and they count toward the same per-activity collection-reference limit, which is the effective cap here. A collection that already exists has its custom data updated. Use collection_refs instead when the collection already exists and you are only referencing it, which requires no collection permissions. + /// + [JsonPropertyName("collections")] + public List Collections { get; set; } + /// /// Tags for filtering activities /// [JsonPropertyName("filter_tags")] @@ -1351,6 +1356,11 @@ public class AddActivityRequest [JsonPropertyName("collection_refs")] public List CollectionRefs { get; set; } /// + /// Collections to create or update as part of this request, so an activity and the collections it references can be written in one call. Their refs (name:id) are added to collection_refs automatically; you do not need to restate them, and they count toward the same per-activity collection-reference limit, which is the effective cap here. A collection that already exists has its custom data updated. Use collection_refs instead when the collection already exists and you are only referencing it, which requires no collection permissions. + /// + [JsonPropertyName("collections")] + public List Collections { get; set; } + /// /// Tags for filtering activities /// [JsonPropertyName("filter_tags")] @@ -2002,6 +2012,8 @@ public class AppResponseFields public bool ImageModerationEnabled { get; set; } [JsonPropertyName("max_aggregated_activities_length")] public int MaxAggregatedActivitiesLength { get; set; } + [JsonPropertyName("member_custom_on_messages_enabled")] + public bool MemberCustomOnMessagesEnabled { get; set; } [JsonPropertyName("moderation_audio_call_moderation_enabled")] public bool ModerationAudioCallModerationEnabled { get; set; } [JsonPropertyName("moderation_enabled")] @@ -2072,6 +2084,8 @@ public class AppResponseFields public int? BeforeMessageSendHookAttemptTimeoutMs { get; set; } [JsonPropertyName("before_message_send_hook_url")] public string? BeforeMessageSendHookUrl { get; set; } + [JsonPropertyName("chat_primary_use_case")] + public string? ChatPrimaryUseCase { get; set; } [JsonPropertyName("moderation_onboarding_complete")] public bool? ModerationOnboardingComplete { get; set; } [JsonPropertyName("moderation_s3_image_access_role_arn")] @@ -2697,6 +2711,11 @@ public class BanInfoResponse [JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; } /// + /// The channel this ban applies to. Empty if this is an app-wide (global) ban rather than a per-channel ban. + /// + [JsonPropertyName("channel_cid")] + public string? ChannelCid { get; set; } + /// /// When the ban expires /// [JsonPropertyName("expires")] @@ -2711,6 +2730,8 @@ public class BanInfoResponse /// [JsonPropertyName("shadow")] public bool? Shadow { get; set; } + [JsonPropertyName("channel")] + public ChannelMetadata? Channel { get; set; } [JsonPropertyName("created_by")] public UserResponse? CreatedBy { get; set; } [JsonPropertyName("user")] @@ -2947,6 +2968,8 @@ public class BlockListResponse public DateTime? CreatedAt { get; set; } [JsonPropertyName("id")] public string? ID { get; set; } + [JsonPropertyName("owner_user_id")] + public string? OwnerUserID { get; set; } [JsonPropertyName("team")] public string? Team { get; set; } /// @@ -4627,6 +4650,8 @@ public class CallSettingsRequest public BackstageSettingsRequest? Backstage { get; set; } [JsonPropertyName("broadcasting")] public BroadcastSettingsRequest? Broadcasting { get; set; } + [JsonPropertyName("encryption")] + public EncryptionSettingsRequest? Encryption { get; set; } [JsonPropertyName("frame_recording")] public FrameRecordingSettingsRequest? FrameRecording { get; set; } [JsonPropertyName("geofencing")] @@ -4663,6 +4688,8 @@ public class CallSettingsResponse public BackstageSettingsResponse Backstage { get; set; } [JsonPropertyName("broadcasting")] public BroadcastSettingsResponse Broadcasting { get; set; } + [JsonPropertyName("encryption")] + public EncryptionSettingsResponse Encryption { get; set; } [JsonPropertyName("frame_recording")] public FrameRecordingSettingsResponse FrameRecording { get; set; } [JsonPropertyName("geofencing")] @@ -5433,6 +5460,9 @@ public class ChannelBatchUpdateRequest { [JsonPropertyName("operation")] public string Operation { get; set; } + /// + /// Filter to apply to the query + /// [JsonPropertyName("filter")] public object Filter { get; set; } [JsonPropertyName("members")] @@ -5681,6 +5711,27 @@ public class ChannelConfigWithInfo public Dictionary> Grants { get; set; } } + public class ChannelContextResponse + { + /// + /// Channel CID (:) + /// + [JsonPropertyName("cid")] + public string Cid { get; set; } + /// + /// Channel ID + /// + [JsonPropertyName("id")] + public string ID { get; set; } + /// + /// Channel type + /// + [JsonPropertyName("type")] + public string Type { get; set; } + [JsonPropertyName("created_by")] + public UserResponse? CreatedBy { get; set; } + } + public class ChannelCreatedEvent { /// @@ -5874,6 +5925,11 @@ public class ChannelGetOrCreateRequest public bool? State { get; set; } [JsonPropertyName("thread_unread_counts")] public bool? ThreadUnreadCounts { get; set; } + /// + /// Top-level keys of the message sender's channel-member custom data to include under member.custom (max 8 keys, 64 chars each) + /// + [JsonPropertyName("member_custom_include")] + public List MemberCustomInclude { get; set; } [JsonPropertyName("data")] public ChannelInput? Data { get; set; } [JsonPropertyName("members")] @@ -5948,7 +6004,7 @@ public class ChannelInput [JsonPropertyName("auto_translation_enabled")] public bool? AutoTranslationEnabled { get; set; } /// - /// Switch auto translation language + /// Language (or comma-separated list of languages) to translate to when auto translation is active /// [JsonPropertyName("auto_translation_language")] public string? AutoTranslationLanguage { get; set; } @@ -6006,6 +6062,25 @@ public class ChannelInputRequest public object Custom { get; set; } } + public class ChannelMemberPartialResponse + { + /// + /// Role of the member in the channel + /// + [JsonPropertyName("channel_role")] + public string ChannelRole { get; set; } + /// + /// Whether the user muted notifications for this channel + /// + [JsonPropertyName("notifications_muted")] + public bool NotificationsMuted { get; set; } + /// + /// Channel-member custom fields projected via `member_custom_include` + /// + [JsonPropertyName("custom")] + public object Custom { get; set; } + } + public class ChannelMemberRequest { [JsonPropertyName("user_id")] @@ -6059,9 +6134,19 @@ public class ChannelMemberResponse /// [JsonPropertyName("ban_expires")] public DateTime? BanExpires { get; set; } + /// + /// Whether the member's ban also applies to channels the channel's creator will create in the future (an active future channel ban by the creator targets this member) + /// + [JsonPropertyName("ban_from_future_channels")] + public bool? BanFromFutureChannels { get; set; } [JsonPropertyName("deleted_at")] public DateTime? DeletedAt { get; set; } /// + /// Expiration date of the future channel ban; absent when the future channel ban is permanent + /// + [JsonPropertyName("future_channel_ban_expires")] + public DateTime? FutureChannelBanExpires { get; set; } + /// /// Date when invite was accepted /// [JsonPropertyName("invite_accepted_at")] @@ -6117,6 +6202,28 @@ public class ChannelMessagesResponse public ChannelResponse Channel { get; set; } } + public class ChannelMetadata + { + [JsonPropertyName("cid")] + public string Cid { get; set; } + [JsonPropertyName("id")] + public string ID { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + [JsonPropertyName("custom")] + public object Custom { get; set; } + [JsonPropertyName("last_message_at")] + public DateTime? LastMessageAt { get; set; } + [JsonPropertyName("member_count")] + public int? MemberCount { get; set; } + [JsonPropertyName("message_count")] + public int? MessageCount { get; set; } + [JsonPropertyName("push_level")] + public string? PushLevel { get; set; } + [JsonPropertyName("team")] + public string? Team { get; set; } + } + public class ChannelMute { /// @@ -6226,7 +6333,7 @@ public class ChannelResponse [JsonPropertyName("auto_translation_enabled")] public bool? AutoTranslationEnabled { get; set; } /// - /// Language to translate to when auto translation is active + /// Language (or comma-separated list of languages) to translate to when auto translation is active /// [JsonPropertyName("auto_translation_language")] public string? AutoTranslationLanguage { get; set; } @@ -6851,7 +6958,7 @@ public class ChatMessageResponse [JsonPropertyName("image_labels")] public Dictionary> ImageLabels { get; set; } [JsonPropertyName("member")] - public ChannelMemberResponse? Member { get; set; } + public ChannelMemberPartialResponse? Member { get; set; } [JsonPropertyName("moderation")] public ChatModerationV2Response? Moderation { get; set; } [JsonPropertyName("pinned_by")] @@ -7365,6 +7472,11 @@ public class ClientEvent [JsonPropertyName("join_attempt_id")] public string? JoinAttemptID { get; set; } /// + /// Reason the client initiated the join. Optional on CoordinatorJoin events; empty when not provided. + /// + [JsonPropertyName("join_reason")] + public string? JoinReason { get; set; } + /// /// Microphone permission status: INITIATED, FAILED, GRANTED, or NOT_INITIATED. Required on every MediaDevicePermission event. /// [JsonPropertyName("microphone_permission_status")] @@ -8166,6 +8278,10 @@ public class CreateBlockListRequest /// [JsonPropertyName("type")] public string? Type { get; set; } + [JsonPropertyName("user_id")] + public string? UserID { get; set; } + [JsonPropertyName("user")] + public UserRequest? User { get; set; } } public class CreateBlockListResponse @@ -8921,6 +9037,37 @@ public class CreateMembershipLevelResponse public MembershipLevelResponse MembershipLevel { get; set; } } + public class CreatePolicyTestSetRequest + { + /// + /// Display name; unique within an app + /// + [JsonPropertyName("name")] + public string Name { get; set; } + /// + /// Moderation config key (default: app default) + /// + [JsonPropertyName("config_key")] + public string? ConfigKey { get; set; } + /// + /// Execution target: 'check' or 'labels'. Optional — defaults to 'labels' when the org has the labels API enabled, 'check' otherwise + /// + [JsonPropertyName("mode")] + public string? Mode { get; set; } + /// + /// Team scope for the config (optional) + /// + [JsonPropertyName("team")] + public string? Team { get; set; } + /// + /// Messages to test; capped at 1000. Mutually exclusive with seed + /// + [JsonPropertyName("rows")] + public List Rows { get; set; } + [JsonPropertyName("seed")] + public PolicyTestSeedSpec? Seed { get; set; } + } + public class CreatePollOptionRequest { /// @@ -8930,7 +9077,10 @@ public class CreatePollOptionRequest public string Text { get; set; } [JsonPropertyName("user_id")] public string? UserID { get; set; } - [JsonPropertyName("Custom")] + /// + /// Custom data for this object + /// + [JsonPropertyName("custom")] public object Custom { get; set; } [JsonPropertyName("user")] public UserRequest? User { get; set; } @@ -8978,7 +9128,10 @@ public class CreatePollRequest public string? VotingVisibility { get; set; } [JsonPropertyName("options")] public List Options { get; set; } - [JsonPropertyName("Custom")] + /// + /// Custom data for this object + /// + [JsonPropertyName("custom")] public object Custom { get; set; } [JsonPropertyName("user")] public UserRequest? User { get; set; } @@ -9969,6 +10122,40 @@ public class DeleteTranscriptionResponse public string Duration { get; set; } } + public class DeleteUserMessagesRequestPayload + { + /// + /// Message deletion mode: soft, pruning, or hard + /// + [JsonPropertyName("delete_messages")] + public string DeleteMessages { get; set; } + /// + /// Optional: scope deletion to a single channel (alternative to app-wide deletion) + /// + [JsonPropertyName("channel_cid")] + public string? ChannelCid { get; set; } + /// + /// Whether to also delete the user's reactions on other users' messages + /// + [JsonPropertyName("delete_reactions")] + public bool? DeleteReactions { get; set; } + /// + /// ID of the user whose messages should be deleted (alternative to item_id) + /// + [JsonPropertyName("entity_id")] + public string? EntityID { get; set; } + /// + /// Type of the entity + /// + [JsonPropertyName("entity_type")] + public string? EntityType { get; set; } + /// + /// Reason for the deletion + /// + [JsonPropertyName("reason")] + public string? Reason { get; set; } + } + public class DeleteUserRequestPayload { /// @@ -10320,6 +10507,24 @@ public class EgressResponse public RawRecordingResponse? RawRecording { get; set; } } + public class EncryptionSettingsRequest + { + /// + /// Encryption mode. One of: available, disabled, auto-on + /// + [JsonPropertyName("mode")] + public string? Mode { get; set; } + } + + public class EncryptionSettingsResponse + { + /// + /// the resolved encryption mode for the call + /// + [JsonPropertyName("mode")] + public string Mode { get; set; } + } + public class EndCallRequest { } @@ -12136,6 +12341,11 @@ public class FilterConfigResponse /// [JsonPropertyName("filterable_custom_keys")] public List FilterableCustomKeys { get; set; } + /// + /// AI image moderation labels available as filter values, as a map of L1 label to its L2 sub-labels. Reflects the app's effective image taxonomy: custom Bodyguard taxonomy when enabled, otherwise the standard catalogue of the org's enabled image providers. + /// + [JsonPropertyName("ai_image_taxonomy")] + public Dictionary> AiImageTaxonomy { get; set; } } public class FirebaseConfig @@ -12320,6 +12530,8 @@ public class FlagUserOptions public class FloodConfig { + [JsonPropertyName("allowlist")] + public List Allowlist { get; set; } [JsonPropertyName("identical")] public FloodIdenticalConfig? Identical { get; set; } [JsonPropertyName("similar")] @@ -12338,6 +12550,16 @@ public class FloodIdenticalConfig public string? TimeWindow { get; set; } } + public class FloodIdenticalRuleParameters + { + [JsonPropertyName("threshold")] + public int? Threshold { get; set; } + [JsonPropertyName("time_window")] + public string? TimeWindow { get; set; } + [JsonPropertyName("allowlist")] + public List Allowlist { get; set; } + } + public class FloodSimilarConfig { [JsonPropertyName("action")] @@ -12352,6 +12574,18 @@ public class FloodSimilarConfig public string? TimeWindow { get; set; } } + public class FloodSimilarRuleParameters + { + [JsonPropertyName("similarity_distance")] + public int? SimilarityDistance { get; set; } + [JsonPropertyName("threshold")] + public int? Threshold { get; set; } + [JsonPropertyName("time_window")] + public string? TimeWindow { get; set; } + [JsonPropertyName("allowlist")] + public List Allowlist { get; set; } + } + public class FollowBatchRequest { /// @@ -13135,6 +13369,16 @@ public class GetExternalStorageAWSS3Response public string? PathPrefix { get; set; } } + public class GetExternalStorageGCSResponse + { + [JsonPropertyName("bucket")] + public string Bucket { get; set; } + [JsonPropertyName("credentials_set")] + public bool CredentialsSet { get; set; } + [JsonPropertyName("path_prefix")] + public string? PathPrefix { get; set; } + } + public class GetExternalStorageResponse { [JsonPropertyName("created_at")] @@ -13150,6 +13394,8 @@ public class GetExternalStorageResponse public DateTime UpdatedAt { get; set; } [JsonPropertyName("aws_s3")] public GetExternalStorageAWSS3Response? AWSS3 { get; set; } + [JsonPropertyName("gcs")] + public GetExternalStorageGCSResponse? Gcs { get; set; } } public class GetFeedGroupResponse @@ -13196,6 +13442,11 @@ public class GetFeedsRateLimitsResponse [JsonPropertyName("server_side")] public Dictionary ServerSide { get; set; } /// + /// Rate limits for Unity platform (endpoint name -> limit info) + /// + [JsonPropertyName("unity")] + public Dictionary Unity { get; set; } + /// /// Rate limits for Web platform (endpoint name -> limit info) /// [JsonPropertyName("web")] @@ -13668,6 +13919,11 @@ public class GetRateLimitsResponse [JsonPropertyName("server_side")] public Dictionary ServerSide { get; set; } /// + /// Map of endpoint rate limits for the Unity platform + /// + [JsonPropertyName("unity")] + public Dictionary Unity { get; set; } + /// /// Map of endpoint rate limits for the web platform /// [JsonPropertyName("web")] @@ -14037,6 +14293,26 @@ public class HuaweiConfigFields public string? Secret { get; set; } } + public class IPContentCountRuleParameters + { + [JsonPropertyName("threshold")] + public int? Threshold { get; set; } + [JsonPropertyName("time_window")] + public string? TimeWindow { get; set; } + } + + public class IPFlagCountRuleParameters + { + [JsonPropertyName("severity")] + public string? Severity { get; set; } + [JsonPropertyName("threshold")] + public int? Threshold { get; set; } + [JsonPropertyName("time_window")] + public string? TimeWindow { get; set; } + [JsonPropertyName("harm_labels")] + public List HarmLabels { get; set; } + } + public class ImageContentParameters { [JsonPropertyName("label_operator")] @@ -14146,6 +14422,25 @@ public class Images public ImageData Original { get; set; } } + public class ImportBlockListRequest + { + [JsonPropertyName("items")] + public List Items { get; set; } + [JsonPropertyName("chunk_size")] + public int? ChunkSize { get; set; } + } + + public class ImportBlockListResponse + { + /// + /// Duration of the request in milliseconds + /// + [JsonPropertyName("duration")] + public string Duration { get; set; } + [JsonPropertyName("task_id")] + public string TaskID { get; set; } + } + public class ImportTask { [JsonPropertyName("created_at")] @@ -14206,8 +14501,6 @@ public class ImportV2TaskSettings public string? Path { get; set; } [JsonPropertyName("skip_references_check")] public bool? SkipReferencesCheck { get; set; } - [JsonPropertyName("source")] - public string? Source { get; set; } [JsonPropertyName("use_import_time_as_op_time")] public bool? UseImportTimeAsOpTime { get; set; } [JsonPropertyName("s3")] @@ -14982,6 +15275,8 @@ public class ListBlockListResponse public string Duration { get; set; } [JsonPropertyName("blocklists")] public List Blocklists { get; set; } + [JsonPropertyName("next_cursor")] + public string? NextCursor { get; set; } } public class ListCallTypeResponse @@ -15409,6 +15704,8 @@ public class MatchedContent /// [JsonPropertyName("severity")] public string? Severity { get; set; } + [JsonPropertyName("text")] + public string? Text { get; set; } /// /// Image-classification entries (keyframe rule, Type=image) carry nested L1 → L2 classifications. Text entries (closed_caption rule, Type=text) carry flat label + severity. Resolved against the app's effective taxonomy on the image side. /// @@ -15933,7 +16230,7 @@ public class MessageHistoryEntryResponse public string Text { get; set; } [JsonPropertyName("attachments")] public List Attachments { get; set; } - [JsonPropertyName("Custom")] + [JsonPropertyName("custom")] public object Custom { get; set; } } @@ -16078,6 +16375,8 @@ public class MessageOptions { [JsonPropertyName("include_thread_participants")] public bool? IncludeThreadParticipants { get; set; } + [JsonPropertyName("member_custom_include")] + public List MemberCustomInclude { get; set; } } public class MessagePaginationParams @@ -16487,7 +16786,7 @@ public class MessageResponse [JsonPropertyName("image_labels")] public Dictionary> ImageLabels { get; set; } [JsonPropertyName("member")] - public ChannelMemberResponse? Member { get; set; } + public ChannelMemberPartialResponse? Member { get; set; } [JsonPropertyName("moderation")] public ModerationV2Response? Moderation { get; set; } [JsonPropertyName("pinned_by")] @@ -16848,7 +17147,7 @@ public class MessageWithChannelResponse [JsonPropertyName("image_labels")] public Dictionary> ImageLabels { get; set; } [JsonPropertyName("member")] - public ChannelMemberResponse? Member { get; set; } + public ChannelMemberPartialResponse? Member { get; set; } [JsonPropertyName("moderation")] public ModerationV2Response? Moderation { get; set; } [JsonPropertyName("pinned_by")] @@ -16950,6 +17249,46 @@ public class ModerationActionConfigResponse public object Custom { get; set; } } + public class ModerationAnalysisFailedEvent + { + [JsonPropertyName("created_at")] + public DateTime CreatedAt { get; set; } + [JsonPropertyName("type")] + public string Type { get; set; } + /// + /// The moderation policy key the request targeted. + /// + [JsonPropertyName("config_key")] + public string? ConfigKey { get; set; } + /// + /// Echo of the `entity_creator_id` on the /analyze request. + /// + [JsonPropertyName("entity_creator_id")] + public string? EntityCreatorID { get; set; } + /// + /// Echo of the `entity_id` on the /analyze request. + /// + [JsonPropertyName("entity_id")] + public string? EntityID { get; set; } + /// + /// Echo of the `entity_type` on the /analyze request. + /// + [JsonPropertyName("entity_type")] + public string? EntityType { get; set; } + [JsonPropertyName("received_at")] + public DateTime? ReceivedAt { get; set; } + /// + /// Echo of the request's `content_ids`, keyed by text/image label. On keyframe and caption streams every request repeats the same entity_type/entity_id/entity_creator_id, so this is what identifies the specific submission that went unscreened. + /// + [JsonPropertyName("content_ids")] + public Dictionary ContentIds { get; set; } + /// + /// Echo of the `custom` metadata on the /analyze request. + /// + [JsonPropertyName("custom")] + public object Custom { get; set; } + } + public class ModerationBanResponse { [JsonPropertyName("duration")] @@ -18645,6 +18984,198 @@ public class PolicyRequest public List Roles { get; set; } } + public class PolicyTestLabelDrift + { + [JsonPropertyName("changed")] + public int Changed { get; set; } + [JsonPropertyName("same")] + public int Same { get; set; } + } + + public class PolicyTestResult + { + [JsonPropertyName("created_at")] + public DateTime CreatedAt { get; set; } + [JsonPropertyName("id")] + public int ID { get; set; } + [JsonPropertyName("message_text")] + public string MessageText { get; set; } + [JsonPropertyName("row_index")] + public int RowIndex { get; set; } + [JsonPropertyName("run_id")] + public string RunID { get; set; } + [JsonPropertyName("scored")] + public bool Scored { get; set; } + [JsonPropertyName("actual_action")] + public string? ActualAction { get; set; } + [JsonPropertyName("expected_action")] + public string? ExpectedAction { get; set; } + [JsonPropertyName("failure_reason")] + public string? FailureReason { get; set; } + [JsonPropertyName("passed")] + public bool? Passed { get; set; } + [JsonPropertyName("severity")] + public string? Severity { get; set; } + [JsonPropertyName("actual_labels")] + public List ActualLabels { get; set; } + [JsonPropertyName("expected_labels")] + public List ExpectedLabels { get; set; } + [JsonPropertyName("raw_response")] + public object RawResponse { get; set; } + } + + public class PolicyTestRow + { + [JsonPropertyName("content_type")] + public string? ContentType { get; set; } + [JsonPropertyName("policy")] + public string? Policy { get; set; } + [JsonPropertyName("recommended_action")] + public string? RecommendedAction { get; set; } + [JsonPropertyName("text")] + public string? Text { get; set; } + [JsonPropertyName("labels")] + public List Labels { get; set; } + } + + public class PolicyTestRun + { + [JsonPropertyName("config_key")] + public string ConfigKey { get; set; } + [JsonPropertyName("created_at")] + public DateTime CreatedAt { get; set; } + [JsonPropertyName("id")] + public string ID { get; set; } + [JsonPropertyName("rows_completed")] + public int RowsCompleted { get; set; } + [JsonPropertyName("rows_total")] + public int RowsTotal { get; set; } + [JsonPropertyName("set_id")] + public string SetID { get; set; } + [JsonPropertyName("status")] + public string Status { get; set; } + [JsonPropertyName("task_id")] + public string TaskID { get; set; } + [JsonPropertyName("triggered_by")] + public string TriggeredBy { get; set; } + [JsonPropertyName("completed_at")] + public DateTime? CompletedAt { get; set; } + [JsonPropertyName("config_updated_at")] + public DateTime? ConfigUpdatedAt { get; set; } + [JsonPropertyName("error_message")] + public string? ErrorMessage { get; set; } + [JsonPropertyName("started_at")] + public DateTime? StartedAt { get; set; } + [JsonPropertyName("metrics")] + public PolicyTestRunMetrics? Metrics { get; set; } + } + + public class PolicyTestRunMetrics + { + [JsonPropertyName("mode")] + public string Mode { get; set; } + [JsonPropertyName("totals")] + public PolicyTestTotals Totals { get; set; } + [JsonPropertyName("by_label")] + public Dictionary ByLabel { get; set; } + } + + public class PolicyTestRunResponse + { + [JsonPropertyName("duration")] + public string Duration { get; set; } + /// + /// Per-row results (only present once the run has finished) + /// + [JsonPropertyName("results")] + public List Results { get; set; } + [JsonPropertyName("run")] + public PolicyTestRun? Run { get; set; } + } + + public class PolicyTestSeedSpec + { + /// + /// How many rows to sample, newest first; capped at 1000 + /// + [JsonPropertyName("limit")] + public int Limit { get; set; } + /// + /// Sample only records carrying any of these labels; empty samples everything + /// + [JsonPropertyName("labels")] + public List Labels { get; set; } + } + + public class PolicyTestSet + { + [JsonPropertyName("config_key")] + public string ConfigKey { get; set; } + [JsonPropertyName("created_at")] + public DateTime CreatedAt { get; set; } + [JsonPropertyName("created_by")] + public string CreatedBy { get; set; } + [JsonPropertyName("id")] + public string ID { get; set; } + [JsonPropertyName("mode")] + public string Mode { get; set; } + [JsonPropertyName("name")] + public string Name { get; set; } + [JsonPropertyName("row_count")] + public int RowCount { get; set; } + [JsonPropertyName("updated_at")] + public DateTime UpdatedAt { get; set; } + [JsonPropertyName("team")] + public string? Team { get; set; } + [JsonPropertyName("rows")] + public List Rows { get; set; } + [JsonPropertyName("last_run")] + public PolicyTestRun? LastRun { get; set; } + } + + public class PolicyTestSetListResponse + { + [JsonPropertyName("duration")] + public string Duration { get; set; } + /// + /// List of policy test sets for the app + /// + [JsonPropertyName("sets")] + public List Sets { get; set; } + } + + public class PolicyTestSetResponse + { + [JsonPropertyName("duration")] + public string Duration { get; set; } + /// + /// The set's baseline run (earliest completed run); later runs are scored against it. Absent until the first run completes + /// + [JsonPropertyName("baseline_run_id")] + public string? BaselineRunID { get; set; } + /// + /// Retained run history for this set, newest first + /// + [JsonPropertyName("recent_runs")] + public List RecentRuns { get; set; } + [JsonPropertyName("set")] + public PolicyTestSet? Set { get; set; } + } + + public class PolicyTestTotals + { + [JsonPropertyName("failed")] + public int Failed { get; set; } + [JsonPropertyName("passed")] + public int Passed { get; set; } + [JsonPropertyName("rows")] + public int Rows { get; set; } + [JsonPropertyName("scored")] + public int Scored { get; set; } + [JsonPropertyName("unscored")] + public int Unscored { get; set; } + } + public class PollOptionInput { [JsonPropertyName("text")] @@ -19288,6 +19819,9 @@ public class QueryActivityReactionsRequest public string? Prev { get; set; } [JsonPropertyName("sort")] public List Sort { get; set; } + /// + /// Filters to apply to the query + /// [JsonPropertyName("filter")] public object Filter { get; set; } } @@ -19827,6 +20361,9 @@ public class QueryCampaignsRequest public int? UserLimit { get; set; } [JsonPropertyName("sort")] public List Sort { get; set; } + /// + /// Filter to apply to the query + /// [JsonPropertyName("filter")] public object Filter { get; set; } } @@ -19881,6 +20418,11 @@ public class QueryChannelsRequest [JsonPropertyName("user_id")] public string? UserID { get; set; } /// + /// Top-level keys of the message sender's channel-member custom data to include under member.custom (max 8 keys, 64 chars each) + /// + [JsonPropertyName("member_custom_include")] + public List MemberCustomInclude { get; set; } + /// /// List of sort parameters /// [JsonPropertyName("sort")] @@ -19972,6 +20514,9 @@ public class QueryCommentReactionsRequest public string? Prev { get; set; } [JsonPropertyName("sort")] public List Sort { get; set; } + /// + /// Filters to apply to the query + /// [JsonPropertyName("filter")] public object Filter { get; set; } } @@ -20286,6 +20831,11 @@ public class QueryFutureChannelBansPayload [JsonPropertyName("exclude_expired_bans")] public bool? ExcludeExpiredBans { get; set; } /// + /// When true, the response includes the total number of bans matching the query filter (independent of limit and offset, capped at 100000) + /// + [JsonPropertyName("include_total")] + public bool? IncludeTotal { get; set; } + /// /// Number of records to return /// [JsonPropertyName("limit")] @@ -20296,7 +20846,7 @@ public class QueryFutureChannelBansPayload [JsonPropertyName("offset")] public int? Offset { get; set; } /// - /// Filter by the target user ID. For server-side requests only. + /// Filter by the target user ID. Server-side: returns all bans against this user. Client-side: narrows the authenticated user's own bans to this target. /// [JsonPropertyName("target_user_id")] public string? TargetUserID { get; set; } @@ -20318,6 +20868,11 @@ public class QueryFutureChannelBansResponse /// [JsonPropertyName("bans")] public List Bans { get; set; } + /// + /// Total number of bans matching the query filter, computed at query time and capped at 100000. Only present when include_total is set on the request; omitted when computing the total timed out + /// + [JsonPropertyName("total")] + public int? Total { get; set; } } public class QueryLabelResultsRequest @@ -20553,6 +21108,9 @@ public class QueryModerationFlagsRequest public string? Prev { get; set; } [JsonPropertyName("sort")] public List Sort { get; set; } + /// + /// Filter conditions for moderation flags + /// [JsonPropertyName("filter")] public object Filter { get; set; } } @@ -22718,10 +23276,18 @@ public class RuleBuilderCondition public ContentCustomPropertyParameters? ContentCustomPropertyParams { get; set; } [JsonPropertyName("content_flag_count_rule_params")] public FlagCountRuleParameters? ContentFlagCountRuleParams { get; set; } + [JsonPropertyName("flood_identical_params")] + public FloodIdenticalRuleParameters? FloodIdenticalParams { get; set; } + [JsonPropertyName("flood_similar_params")] + public FloodSimilarRuleParameters? FloodSimilarParams { get; set; } [JsonPropertyName("image_content_params")] public ImageContentParameters? ImageContentParams { get; set; } [JsonPropertyName("image_rule_params")] public ImageRuleParameters? ImageRuleParams { get; set; } + [JsonPropertyName("ip_content_count_rule_params")] + public IPContentCountRuleParameters? IpContentCountRuleParams { get; set; } + [JsonPropertyName("ip_flag_count_rule_params")] + public IPFlagCountRuleParameters? IpFlagCountRuleParams { get; set; } [JsonPropertyName("keyframe_ocr_rule_params")] public KeyframeOCRRuleParameters? KeyframeOcrRuleParams { get; set; } [JsonPropertyName("keyframe_rule_params")] @@ -23442,7 +24008,7 @@ public class SearchResultMessage [JsonPropertyName("image_labels")] public Dictionary> ImageLabels { get; set; } [JsonPropertyName("member")] - public ChannelMemberResponse? Member { get; set; } + public ChannelMemberPartialResponse? Member { get; set; } [JsonPropertyName("moderation")] public ModerationV2Response? Moderation { get; set; } [JsonPropertyName("pinned_by")] @@ -23630,6 +24196,16 @@ public class SendMessageRequest public MessageRequest Message { get; set; } [JsonPropertyName("force_moderation")] public bool? ForceModeration { get; set; } + /// + /// When true, the response includes channel_context: a slim channel object with cid, type, id and created_by + /// + [JsonPropertyName("include_channel_context")] + public bool? IncludeChannelContext { get; set; } + /// + /// When true, the response includes mentioned_members: for each mentioned user, whether that user is currently a channel member. Requires the ReadChannelMembers permission + /// + [JsonPropertyName("include_mentioned_members")] + public bool? IncludeMentionedMembers { get; set; } [JsonPropertyName("keep_channel_hidden")] public bool? KeepChannelHidden { get; set; } [JsonPropertyName("pending")] @@ -23651,6 +24227,13 @@ public class SendMessageResponse public string Duration { get; set; } [JsonPropertyName("message")] public MessageResponse Message { get; set; } + [JsonPropertyName("channel_context")] + public ChannelContextResponse? ChannelContext { get; set; } + /// + /// Map of mentioned user ID to whether that user is currently an active channel member. Only set when include_mentioned_members was requested; omitted when the message has no mentions or the membership lookup failed + /// + [JsonPropertyName("mentioned_members")] + public Dictionary MentionedMembers { get; set; } /// /// Pending message metadata /// @@ -24070,6 +24653,10 @@ public class StartHLSBroadcastingResponse public string PlaylistUrl { get; set; } } + public class StartPolicyTestRunRequest + { + } + public class StartRTMPBroadcastsRequest { /// @@ -24312,7 +24899,7 @@ public class StoriesFeedUpdatedEvent public class SubmitActionRequest { /// - /// Type of moderation action to perform. One of: mark_reviewed, delete_message, delete_activity, delete_comment, delete_reaction, ban, custom, unban, restore, delete_user, unblock, block, shadow_block, unmask, kick_user, end_call, escalate, de_escalate + /// Type of moderation action to perform. One of: mark_reviewed, delete_message, delete_activity, delete_comment, delete_reaction, ban, custom, unban, restore, delete_user, delete_user_messages, unblock, block, shadow_block, unmask, kick_user, end_call, escalate, de_escalate /// [JsonPropertyName("action_type")] public string ActionType { get; set; } @@ -24346,6 +24933,8 @@ public class SubmitActionRequest public DeleteReactionRequestPayload? DeleteReaction { get; set; } [JsonPropertyName("delete_user")] public DeleteUserRequestPayload? DeleteUser { get; set; } + [JsonPropertyName("delete_user_messages")] + public DeleteUserMessagesRequestPayload? DeleteUserMessages { get; set; } [JsonPropertyName("escalate")] public EscalatePayload? Escalate { get; set; } [JsonPropertyName("flag")] @@ -24577,8 +25166,6 @@ public class TextRuleParameters public class ThreadParticipant { - [JsonPropertyName("app_pk")] - public int AppPk { get; set; } [JsonPropertyName("channel_cid")] public string ChannelCid { get; set; } /// @@ -24644,6 +25231,11 @@ public class ThreadResponse [JsonPropertyName("participant_count")] public int ParticipantCount { get; set; } /// + /// Reply Count + /// + [JsonPropertyName("reply_count")] + public int ReplyCount { get; set; } + /// /// Title /// [JsonPropertyName("title")] @@ -24669,11 +25261,6 @@ public class ThreadResponse [JsonPropertyName("last_message_at")] public DateTime? LastMessageAt { get; set; } /// - /// Reply Count - /// - [JsonPropertyName("reply_count")] - public int? ReplyCount { get; set; } - /// /// Thread Participants /// [JsonPropertyName("thread_participants")] @@ -24719,6 +25306,11 @@ public class ThreadStateResponse [JsonPropertyName("participant_count")] public int ParticipantCount { get; set; } /// + /// Reply Count + /// + [JsonPropertyName("reply_count")] + public int ReplyCount { get; set; } + /// /// Title /// [JsonPropertyName("title")] @@ -24745,11 +25337,6 @@ public class ThreadStateResponse /// [JsonPropertyName("last_message_at")] public DateTime? LastMessageAt { get; set; } - /// - /// Reply Count - /// - [JsonPropertyName("reply_count")] - public int? ReplyCount { get; set; } [JsonPropertyName("read")] public List Read { get; set; } /// @@ -25170,6 +25757,11 @@ public class UnbanActionRequestPayload /// [JsonPropertyName("remove_future_channels_ban")] public bool? RemoveFutureChannelsBan { get; set; } + /// + /// Optional: unban user directly without review item + /// + [JsonPropertyName("target_user_id")] + public string? TargetUserID { get; set; } } public class UnbanRequest @@ -25718,6 +26310,8 @@ public class UpdateAppRequest public int? CdnExpirationSeconds { get; set; } [JsonPropertyName("channel_hide_members_only")] public bool? ChannelHideMembersOnly { get; set; } + [JsonPropertyName("chat_primary_use_case")] + public string? ChatPrimaryUseCase { get; set; } [JsonPropertyName("custom_action_handler_url")] public string? CustomActionHandlerUrl { get; set; } [JsonPropertyName("disable_auth_checks")] @@ -25740,6 +26334,8 @@ public class UpdateAppRequest public bool? ImageModerationEnabled { get; set; } [JsonPropertyName("max_aggregated_activities_length")] public int? MaxAggregatedActivitiesLength { get; set; } + [JsonPropertyName("member_custom_on_messages_enabled")] + public bool? MemberCustomOnMessagesEnabled { get; set; } [JsonPropertyName("migrate_permissions_to_v2")] public bool? MigratePermissionsToV2 { get; set; } [JsonPropertyName("moderation_analytics_enabled")] @@ -25832,11 +26428,15 @@ public class UpdateBlockListRequest public bool? IsSubstringMatchingEnabled { get; set; } [JsonPropertyName("team")] public string? Team { get; set; } + [JsonPropertyName("user_id")] + public string? UserID { get; set; } /// /// List of words to block /// [JsonPropertyName("words")] public List Words { get; set; } + [JsonPropertyName("user")] + public UserRequest? User { get; set; } } public class UpdateBlockListResponse @@ -26981,7 +27581,10 @@ public class UpdatePollOptionRequest public string Text { get; set; } [JsonPropertyName("user_id")] public string? UserID { get; set; } - [JsonPropertyName("Custom")] + /// + /// Custom data for this object + /// + [JsonPropertyName("custom")] public object Custom { get; set; } [JsonPropertyName("user")] public UserRequest? User { get; set; } @@ -27059,7 +27662,10 @@ public class UpdatePollRequest /// [JsonPropertyName("options")] public List Options { get; set; } - [JsonPropertyName("Custom")] + /// + /// Custom data for this object + /// + [JsonPropertyName("custom")] public object Custom { get; set; } [JsonPropertyName("user")] public UserRequest? User { get; set; } @@ -27624,12 +28230,24 @@ public class UpsertExternalStorageAWSS3Request public string? PathPrefix { get; set; } } + public class UpsertExternalStorageGCSRequest + { + [JsonPropertyName("bucket")] + public string Bucket { get; set; } + [JsonPropertyName("credentials")] + public string Credentials { get; set; } + [JsonPropertyName("path_prefix")] + public string? PathPrefix { get; set; } + } + public class UpsertExternalStorageRequest { [JsonPropertyName("type")] public string Type { get; set; } [JsonPropertyName("aws_s3")] public UpsertExternalStorageAWSS3Request? AWSS3 { get; set; } + [JsonPropertyName("gcs")] + public UpsertExternalStorageGCSRequest? Gcs { get; set; } } public class UpsertExternalStorageResponse @@ -27649,7 +28267,7 @@ public class UpsertModerationRuleRequest [JsonPropertyName("name")] public string Name { get; set; } /// - /// Type of rule: user, content, or call + /// Type of rule: user, content, call, or flood /// [JsonPropertyName("rule_type")] public string RuleType { get; set; } diff --git a/tests/WebhookTests.cs b/tests/WebhookTests.cs index b8c4043..3a18f6e 100644 --- a/tests/WebhookTests.cs +++ b/tests/WebhookTests.cs @@ -1169,6 +1169,14 @@ public void ParseWebhookEvent_MessageUpdated_ReturnsCorrectType() Assert.That(result, Is.InstanceOf()); } + [Test] + public void ParseWebhookEvent_ModerationAnalysisFailed_ReturnsCorrectType() + { + var payload = "{\"type\":\"moderation.analysis.failed\"}"; + var result = Webhook.ParseWebhookEvent(payload); + Assert.That(result, Is.InstanceOf()); + } + [Test] public void ParseWebhookEvent_ModerationCustomAction_ReturnsCorrectType() {