Skip to content

Commit

Permalink
Merge pull request #2369 from GreenAsJade/score_cheat_community_moder…
Browse files Browse the repository at this point in the history
…ator_power

Score cheat community moderator power
  • Loading branch information
anoek authored Oct 19, 2023
2 parents 13b1f90 + 8823e1a commit 11cd600
Show file tree
Hide file tree
Showing 17 changed files with 706 additions and 200 deletions.
60 changes: 60 additions & 0 deletions src/components/AccountWarning/AccountWarning.styl
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,64 @@



}

.AccountWarningAck {
position: fixed;
top: auto;
bottom: auto;
left: auto;
right: auto;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 30rem;
max-width: calc(95vw - 2rem);
max-height: calc(95vh - 2rem);

box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.5);

z-index: z.account-warning;
themed background-color info
themed color colored-background-fg
border-radius: 0.5rem;
padding: 1rem;

display: flex;
flex-direction: column;
justify-content: center;
align-items: center;


.space {
flex-grow: 1;
height: 1rem;
}

.buttons {
font-size: 1.5rem;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
user-select: none;
}

button {
font-size: 1.1rem;
}

input[type="checkbox"] {
margin-right: 0.5rem;
width: 1.2rem;
height: 1.2rem;
}

label {
cursor: pointer;
margin-right: 1rem;
}



}
102 changes: 85 additions & 17 deletions src/components/AccountWarning/AccountWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

// An "AccountWarning" was initially "warn them not to do bad things" and ensure they acknowledge
// Now it is extended to "notify them about something (maybe just information)" and record that they saw it
// So the word "warning" kind of means "message" now.

import * as React from "react";
import { _, pgettext } from "translate";
import { get, patch } from "requests";
import { useUser } from "hooks";
import { AutoTranslate } from "AutoTranslate";
import { useLocation } from "react-router";

import { CANNED_MESSAGES } from "./CannedMessages";

const BUTTON_COUNTDOWN_TIME = 10000; // ms;

