Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions packages/core/src/engagement/chat/contract/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export const ROOM_ACTIVITY_WINDOW_HOURS_MAX = 168;

export const CHAT_ROOM_ROLES = ['member', 'moderator', 'owner'] as const;

export const CHAT_ROOM_RESTRICTION_TYPES = ['mute', 'ban', 'cooldown'] as const;
// `admin`: set by staff through the backoffice. `room`: set by the room's owner or a moderator.
export const CHAT_ROOM_RESTRICTION_SOURCES = ['admin', 'room'] as const;

// Roles a room owner can grant or revoke through the member-role route. `owner` is absent
// on purpose: ownership moves through its own transfer flow, never through a role write.
export const CHAT_ROOM_ASSIGNABLE_ROLES = ['member', 'moderator'] as const;
Expand Down
33 changes: 31 additions & 2 deletions packages/core/src/engagement/chat/contract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import {
CHAT_ROOM_ASSIGNABLE_ROLES,
ROOM_ACTIVITY_WINDOW_HOURS_DEFAULT,
ROOM_ACTIVITY_WINDOW_HOURS_MAX,
CHAT_ROOM_RESTRICTION_TYPES,
CHAT_ROOM_RESTRICTION_SOURCES,
} from './constants.js';

export * from './constants.js';
Expand Down Expand Up @@ -294,6 +296,22 @@ export const ChatPlatformBanSchema = z.object({
});

const ModerationReasonSchema = z.string().trim().min(1).max(CHAT_MODERATION_REASON_MAX_LENGTH);
export const ChatRoomRestrictionTypeSchema = z.enum(CHAT_ROOM_RESTRICTION_TYPES);
export type ChatRoomRestrictionType = z.infer<typeof ChatRoomRestrictionTypeSchema>;
export const ChatRoomRestrictionSourceSchema = z.enum(CHAT_ROOM_RESTRICTION_SOURCES);
export type ChatRoomRestrictionSource = z.infer<typeof ChatRoomRestrictionSourceSchema>;
// A room-moderator restriction reports scope `room` with its room id.
export const ChatRoomRestrictionSchema = ChatModerationEntrySchema.extend({
type: ChatRoomRestrictionTypeSchema,
source: ChatRoomRestrictionSourceSchema,
username: z.string().nullable(),
// Null for a room-moderator ban, which records no reason.
reason: z.string().nullable(),
setByName: z.string().nullable(),
cooldownSeconds: z.number().int().positive().nullable(),
});
export type ChatRoomRestriction = z.infer<typeof ChatRoomRestrictionSchema>;

const AdminModerationInput = z.object({
userId: UuidSchema,
reason: ModerationReasonSchema,
Expand All @@ -317,7 +335,12 @@ export const AdminLiftChatCooldownInputSchema = z.object({
});

const RoomIdInput = z.object({ roomId: UuidSchema });
const RoomRulesInput = z.object({ roomId: UuidSchema.or(z.literal(GLOBAL_CHAT_ROOM_ID)) });
const RoomRefInput = z.object({ roomId: UuidSchema.or(z.literal(GLOBAL_CHAT_ROOM_ID)) });
const ListRoomRestrictionsInputSchema = RoomRefInput.extend({
type: ChatRoomRestrictionTypeSchema.optional(),
...PageQuerySchema.shape,
});
export type ListRoomRestrictionsInput = z.infer<typeof ListRoomRestrictionsInputSchema>;
const RoomUserInput = z.object({ roomId: UuidSchema, userId: UuidSchema });
const RoomModerationInput = RoomUserInput.extend({
reason: ModerationReasonSchema.default(''),
Expand Down Expand Up @@ -535,7 +558,7 @@ export const chatContract = {

getRoomRules: oc
.route({ method: 'GET', path: '/chat/rooms/{roomId}/rules' })
.input(RoomRulesInput)
.input(RoomRefInput)
.output(z.array(ChatRoomRuleSchema)),

createRoomRule: oc
Expand Down Expand Up @@ -657,6 +680,12 @@ export const chatContract = {
)
.output(ChatRoomSchema),

// Active restrictions set in the room plus the platform-wide scopes that cover its kind.
adminListRoomRestrictions: oc
.route({ method: 'GET', path: '/backoffice/chat/rooms/{roomId}/restrictions' })
.input(ListRoomRestrictionsInputSchema)
.output(paginated(ChatRoomRestrictionSchema)),

adminCreateRoomRule: oc
.route({ method: 'POST', path: '/backoffice/chat/rooms/{roomId}/rules' })
.input(CreateRoomRuleInputSchema)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE INDEX "chat_mute_active_room_idx" ON "chat_mute" USING btree ("room_id","expires_at") WHERE "chat_mute"."lifted_at" IS NULL;--> statement-breakpoint
CREATE INDEX "chat_mute_active_scope_idx" ON "chat_mute" USING btree ("scope","expires_at") WHERE "chat_mute"."lifted_at" IS NULL;--> statement-breakpoint
CREATE INDEX "chat_platform_ban_active_room_idx" ON "chat_platform_ban" USING btree ("room_id","expires_at") WHERE "chat_platform_ban"."lifted_at" IS NULL;--> statement-breakpoint
CREATE INDEX "chat_platform_ban_active_scope_idx" ON "chat_platform_ban" USING btree ("scope","expires_at") WHERE "chat_platform_ban"."lifted_at" IS NULL;--> statement-breakpoint
CREATE INDEX "chat_player_cooldown_active_room_idx" ON "chat_player_cooldown" USING btree ("room_id","expires_at") WHERE "chat_player_cooldown"."lifted_at" IS NULL;--> statement-breakpoint
CREATE INDEX "chat_player_cooldown_active_scope_idx" ON "chat_player_cooldown" USING btree ("scope","expires_at") WHERE "chat_player_cooldown"."lifted_at" IS NULL;
Loading
Loading