text: import logging
  import typing
  from datetime import timedelta
  
  import disnake
  from disnake.abc import PrivateChannel
  
  from cogs.cog import Cog
  from enums.data_enums import RedisKeyNamespaces
  from utils.utilities import (split_string, format_on_delete, format_on_edit,
                               format_join_leave, get_avatar,
                               get_image_from_embeds,
                               is_image_url)
  
  if typing.TYPE_CHECKING:
      from bot.Not_a_bot import NotABot
  
  logger = logging.getLogger('terminal')
  day = timedelta(days=1)
  
  
  class Logger(Cog):
      def __init__(self, bot):
          super().__init__(bot)
  
      @property
      def bot(self) -> 'NotABot':
          return super().bot
  
      @staticmethod
      def format_for_db(message):
          is_pm = isinstance(message.channel, PrivateChannel)
          guild = message.guild.id if not is_pm else None
          # guild_name = message.guild.name if not is_pm else 'DM'
          channel = message.channel.id if not is_pm else None
          # channel_name = message.channel.name if not is_pm else None
          user_id = message.author.id
          message_id = message.id
  
          # Only save image links for later use in image commands
          attachment = message.attachments[0].url if message.attachments else None
          if attachment and not message.attachments[0].width:
              attachment = None
  
          if attachment is None:
              attachment = get_image_from_embeds(message.embeds)
  
          if not is_image_url(attachment):
              attachment = None
  
          return (guild,
                  channel,
                  user_id,
                  message_id), attachment
  
      async def check_mentions(self, message):
          if message.guild is None:
              return
  
          if not message.raw_role_mentions:
              return
  
          roles = []
          guild = message.guild
          for role_id in set(message.raw_role_mentions):
              role = guild.get_role(role_id)
              if role:
                  roles.append(role)
  
          if not roles:
              return
  
          sql = 'INSERT INTO mention_stats AS ms (guild, role, role_name) ' \
                'VALUES %s' % self.bot.dbutil.create_bind_groups(len(roles), 3)
  
          data = []
          for role in roles:
              data.extend((guild.id, role.id, role.name))
  
          sql = sql.rstrip(',')
          sql += ' ON CONFLICT (guild, role) DO UPDATE SET amount=ms.amount+1, role_name=EXCLUDED.role_name'
          await self.bot.dbutil.execute(sql, data)
  
      @Cog.listener()
      async def on_message(self, message):
          await self.check_mentions(message)
          d, attachment = self.format_for_db(message)
  
          if message.guild and message.guild.id in (217677285442977792,475623556164878347) and self.bot.can_track(message.author):
              sql = "INSERT INTO messages (guild, channel, user_id, message_id) " \
                    "VALUES ($1, $2, $3, $4)"
  
              await self.bot.dbutil.execute(sql, d)
  
          # Channel index is 1
          if self.bot.redis and attachment and d[1] and self.bot.can_track(message.author):
              redis_key = f'{RedisKeyNamespaces.Attachment.value}:{d[1]}'
              await self.bot.redis.set(redis_key, attachment, ex=day)
  
      @Cog.listener()
      async def on_member_join(self, member):
          guild = member.guild
  
          if self.bot.can_track(member):
              sql = "INSERT INTO join_dates (uid, guild, first_join) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING"
              await self.bot.dbutil.execute(sql, (member.id, guild.id, member.joined_at))
  
          channel = self.bot.guild_cache.join_channel(guild.id)
          channel = guild.get_channel(channel)
          if channel is None:
              return
  
          message = self.bot.guild_cache.join_message(guild.id, default_message=True)
          if not message:
              return
  
          perms = channel.permissions_for(channel.guild.get_member(self.bot.user.id))
          if not perms.send_messages:
              return
  
          if member.id == 287664210152783873:
              message = 'Cease the tag %s' % member.mention
          else:
              message = format_join_leave(member, message)
  
          await channel.send(message)
  
      @Cog.listener()
      async def on_member_remove(self, member):
          guild = member.guild
  
          channel = self.bot.guild_cache.leave_channel(guild.id)
          channel = guild.get_channel(channel)
          if channel is None:
              return
  
          message = self.bot.guild_cache.leave_message(guild.id, default_message=True)
          if not message:
              return
  
          perms = channel.permissions_for(channel.guild.get_member(self.bot.user.id))
          if not perms.send_messages:
              return
  
          message = format_join_leave(member, message)
          await channel.send(message)
  
      @Cog.listener()
      async def on_message_delete(self, msg):
          if isinstance(msg.channel, disnake.DMChannel) or msg.guild is None:
              return
  
          if msg.author.bot or msg.channel.id == 336917918040326166:
              return
  
          channel = self.bot.guild_cache.on_delete_channel(msg.guild.id)
          channel = self.bot.get_channel(channel)
          if channel is None:
              return
  
          is_embed = self.bot.guild_cache.on_delete_embed(msg.guild.id)
  
          perms = channel.permissions_for(channel.guild.get_member(self.bot.user.id))
          if not perms.send_messages or (is_embed and not perms.embed_links):
              return
  
          message = self.bot.guild_cache.on_delete_message(msg.guild.id, default_message=True)
          message = format_on_delete(msg, message)
          message = split_string(message, splitter='\n', maxlen=2048 if is_embed else 2000)
          if len(message) > 2:
              m = '{0.id}: {0.name} On delete message had to post over 2 messages'.format(msg.guild)
              logger.info(m)
              logger.warning(m)
  
          for m in message:
              if is_embed:
                  await channel.send(embed=self.create_embed(msg,
                                                             f'Message deleted in #{msg.channel.name} {msg.channel.id}',
                                                             m,
                                                             msg.created_at))
              else:
                  await channel.send(m)
  
      @Cog.listener()
      async def on_message_edit(self, before, after):
          if isinstance(before.channel, disnake.DMChannel) or before.guild is None:
              return
  
          if self.bot.redis and before.content == after.content and self.bot.can_track(before.author):
              image = get_image_from_embeds(after.embeds)
              if not image:
                  return
  
              redis_key = f'{RedisKeyNamespaces.Attachment.value}:{after.channel.id}'
              await self.bot.redis.set(redis_key, image, ex=day)
  
          if before.author.bot or before.channel.id == 336917918040326166:
              return
  
          channel = self.bot.guild_cache.on_edit_channel(before.guild.id)
          channel = self.bot.get_channel(channel)
          if not channel:
              return
  
          message = self.bot.guild_cache.on_edit_message(before.guild.id, default_message=True)
          if message is None:
              return
  
          is_embed = self.bot.guild_cache.on_edit_embed(before.guild.id)
  
          message = format_on_edit(before, after, message)
          if message is None:
              return
  
          perms = channel.permissions_for(channel.guild.get_member(self.bot.user.id))
          if not perms.send_messages or (is_embed and not perms.embed_links):
              return
  
          message = split_string(message, maxlen=2048 if is_embed else 2000)
          if len(message) > 4:
              m = '{0.id}: {0.name} On edit message had to post over 4 messages'.format(before.guild)
              logger.info(m)
              logger.warning(m)
  
          for m in message:
              if is_embed:
                  await channel.send(
                      embed=self.create_embed(after,
                                              f'Message edited in #{after.channel.name} {after.channel.id}',
                                              m,
                                              after.edited_at))
              else:
                  await channel.send(m)
  
      @staticmethod
      def create_embed(message, title, description, timestamp):
          embed = disnake.Embed(title=title, description=description, timestamp=timestamp)
          embed.set_author(name=str(message.author), icon_url=get_avatar(message.author))
          return embed
  
      @Cog.listener()
      async def on_guild_role_delete(self, role):
          await self.bot.dbutil.delete_role(role.id, role.guild.id)
  
      @Cog.listener()
      async def on_guild_role_create(self, role):
          await self.bot.dbutil.add_roles(role.guild.id, role.id)
  
      @Cog.listener()
      async def on_command_completion(self, ctx):
          entries = []
          cmd = ctx.command
          command = cmd
          while command.parent is not None:
              command = command.parent
              entries.append(command.name)
          entries = list(reversed(entries))
          entries.append(cmd.name)
          guild = ctx.guild.id if ctx.guild else None
          await self.bot.dbutil.command_used(entries[0], ' '.join(entries[1:]) or "",
                                             ctx.message.created_at, ctx.author.id,
                                             guild)
  
  
  def setup(bot):
      bot.add_cog(Logger(bot))
  