text: import datetime
  from typing import Literal
  
  import discord
  import logging
  from redbot.core import Config, commands, checks, modlog
  from redbot.core.utils.chat_formatting import box, pagify
  
  
  log = logging.getLogger("red.aikaterna.dungeon")
  
  
  class Dungeon(commands.Cog):
      """Auto-quarantine suspicious users."""
  
      async def red_delete_data_for_user(
          self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int,
      ):
          if requester == "discord":
              # user is deleted, just comply
  
              data = await self.config.all_guilds()
              for guild_id, guild_data in data.items():
                  if user_id in guild_data.get("bypass", []):
                      bypass = guild_data.get("bypass", [])
                      bypass = set(bypass)
                      bypass.discard(user_id)
                      await self.config.guild_from_id(guild_id).bypass.set(list(bypass))
  
      def __init__(self, bot):
          self.bot = bot
          self.config = Config.get_conf(self, 2700081001, force_registration=True)
  
          default_guild = {
              "announce_channel": None,
              "auto_ban": False,
              "auto_ban_message": None,
              "auto_blacklist": False,
              "bypass": [],
              "dm_message": None,
              "dm_toggle": False,
              "dungeon_channel": None,
              "dungeon_role": None,
              "join_days": 7,
              "mod_log": False,
              "profile_toggle": False,
              "toggle": False,
              "user_role": None,
              "user_role_toggle": False,
          }
  
          self.config.register_guild(**default_guild)
  
      @checks.mod_or_permissions(administrator=True)
      @commands.command()
      async def banish(self, ctx, user: discord.Member):
          """Strip a user of their roles, apply the dungeon role, and blacklist them.
  		If the blacklist toggle is off, the user will not be blacklisted from the bot."""
          data = await self.config.guild(ctx.guild).all()
          blacklist = data["auto_blacklist"]
          dungeon_role_id = data["dungeon_role"]
          dungeon_role_obj = discord.utils.get(ctx.guild.roles, id=dungeon_role_id)
  
          if blacklist:
              # if you are reading this to learn, DON'T do this, there will be a real way
              # to interact with bot-owned data without touching config directly at some
              # point in the future after Red 3.2
              async with self.bot._config.blacklist() as blacklist_list:
                  if user.id not in blacklist_list:
                      blacklist_list.append(user.id)
  
          if not dungeon_role_obj:
              return await ctx.send("No dungeon role set.")
  
          try:
              await user.edit(roles=[], reason=f"Removing all roles, {ctx.message.author} is banishing user")
          except discord.Forbidden:
              return await ctx.send(
                  "I need permission to manage roles or the role hierarchy might not allow me to do this. I need a role higher than the person you're trying to banish."
              )
  
          await user.add_roles(dungeon_role_obj, reason=f"Adding dungeon role, {ctx.message.author} is banishing user")
  
          if blacklist:
              blacklist_msg = ", blacklisted from the bot,"
          else:
              blacklist_msg = ""
          msg = f"{user} has been sent to the dungeon{blacklist_msg} and has had all previous roles stripped."
          await ctx.send(msg)
  
      @commands.group(autohelp=True)
      @commands.guild_only()
      @checks.admin_or_permissions(manage_guild=True)
      async def dungeon(self, ctx):
          """Main dungeon commands."""
          pass
  
      @dungeon.command()
      async def announce(self, ctx, channel: discord.TextChannel):
          """Sets the announcement channel for users moved to the dungeon."""
          await self.config.guild(ctx.guild).announce_channel.set(channel.id)
          announce_channel_id = await self.config.guild(ctx.guild).announce_channel()
          await ctx.send(f"User announcement channel set to: {self.bot.get_channel(announce_channel_id).mention}.")
  
      @dungeon.command()
      async def autoban(self, ctx):
          """Toggle auto-banning users instead of sending them to the dungeon."""
          auto_ban = await self.config.guild(ctx.guild).auto_ban()
          await self.config.guild(ctx.guild).auto_ban.set(not auto_ban)
          await ctx.send(f"Auto-ban instead of sending to the dungeon: {not auto_ban}.")
  
      @dungeon.command()
      async def banmessage(self, ctx, *, ban_message=None):
          """Set the message to send on an autoban. If message is left blank, no message will be sent."""
          auto_ban = await self.config.guild(ctx.guild).auto_ban()
          if not ban_message:
              await self.config.guild(ctx.guild).auto_ban_message.set(None)
              return await ctx.send("Auto-ban message removed. No message will be sent on an auto-ban.")
          await self.config.guild(ctx.guild).auto_ban_message.set(str(ban_message))
          await self.config.guild(ctx.guild).auto_ban.set(True)
          await ctx.send(f"Auto-ban has been turned on.\nMessage to send on ban:\n{ban_message}")
  
      @dungeon.command()
      async def blacklist(self, ctx):
          """Toggle auto-blacklisting for the bot for users moved to the dungeon."""
          auto_blacklist = await self.config.guild(ctx.guild).auto_blacklist()
          await self.config.guild(ctx.guild).auto_blacklist.set(not auto_blacklist)
          await ctx.send(f"Auto-blacklisting dungeon users from the bot: {not auto_blacklist}.")
  
      @dungeon.command()
      @commands.cooldown(1, 5, discord.ext.commands.BucketType.guild)
      async def bypass(self, ctx, user_id: int):
          """Add a user ID to the bypass list - user will be able to join without restrictions."""
          if not 17 <= len(str(user_id)) >= 18:
              return await ctx.send("This doesn't look like a Discord user id.")
          bypass_ids = await self.config.guild(ctx.guild).bypass()
          if user_id in bypass_ids:
              return await ctx.send(f"**{user_id}** is already in the list of bypassed users.")
          bypass_ids.append(user_id)
          await self.config.guild(ctx.guild).bypass.set(bypass_ids)
          bypassed_user = await self.bot.fetch_user(user_id)
          await ctx.send(
              f"**{bypassed_user}** ({user_id}) has been added to the bypass list. The next time they attempt to join this server, they will bypass any Dungeon restrictions."
          )
  
      @dungeon.command()
      @commands.cooldown(1, 5, discord.ext.commands.BucketType.guild)
      async def bypasslist(self, ctx):
          """Show the current bypass list."""
          bypass_ids = await self.config.guild(ctx.guild).bypass()
          msg = "[Bypassed IDs]\n"
          if not bypass_ids:
              msg += "None."
          for id in bypass_ids:
              msg += f"{id}\n"
          for page in pagify(msg, delims=["\n"], page_length=1000):
              await ctx.send(box(msg, lang="ini"))
  
      @dungeon.command()
      async def channel(self, ctx, channel: discord.TextChannel):
          """Sets the text channel to use for the dungeon."""
          await self.config.guild(ctx.guild).dungeon_channel.set(channel.id)
          dungeon_channel_id = await self.config.guild(ctx.guild).dungeon_channel()
          await ctx.send(f"Dungeon channel set to: {self.bot.get_channel(dungeon_channel_id).name}.")
  
      @dungeon.command()
      async def dm(self, ctx, *, dm_message=None):
          """Set the message to send on successful verification.
          A blank message will turn off the DM setting."""
          if not dm_message:
              await self.config.guild(ctx.guild).dm_toggle.set(False)
              await self.config.guild(ctx.guild).dm_message.set(None)
              return await ctx.send("DM message on verification turned off.")
          await self.config.guild(ctx.guild).dm_message.set(str(dm_message))
          await self.config.guild(ctx.guild).dm_toggle.set(True)
          await ctx.send(f"DM message on verification turned on.\nMessage to send:\n{dm_message}")
  
      @dungeon.command()
      async def joindays(self, ctx, days: int):
          """Set how old an account needs to be a trusted user."""
          await self.config.guild(ctx.guild).join_days.set(days)
          await ctx.send(
              f"Users must have accounts older than {days} day(s) to be awarded the member role instead of the dungeon role on join."
          )
  
      @dungeon.command()
      async def modlog(self, ctx):
          """Toggle using the Red mod-log for auto-bans.
          The mod-log has to be configured to display bans for this to work."""
          mod_log = await self.config.guild(ctx.guild).mod_log()
          await self.config.guild(ctx.guild).mod_log.set(not mod_log)
          await ctx.send(f"Mod-log entry on auto-ban: {not mod_log}.")
  
      @dungeon.command()
      async def role(self, ctx, role_name: discord.Role):
          """Sets the role to use for the dungeon."""
          await self.config.guild(ctx.guild).dungeon_role.set(role_name.id)
          dungeon_role_id = await self.config.guild(ctx.guild).dungeon_role()
          dungeon_role_obj = discord.utils.get(ctx.guild.roles, id=dungeon_role_id)
          await ctx.send(f"Dungeon role set to: {dungeon_role_obj.name}.")
  
      @dungeon.command()
      async def toggle(self, ctx):
          """Toggle the dungeon on or off."""
          dungeon_enabled = await self.config.guild(ctx.guild).toggle()
          await self.config.guild(ctx.guild).toggle.set(not dungeon_enabled)
          await ctx.send(f"Dungeon enabled: {not dungeon_enabled}.")
  
      @dungeon.command()
      async def profiletoggle(self, ctx):
          """Toggles flagging accounts that have a default profile pic.
          Accounts that are over the join days threshold will still be flagged if they have a default profile pic."""
          profile_toggle = await self.config.guild(ctx.guild).profile_toggle()
          await self.config.guild(ctx.guild).profile_toggle.set(not profile_toggle)
          await ctx.send(f"Default profile pic flagging: {not profile_toggle}.")
  
      @dungeon.command()
      async def userrole(self, ctx, *, role_name: discord.Role = None):
          """Sets the role to give to new users that are not sent to the dungeon."""
          if not role_name:
              await self.config.guild(ctx.guild).user_role.set(None)
              return await ctx.send("Member role cleared.")
          await self.config.guild(ctx.guild).user_role.set(role_name.id)
          user_role_id = await self.config.guild(ctx.guild).user_role()
          user_role_obj = discord.utils.get(ctx.guild.roles, id=user_role_id)
          await ctx.send(f"Member role set to: {user_role_obj.name}.")
  
      @dungeon.command()
      async def usertoggle(self, ctx):
          """Toggle the user role on or off."""
          user_role_toggle = await self.config.guild(ctx.guild).user_role_toggle()
          await self.config.guild(ctx.guild).user_role_toggle.set(not user_role_toggle)
          await ctx.send(f"New user dungeon role enabled: {not user_role_toggle}.")
  
      @dungeon.command()
      async def verify(self, ctx, user: discord.Member):
          """Verify a user: remove the dungeon role and add initial user role."""
          data = await self.config.guild(ctx.guild).all()
          announce_channel = data["announce_channel"]
          blacklist = data["auto_blacklist"]
          dungeon_role_id = data["dungeon_role"]
          dungeon_role_obj = discord.utils.get(ctx.guild.roles, id=dungeon_role_id)
          user_role_id = data["user_role"]
          user_role_obj = discord.utils.get(ctx.guild.roles, id=user_role_id)
          dm_toggle = data["dm_toggle"]
          dm_message = data["dm_message"]
  
          if blacklist:
              # if you are reading this to learn, DON'T do this, there will be a real way
              # to interact with bot-owned data without touching config directly at some
              # point in the future after Red 3.2
              async with self.bot._config.blacklist() as blacklist_list:
                  if user.id in blacklist_list:
                      blacklist_list.remove(user.id)
  
          role_check = False
          for role in user.roles:
              if not dungeon_role_obj:
                  return await ctx.send("No dungeon role set.")
              if role == dungeon_role_obj:
                  role_check = True
                  try:
                      await user.remove_roles(
                          dungeon_role_obj, reason=f"Removing dungeon role, verified by {ctx.message.author}.",
                      )
                      if not user_role_obj:
                          return await ctx.send("Dungeon role removed, but no member role is set so I can't award one.")
                      await user.add_roles(user_role_obj, reason="Adding member role.")
                  except discord.Forbidden:
                      return await ctx.send(
                          "I need permissions to manage roles or the role hierarchy might not allow me to do this."
                      )
  
          if not role_check:
              return await ctx.send("User is not in the dungeon.")
  
          if blacklist:
              blacklist_msg = " and the bot blacklist"
          else:
              blacklist_msg = ""
          msg = f"{user} has been removed from the dungeon{blacklist_msg} and now has the initial user role."
          await ctx.send(msg)
  
          if dm_toggle:
              try:
                  await user.send(dm_message)
              except discord.Forbidden:
                  await ctx.send(f"I couldn't DM {user} to let them know they've been verified, they've blocked me.")
  
      @dungeon.command()
      async def autosetup(self, ctx):
          """Automatically set up the dungeon channel and role to apply to suspicious users.
          You must deny the default role (@ everyone) from viewing or typing in other channels in your server manually.
          """
          try:
              overwrites = {ctx.guild.default_role: discord.PermissionOverwrite(send_messages=False, read_messages=False)}
  
              dungeon_role = await ctx.guild.create_role(name="Dungeon")
  
              dungeon_category = await ctx.guild.create_category("Dungeon", overwrites=overwrites)
              await dungeon_category.set_permissions(
                  dungeon_role, read_messages=True, send_messages=False, read_message_history=True
              )
  
              dungeon_channel = await ctx.guild.create_text_channel("Silenced", category=dungeon_category)
              await dungeon_channel.set_permissions(
                  dungeon_role, read_messages=True, send_messages=False, read_message_history=True
              )
              await dungeon_channel.set_permissions(
                  ctx.guild.me, read_messages=True, send_messages=True, read_message_history=True
              )
              await dungeon_channel.send("Please wait while an admin verifies your account.")
  
              await self.config.guild(ctx.guild).dungeon_channel.set(dungeon_channel.id)
              await self.config.guild(ctx.guild).dungeon_role.set(dungeon_role.id)
              await self.config.guild(ctx.guild).announce_channel.set(ctx.channel.id)
              toggle = await self.config.guild(ctx.guild).toggle()
              if not toggle:
                  await ctx.invoke(self.usertoggle)
              await ctx.send(
                  f"Done.\nDungeon channel created: {dungeon_channel.mention}\nDungeon role created: {dungeon_role.name}\n\nPlease set these items manually:\n- The announce channel for reporting new users that are moved to the dungeon ({ctx.prefix}dungeon announce)\n- The role you wish to award regular members when they join the server ({ctx.prefix}dungeon userrole)\n- The toggle for enabling the regular user role awarding ({ctx.prefix}dungeon usertoggle)"
              )
  
          except discord.Forbidden:
              await ctx.send("I need permissions to manage channels and manage roles.")
  
      @dungeon.command()
      async def settings(self, ctx):
          """Show the current settings."""
          data = await self.config.guild(ctx.guild).all()
  
          try:
              drole = discord.utils.get(ctx.guild.roles, id=data["dungeon_role"]).name
          except AttributeError:
              drole = None
          try:
              urole = discord.utils.get(ctx.guild.roles, id=data["user_role"]).name
          except AttributeError:
              urole = None
          try:
              achannel = self.bot.get_channel(data["announce_channel"]).name
          except AttributeError:
              achannel = None
          try:
              dchannel = self.bot.get_channel(data["dungeon_channel"]).name
          except AttributeError:
              dchannel = None
          dungeon_enabled = data["toggle"]
          user_role_enabled = data["user_role_toggle"]
          join_days = data["join_days"]
          auto_blacklist = data["auto_blacklist"]
          profile_toggle = data["profile_toggle"]
          dm_toggle = data["dm_toggle"]
          auto_ban = data["auto_ban"]
          ban_msg = data["auto_ban_message"]
          mod_log = data["mod_log"]
  
          if ban_msg:
              ban_msg = "True"
  
          msg = (
              "```ini\n----Dungeon Settings----\n"
              f"Dungeon Enabled:  [{dungeon_enabled}]\n"
              f"Dungeon Role:     [{drole}]\n"
              f"Dungeon Channel:  [{dchannel}]\n"
              f"Announce Channel: [{achannel}]\n"
              f"Autorole Enabled: [{user_role_enabled}]\n"
              f"Autorole Role:    [{urole}]\n"
              f"Auto-blacklist:   [{auto_blacklist}]\n"
              f"Default PFP Flag: [{profile_toggle}]\n"
              f"Day Threshold:    [{str(join_days)}]\n"
              f"Msg on Verify:    [{dm_toggle}]\n"
              f"Auto-ban:         [{auto_ban}]\n"
              f"Ban Message:      [{ban_msg}]\n"
              f"Mod-log on Ban:   [{mod_log}]\n```"
          )
  
          embed = discord.Embed(colour=ctx.guild.me.top_role.colour, description=msg)
          return await ctx.send(embed=embed)
  
      @commands.Cog.listener()
      async def on_member_join(self, member):
          default_avatar = False
          toggle = await self.config.guild(member.guild).toggle()
          if not toggle:
              return
          if member.avatar_url == member.default_avatar_url:
              default_avatar = True
          join_date = datetime.datetime.strptime(str(member.created_at), "%Y-%m-%d %H:%M:%S.%f")
          now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
          since_join = now - join_date
          join_days = await self.config.guild(member.guild).join_days()
          profile_toggle = await self.config.guild(member.guild).profile_toggle()
          announce_channel = await self.config.guild(member.guild).announce_channel()
          channel_object = self.bot.get_channel(announce_channel)
          auto_ban = await self.config.guild(member.guild).auto_ban()
          auto_ban_msg = await self.config.guild(member.guild).auto_ban_message()
          mod_log = await self.config.guild(member.guild).mod_log()
          bypassed_ids = await self.config.guild(member.guild).bypass()
  
          if member.id in bypassed_ids:
              bypassed_ids.remove(member.id)
              await self.config.guild(member.guild).bypass.set(bypassed_ids)
              user_role_id = await self.config.guild(member.guild).user_role()
              user_role_obj = discord.utils.get(member.guild.roles, id=user_role_id)
              try:
                  await member.add_roles(
                      user_role_obj, reason="User has bypassed Dungeon checks. Assigning member role.",
                  )
              except discord.Forbidden:
                  pass
              except AttributeError:
                  pass
              bypass_msg = f"**{member}** ({member.id}) was in the bypass list for **{member.guild.name}** ({member.guild.id}). They were allowed to join without Dungeon checks and I have assigned them the Member role specified in the settings, if any."
              if announce_channel:
                  await channel_object.send(bypass_msg)
              else:
                  log.debug(f"dungeon.py: {bypass_msg}")
              return
  
          if (since_join.days < join_days) or (profile_toggle and default_avatar):
              banned = False
              blacklist = await self.config.guild(member.guild).auto_blacklist()
              dungeon_role_id = await self.config.guild(member.guild).dungeon_role()
              dungeon_role_obj = discord.utils.get(member.guild.roles, id=dungeon_role_id)
              perm_msg = f"dungeon.py: Unable to auto-ban user, permissions needed and no announce channel set. Guild: {member.guild.id}"
  
              if auto_ban:
                  banned = True
                  if auto_ban_msg:
                      try:
                          await member.send(auto_ban_msg)
                      except discord.Forbidden:
                          if announce_channel:
                              await channel_object.send(
                                  f"I couldn't DM {member} ({member.id}) to let them know they've been banned, they've blocked me."
                              )
                          else:
                              log.debug(perm_msg)
                              return
                  try:
                      await member.guild.ban(member, reason="Dungeon auto-ban", delete_message_days=0)
                  except discord.Forbidden:
                      if announce_channel:
                          return await channel_object.send(
                              f"I tried to auto-ban someone ({member}, {member.id}) but I don't have ban permissions."
                          )
                      else:
                          log.debug(perm_msg)
                          return
  
                  if not mod_log:
                      if announce_channel:
                          msg = f"Auto-banned new user: \n**{member}** ({member.id})\n{self._dynamic_time(int(since_join.total_seconds()))} old account"
                          return await channel_object.send(msg)
                      else:
                          log.debug(perm_msg)
                          return
                  else:
                      try:
                          await modlog.create_case(
                              self.bot, member.guild, now, "ban", member, member.guild.me, until=None, channel=None,
                          )
                      except RuntimeError:
                          log.error(
                              f"dungeon.py error while autobanning user and attempting to create modlog entry in guild: {member.guild.id}",
                              exc_info=True,
                          )
  
              if blacklist:
                  # if you are reading this to learn, DON'T do this, there will be a real way
                  # to interact with bot-owned data without touching config directly at some
                  # point in the future after Red 3.2
                  async with self.bot._config.blacklist() as blacklist_list:
                      if member.id not in blacklist_list:
                          blacklist_list.append(member.id)
              if banned:
                  return
              try:
                  if since_join.days < join_days:
                      reason = "Adding dungeon role, new account."
                  else:
                      reason = "Adding dungeon role, default profile pic."
                  await member.add_roles(dungeon_role_obj, reason=reason)
              except discord.Forbidden:
                  if announce_channel:
                      return await channel_object.send(
                          "Someone suspicious joined but something went wrong. I need permissions to manage channels and manage roles."
                      )
                  else:
                      log.info(
                          f"dungeon.py: I need permissions to manage channels and manage roles in {member.guild.name} ({member.guild.id})."
                      )
                      return
  
              msg = f"Auto-banished new user: \n**{member}** ({member.id})\n{self._dynamic_time(int(since_join.total_seconds()))} old account"
              if default_avatar:
                  msg += ", no profile picture set"
              await channel_object.send(msg)
          else:
              user_role_toggle = await self.config.guild(member.guild).user_role_toggle()
              if not user_role_toggle:
                  return
              user_role_id = await self.config.guild(member.guild).user_role()
              user_role_obj = discord.utils.get(member.guild.roles, id=user_role_id)
              try:
                  await member.add_roles(user_role_obj, reason="Adding member role to new user.")
              except discord.Forbidden:
                  pass
              except AttributeError:
                  pass
  
      @staticmethod
      def _dynamic_time(time):
          m, s = divmod(time, 60)
          h, m = divmod(m, 60)
          d, h = divmod(h, 24)
  
          if d > 0:
              msg = "{0}d {1}h"
          elif d == 0 and h > 0:
              msg = "{1}h {2}m"
          elif d == 0 and h == 0 and m > 0:
              msg = "{2}m {3}s"
          elif d == 0 and h == 0 and m == 0 and s > 0:
              msg = "{3}s"
          else:
              msg = ""
          return msg.format(d, h, m, s)
  