Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle system reports of detected cheating and false positives #2405

Merged
merged 12 commits into from
Oct 30, 2023
4 changes: 2 additions & 2 deletions src/components/AnnulQueueModal/AnnulQueueModal.styl
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@
width: 85%;
}

.actions, .close {
margin: 0 20px;
.modal-actions, .close {
margin: 0 0px;
}

.strikethrough {
Expand Down
83 changes: 59 additions & 24 deletions src/components/AnnulQueueModal/AnnulQueueModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ import { Goban } from "goban";
import { AIReview, GameTimings, ChatMode, GameChat, GobanContext } from "Game";
import { Player } from "Player";
import { Resizable } from "Resizable";
import { post } from "requests";
import { post, put } from "requests";

// Define the AnnulQueueModalProps interface
interface AnnulQueueModalProps {
annulQueue: any[];
setSelectModeActive: React.Dispatch<boolean>;
setSelectModeActive?: React.Dispatch<boolean>;
setAnnulQueue: React.Dispatch<any[]>;
onClose: () => void;
forDetectedAI: boolean;
player?: any;
}

// Define the AnnulQueueModal component
Expand All @@ -38,6 +40,8 @@ export function AnnulQueueModal({
setSelectModeActive,
setAnnulQueue,
onClose,
forDetectedAI,
player,
}: AnnulQueueModalProps) {
// Declare state variables
const [selectedGameIndex, setSelectedGameIndex] = React.useState(0);
Expand Down Expand Up @@ -66,8 +70,12 @@ export function AnnulQueueModal({

// Close the modal
const closeModal = () => {
setAnnulQueue([]);
setSelectModeActive(false);
if (!forDetectedAI) {
setAnnulQueue([]);
if (setSelectModeActive) {
setSelectModeActive(false);
}
}
setShowAnnulOverlay(false);
setAnnulResponse(null);
onClose();
Expand Down Expand Up @@ -158,27 +166,33 @@ export function AnnulQueueModal({

// Prompt the user for a moderation note
const promptForModerationNote = () => {
let moderation_note: string | null = null;
let moderationNote: string | null = null;
let currentPlayer: any;
if (forDetectedAI) {
currentPlayer = player;
} else {
currentPlayer = currentGame.player;
}
do {
moderation_note = prompt(
`Annulling ${validGameIds.length} of ${currentGame.player.username}'s games.\nEnter moderation note: (will be entered with '${currentGame.player.username} mass annull:')`,
moderationNote = prompt(
`Annulling ${validGameIds.length} of ${currentPlayer.username}'s games.\nEnter moderation note: (will be entered with '${currentPlayer.username} mass annull:')`,
);

if (moderation_note == null) {
if (moderationNote == null) {
return null;
}
moderation_note = moderation_note.trim();
} while (moderation_note === "");
return `${currentGame.player.username} mass annul: ${moderation_note}`;
moderationNote = moderationNote.trim();
} while (moderationNote === "");
return `player ${currentPlayer.id} mass annul: ${moderationNote}`;
};

// Annul the specified games
const annul = (validGameIds: number[], moderation_note: string) => {
const annul = (validGameIds: number[], moderationNote: string) => {
setShowAnnulOverlay(true);
post("moderation/annul", {
games: validGameIds,
annul: true,
moderation_note: moderation_note,
moderation_note: moderationNote,
})
.then((res) => {
const successful = res.done;
Expand Down Expand Up @@ -207,6 +221,24 @@ export function AnnulQueueModal({
});
};

const markFalsePositive = () => {
if (confirm("Mark this game as a false positive detection?") === true) {
put("cheat_detection/false_positive", {
game_id: currentGame.id,
player_id: currentGame.player,
false_positive: true,
})
.then((res) => {
if (res.success) {
setDequeueRequested(true);
}
})
.catch((err) => {
console.error(err);
});
}
};

return (
<>
<div className="bg-overlay"></div>
Expand Down Expand Up @@ -239,10 +271,10 @@ export function AnnulQueueModal({
response={annulResponse}
/>
<div className="game">
{queue[selectedGameIndex] && (
{currentGame && (
<MiniGoban
key={selectedGameIndex}
id={queue[selectedGameIndex].id}
id={currentGame.id}
noLink={true}
onGobanCreated={onGobanCreated}
chat={true}
Expand All @@ -253,10 +285,10 @@ export function AnnulQueueModal({
<GobanContext.Provider value={goban}>
<div className="col">
<div>
Black: <Player user={queue[selectedGameIndex]?.black} />
Black: <Player user={currentGame?.black} />
</div>
<div>
White: <Player user={queue[selectedGameIndex]?.white} />
White: <Player user={currentGame?.white} />
</div>
<div>
Game Outcome: {winner} (
Expand Down Expand Up @@ -310,7 +342,7 @@ export function AnnulQueueModal({
</div>

<div className="button-bar">
<div className="actions">
<div className="modal-actions">
<button className="next-btn" onClick={goToNextGame}>
Next
</button>
Expand Down Expand Up @@ -346,6 +378,14 @@ export function AnnulQueueModal({
</div>
</div>
<div className="close">
{forDetectedAI && (
<button
className="false-pos-btn danger"
onClick={markFalsePositive}
>
Mark False Positive
</button>
)}
<button className="close-btn" onClick={() => closeModal()}>
{_("Close")}
</button>
Expand All @@ -358,12 +398,7 @@ export function AnnulQueueModal({
}

// Open the AnnulQueueModal
export function openAnnulQueueModal(
annulQueue,
setSelectModeActive,
setAnnulQueue,
setIsAnnulQueueModalOpen,
) {
export function openAnnulQueueModal(setIsAnnulQueueModalOpen) {
setIsAnnulQueueModalOpen(true);

// Disable body scroll
Expand Down
1 change: 1 addition & 0 deletions src/lib/report_manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface Report {
};
moderator_note: string;
system_note: string;
detected_ai_games: Array<Object>;

automod_to_moderator?: string; // Suggestions from "automod"
automod_to_reporter?: string;
Expand Down
3 changes: 2 additions & 1 deletion src/views/ReportsCenter/ReportsCenterHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ export function ReportsCenterHistory(): JSX.Element {
{
header: "Reporter",
className: () => "state",
render: (X) => <Player user={X.reporting_user} />,
render: (X) =>
X.reporting_user ? <Player user={X.reporting_user} /> : "System",
},
{
header: "Type",
Expand Down
4 changes: 4 additions & 0 deletions src/views/ReportsCenter/ViewReport.styl
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@

.actions-right {
align-items: flex-end; // put the buttons down close-ish to the send-warning buttons
button {
height: auto;
padding: 7px;
}
}

.automod-analysis {
Expand Down
55 changes: 43 additions & 12 deletions src/views/ReportsCenter/ViewReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { AppealView } from "./AppealView";
import { get } from "requests";
import { MessageTemplate, WARNING_TEMPLATES, REPORTER_RESPONSE_TEMPLATES } from "./MessageTemplate";
import { ModerationActionSelector } from "./ModerationActionSelector";
import { openAnnulQueueModal, AnnulQueueModal } from "AnnulQueueModal";

// Used for saving updates to the report
let report_note_id = 0;
Expand All @@ -56,6 +57,8 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
const [error, setError] = React.useState(null);
const [moderator_id, setModeratorId] = React.useState(report?.moderator?.id);
const [reportState, setReportState] = React.useState(report?.state);
const [isAnnulQueueModalOpen, setIsAnnulQueueModalOpen] = React.useState(false);
const [annulQueue, setAnnulQueue] = React.useState<null | any[]>(report?.detected_ai_games);

const related = report_manager.getRelatedReports(report_id);

Expand All @@ -68,6 +71,7 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
setReport(report);
setModeratorId(report?.moderator?.id);
setReportState(report?.state);
setAnnulQueue(report?.detected_ai_games);
})
.catch((err) => {
console.error(err);
Expand Down Expand Up @@ -229,8 +233,21 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
}
};

const handleCloseAnnulQueueModal = () => {
setIsAnnulQueueModalOpen(false);
};

return (
<div id="ViewReport">
{isAnnulQueueModalOpen && (
<AnnulQueueModal
annulQueue={annulQueue}
setAnnulQueue={setAnnulQueue}
onClose={handleCloseAnnulQueueModal}
forDetectedAI={true}
player={report.reported_user}
/>
)}
<div className="header">
{report_in_reports ? (
<Select
Expand Down Expand Up @@ -369,7 +386,12 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
"A label for the user name that reported an incident (followed by colon and the username)",
"Reported by",
)}
: <Player user={report.reporting_user} />
:{" "}
{report.reporting_user ? (
<Player user={report.reporting_user} />
) : (
"System"
)}
<span className="when">{moment(report.created).fromNow()}</span>
</span>
</div>
Expand Down Expand Up @@ -468,6 +490,13 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
</div>

<div className="actions-right">
{reportState !== "resolved" && report.detected_ai_games ? (
<button
onClick={() => openAnnulQueueModal(setIsAnnulQueueModalOpen)}
>
Inspect & Annul Games
</button>
) : null}
{reportState !== "resolved" && claimed_by_me && (
<button
className="success"
Expand Down Expand Up @@ -521,17 +550,19 @@ export function ViewReport({ report_id, reports, onChange }: ViewReportProps): J
onMessage={claimReport}
/>

<MessageTemplate
title="Reporter"
player={report.reporting_user}
reported={report.reported_user}
templates={REPORTER_RESPONSE_TEMPLATES}
game_id={report.reported_game}
gpt={report.automod_to_reporter}
logByDefault={!user.is_moderator} // log community moderator actions
onSelect={claimReport}
onMessage={claimReport}
/>
{report.reporting_user && (
<MessageTemplate
title="Reporter"
player={report.reporting_user}
reported={report.reported_user}
templates={REPORTER_RESPONSE_TEMPLATES}
game_id={report.reported_game}
gpt={report.automod_to_reporter}
logByDefault={!user.is_moderator} // log community moderator actions
onSelect={claimReport}
onMessage={claimReport}
/>
)}
</div>
<hr />
{(user.is_moderator || null) && <UserHistory user={report.reported_user} />}
Expand Down
8 changes: 2 additions & 6 deletions src/views/User/GameHistoryTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ export function GameHistoryTable(props: GameHistoryProps) {
annulQueue={annulQueue}
setAnnulQueue={setAnnulQueue}
onClose={handleCloseAnnulQueueModal}
forDetectedAI={false}
/>
)}
{/* loading-container="game_history.settings().$loading" */}
Expand All @@ -226,12 +227,7 @@ export function GameHistoryTable(props: GameHistoryProps) {
<button
className="sm info"
onClick={() =>
openAnnulQueueModal(
annulQueue,
setSelectModeActive,
setAnnulQueue,
setIsAnnulQueueModalOpen,
)
openAnnulQueueModal(setIsAnnulQueueModalOpen)
}
>
{_("View Queue")} {`(${annulQueue.length})`}
Expand Down
Loading