Add player bug reports and profile identity features
This commit is contained in:
103
shared/bug-reports.ts
Normal file
103
shared/bug-reports.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
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, "\\$&");
|
||||
}
|
||||
Reference in New Issue
Block a user