export function AccountWarning() {
const user = useUser();
const location = useLocation();
const [warning, setWarning] = React.useState(null);
const [acceptTimer, setAcceptTimer] = React.useState(null);
const [boxChecked, setBoxChecked] = React.useState(false);
const [warning, setWarning] = React.useState<rest_api.warnings.Warning>(null);

React.useEffect(() => {
if (user && !user.anonymous && user.has_active_warning_flag) {
Expand All @@ -38,14 +42,6 @@ export function AccountWarning() {
console.log(warning);
if (Object.keys(warning).length > 0) {
setWarning(warning);

const now = Date.now();
const interval = setInterval(() => {
setAcceptTimer(BUTTON_COUNTDOWN_TIME - (Date.now() - now));
if (Date.now() - now > BUTTON_COUNTDOWN_TIME) {
clearInterval(interval);
}
}, 100);
} else {
setWarning(null);
}
Expand Down Expand Up @@ -75,15 +71,87 @@ export function AccountWarning() {
void patch(`me/warning/${warning.id}`, { accept: true });
};

const Renderers = {
warning: WarningModal,
acknowledgement: AckModal,
};

const MessageRenderer = Renderers[warning.severity];

return (
<>
<MessageRenderer warning={warning} accept={ok} />
</>
);
}

// Support warnings that carry messages either as a reference to a a canned message, or explicit text...

interface MessageTextRenderProps {
warning: rest_api.warnings.Warning;
}
function MessageTextRender(props: MessageTextRenderProps): JSX.Element {
console.log("rendering", props);
if (props.warning.message_id) {
return (
<div>{CANNED_MESSAGES[props.warning.message_id](props.warning.interpolation_data)}</div>
);
} else {
return (
<AutoTranslate
source={props.warning.text.trim()}
source_language={"en"}
markdown={true}
/>
);
}
}

// Support "warnings" that should be displayed differently depending on severity...

interface WarningModalProps {
warning: rest_api.warnings.Warning;
accept: () => void;
}

function AckModal(props: WarningModalProps): JSX.Element {
return (
<>
<div className="AccountWarning-backdrop" />
<div className="AccountWarningAck">
<MessageTextRender warning={props.warning} />
<div className="space" />
<div className="buttons">
<button className="primary" onClick={props.accept}>
{_("OK")}
</button>
</div>
</div>
</>
);
}

function WarningModal(props: WarningModalProps): JSX.Element {
const [acceptTimer, setAcceptTimer] = React.useState(null);
const [boxChecked, setBoxChecked] = React.useState(false);

React.useEffect(() => {
if (props.warning) {
const now = Date.now();
const interval = setInterval(() => {
setAcceptTimer(BUTTON_COUNTDOWN_TIME - (Date.now() - now));
if (Date.now() - now > BUTTON_COUNTDOWN_TIME) {
clearInterval(interval);
}
}, 100);
}
}, [props.warning]);

return (
<>
<div className="AccountWarning-backdrop" />
<div className="AccountWarning">
<AutoTranslate
source={warning.text.trim()}
source_language={"en"}
markdown={true}
/>
<MessageTextRender warning={props.warning} />
<div className="space" />
<div className="buttons">
<input
Expand All @@ -102,7 +170,7 @@ export function AccountWarning() {
<button
className="primary"
disabled={acceptTimer > 0 || !boxChecked}
onClick={ok}
onClick={props.accept}
>
{_("OK") +
(acceptTimer > 0 ? " (" + Math.ceil(acceptTimer / 1000) + ")" : "")}
Expand Down
95 changes: 95 additions & 0 deletions src/components/AccountWarning/CannedMessages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (C) Online-Go.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { _, interpolate } from "translate";

export const CANNED_MESSAGES: rest_api.warnings.WarningMessages = {
warn_beginner_score_cheat: (game_id) =>
interpolate(
_(`
It appears that you delayed the end of game #{{game_id}}, by clicking
on the board to change the score incorrectly. This can frustrate your
opponent and prevent them from moving on to the next game.
Since you are a new player, no action will be taken against
your account. We simply ask that you learn when to end a game.
Until you develop the experience to judge better, if your
opponent passes and there are no open borders between your
stones then you should also pass.
After passing, promptly accept the correct score.
If in doubt about this sort of situation. please ask for help in chat
or the forums.`),
{ game_id },
),
warn_score_cheat: (game_id) =>
interpolate(
_(`
Our records show that you attempted to illegally change the score at the end of
game #{{game_id}}. This is a form of cheating and is prohibited by the
OGS Terms of Service.
https://online-go.com/docs/terms-of-service
We ask that you end your games properly by accepting the correct score
immediately after passing. Further instances of score cheating will result
in suspension of your account.`),
{ game_id },
),
ack_educated_beginner_score_cheat: (reported) =>
interpolate(
_(`
Thanks for the report about {{reported}}. It seems you were playing against a
complete beginner - we have tried to explain that games should
be ended correctly, to pass when their opponent passes, and to accept promptly,
trusting the auto-score.`),
{ reported },
),
ack_educated_beginner_score_cheat_and_annul: (reported) =>
interpolate(
_(`
Thanks for the report about {{reported}}. It seems you were playing against a
complete beginner - we have tried to explain that games should
be ended correctly, to pass when their opponent passes, and to accept promptly,
trusting the auto-score. That incorrectly scored game has been annulled.`),
{ reported },
),
ack_warned_score_cheat: (reported) =>
interpolate(
_(`
Thank you for your report, {{reported}} has been given a formal warning about scoring properly.`),
{ reported },
),
ack_warned_score_cheat_and_annul: (reported) =>
interpolate(
_(`
Thank you for your report, {{reported}} has been given a formal warning about scoring properly, and that cheated game annulled.`),
reported,
),
no_score_cheating_evident: (reported) =>
interpolate(
_(`
Thank you for bringing the possible instance of score cheating by {{reported}} to
our attention. We looked into the report and their actions seemed approprate. If a pattern of
complaints emerges, we will investigate further.
Thank you for helping keep OGS enjoyable for everyone. We appreciate it.`),
{ reported },
),
};
5 changes: 5 additions & 0 deletions src/components/ModerateUser/ModerateUser.styl
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,9 @@
#ui-class-extra {
width: 5rem;
}

// We'll probably lay these out better when there are more of them.
.avoid-wrap {
font-size: smaller;
}
}
21 changes: 11 additions & 10 deletions src/components/ModerateUser/ModerateUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ import * as React from "react";
import * as data from "data";
import { _ } from "translate";
import { put, get, del } from "requests";
import { errorAlerter } from "misc";
import { MOD_POWER_HANDLE_SCORE_CHEAT, errorAlerter } from "misc";
import { proRankList } from "rank_utils";
import { Modal, openModal } from "Modal";
import { lookup } from "player_cache";

import { MOD_POWER_ANNUL } from "misc";

interface Events {}

interface ModerateUserProperties {
Expand All @@ -52,7 +50,8 @@ export class ModerateUser extends Modal<Events, ModerateUserProperties, any> {
this.setState(
Object.assign({ loading: false }, result.user, {
bot_owner: result.user.bot_owner ? result.user.bot_owner.id : null,
can_annul: result.user.moderator_powers & MOD_POWER_ANNUL,
can_handle_score_cheat:
result.user.moderator_powers & MOD_POWER_HANDLE_SCORE_CHEAT,
}),
);
})
Expand Down Expand Up @@ -95,9 +94,9 @@ export class ModerateUser extends Modal<Events, ModerateUserProperties, any> {
settings[f] = this.state[f];
}

// Can-annul is bit zero of moderator_powers
// handle_score_cheat is bit zero of moderator_powers
settings["moderator_powers"] =
(this.state.moderator_powers & ~1) | this.state.can_annul;
(this.state.moderator_powers & ~1) | this.state.can_handle_score_cheat;

settings.moderation_note = reason;

Expand All @@ -111,7 +110,7 @@ export class ModerateUser extends Modal<Events, ModerateUserProperties, any> {
setLockedUsername = (ev) => this.setState({ locked_username: ev.target.checked });
setSupporter = (ev) => this.setState({ supporter: ev.target.checked });
setAnnouncer = (ev) => this.setState({ is_announcer: ev.target.checked });
setCanAnnul = (ev) => this.setState({ can_annul: ev.target.checked });
setHandleScoreCheat = (ev) => this.setState({ can_handle_score_cheat: ev.target.checked });
setProfessional = (ev) => this.setState({ professional: ev.target.checked });
//setBanned = (ev) => this.setState({ is_banned: ev.target.checked });
setShadowbanned = (ev) => this.setState({ is_shadowbanned: ev.target.checked });
Expand Down Expand Up @@ -231,14 +230,16 @@ export class ModerateUser extends Modal<Events, ModerateUserProperties, any> {
)}
</dd>
<dt>
<label htmlFor="annul">Can Annul</label>
<label className="avoid-wrap" htmlFor="annul">
Handle Score Cheat
</label>
</dt>
<dd>
<input
id="annul"
type="checkbox"
checked={this.state.can_annul}
onChange={this.setCanAnnul}
checked={this.state.can_handle_score_cheat}
onChange={this.setHandleScoreCheat}
/>
</dd>
</dl>
Expand Down
Loading

0 comments on commit 11cd600

Please sign in to comment.