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

Score cheat community moderator power #2369

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
53493ea
Work in progress - updated previous functionality around moderator po…
GreenAsJade Sep 18, 2023
1730913
wip: send voted action to the back end
GreenAsJade Sep 23, 2023
5a95132
Merge branch 'devel' into score_cheat_community_moderator_power
GreenAsJade Sep 23, 2023
2ad8a85
wip: disappear reports for community moderators when they've voted o…
GreenAsJade Sep 24, 2023
1fbaca8
wip: small refactor to make the moderator action `vote` use the same …
GreenAsJade Sep 27, 2023
a01d556
Bugfix: make the report go away when it has been voted on
GreenAsJade Sep 27, 2023
a8f7fd7
Fix undefined report.voters
GreenAsJade Sep 27, 2023
9fd1193
Don't apply "daily report goals" to community moderators
GreenAsJade Sep 27, 2023
8068ef0
Make selecting an action claim the report.
GreenAsJade Sep 27, 2023
2a493dd
wip: Handling for the currently-defined canned messages for warning/a…
GreenAsJade Sep 30, 2023
d436df5
wip: Community moderators just vote, they don't claim.
GreenAsJade Sep 30, 2023
8bfc967
Gracefully handle ModNotes that have no moderator (community moderati…
GreenAsJade Oct 1, 2023
79dd0e5
Merge branch 'devel' into score_cheat_community_moderator_power
GreenAsJade Oct 1, 2023
acb2f62
Make it easier to see which are resolved, given that they can be reso…
GreenAsJade Oct 1, 2023
f6fd9d8
Layout tweak
GreenAsJade Oct 1, 2023
33b98f7
Merge commit '35ecee6b12bf5e6b4cfc0909e3d212b847e51208' into score_ch…
GreenAsJade Oct 6, 2023
4c8a52c
Merge commit '2e9ccea12b16fefeba8fb86e0312875964e16857' into score_ch…
GreenAsJade Oct 17, 2023
f84dfdd
Don't hand non-escalated score cheating reports to full moderators
GreenAsJade Oct 17, 2023
8823e1a
Merge commit '9e79564feede8059f8ec28da7abbcd288f162186' into score_ch…
GreenAsJade Oct 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading