From 16f303622bd880e9ace57d1ef024e84d80c79c04 Mon Sep 17 00:00:00 2001 From: GingerPlusPlus Date: Fri, 31 May 2019 13:36:37 +0200 Subject: [PATCH] Allow to remove any warn by specifying its date --- handlers/commands/unwarn.js | 39 +++++++++++++++++++++++++++++++++---- handlers/commands/user.js | 3 ++- stores/user.js | 4 ++-- utils/parse.js | 5 ++--- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/handlers/commands/unwarn.js b/handlers/commands/unwarn.js index f12a943..289340f 100644 --- a/handlers/commands/unwarn.js +++ b/handlers/commands/unwarn.js @@ -1,6 +1,7 @@ 'use strict'; const { last } = require('ramda'); +const XRegExp = require('xregexp'); // Utils const { escapeHtml, link, scheduleDeletion } = require('../../utils/tg'); @@ -18,10 +19,21 @@ const { getUser, unwarn } = require('../../stores/user'); const noop = Function.prototype; +const dateRegex = XRegExp.tag('nix')`^ + \d{4} # year + -\d{2} # month + (-\d{2} # day + ([T\s]\d{2} # hour + (:\d{2} # min + (:\d{2} # sec + (.\d{3}Z? # ms + )?)?)?)?)? +$`; + const unwarnHandler = async ({ from, message, reply, telegram }) => { if (!from || from.status !== 'admin') return null; - const { targets } = parse(message); + const { reason, targets } = parse(message); if (targets.length !== 1) { return reply( @@ -55,7 +67,28 @@ const unwarnHandler = async ({ from, message, reply, telegram }) => { telegram.unbanChatMember(group.id, userToUnwarn.id)); } - await unwarn(userToUnwarn); + let lastWarn; + if (!reason) { + lastWarn = last(allWarns); + } else if (dateRegex.test(reason)) { + const normalized = reason.replace(' ', 'T').toUpperCase(); + lastWarn = allWarns.find(({ date }) => + date && date.toISOString().startsWith(normalized)); + } else { + return reply( + '⚠ Invalid date', + replyOptions + ).then(scheduleDeletion()); + } + + if (!lastWarn) { + return reply( + '❓ 404: Warn not found', + replyOptions + ).then(scheduleDeletion()); + } + + await unwarn(userToUnwarn, lastWarn); if (userToUnwarn.status === 'banned') { telegram.sendMessage( @@ -68,8 +101,6 @@ const unwarnHandler = async ({ from, message, reply, telegram }) => { // (it's an expected, non-critical failure) } - const lastWarn = last(allWarns); - return reply( `❎ ${link(from)} pardoned ${link(userToUnwarn)} ` + `for:\n\n${escapeHtml(lastWarn.reason || lastWarn)}` + diff --git a/handlers/commands/user.js b/handlers/commands/user.js index f993df8..65febea 100644 --- a/handlers/commands/user.js +++ b/handlers/commands/user.js @@ -12,7 +12,8 @@ const { getUser } = require('../../stores/user'); const html = require('tg-html'); -const formatDate = date => date && date.toISOString().slice(0, 10); +const formatDate = date => + date && date.toISOString().slice(0, -5).replace('T', ' '); const formatEntry = async (entry, defaultVal) => { if (!entry || !entry.by_id) return defaultVal; diff --git a/stores/user.js b/stores/user.js index c579256..ea32f63 100644 --- a/stores/user.js +++ b/stores/user.js @@ -146,11 +146,11 @@ const warn = ({ id }, reason) => { returnUpdatedDocs: true } ).then(getUpdatedDocument); -const unwarn = ({ id }) => +const unwarn = ({ id }, warnQuery) => User.update( { id }, { - $pop: { warns: 1 }, + $pull: { warns: warnQuery }, $set: { status: 'member' }, $unset: { ban_details: true, ban_reason: true }, } diff --git a/utils/parse.js b/utils/parse.js index 8682504..a63db32 100644 --- a/utils/parse.js +++ b/utils/parse.js @@ -18,13 +18,12 @@ const botReply = ({ from, entities = [] }) => { }; const parse = message => { - // eslint-disable-next-line no-empty-character-class - const regex = /^\/\w+(?:@\w+)?((?:@\w+|\d+|\s+)*)(.*)$/s; + const regex = /^\/\w+(?:@\w+)?((?:@\w+|\d+|\s+)*)(?:\s+(.*))?$/s; const textMentions = message.entities.filter(isTextMention); const noTextMentions = textMentions.reduceRight(spliceOut, message.text); - const [ , ids, reason ] = regex.exec(noTextMentions); + const [ , ids, reason = '' ] = regex.exec(noTextMentions); const users = textMentions.concat(ids.match(/@\w+|\d+/g) || []); const { reply_to_message } = message; const targets = users.length