Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a86a02b
add appeal endpoint
varbhat Jul 1, 2025
a2ebcea
add appeal endpoint
varbhat Jul 1, 2025
351d49a
add appeal endpoint
varbhat Jul 1, 2025
1f9d1d1
add appeal endpoint
varbhat Jul 1, 2025
b423807
add query Appeals endpoint
varbhat Jul 1, 2025
5dfea8a
use property shorthand
varbhat Jul 1, 2025
032f203
add decide_appeal to submit action
varbhat Jul 1, 2025
76f5518
DecisionReason to decision_reason
varbhat Jul 1, 2025
7969bc5
update appeal structures
varbhat Jul 1, 2025
e96d3e8
feat: add user_id to appeal for server side call
varbhat Jul 1, 2025
e489934
feat: add id to appeal item
varbhat Jul 1, 2025
45c8ecd
Merge branch 'master' into mod210
varbhat Jul 9, 2025
f653a72
Merge branch 'master' into mod210
varbhat Jul 17, 2025
bedb0f3
Merge branch 'master' into mod210
varbhat Jul 23, 2025
066a0d1
Merge branch 'master' into mod210
varbhat Jul 28, 2025
3f15d4a
Merge branch 'master' into mod210
varbhat Jul 30, 2025
9204cd7
Merge branch 'master' into mod210
varbhat Aug 6, 2025
87a4577
Merge branch 'master' into mod210
varbhat Aug 18, 2025
db2b996
Merge branch 'master' into mod210
varbhat Aug 18, 2025
cca0162
add channel_cid to appeal item
varbhat Aug 20, 2025
c141f8a
add channel_cid to appeal item
varbhat Aug 20, 2025
30c4426
chore: run prettier
varbhat Aug 20, 2025
3459891
Merge branch 'master' into mod210
varbhat Aug 26, 2025
2955024
update appeal fn
varbhat Aug 26, 2025
8ef4c30
Merge branch 'master' into mod210
varbhat Aug 27, 2025
b1edd6e
Merge branch 'master' into mod210
varbhat Aug 27, 2025
1fc6b5a
Merge branch 'master' into mod210
varbhat Sep 7, 2025
963ce18
Merge branch 'master' into mod210
varbhat Sep 10, 2025
ce6de19
Merge branch 'master' into mod210
varbhat Sep 16, 2025
d53a641
Merge branch 'master' into mod210
varbhat Sep 18, 2025
e3ab967
Merge branch 'master' into mod210
varbhat Sep 30, 2025
bb78ede
Merge branch 'master' into mod210
varbhat Oct 2, 2025
806f15a
Merge branch 'master' into mod210
varbhat Oct 3, 2025
39001f9
feat(mod): update appeal APIs
varbhat Oct 3, 2025
585b49a
feat(mod): update appeal APIs
varbhat Oct 3, 2025
85a2ae3
feat(mod): update appeal APIs
varbhat Oct 3, 2025
367f26a
feat(mod): add acceptAppeal and rejectAppeal
varbhat Oct 6, 2025
395054b
Merge branch 'master' into mod210
varbhat Oct 6, 2025
1b74aaf
Merge branch 'master' into mod210
varbhat Oct 16, 2025
2b8555a
Merge branch 'master' into mod210
varbhat Oct 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions src/moderation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type {
APIResponse,
AppealOptions,
AppealRequest,
AppealResponse,
AppealsSort,
CustomCheckFlag,
DecideAppealRequest,
GetAppealResponse,
GetConfigResponse,
GetUserModerationReportOptions,
GetUserModerationReportResponse,
Expand All @@ -11,6 +17,9 @@ import type {
ModerationRuleRequest,
MuteUserResponse,
Pager,
QueryAppealsFilters,
QueryAppealsPaginationOptions,
QueryAppealsResponse,
QueryConfigsResponse,
QueryModerationConfigsFilters,
QueryModerationConfigsSort,
Expand Down Expand Up @@ -186,6 +195,108 @@ export class Moderation {
);
}

/**
* Appeal against the moderation decision
* @param {AppealRequest} appealRequest Appeal request to be appealed against
*/
async appeal(appealRequest: AppealRequest, options: AppealOptions = {}) {
return await this.client.post<AppealResponse>(
this.client.baseURL + '/api/v2/moderation/appeal',
{
text: appealRequest.text,
entity_id: appealRequest.entityID,
entity_type: appealRequest.entityType,
attachments: appealRequest.attachments,
...options,
},
);
}

/**
* Decide on an appeal
* @param {DecideAppealRequest} decideAppealRequest Request to decide on an appeal
*/
async decideAppeal(
decideAppealRequest: DecideAppealRequest,
options: AppealOptions = {},
) {
return await this.client.post<APIResponse>(
this.client.baseURL + '/api/v2/moderation/decide_appeal',
{
appeal_id: decideAppealRequest.appealID,
status: decideAppealRequest.status,
decision_reason: decideAppealRequest.decisionReason,
...(decideAppealRequest.channelCIDs
? { channel_cids: decideAppealRequest.channelCIDs }
: {}),
...options,
},
);
}

/**
* Accept an appeal
* @param {appealID} appealID ID of appeal
* @param {decisionReason} decisionReason Reason for accepting an appeal
*/
async acceptAppeal(
appealID: string,
decisionReason: string,
options: AppealOptions = {},
) {
return await this.decideAppeal(
{ appealID, decisionReason, status: 'accepted' },
options,
);
}

/**
* Reject an appeal
* @param {appealID} appealID ID of appeal
* @param {decisionReason} decisionReason Reason for rejecting an appeal
*/
async rejectAppeal(
appealID: string,
decisionReason: string,
options: AppealOptions = {},
) {
return await this.decideAppeal(
{ appealID, decisionReason, status: 'rejected' },
options,
);
}

/**
* Get Appeal Item
* @param {string} appealID ID of the appeal to be fetched
*/
async getAppeal(appealID: string) {
return await this.client.get<GetAppealResponse>(
this.client.baseURL + '/api/v2/moderation/appeal/' + appealID,
);
}

/**
* Query appeals
* @param {Object} filterConditions Filter conditions for querying appeals
* @param {Object} sort Sort conditions for querying appeals
* @param {Object} options Pagination options for querying appeals
*/
async queryAppeals(
filterConditions: QueryAppealsFilters = {},
sort: AppealsSort = [],
options: QueryAppealsPaginationOptions = {},
) {
return await this.client.post<QueryAppealsResponse>(
this.client.baseURL + '/api/v2/moderation/appeals',
{
filter: filterConditions,
sort: normalizeQuerySort(sort),
...options,
},
);
}

/**
* Upsert moderation config
* @param {Object} config Moderation config to be upserted
Expand Down
117 changes: 116 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3573,6 +3573,19 @@ export type ModerationFlag = {
moderation_payload_hash?: string;
};

export type AppealItem = {
attachments: string[];
created_at: string;
updated_at: string;
decision_reason: string;
entity_id: string;
entity_type: string;
status: string;
text: string;
user: UserResponse;
id: string;
};

export type ReviewQueueItem = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
actions_taken: any[];
Expand Down Expand Up @@ -3601,6 +3614,7 @@ export type ReviewQueueItem = {
reviewed_at: string;
status: string;
updated_at: string;
appeal?: AppealItem;
};

export type CustomCheckFlag = {
Expand All @@ -3625,19 +3639,25 @@ export type SubmitActionOptions = {
channel_ban_only?: boolean;
reason?: string;
timeout?: number;
decision_reason?: string;
delete_messages?: MessageDeletionStrategy;
};
delete_message?: {
hard_delete?: boolean;
decision_reason?: string;
};
delete_user?: {
delete_conversation_channels?: boolean;
hard_delete?: boolean;
mark_messages_deleted?: boolean;
decision_reason?: string;
};
restore?: {
decision_reason?: string;
};
restore?: {};
unban?: {
channel_cid?: string;
decision_reason?: string;
};
user_id?: string;
};
Expand Down Expand Up @@ -3782,13 +3802,73 @@ export type ReviewQueueFilters = QueryFilters<
date_range?: RequireOnlyOne<{
$eq?: string; // Format: "date1_date2"
}>;
} & {
appeal?: boolean;
} & {
appeal_status?: RequireOnlyOne<{
$eq?: 'submitted' | 'accepted' | 'rejected';
}>;
}
>;

export type ReviewQueueSort =
| Sort<Pick<ReviewQueueItem, 'id' | 'created_at' | 'updated_at'>>
| Array<Sort<Pick<ReviewQueueItem, 'id' | 'created_at' | 'updated_at'>>>;

export type AppealsSort =
| Sort<Pick<AppealItem, 'created_at' | 'updated_at'>>
| Array<Sort<Pick<AppealItem, 'created_at' | 'updated_at'>>>;

export type QueryAppealsFilters = QueryFilters<
{
entity_type?:
| RequireOnlyOne<Pick<QueryFilter<AppealItem['entity_type']>, '$eq' | '$in'>>
| PrimitiveFilter<AppealItem['entity_type']>;
} & {
created_at?:
| RequireOnlyOne<
Pick<
QueryFilter<AppealItem['created_at']>,
'$eq' | '$gt' | '$lt' | '$gte' | '$lte'
>
>
| PrimitiveFilter<AppealItem['created_at']>;
} & {
id?:
| RequireOnlyOne<Pick<QueryFilter<AppealItem['id']>, '$eq' | '$in'>>
| PrimitiveFilter<AppealItem['id']>;
} & {
entity_id?:
| RequireOnlyOne<Pick<QueryFilter<AppealItem['entity_id']>, '$eq' | '$in'>>
| PrimitiveFilter<AppealItem['entity_id']>;
} & {
status?:
| RequireOnlyOne<Pick<QueryFilter<AppealItem['status']>, '$eq' | '$in'>>
| PrimitiveFilter<AppealItem['status']>;
} & {
updated_at?:
| RequireOnlyOne<
Pick<
QueryFilter<AppealItem['updated_at']>,
'$eq' | '$gt' | '$lt' | '$gte' | '$lte'
>
>
| PrimitiveFilter<AppealItem['updated_at']>;
} & {
text?: RequireOnlyOne<{
$eq?: string;
}>;
} & {
decision_reason?: RequireOnlyOne<{
$eq?: string;
}>;
} & {
review_queue_item_id?: RequireOnlyOne<{
$eq?: string;
}>;
}
>;

export type QueryModerationConfigsSort = Array<Sort<'key' | 'created_at' | 'updated_at'>>;

export type ReviewQueuePaginationOptions = Pager;
Expand All @@ -3799,6 +3879,14 @@ export type ReviewQueueResponse = {
prev?: string;
};

export type QueryAppealsResponse = APIResponse & {
items: AppealItem[];
next?: string;
prev?: string;
};

export type QueryAppealsPaginationOptions = Pager;

export type ModerationConfig = {
key: string;
ai_image_config?: AIImageConfig;
Expand Down Expand Up @@ -3831,6 +3919,14 @@ export type UpsertConfigResponse = {
config: ModerationConfigResponse;
};

export type AppealResponse = APIResponse & {
appeal_id: string;
};

export type GetAppealResponse = APIResponse & {
item: AppealItem;
};

// Moderation Rule Builder Types
export type ModerationRule = {
id: string;
Expand All @@ -3853,6 +3949,20 @@ export type ModerationRuleRequest = {
enabled: boolean;
};

export type AppealRequest = {
text: string;
entityID: string;
entityType: string;
attachments: string[];
};

export type DecideAppealRequest = {
appealID: string;
status: 'accepted' | 'rejected';
decisionReason: string;
channelCIDs?: string[];
};

export type RuleBuilderRule = {
id: string;
rule_type: 'user' | 'content';
Expand Down Expand Up @@ -3987,6 +4097,11 @@ export type ModerationFlagOptions = {
user_id?: string;
};

export type AppealOptions = {
custom?: Record<string, unknown>;
user_id?: string;
};

export type ModerationMuteOptions = {
timeout?: number;
user_id?: string;
Expand Down