feat: post auto flagging on downvotes #10029 (#10367)

* feat: post auto flagging on downvotes

* fix: just get one admin
isekai-main
gasoved 3 years ago committed by GitHub
parent 56345777ce
commit 62187caa67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -87,6 +87,7 @@
"min:rep:aboutme": 0,
"min:rep:signature": 0,
"flags:limitPerTarget": 0,
"flags:autoFlagOnDownvoteThreshold": 0,
"notificationType_upvote": "notification",
"notificationType_new-topic": "notification",
"notificationType_new-reply": "notification",

@ -18,5 +18,6 @@
"flags.limit-per-target": "Maximum number of times something can be flagged",
"flags.limit-per-target-placeholder": "Default: 0",
"flags.limit-per-target-help": "When a post or user is flagged multiple times, each additional flag is considered a "report" and added to the original flag. Set this option to a number other than zero to limit the number of reports an item can receive.",
"flags.auto-flag-on-downvote-threshold": "Number of downvotes to auto flag posts (Set to 0 to disable, default: 0)",
"flags.auto-resolve-on-ban": "Automatically resolve all of a user's tickets when they are banned"
}

@ -81,5 +81,6 @@
"bulk-actions": "Bulk Actions",
"bulk-resolve": "Resolve Flag(s)",
"bulk-success": "%1 flags updated",
"flagged-timeago-readable": "Flagged <span class=\"timeago\" title=\"%1\"></span> (%2)"
"flagged-timeago-readable": "Flagged <span class=\"timeago\" title=\"%1\"></span> (%2)",
"auto-flagged": "[Auto Flagged] Received %1 downvotes."
}

@ -101,7 +101,7 @@ async function execute(cmd, args) {
function UserCmdHelpers() {
async function getAdminUidOrFail() {
const adminUid = (await db.getSortedSetMembers('group:administrators:members')).reverse()[0];
const adminUid = await user.getFirstAdminUid();
if (!adminUid) {
const err = new Error('An admin account does not exists to execute the operation.');
err.name = 'UserError';

@ -377,7 +377,7 @@ Flags.deleteNote = async function (flagId, datetime) {
await db.sortedSetRemove(`flag:${flagId}:notes`, note[0]);
};
Flags.create = async function (type, id, uid, reason, timestamp) {
Flags.create = async function (type, id, uid, reason, timestamp, forceFlag = false) {
let doHistoryAppend = false;
if (!timestamp) {
timestamp = Date.now();
@ -387,14 +387,14 @@ Flags.create = async function (type, id, uid, reason, timestamp) {
// Sanity checks
Flags.exists(type, id, uid),
Flags.targetExists(type, id),
Flags.canFlag(type, id, uid),
Flags.canFlag(type, id, uid, forceFlag),
Flags.targetFlagged(type, id),
// Extra data for zset insertion
Flags.getTargetUid(type, id),
Flags.getTargetCid(type, id),
]);
if (flagExists) {
if (!forceFlag && flagExists) {
throw new Error(`[[error:${type}-already-flagged]]`);
} else if (!targetExists) {
throw new Error('[[error:invalid-data]]');
@ -499,9 +499,9 @@ Flags.exists = async function (type, id, uid) {
return await db.isSortedSetMember('flags:hash', [type, id, uid].join(':'));
};
Flags.canFlag = async function (type, id, uid) {
Flags.canFlag = async function (type, id, uid, skipLimitCheck = false) {
const limit = meta.config['flags:limitPerTarget'];
if (limit > 0) {
if (!skipLimitCheck && limit > 0) {
const score = await db.sortedSetScore('flags:byTarget', `${type}:${id}`);
if (score >= limit) {
throw new Error(`[[error:${type}-flagged-too-many-times]]`);
@ -729,7 +729,7 @@ Flags.appendNote = async function (flagId, uid, note, datetime) {
});
};
Flags.notify = async function (flagObj, uid) {
Flags.notify = async function (flagObj, uid, notifySelf = false) {
const [admins, globalMods] = await Promise.all([
groups.getMembers('administrators', 0, -1),
groups.getMembers('Global Moderators', 0, -1),
@ -780,7 +780,9 @@ Flags.notify = async function (flagObj, uid) {
from: uid,
to: uids,
});
uids = uids.filter(_uid => parseInt(_uid, 10) !== parseInt(uid, 10));
if (!notifySelf) {
uids = uids.filter(_uid => parseInt(_uid, 10) !== parseInt(uid, 10));
}
await notifications.push(notifObj, uids);
};

@ -2,10 +2,12 @@
const meta = require('../meta');
const db = require('../database');
const flags = require('../flags');
const user = require('../user');
const topics = require('../topics');
const plugins = require('../plugins');
const privileges = require('../privileges');
const translator = require('../translator');
module.exports = function (Posts) {
const votesInProgress = {};
@ -243,6 +245,13 @@ module.exports = function (Posts) {
if (!postData || !postData.pid || !postData.tid) {
return;
}
const threshold = meta.config['flags:autoFlagOnDownvoteThreshold'];
if (threshold && postData.votes <= (-threshold)) {
const adminUid = await user.getFirstAdminUid();
const reportMsg = await translator.translate(`[[flags:auto-flagged, ${-postData.votes}]]`);
const flagObj = await flags.create('post', postData.pid, adminUid, reportMsg, null, true);
await flags.notify(flagObj, adminUid, true);
}
await Promise.all([
updateTopicVoteCount(postData),
db.sortedSetAdd('posts:votes', postData.votes, postData.pid),

@ -211,6 +211,10 @@ User.getAdminsandGlobalModsandModerators = async function () {
return await User.getUsersData(_.union(...results));
};
User.getFirstAdminUid = async function () {
return (await db.getSortedSetRange('group:administrators:members', 0, 0))[0];
};
User.getModeratorUids = async function () {
const cids = await categories.getAllCidsFromSet('categories:cid');
const uids = await categories.getModeratorUids(cids);

@ -82,6 +82,10 @@
[[admin/settings/reputation:flags.limit-per-target-help]]
</p>
</div>
<div class="form-group">
<label for="flags:autoFlagOnDownvoteThreshold">[[admin/settings/reputation:flags.auto-flag-on-downvote-threshold]]</label>
<input type="text" class="form-control" placeholder="0" data-field="flags:autoFlagOnDownvoteThreshold" id="flags:autoFlagOnDownvoteThreshold">
</div>
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input type="checkbox" class="mdl-switch__input" data-field="flags:autoResolveOnBan">

Loading…
Cancel
Save