Skip to content

Add the Mandatory Close Duration plugin. #16

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Modmail plugins for our Modmail bot, located at https://github.com/kyb3r/modmail
## List of plugins
- **Ban appeals**: Threads created by users in a defined "appeal" guild get moved to a configured appeal category. This also both kicks users from the appeal guild when they rejoin the main guild, and kicks users from the appeal guild if they're not banned in the main guild.
- **Close message:** Add a `?closemessage` command that will close the thread after 15 minutes with a default message.
- **Mandatory Close Duration** Makes the `?close` command require confirmation if a time duration isn't provided when closing with a custom close message.
- **MDLink**: Generate a ready to paste link to the thread logs.
- **Ping manager**: Delay pings by a configurable time period, cancel the ping task if a message is sent in the thread (Other than an author message).
- **Reply cooldown**: Forbid you from sending the same message twice in ten seconds.
Expand Down
98 changes: 98 additions & 0 deletions mandatory_close_duration/mandatory_close_duration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import asyncio

import discord
from discord.ext import commands

from bot import ModmailBot
from core import time


async def close_after_confirmation(ctx: commands.Context, converted_arg: time.UserFriendlyTime) -> None:
"""
Send a message and allow users to react to it to close the thread.

The reaction times out after 5 minutes.
"""
unicode_reaction = '\N{WHITE HEAVY CHECK MARK}'
warning_message = ("\N{WARNING SIGN} A time duration wasn't provided, reacting to this message will close"
" this thread instantly with the provided custom close message.")

message = await ctx.send(warning_message)
await message.add_reaction(unicode_reaction)

def checkmark_press_check(reaction: discord.Reaction, user: discord.User) -> bool:
is_right_reaction = (
user != ctx.bot.user
and reaction.message.id == message.id
and str(reaction.emoji) == unicode_reaction
)

return is_right_reaction

try:
await ctx.bot.wait_for('reaction_add', check=checkmark_press_check, timeout=5 * 60)
except asyncio.TimeoutError:
try:
await message.edit(content=message.content+'\n\n**Timed out.**')
await message.clear_reactions()
except discord.NotFound:
# The thread might have been closed by now.
pass
else:
await original_close_command(ctx, after=converted_arg)


async def safe_close(
self: time.UserFriendlyTime,
ctx: commands.Context,
*,
after: time.UserFriendlyTime = None
) -> None:
"""
Close the current thread.

Unlike the original close command, confirmation is awaited when
a time duration isn't provided but a custom close message is.
"""
modifiers = {'silently', 'silent', 'cancel'}

argument_passed = after is not None

if argument_passed:
not_a_modifier = after.arg not in modifiers

# These changes are always made to the argument by the super
# class so they need to be replicated before the raw argument
# is compared with the parsed message.
stripped_argument = after.raw.strip()
argument_without_phrases = stripped_argument.removeprefix('in ').removesuffix(' from now')

no_duration = after.arg == argument_without_phrases

if argument_passed and not_a_modifier and no_duration:
# Ask for confirmation since only a close message was provided.
await close_after_confirmation(ctx, after)
else:
await original_close_command(ctx, after=after)


def setup(bot: ModmailBot) -> None:
"""
Monkey patch the close command's callback to safe_close.

The help text is also updated to reflect the new behaviour.
"""
global original_close_command

command = bot.get_command('close')
original_close_command = command.copy()
original_close_command.cog = command.cog

command.callback = safe_close
command.help += '\n\n*Note: A time duration should be provided when closing with a custom message.*'


def teardown(bot: ModmailBot) -> None:
"""Restore the original close command."""
bot.remove_command('close')
bot.add_command(original_close_command)