Skip to content

Commit

Permalink
Merge pull request online-go#2405 from RubyMineshaft/bot_detection
Browse files Browse the repository at this point in the history
Handle system reports of detected cheating and false positives
  • Loading branch information
anoek authored Oct 30, 2023
2 parents 82226d2 + aa1bd8f commit c5e5c8d
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 45 deletions.
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

0 comments on commit c5e5c8d

Please sign in to comment.