forked from administration/panel
feat: create user strikes
parent
f005456f0d
commit
006acdb6ac
|
@ -106,7 +106,11 @@ export default async function User({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
<RelevantModerationNotices strikes={strikes} notices={notices} />
|
<RelevantModerationNotices
|
||||||
|
userId={user._id}
|
||||||
|
strikes={strikes}
|
||||||
|
notices={notices}
|
||||||
|
/>
|
||||||
|
|
||||||
<Separator />
|
<Separator />
|
||||||
<RelevantObjects
|
<RelevantObjects
|
||||||
|
|
|
@ -3,19 +3,144 @@
|
||||||
import { AccountStrike, Message } from "revolt-api";
|
import { AccountStrike, Message } from "revolt-api";
|
||||||
import { ListCompactor } from "../common/ListCompactor";
|
import { ListCompactor } from "../common/ListCompactor";
|
||||||
import { CompactMessage } from "../cards/CompactMessage";
|
import { CompactMessage } from "../cards/CompactMessage";
|
||||||
|
import { useRef, useState } from "react";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
AlertDialogTrigger,
|
||||||
|
} from "../ui/alert-dialog";
|
||||||
|
import { Input } from "../ui/input";
|
||||||
|
import { createStrike } from "@/lib/actions";
|
||||||
|
import { useToast } from "../ui/use-toast";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "../ui/table";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { decodeTime } from "ulid";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param param0 You have received an account strike, for one or more reasons:
|
||||||
|
- REASON_HERE
|
||||||
|
|
||||||
|
Further violations will result in suspension or a permanent ban depending on severity, please abide by the [Acceptable Usage Policy](https://revolt.chat/aup).
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
|
||||||
export function RelevantModerationNotices({
|
export function RelevantModerationNotices({
|
||||||
|
userId,
|
||||||
strikes,
|
strikes,
|
||||||
notices,
|
notices,
|
||||||
}: {
|
}: {
|
||||||
|
userId: string;
|
||||||
strikes: AccountStrike[];
|
strikes: AccountStrike[];
|
||||||
notices: Message[];
|
notices: Message[];
|
||||||
}) {
|
}) {
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [strikesDraft, setStrikesDraft] = useState(strikes);
|
||||||
|
|
||||||
|
const givenReason = useRef("");
|
||||||
|
const additionalContext = useRef("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
||||||
<h2 className="text-md text-center pb-2">Strikes</h2>
|
<h2 className="text-md text-center pb-2">Strikes</h2>
|
||||||
<ListCompactor data={strikes} Component={({ item }) => null} />
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>Reason</TableHead>
|
||||||
|
<TableHead>Created</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{strikesDraft.map((strike) => (
|
||||||
|
<TableRow key={strike._id}>
|
||||||
|
<TableCell>{strike.reason}</TableCell>
|
||||||
|
<TableCell>{dayjs(decodeTime(strike._id)).fromNow()}</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<AlertDialog>
|
||||||
|
<AlertDialogTrigger asChild>
|
||||||
|
<Button className="mx-auto" variant="outline">
|
||||||
|
Create
|
||||||
|
</Button>
|
||||||
|
</AlertDialogTrigger>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Create Strike</AlertDialogTitle>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<p className="text-sm flex flex-col gap-2">
|
||||||
|
<span>
|
||||||
|
You have received an account strike, for one or more reasons:
|
||||||
|
</span>
|
||||||
|
<Input
|
||||||
|
defaultValue=""
|
||||||
|
placeholder="Short reason"
|
||||||
|
onChange={(e) => (givenReason.current = e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
Further violations will result in suspension or a permanent ban
|
||||||
|
depending on severity, please abide by the{" "}
|
||||||
|
<a href="https://revolt.chat/aup" target="_blank">
|
||||||
|
Acceptable Usage Policy
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</span>
|
||||||
|
<Input
|
||||||
|
defaultValue=""
|
||||||
|
placeholder="Additional context (only moderators see this)"
|
||||||
|
onChange={(e) =>
|
||||||
|
(additionalContext.current = e.currentTarget.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={() =>
|
||||||
|
givenReason.current &&
|
||||||
|
createStrike(
|
||||||
|
userId,
|
||||||
|
givenReason.current,
|
||||||
|
additionalContext.current
|
||||||
|
)
|
||||||
|
.then((strike) => {
|
||||||
|
setStrikesDraft((strikes) => [strike, ...strikes]);
|
||||||
|
toast({ title: "Created strike" });
|
||||||
|
})
|
||||||
|
.catch((err) =>
|
||||||
|
toast({
|
||||||
|
title: "Failed to create strike!",
|
||||||
|
description: String(err),
|
||||||
|
variant: "destructive",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.finally(() => {
|
||||||
|
givenReason.current = "";
|
||||||
|
additionalContext.current = "";
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Create
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
<div className="flex-1 min-w-0 flex flex-col gap-2">
|
||||||
<h2 className="text-md text-center pb-2">Alerts</h2>
|
<h2 className="text-md text-center pb-2">Alerts</h2>
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { publishMessage, sendChatMessage } from "./redis";
|
||||||
import { ulid } from "ulid";
|
import { ulid } from "ulid";
|
||||||
import {
|
import {
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
|
AccountStrike,
|
||||||
Bot,
|
Bot,
|
||||||
File,
|
File,
|
||||||
Member,
|
Member,
|
||||||
|
@ -41,6 +42,35 @@ export async function sendAlert(userId: string, content: string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createStrike(
|
||||||
|
userId: string,
|
||||||
|
givenReason: string,
|
||||||
|
additionalContext: string
|
||||||
|
) {
|
||||||
|
const strike: AccountStrike & { moderator_id: string } = {
|
||||||
|
_id: ulid(),
|
||||||
|
user_id: userId,
|
||||||
|
moderator_id: "01EX2NCWQ0CHS3QJF0FEQS1GR4", // TODO
|
||||||
|
reason: additionalContext
|
||||||
|
? givenReason + " - " + additionalContext
|
||||||
|
: givenReason,
|
||||||
|
};
|
||||||
|
|
||||||
|
await mongo()
|
||||||
|
.db("revolt")
|
||||||
|
.collection<{ _id: string }>("safety_strikes")
|
||||||
|
.insertOne(strike);
|
||||||
|
await sendAlert(
|
||||||
|
userId,
|
||||||
|
`You have received an account strike, for one or more reasons:
|
||||||
|
- ${givenReason}
|
||||||
|
|
||||||
|
Further violations will result in suspension or a permanent ban depending on severity, please abide by the [Acceptable Usage Policy](https://revolt.chat/aup).`
|
||||||
|
);
|
||||||
|
|
||||||
|
return strike;
|
||||||
|
}
|
||||||
|
|
||||||
export async function updateReportNotes(reportId: string, notes: string) {
|
export async function updateReportNotes(reportId: string, notes: string) {
|
||||||
return await mongo()
|
return await mongo()
|
||||||
.db("revolt")
|
.db("revolt")
|
||||||
|
|
|
@ -274,7 +274,14 @@ export async function fetchStrikesByUser(userId: string) {
|
||||||
return await mongo()
|
return await mongo()
|
||||||
.db("revolt")
|
.db("revolt")
|
||||||
.collection<AccountStrike>("safety_strikes")
|
.collection<AccountStrike>("safety_strikes")
|
||||||
.find({ user_id: userId })
|
.find(
|
||||||
|
{ user_id: userId },
|
||||||
|
{
|
||||||
|
sort: {
|
||||||
|
_id: -1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
.toArray();
|
.toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue