Skip to content

Commit 2276b85

Browse files
feat: moderation v2 endpoints under client.moderation (#1327)
1 parent b1f47f0 commit 2276b85

File tree

4 files changed

+206
-96
lines changed

4 files changed

+206
-96
lines changed

src/client.ts

Lines changed: 4 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -203,17 +203,10 @@ import {
203203
QueryMessageHistorySort,
204204
QueryMessageHistoryOptions,
205205
QueryMessageHistoryResponse,
206-
GetUserModerationReportResponse,
207-
ReviewQueueFilters,
208-
ReviewQueueSort,
209-
ReviewQueuePaginationOptions,
210-
ReviewQueueResponse,
211-
GetConfigResponse,
212-
UpsertConfigResponse,
213-
Config,
214206
} from './types';
215207
import { InsightMetrics, postInsights } from './insights';
216208
import { Thread } from './thread';
209+
import { Moderation } from './moderation';
217210

218211
function isString(x: unknown): x is string {
219212
return typeof x === 'string' || x instanceof String;
@@ -247,6 +240,7 @@ export class StreamChat<StreamChatGenerics extends ExtendableGenerics = DefaultG
247240
* manually calling queryChannels endpoint.
248241
*/
249242
recoverStateOnReconnect?: boolean;
243+
moderation: Moderation<StreamChatGenerics>;
250244
mutedChannels: ChannelMute<StreamChatGenerics>[];
251245
mutedUsers: Mute<StreamChatGenerics>[];
252246
node: boolean;
@@ -298,6 +292,8 @@ export class StreamChat<StreamChatGenerics extends ExtendableGenerics = DefaultG
298292
this.mutedChannels = [];
299293
this.mutedUsers = [];
300294

295+
this.moderation = new Moderation(this);
296+
301297
// set the secret
302298
if (secretOrOptions && isString(secretOrOptions)) {
303299
this.secret = secretOrOptions;
@@ -1557,91 +1553,6 @@ export class StreamChat<StreamChatGenerics extends ExtendableGenerics = DefaultG
15571553
});
15581554
}
15591555

