import { z } from 'zod'; import { MessageComponentsV2Schema } from './components-v2.js'; import { WelcomeEmbedSchema } from './phase2.js'; const snowflake = z.string().regex(/^\d{17,20}$/); export const OwnerRoleSchema = z.enum(['VIEWER', 'SUPPORT', 'ADMIN', 'OWNER']); export type OwnerRole = z.infer; export const OWNER_ROLE_RANK: Record = { VIEWER: 1, SUPPORT: 2, ADMIN: 3, OWNER: 4 }; export function ownerRoleAtLeast(role: OwnerRole, minimum: OwnerRole): boolean { return OWNER_ROLE_RANK[role] >= OWNER_ROLE_RANK[minimum]; } export const BotPresenceStatusSchema = z.enum(['online', 'idle', 'dnd', 'invisible']); export const BotActivityTypeSchema = z.enum(['Playing', 'Listening', 'Watching', 'Competing']); export const BotPresenceConfigSchema = z.object({ status: BotPresenceStatusSchema, activityType: BotActivityTypeSchema, activityText: z.string().min(1).max(128), rotatingMessages: z.array(z.string().min(1).max(128)).max(20), maintenanceMode: z.boolean(), maintenanceMessage: z.string().max(500).nullable() }); export type BotPresenceConfig = z.infer; export const BotPresenceConfigPatchSchema = BotPresenceConfigSchema.partial().refine( (value) => Object.keys(value).length > 0, { message: 'At least one field is required' } ); export const OwnerTeamMemberSchema = z.object({ id: z.string().optional(), userId: snowflake, role: OwnerRoleSchema, note: z.string().max(500).nullable().optional() }); export type OwnerTeamMember = z.infer; export const OwnerTeamUpsertSchema = OwnerTeamMemberSchema; export const FeatureFlagSchema = z.object({ id: z.string().optional(), key: z .string() .min(1) .max(64) .regex(/^[a-z0-9._-]+$/i), enabled: z.boolean(), rolloutPercent: z.number().int().min(0).max(100), whitelistGuildIds: z.array(snowflake) }); export type FeatureFlag = z.infer; export const FeatureFlagUpsertSchema = FeatureFlagSchema; export const GlobalUserBlacklistSchema = z.object({ id: z.string().optional(), userId: snowflake, reason: z.string().max(500).nullable().optional(), note: z.string().max(1000).nullable().optional() }); export type GlobalUserBlacklist = z.infer; export const GuildBlacklistSchema = z.object({ id: z.string().optional(), guildId: snowflake, reason: z.string().max(500).nullable().optional() }); export type GuildBlacklist = z.infer; /** Enriched guild row for the Owner panel guild manager. */ export const OwnerGuildListItemSchema = z.object({ id: snowflake, name: z.string().nullable(), iconUrl: z.string().url().nullable(), memberCount: z.number().int().nonnegative().nullable(), locale: z.string().min(1).max(16), createdAt: z.string(), blacklisted: z.boolean() }); export type OwnerGuildListItem = z.infer; export const ChangelogEntrySchema = z.object({ id: z.string().optional(), version: z.string().min(1).max(32), title: z.string().min(1).max(200), body: z.string().min(1).max(10000), publishedAt: z.string().datetime().optional() }); export type ChangelogEntry = z.infer; export const OwnerAuditEntrySchema = z.object({ id: z.string(), actorUserId: z.string(), action: z.string(), targetType: z.string().nullable(), targetId: z.string().nullable(), createdAt: z.string() }); export type OwnerAuditEntry = z.infer; export const PRESENCE_REDIS_KEY = 'bot:presence'; export const BotAboutMessageTypeSchema = z.enum(['TEXT', 'EMBED', 'COMPONENTS_V2']); export type BotAboutMessageType = z.infer; /** Global `/about` reply configured in the Owner panel. */ export const BotAboutConfigSchema = z.object({ enabled: z.boolean(), messageType: BotAboutMessageTypeSchema, content: z.string().max(2000).nullable(), embed: WelcomeEmbedSchema.nullable(), components: MessageComponentsV2Schema.nullable() }); export type BotAboutConfig = z.infer; export const BotAboutConfigPatchSchema = BotAboutConfigSchema.partial().refine( (value) => Object.keys(value).length > 0, { message: 'At least one field is required' } ); export type BotAboutConfigPatch = z.infer; export const ABOUT_REDIS_KEY = 'bot:about';