104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
import { z } from "zod";
|
|
import { displayNameSchema } from "./account";
|
|
|
|
export const screenshotLimit = 5 * 1024 * 1024;
|
|
const text = (min: number, max: number) =>
|
|
z
|
|
.string()
|
|
.trim()
|
|
.min(min)
|
|
.max(max)
|
|
.refine(
|
|
(value) =>
|
|
!/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/u.test(
|
|
value,
|
|
),
|
|
"Remove hidden control characters",
|
|
);
|
|
export const bugReportSchema = z
|
|
.object({
|
|
clientId: z.string().uuid(),
|
|
title: text(5, 120),
|
|
description: text(15, 6000),
|
|
displayName: displayNameSchema.optional(),
|
|
pagePath: z
|
|
.string()
|
|
.max(300)
|
|
.regex(/^\/(?!\/)[a-zA-Z0-9/_-]*$/)
|
|
.default("/"),
|
|
screenshot: z.boolean().default(false),
|
|
})
|
|
.strict();
|
|
export const bugMessageSchema = z
|
|
.object({ clientId: z.string().uuid(), text: text(1, 6000) })
|
|
.strict();
|
|
export type BugReportRecord = {
|
|
id: string;
|
|
client_id: string;
|
|
owner_id: string | null;
|
|
access_hash: string;
|
|
display_name: string;
|
|
title: string;
|
|
description: string;
|
|
page_path: string;
|
|
state: "draft" | "pending" | "open";
|
|
screenshot_expected: boolean;
|
|
screenshot_path: string | null;
|
|
linear_asset_url: string | null;
|
|
linear_identifier: string | null;
|
|
issue_synced: boolean;
|
|
glow_requested: boolean;
|
|
bug_confirmed?: boolean;
|
|
lease_owner: string | null;
|
|
lease_until: string | null;
|
|
retry_at: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
};
|
|
export type BugMessageRecord = {
|
|
id: string;
|
|
client_id: string | null;
|
|
report_id: string;
|
|
kind: "user" | "staff" | "system";
|
|
body: string;
|
|
author_name: string;
|
|
delivered: boolean;
|
|
created_at: string;
|
|
external_id: string | null;
|
|
};
|
|
export type BugReportView = Pick<
|
|
BugReportRecord,
|
|
| "id"
|
|
| "title"
|
|
| "description"
|
|
| "page_path"
|
|
| "state"
|
|
| "linear_identifier"
|
|
| "glow_requested"
|
|
| "created_at"
|
|
> & {
|
|
screenshot: string | null;
|
|
isGuest: boolean;
|
|
authorAppearance?: import("./profile-appearance").ProfileAppearance;
|
|
canClaim: boolean;
|
|
messages: Pick<
|
|
BugMessageRecord,
|
|
"id" | "kind" | "body" | "author_name" | "delivered" | "created_at"
|
|
>[];
|
|
};
|
|
export type SupportCommand =
|
|
| { kind: "reply"; text: string }
|
|
| { kind: "glow"; enabled: boolean };
|
|
export function supportCommand(body: string): SupportCommand | null {
|
|
const reply = /^#player-report-send\s+([\s\S]+)$/.exec(body.trim());
|
|
if (reply) {
|
|
const parsed = text(1, 6000).safeParse(reply[1]);
|
|
return parsed.success ? { kind: "reply", text: parsed.data } : null;
|
|
}
|
|
const glow = /^#player-report-glow (grant|revoke)$/.exec(body.trim());
|
|
return glow ? { kind: "glow", enabled: glow[1] === "grant" } : null;
|
|
}
|
|
export function escapeSupportMarkdown(value: string) {
|
|
return value.replace(/[\\`*_{}\[\]()<>#!|~]/g, "\\$&");
|
|
}
|