From eaafef0cea62993663c572b007deb5935cb034dc Mon Sep 17 00:00:00 2001 From: Farhad Jay Date: Sun, 15 Mar 2026 20:50:02 -0700 Subject: [PATCH] Add mute reason support The mute command now accepts an optional reason after the username, matching the ban command's behavior. Updated the regex to support reason capture and perm duration keyword. --- lib/commands/implementations/mute.js | 14 ++--- .../lib/commands/implementations/mute.test.js | 51 +++++++++++++++++++ 2 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 tests/lib/commands/implementations/mute.test.js diff --git a/lib/commands/implementations/mute.js b/lib/commands/implementations/mute.js index ee93fe6..5411f7c 100644 --- a/lib/commands/implementations/mute.js +++ b/lib/commands/implementations/mute.js @@ -15,16 +15,16 @@ function mute(input, services) { ); } parsedInput.forEach((result) => { - const { userToPunish, parsedDuration } = result; + const { userToPunish, parsedDuration, parsedReason } = result; + const durationStr = formatDuration(moment.duration(parsedDuration, 'seconds')); + const reason = parsedReason === null + ? `Muting: ${userToPunish} for ${durationStr}` + : `Muting: ${userToPunish} for ${durationStr}. Reason: ${parsedReason}`; services.punishmentStream.write( - makeMute( - userToPunish, - parsedDuration, - `Muting: ${userToPunish} for ${formatDuration(moment.duration(parsedDuration, 'seconds'))}`, - ), + makeMute(userToPunish, parsedDuration, reason), ); }); return new CommandOutput(null, null); } -module.exports = new Command(mute, false, true, /(\d+[HMDSWwhmds])?\s?(\w+)/, null); +module.exports = new Command(mute, false, true, /((?:\d+[HMDSWwhmds])|(?:perm))?\s?(\w+)(?:\s(.*))?/, null); diff --git a/tests/lib/commands/implementations/mute.test.js b/tests/lib/commands/implementations/mute.test.js new file mode 100644 index 0000000..6b54cb1 --- /dev/null +++ b/tests/lib/commands/implementations/mute.test.js @@ -0,0 +1,51 @@ +const assert = require('assert'); +const sinon = require('sinon'); +const mute = require('../../../../lib/commands/implementations/mute'); + +describe('Mute command', () => { + beforeEach(() => { + this.mockServices = { + punishmentStream: { + write: sinon.spy(), + }, + }; + }); + + it('mutes a user with duration and no reason', () => { + mute.work('10m TestUser', this.mockServices); + assert(this.mockServices.punishmentStream.write.calledOnce); + const call = this.mockServices.punishmentStream.write.firstCall.args[0]; + assert.strictEqual(call.user, 'testuser'); + assert.strictEqual(call.duration, 600); + assert.strictEqual(call.type, 'mute'); + assert(call.reason.includes('Muting: testuser for')); + assert(!call.reason.includes('Reason:')); + }); + + it('mutes a user with duration and reason', () => { + mute.work('10m TestUser being annoying', this.mockServices); + assert(this.mockServices.punishmentStream.write.calledOnce); + const call = this.mockServices.punishmentStream.write.firstCall.args[0]; + assert.strictEqual(call.user, 'testuser'); + assert.strictEqual(call.duration, 600); + assert.strictEqual(call.type, 'mute'); + assert(call.reason.includes('Reason: being annoying')); + }); + + it('mutes a user with default duration when none provided', () => { + mute.work('TestUser', this.mockServices); + assert(this.mockServices.punishmentStream.write.calledOnce); + const call = this.mockServices.punishmentStream.write.firstCall.args[0]; + assert.strictEqual(call.user, 'testuser'); + assert.strictEqual(call.duration, 600); + assert.strictEqual(call.type, 'mute'); + }); + + it('mutes a user with default duration and reason', () => { + mute.work('TestUser spamming links', this.mockServices); + assert(this.mockServices.punishmentStream.write.calledOnce); + const call = this.mockServices.punishmentStream.write.firstCall.args[0]; + assert.strictEqual(call.user, 'testuser'); + assert(call.reason.includes('Reason: spamming links')); + }); +});