diff --git a/src/client.ts b/src/client.ts index b82004bced..ab3966d64e 100644 --- a/src/client.ts +++ b/src/client.ts @@ -203,6 +203,14 @@ import { QueryMessageHistorySort, QueryMessageHistoryOptions, QueryMessageHistoryResponse, + GetUserModerationReportResponse, + ReviewQueueFilters, + ReviewQueueSort, + ReviewQueuePaginationOptions, + ReviewQueueResponse, + GetConfigResponse, + UpsertConfigResponse, + Config, } from './types'; import { InsightMetrics, postInsights } from './insights'; import { Thread } from './thread'; @@ -1549,6 +1557,91 @@ export class StreamChat>( + this.baseURL + `/api/v2/moderation/user_report`, + { + user_id: userID, + ...options, + }, + ); + } + + async queryReviewQueue( + filterConditions: ReviewQueueFilters = {}, + sort: ReviewQueueSort = [], + options: ReviewQueuePaginationOptions = {}, + ) { + return await this.post(this.baseURL + '/api/v2/moderation/review_queue', { + filter: filterConditions, + sort: normalizeQuerySort(sort), + ...options, + }); + } + + async upsertConfig(config: Config = {}) { + return await this.post(this.baseURL + '/api/v2/moderation/config', config); + } + + async getConfig(key: string) { + return await this.get(this.baseURL + '/api/v2/moderation/config/' + key); + } + + async flagUserV2(flaggedUserID: string, reason: string, options: Record = {}) { + return this.flagV2('stream:user', flaggedUserID, '', reason, options); + } + + async flagMessageV2(messageID: string, reason: string, options: Record = {}) { + return this.flagV2('stream:chat:v1:message', messageID, '', reason, options); + } + + async flagV2( + entityType: string, + entityId: string, + entityCreatorID: string, + reason: string, + options: Record = {}, + ) { + return await this.post<{ item_id: string } & APIResponse>(this.baseURL + '/api/v2/moderation/flag', { + entity_type: entityType, + entity_id: entityId, + entity_creator_id: entityCreatorID, + reason, + ...options, + }); + } + + async muteUserV2( + targetID: string, + options: { + timeout?: number; + user_id?: string; + } = {}, + ) { + return await this.post(this.baseURL + '/api/v2/moderation/mute', { + target_ids: [targetID], + ...options, + }); + } + + async unmuteUserV2( + targetID: string, + options: { + user_id?: string; + }, + ) { + return await this.post<{ item_id: string } & APIResponse>(this.baseURL + '/api/v2/moderation/unmute', { + target_ids: [targetID], + ...options, + }); + } /** * queryChannels - Query channels * @@ -2258,7 +2351,7 @@ export class StreamChat} */ - async flagMessage(targetMessageID: string, options: { user_id?: string } = {}) { + async flagMessage(targetMessageID: string, options: { reason?: string; user_id?: string } = {}) { return await this.post>(this.baseURL + '/moderation/flag', { target_message_id: targetMessageID, ...options, @@ -2271,7 +2364,7 @@ export class StreamChat} */ - async flagUser(targetID: string, options: { user_id?: string } = {}) { + async flagUser(targetID: string, options: { reason?: string; user_id?: string } = {}) { return await this.post>(this.baseURL + '/moderation/flag', { target_user_id: targetID, ...options, diff --git a/src/types.ts b/src/types.ts index e67cdae1b4..8644040234 100644 --- a/src/types.ts +++ b/src/types.ts @@ -442,6 +442,7 @@ export type FlagMessageResponse = APIResponse & { @@ -457,6 +458,7 @@ export type FlagUserResponse = Omit< @@ -1899,6 +1901,7 @@ export type AsyncModerationOptions = { export type AppSettings = { agora_options?: AgoraOptions | null; + allowed_flag_reasons?: string[]; apn_config?: { auth_key?: string; auth_type?: string; @@ -2006,6 +2009,8 @@ export type OGAttachment = { export type BlockList = { name: string; words: string[]; + type?: string; + validate?: boolean; }; export type ChannelConfig = ChannelConfigFields & @@ -3149,3 +3154,147 @@ export type QueryMessageHistoryResponse; + images?: string[]; + texts?: string[]; + videos?: string[]; +}; + +export type ModV2ReviewStatus = 'complete' | 'flagged' | 'partial'; + +export type ModerationFlag = { + created_at: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + custom: Record; + entity_creator_id: string; + entity_id: string; + entity_type: string; + id: string; + reason: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + result: Record[]; + review_queue_item_id: string; + updated_at: string; + user: UserResponse; + moderation_payload?: ModerationPayload; + moderation_payload_hash?: string; +}; + +export type ReviewQueueItem = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + actions_taken: any[]; + appealed_by: string; + assigned_to: string; + completed_at: string; + config_key: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + context: any[]; + created_at: string; + created_by: string; + entity_id: string; + entity_type: string; + flags: ModerationFlag[]; + has_image: boolean; + has_text: boolean; + has_video: boolean; + id: string; + moderation_payload: ModerationPayload; + moderation_payload_hash: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: any; + recommended_action: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + results: any; + reviewed_at: string; + status: string; + updated_at: string; +}; + +export type GetUserModerationReportResponse = { + user: UserResponse; + user_blocks?: Array<{ + blocked_at: string; + blocked_by_user_id: string; + blocked_user_id: string; + }>; + user_mutes?: Mute[]; +}; + +export type ReviewQueueFilters = QueryFilters< + { + assigned_to?: + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; + } & { + completed_at?: + | RequireOnlyOne, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> + | PrimitiveFilter; + } & { + config_key?: + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; + } & { + entity_type?: + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; + } & { + created_at?: + | RequireOnlyOne, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> + | PrimitiveFilter; + } & { + id?: + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; + } & { + entity_id?: + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; + } & { + reviewed?: boolean; + } & { + reviewed_at?: + | RequireOnlyOne, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> + | PrimitiveFilter; + } & { + status?: + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; + } & { + updated_at?: + | RequireOnlyOne, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> + | PrimitiveFilter; + } & { + has_image?: boolean; + } & { + has_text?: boolean; + } & { + has_video?: boolean; + } +>; + +export type ReviewQueueSort = + | Sort> + | Array>>; + +export type ReviewQueuePaginationOptions = Pager; + +export type ReviewQueueResponse = { + items: ReviewQueueItem[]; + next?: string; + prev?: string; +}; + +export type Config = {}; + +export type GetConfigResponse = { + config: Config; +}; + +export type UpsertConfigResponse = { + config: Config; +};