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