1560-
async getUserModerationReport(
1561-
userID: string,
1562-
options: {
1563-
create_user_if_not_exists?: boolean;
1564-
include_user_blocks?: boolean;
1565-
include_user_mutes?: boolean;
1566-
} = {},
1567-
) {
1568-
return await this.get<GetUserModerationReportResponse<StreamChatGenerics>>(
1569-
this.baseURL + `/api/v2/moderation/user_report`,
1570-
{
1571-
user_id: userID,
1572-
...options,
1573-
},
1574-
);
1575-
}
1576-
1577-
async queryReviewQueue(
1578-
filterConditions: ReviewQueueFilters = {},
1579-
sort: ReviewQueueSort = [],
1580-
options: ReviewQueuePaginationOptions = {},
1581-
) {
1582-
return await this.post<ReviewQueueResponse>(this.baseURL + '/api/v2/moderation/review_queue', {
1583-
filter: filterConditions,
1584-
sort: normalizeQuerySort(sort),
1585-
...options,
1586-
});
1587-
}
1588-
1589-
async upsertConfig(config: Config = {}) {
1590-
return await this.post<UpsertConfigResponse>(this.baseURL + '/api/v2/moderation/config', config);
1591-
}
1592-
1593-
async getConfig(key: string) {
1594-
return await this.get<GetConfigResponse>(this.baseURL + '/api/v2/moderation/config/' + key);
1595-
}
1596-
1597-
async flagUserV2(flaggedUserID: string, reason: string, options: Record<string, unknown> = {}) {
1598-
return this.flagV2('stream:user', flaggedUserID, '', reason, options);
1599-
}
1600-
1601-
async flagMessageV2(messageID: string, reason: string, options: Record<string, unknown> = {}) {
1602-
return this.flagV2('stream:chat:v1:message', messageID, '', reason, options);
1603-
}
1604-
1605-
async flagV2(
1606-
entityType: string,
1607-
entityId: string,
1608-
entityCreatorID: string,
1609-
reason: string,
1610-
options: Record<string, unknown> = {},
1611-
) {
1612-
return await this.post<{ item_id: string } & APIResponse>(this.baseURL + '/api/v2/moderation/flag', {
1613-
entity_type: entityType,
1614-
entity_id: entityId,
1615-
entity_creator_id: entityCreatorID,
1616-
reason,
1617-
...options,
1618-
});
1619-
}
1620-
1621-
async muteUserV2(
1622-
targetID: string,
1623-
options: {
1624-
timeout?: number;
1625-
user_id?: string;
1626-
} = {},
1627-
) {
1628-
return await this.post<MuteUserResponse & APIResponse>(this.baseURL + '/api/v2/moderation/mute', {
1629-
target_ids: [targetID],
1630-
...options,
1631-
});
1632-
}
1633-
1634-
async unmuteUserV2(
1635-
targetID: string,
1636-
options: {
1637-
user_id?: string;
1638-
},
1639-
) {
1640-
return await this.post<{ item_id: string } & APIResponse>(this.baseURL + '/api/v2/moderation/unmute', {
1641-
target_ids: [targetID],
1642-
...options,
1643-
});
1644-
}
16451556
/**
16461557
* queryChannels - Query channels
16471558
*

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export * from './channel_state';
66
export * from './thread';
77
export * from './connection';
88
export * from './events';
9+
export * from './moderation';
910
export * from './permissions';
1011
export * from './signing';
1112
export * from './token_manager';

src/moderation.ts

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import {
2+
APIResponse,
3+
ModerationConfig,
4+
DefaultGenerics,
5+
ExtendableGenerics,
6+
GetConfigResponse,
7+
GetUserModerationReportResponse,
8+
MuteUserResponse,
9+
ReviewQueueFilters,
10+
ReviewQueuePaginationOptions,
11+
ReviewQueueResponse,
12+
ReviewQueueSort,
13+
UpsertConfigResponse,
14+
ModerationFlagOptions,
15+
ModerationMuteOptions,
16+
GetUserModerationReportOptions,
17+
} from './types';
18+
import { StreamChat } from './client';
19+
import { normalizeQuerySort } from './utils';
20+
21+
export const MODERATION_ENTITY_TYPES = {
22+
user: 'stream:user',
23+
message: 'stream:chat:v1:message',
24+
};
25+
26+
// Moderation class provides all the endpoints related to moderation v2.
27+
export class Moderation<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> {
28+
client: StreamChat<StreamChatGenerics>;
29+
30+
constructor(client: StreamChat<StreamChatGenerics>) {
31+
this.client = client;
32+
}
33+
34+
/**
35+
* Flag a user
36+
*
37+
* @param {string} flaggedUserID User ID to be flagged
38+
* @param {string} reason Reason for flagging the user
39+
* @param {Object} options Additional options for flagging the user
40+
* @param {string} options.user_id (For server side usage) User ID of the user who is flagging the target user
41+
* @param {Object} options.custom Additional data to be stored with the flag
42+
* @returns
43+
*/
44+
async flagUser(flaggedUserID: string, reason: string, options: ModerationFlagOptions = {}) {
45+
return this.flag(MODERATION_ENTITY_TYPES.user, flaggedUserID, '', reason, options);
46+
}
47+
48+
/**
49+
* Flag a message
50+
*
51+
* @param {string} messageID Message ID to be flagged
52+
* @param {string} reason Reason for flagging the message
53+
* @param {Object} options Additional options for flagging the message
54+
* @param {string} options.user_id (For server side usage) User ID of the user who is flagging the target message
55+
* @param {Object} options.custom Additional data to be stored with the flag
56+
* @returns
57+
*/
58+
async flagMessage(messageID: string, reason: string, options: ModerationFlagOptions = {}) {
59+
return this.flag(MODERATION_ENTITY_TYPES.message, messageID, '', reason, options);
60+
}
61+
62+
/**
63+
* Flag a user
64+
*
65+
* @param {string} entityType Entity type to be flagged
66+
* @param {string} entityId Entity ID to be flagged
67+
* @param {string} entityCreatorID User ID of the entity creator
68+
* @param {string} reason Reason for flagging the entity
69+
* @param {Object} options Additional options for flagging the entity
70+
* @param {string} options.user_id (For server side usage) User ID of the user who is flagging the target entity
71+
* @param {Object} options.moderation_payload Content to be flagged e.g., { texts: ['text1', 'text2'], images: ['image1', 'image2']}
72+
* @param {Object} options.custom Additional data to be stored with the flag
73+
* @returns
74+
*/
75+
async flag(
76+
entityType: string,
77+
entityId: string,
78+
entityCreatorID: string,
79+
reason: string,
80+
options: ModerationFlagOptions = {},
81+
) {
82+
return await this.client.post<{ item_id: string } & APIResponse>(this.client.baseURL + '/api/v2/moderation/flag', {
83+
entity_type: entityType,
84+
entity_id: entityId,
85+
entity_creator_id: entityCreatorID,
86+
reason,
87+
...options,
88+
});
89+
}
90+
91+
/**
92+
* Mute a user
93+
* @param {string} targetID User ID to be muted
94+
* @param {Object} options Additional options for muting the user
95+
* @param {string} options.user_id (For server side usage) User ID of the user who is muting the target user
96+
* @param {number} options.timeout Timeout for the mute in minutes
97+
* @returns
98+
*/
99+
async muteUser(targetID: string, options: ModerationMuteOptions = {}) {
100+
return await this.client.post<MuteUserResponse<StreamChatGenerics> & APIResponse>(
101+
this.client.baseURL + '/api/v2/moderation/mute',
102+
{
103+
target_ids: [targetID],
104+
...options,
105+
},
106+
);
107+
}
108+
109+
/**
110+
* Unmute a user
111+
* @param {string} targetID User ID to be unmuted
112+
* @param {Object} options Additional options for unmuting the user
113+
* @param {string} options.user_id (For server side usage) User ID of the user who is unmuting the target user
114+
* @returns
115+
*/
116+
async unmuteUser(
117+
targetID: string,
118+
options: {
119+
user_id?: string;
120+
},
121+
) {
122+
return await this.client.post<{ item_id: string } & APIResponse>(
123+
this.client.baseURL + '/api/v2/moderation/unmute',
124+
{
125+
target_ids: [targetID],
126+
...options,
127+
},
128+
);
129+
}
130+
131+
/**
132+
* Get moderation report for a user
133+
* @param {string} userID User ID for which moderation report is to be fetched
134+
* @param {Object} options Additional options for fetching the moderation report
135+
* @param {boolean} options.create_user_if_not_exists Create user if not exists
136+
* @param {boolean} options.include_user_blocks Include user blocks
137+
* @param {boolean} options.include_user_mutes Include user mutes
138+
*/
139+
async getUserModerationReport(userID: string, options: GetUserModerationReportOptions = {}) {
140+
return await this.client.get<GetUserModerationReportResponse<StreamChatGenerics>>(
141+
this.client.baseURL + `/api/v2/moderation/user_report`,
142+
{
143+
user_id: userID,
144+
...options,
145+
},
146+
);
147+
}
148+
149+
/**
150+
* Query review queue
151+
* @param {Object} filterConditions Filter conditions for querying review queue
152+
* @param {Object} sort Sort conditions for querying review queue
153+
* @param {Object} options Pagination options for querying review queue
154+
*/
155+
async queryReviewQueue(
156+
filterConditions: ReviewQueueFilters = {},
157+
sort: ReviewQueueSort = [],
158+
options: ReviewQueuePaginationOptions = {},
159+
) {
160+
return await this.client.post<ReviewQueueResponse>(this.client.baseURL + '/api/v2/moderation/review_queue', {
161+
filter: filterConditions,
162+
sort: normalizeQuerySort(sort),
163+
...options,
164+
});
165+
}
166+
167+
/**
168+
* Upsert moderation config
169+
* @param {Object} config Moderation config to be upserted
170+
*/
171+
async upsertConfig(config: ModerationConfig = {}) {
172+
return await this.client.post<UpsertConfigResponse>(this.client.baseURL + '/api/v2/moderation/config', config);
173+
}
174+
175+
/**
176+
* Get moderation config
177+
* @param {string} key Key for which moderation config is to be fetched
178+
*/
179+
async getConfig(key: string) {
180+
return await this.client.get<GetConfigResponse>(this.client.baseURL + '/api/v2/moderation/config/' + key);
181+
}
182+
}

src/types.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3289,12 +3289,28 @@ export type ReviewQueueResponse = {
32893289
prev?: string;
32903290
};
32913291

3292-
export type Config = {};
3292+
export type ModerationConfig = {};
32933293

32943294
export type GetConfigResponse = {
3295-
config: Config;
3295+
config: ModerationConfig;
32963296
};
32973297

32983298
export type UpsertConfigResponse = {
3299-
config: Config;
3299+
config: ModerationConfig;
3300+
};
3301+
3302+
export type ModerationFlagOptions = {
3303+
custom?: Record<string, unknown>;
3304+
moderation_payload?: ModerationPayload;
3305+
user_id?: string;
3306+
};
3307+
3308+
export type ModerationMuteOptions = {
3309+
timeout?: number;
3310+
user_id?: string;
3311+
};
3312+
export type GetUserModerationReportOptions = {
3313+
create_user_if_not_exists?: boolean;
3314+
include_user_blocks?: boolean;
3315+
include_user_mutes?: boolean;
33003316
};

0 commit comments

Comments
 (0)