def get_unbanish_duration( ctx: Context, template_engine: TemplateEngine, args: Tuple[str, ...] ) -> Tuple[Optional[datetime.timedelta], Optional[datetime.datetime]]: """Return appropriate duration and end date for a banish.""" duration, end_date = time.extract_time(args) if duration is None: forever = any([arg.lower() in forever_words for arg in args]) if not forever: command_type = template_engine.get_command_type(ctx.invoked_with) current_time = datetime.datetime.now() try: if command_type == MuteCommandType.MICRO: duration = datetime.timedelta(seconds=10) elif command_type == MuteCommandType.SUPER: duration = datetime.timedelta(weeks=1) elif command_type == MuteCommandType.MEGA: duration = datetime.timedelta(days=365) else: duration = datetime.timedelta(minutes=5) end_date = current_time + duration except OverflowError: end_date = datetime.datetime.max duration = end_date - current_time return duration, end_date
async def run_command(ctx: Context, coginfo: CogInfo, template_engine: TemplateEngine, args: Tuple[str, ...]) -> None: """Interpret the command and delegate task to the appropriate sub-function.""" if template_engine.get_command_type( ctx.invoked_with) == MuteCommandType.UNDO: msg = await attempt_unbanish(ctx, coginfo, template_engine) await ctx.send(msg) else: await banish(ctx, coginfo, template_engine, args)
async def attempt_unbanish(ctx: Context, coginfo: CogInfo, template_engine: TemplateEngine) -> str: """Undo one or more banishes.""" if coginfo.bot: bot: MrFreeze = coginfo.bot mentions = ctx.message.mentions victims = [ u for u in mentions if not u.guild_permissions.administrator and u != bot.user ] success_list: List[Member] = list() fails_list: List[Member] = list() success_string = "" fails_string = "" error_string = "" http_exception = False forbidden_exception = False other_exception = False for victim in victims: error = await mute_db.carry_out_unbanish(bot, victim, logger) if isinstance(error, Exception): fails_list.append(victim) if isinstance(error, discord.HTTPException): http_exception = True elif isinstance(error, discord.Forbidden): forbidden_exception = True else: other_exception = True else: success_list.append(victim) success_string = default.mentions_list(success_list) fails_string = default.mentions_list(fails_list) error_string = get_error_string(http_exception, forbidden_exception, other_exception) template = get_mute_response_type(success_list, fails_list, undo=True) logger.debug(f"attempt_banish(): Setting template to {template}") response_template = template_engine.get_template(ctx.invoked_with, template) if response_template: response = response_template.substitute( author=ctx.author.mention, victims=success_string, fails=fails_string, errors=error_string, ) return f"{ctx.author.mention} {response}" else: return f"{ctx.author.mention} Something went wrong, I'm literally at a loss for words."
async def banish(ctx: Context, coginfo: CogInfo, template_engine: TemplateEngine, args: Tuple[str, ...]) -> None: """Carry out one or more banishes.""" if coginfo.bot: bot: MrFreeze = coginfo.bot else: raise InsufficientCogInfo() # Parse targetted users bot_mentioned = bot.user in ctx.message.mentions self_mentioned = ctx.author in ctx.message.mentions mentions = ctx.message.mentions mods = [ u for u in mentions if u.guild_permissions.administrator and u != bot.user ] users = [ u for u in mentions if not u.guild_permissions.administrator and u != bot.user ] if bot_mentioned or self_mentioned or mods: # Illegal banish, prepare silly response. if bot_mentioned and len(mentions) == 1: logger.debug("Setting template to MuteResponseType.FREEZE") template = MuteResponseType.FREEZE fails_list = [bot.user] elif bot_mentioned and self_mentioned and len(mentions) == 2: logger.debug("Setting template to MuteResponseType.FREEZE_SELF") template = MuteResponseType.FREEZE_SELF fails_list = [bot.user, ctx.author] elif bot_mentioned: logger.debug("Setting template to MuteResponseType.FREEZE_OTHERS") template = MuteResponseType.FREEZE_OTHERS fails_list = mods + users elif self_mentioned and len(mentions) == 1: logger.debug("Setting template to MuteResponseType.SELF") template = MuteResponseType.SELF fails_list = mods elif mods and len(mentions) == 1: logger.debug("Setting template to MuteResponseType.MOD") template = MuteResponseType.MOD fails_list = mods elif mods: logger.debug("Setting template to MuteResponseType.MODS") template = MuteResponseType.MODS fails_list = mods else: logger.warn("Setting template to MuteResponseType.INVALID") logger.warn( f"bot_mentioned={bot_mentioned}, self_mentioned={self_mentioned}" ) logger.warn(f"{len(mentions)} mentions={mentions}") logger.warn(f"{len(mods)} mods={mods}") logger.warn(f"{len(users)} users={users}") template = MuteResponseType.INVALID fails_list = mentions banish_template = template_engine.get_template(ctx.invoked_with, template) mention_fails = default.mentions_list(fails_list) if banish_template: msg = banish_template.substitute(author=ctx.author.mention, fails=mention_fails) await ctx.send(msg) else: # Legal banish, attempt banish. msg = await attempt_banish(ctx, coginfo, template_engine, users, args) await ctx.send(msg)
async def attempt_banish(ctx: Context, coginfo: CogInfo, template_engine: TemplateEngine, victims: List[Member], args: Tuple[str, ...]) -> str: """Attempt to carry banish some people, then return an appropriate response.""" if coginfo.bot: bot: MrFreeze = coginfo.bot success_list: List[Member] = list() fails_list: List[Member] = list() success_string = "" fails_string = "" error_string = "" http_exception = False forbidden_exception = False other_exception = False duration, end_date = get_unbanish_duration(ctx, template_engine, args) for victim in victims: error = await mute_db.carry_out_banish(bot, victim, logger, end_date) if isinstance(error, Exception): fails_list.append(victim) if isinstance(error, discord.HTTPException): http_exception = True elif isinstance(error, discord.Forbidden): forbidden_exception = True else: other_exception = True else: success_list.append(victim) success_string = default.mentions_list(success_list) fails_string = default.mentions_list(fails_list) error_string = get_error_string(http_exception, forbidden_exception, other_exception) template = get_mute_response_type(success_list, fails_list) logger.debug(f"attempt_banish(): Setting template to {template}") timestamp_template = template_engine.get_template( ctx.invoked_with, MuteResponseType.TIMESTAMP) if timestamp_template: timestamp = timestamp_template.substitute( duration=time.parse_timedelta(duration)) else: logger.warn( f"template_engine.get_template({ctx.invoked_with}, TIMESTAMP) returned None!" ) timestamp = "" response_template = template_engine.get_template(ctx.invoked_with, template) if response_template: response = response_template.substitute(author=ctx.author.mention, victims=success_string, fails=fails_string, errors=error_string, timestamp=timestamp) return f"{ctx.author.mention} {response}" else: return f"{ctx.author.mention} Something went wrong, I'm literally at a loss for words."
async def run_command(ctx: Context, coginfo: CogInfo, template_engine: TemplateEngine, error: Exception) -> None: """ Trigger on unauthorized banish, i.e. when a non-administrator try to banish people. When _banish() encounters an error this method is automatically triggered. If the error is an instance of discord.ext.commands.CheckFailure the user will be punished accordingly, if not the error is raised again. There are four relevant templates that can be used when sending the response. USER_NONE User invoked mute with no arguments USER_SELF User tried muting themselves USER_USER User tried muting other user(s) USER_MIXED User tried musing themselves and other user(s) """ if coginfo.bot and coginfo.default_self_mute_time and coginfo.logger: bot: MrFreeze = coginfo.bot default_self_mute_time: int = coginfo.default_self_mute_time logger: Logger = coginfo.logger else: raise InsufficientCogInfo if not isinstance(error, CheckFailure): # Only run this on Check Failure. return mentions = ctx.message.mentions author = ctx.author server = ctx.guild none = (len(mentions) == 0) selfmute = (len(mentions) == 1 and author in mentions) mix = (not selfmute and author in mentions) user = (not selfmute and not mix and len(mentions) > 0) fails = default.mentions_list( [mention for mention in mentions if mention != author]) if none: template = MuteResponseType.USER_NONE elif selfmute: template = MuteResponseType.USER_SELF elif user: template = MuteResponseType.USER_USER elif mix: template = MuteResponseType.USER_MIXED self_mute_time: int = bot.get_self_mute_time( server) or default_self_mute_time duration = datetime.timedelta(minutes=float(self_mute_time)) end_date = datetime.datetime.now() + duration duration = time.parse_timedelta(duration) # Carry out the banish with resulting end date banish_error = await mute_db.carry_out_banish(bot, author, logger, end_date) error_msg = "unspecified error" if isinstance(banish_error, Exception): if isinstance(banish_error, discord.Forbidden): error_msg = "**a lack of privilegies**" elif isinstance(banish_error, discord.HTTPException): error_msg = "**an HTTP exception**" else: error_msg = "**an unknown error**" template = MuteResponseType.USER_FAIL banish_template = template_engine.get_template(ctx.invoked_with, template) if banish_template: reply = banish_template.substitute(author=author.mention, fails=fails, errors=error_msg, timestamp=duration) await ctx.send(reply) else: reply = "I couldn't find an appropriate response, but anyway... you're not " reply += f"allowed to do that! Bad {ctx.author.mention}!" await ctx.send(reply)