import discord
from discord.ext import commands
from discord.utils import find
from .utils.chat_formatting import pagify
from __main__ import send_cmd_help
import platform, asyncio, string, operator, random, textwrap
import os, re, aiohttp
import math
from .utils.dataIO import fileIO
from cogs.utils import checks
try:
    import pymongo
    from pymongo import MongoClient
except:
    raise RuntimeError("Can't load pymongo. Do 'pip3 install pymongo'.")
try:
    import scipy
    import scipy.misc
    import scipy.cluster
except:
    pass
try:
    from PIL import Image, ImageDraw, ImageFont, ImageColor, ImageOps, ImageFilter
except:
    raise RuntimeError("Can't load pillow. Do 'pip3 install pillow'.")
import time

# fonts
font_file = 'data/leveler/fonts/font.ttf'
font_bold_file = 'data/leveler/fonts/font_bold.ttf'
font_unicode_file = 'data/leveler/fonts/unicode.ttf'

# Credits (None)
bg_credits = {

}

# directory
user_directory = "data/leveler/users"

prefix = fileIO("data/red/settings.json", "load")['PREFIXES']
default_avatar_url = "http://i.imgur.com/XPDO9VH.jpg"

try:
    client = MongoClient()
    db = client['leveler']
except:
    print("Can't load database. Follow instructions on Git/online to install MongoDB.")

class Leveler:
    """A level up thing with image generation!"""

    def __init__(self, bot):
        self.bot = bot
        self.backgrounds = fileIO("data/leveler/backgrounds.json", "load")
        self.badges = fileIO("data/leveler/badges.json", "load")
        self.settings = fileIO("data/leveler/settings.json", "load")
        bot_settings = fileIO("data/red/settings.json", "load")
        self.owner = bot_settings["OWNER"]

        dbs = client.database_names()
        if 'leveler' not in dbs:
            self.pop_database()

    def pop_database(self):
        if os.path.exists("data/leveler/users"):
            for userid in os.listdir(user_directory):
                userinfo = fileIO("data/leveler/users/{}/info.json".format(userid), "load")
                userinfo['user_id'] = userid
                db.users.insert_one(userinfo)

    def create_global(self):

                userinfo = fileIO("data/leveler/users/{}/info.json".format(userid), "load")
                userinfo['user_id'] = userid
                db.users.insert_one(userinfo)


    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(name = "profile", pass_context=True, no_pm=True)
    async def profile(self,ctx, *, user : discord.Member=None):
        """Displays a user profile."""
        if user == None:
            user = ctx.message.author
        channel = ctx.message.channel
        server = user.server
        curr_time = time.time()

        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        # check if disabled
        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        # no cooldown for text only
        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            em = await self.profile_text(user, server, userinfo)
            await self.bot.send_message(channel, '', embed = em)
        else:
            await self.draw_profile(user, server)
            await self.bot.send_typing(channel)
            await self.bot.send_file(channel, 'data/leveler/temp/{}_profile.png'.format(user.id), content='**User profile for {}**'.format(self._is_mention(user)))
            db.users.update_one({'user_id':user.id}, {'$set':{
                    "profile_block": curr_time,
                }}, upsert = True)
            try:
                os.remove('data/leveler/temp/{}_profile.png'.format(user.id))
            except:
                pass

    async def profile_text(self, user, server, userinfo):
        def test_empty(text):
            if text == '':
                return "None"
            else:
                return text

        em = discord.Embed(description='', colour=user.colour)
        em.add_field(name="Title:", value = test_empty(userinfo["title"]))
        em.add_field(name="Reps:", value= userinfo["rep"])
        em.add_field(name="Global Rank:", value = '#{}'.format(await self._find_global_rank(user)))
        em.add_field(name="Server Rank:", value = '#{}'.format(await self._find_server_rank(user, server)))
        em.add_field(name="Server Level:", value = format(userinfo["servers"][server.id]["level"]))
        em.add_field(name="Total Exp:", value = userinfo["total_exp"])
        em.add_field(name="Server Exp:", value = await self._find_server_exp(user, server))
        try:
            bank = self.bot.get_cog('Economy').bank
            if bank.account_exists(user):
                credits = bank.get_balance(user)
            else:
                credits = 0
        except:
            credits = 0
        em.add_field(name="Credits: ", value = "${}".format(credits))
        em.add_field(name="Info: ", value = test_empty(userinfo["info"]))
        em.add_field(name="Badges: ", value = test_empty(", ".join(userinfo["badges"])).replace("_", " "))
        em.set_author(name="Profile for {}".format(user.name), url = user.avatar_url)
        em.set_thumbnail(url=user.avatar_url)
        return em

    @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True, no_pm=True)
    async def rank(self,ctx,user : discord.Member=None):
        """Displays the rank of a user."""
        if user == None:
            user = ctx.message.author
        channel = ctx.message.channel
        server = user.server
        curr_time = time.time()

        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        # check if disabled
        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        # no cooldown for text only
        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            em = await self.rank_text(user, server, userinfo)
            await self.bot.send_message(channel, '', embed = em)
        else:
            await self.draw_rank(user, server)
            await self.bot.send_typing(channel)
            await self.bot.send_file(channel, 'data/leveler/temp/{}_rank.png'.format(user.id), content='**Ranking & Statistics for {}**'.format(self._is_mention(user)))
            db.users.update_one({'user_id':user.id}, {'$set':{
                    "rank_block".format(server.id): curr_time,
                }}, upsert = True)
            try:
                os.remove('data/leveler/temp/{}_rank.png'.format(user.id))
            except:
                pass

    async def rank_text(self, user, server, userinfo):
        em = discord.Embed(description='', colour=user.colour)
        em.add_field(name="Server Rank", value = '#{}'.format(await self._find_server_rank(user, server)))
        em.add_field(name="Reps", value = userinfo["rep"])
        em.add_field(name="Server Level", value = userinfo["servers"][server.id]["level"])
        em.add_field(name="Server Exp", value = await self._find_server_exp(user, server))
        em.set_author(name="Rank and Statistics for {}".format(user.name), url = user.avatar_url)
        em.set_thumbnail(url=user.avatar_url)
        return em

    # should the user be mentioned based on settings?
    def _is_mention(self,user):
        if "mention" not in self.settings.keys() or self.settings["mention"]:
            return user.mention
        else:
            return user.name

    # @commands.cooldown(1, 10, commands.BucketType.user)
    @commands.command(pass_context=True, no_pm=True)
    async def top(self, ctx, *options):
        '''Displays leaderboard. Add "global" parameter for global'''
        server = ctx.message.server
        user = ctx.message.author

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        users = []
        board_type = ''
        user_stat = None
        if '-rep' in options and '-global' in options:
            title = "Global Rep Leaderboard for {}\n".format(self.bot.user.name)
            for userinfo in db.users.find({}):
                try:
                    users.append((userinfo["username"], userinfo["rep"]))
                except:
                    users.append((userinfo["user_id"], userinfo["rep"]))

                if user.id == userinfo["user_id"]:
                    user_stat = userinfo["rep"]

            board_type = 'Rep'
            footer_text = "Your Rank: {}         {}: {}".format(
                await self._find_global_rep_rank(user), board_type, user_stat)
            icon_url = self.bot.user.avatar_url
        elif '-global' in options:
            title = "Global Exp Leaderboard for {}\n".format(self.bot.user.name)
            for userinfo in db.users.find({}):
                try:
                    users.append((userinfo["username"], userinfo["total_exp"]))
                except:
                    users.append((userinfo["user_id"], userinfo["total_exp"]))

                if user.id == userinfo["user_id"]:
                    user_stat = userinfo["total_exp"]

            board_type = 'Points'
            footer_text = "Your Rank: {}         {}: {}".format(
                await self._find_global_rank(user), board_type, user_stat)
            icon_url = self.bot.user.avatar_url
        elif '-rep' in options:
            title = "Rep Leaderboard for {}\n".format(server.name)
            for userinfo in db.users.find({}):
                userid = userinfo["user_id"]
                if "servers" in userinfo and server.id in userinfo["servers"]:
                    try:
                        users.append((userinfo["username"], userinfo["rep"]))
                    except:
                        users.append((userinfo["user_id"], userinfo["rep"]))

                if user.id == userinfo["user_id"]:
                    user_stat = userinfo["rep"]

            board_type = 'Rep'
            print(await self._find_server_rep_rank(user, server))
            footer_text = "Your Rank: {}         {}: {}".format(
                await self._find_server_rep_rank(user, server), board_type, user_stat)
            icon_url = server.icon_url
        else:
            title = "Exp Leaderboard for {}\n".format(server.name)
            for userinfo in db.users.find({}):
                try:
                    userid = userinfo["user_id"]
                    if "servers" in userinfo and server.id in userinfo["servers"]:
                        server_exp = 0
                        for i in range(userinfo["servers"][server.id]["level"]):
                            server_exp += self._required_exp(i)
                        server_exp +=  userinfo["servers"][server.id]["current_exp"]
                        try:
                            users.append((userinfo["username"], server_exp))
                        except:
                            users.append((userinfo["user_id"], server_exp))
                except:
                    pass
            board_type = 'Points'
            footer_text = "Your Rank: {}         {}: {}".format(
                await self._find_server_rank(user, server), board_type,
                await self._find_server_exp(user, server))
            icon_url = server.icon_url
        sorted_list = sorted(users, key=operator.itemgetter(1), reverse=True)

        # multiple page support
        page = 1
        per_page = 15
        pages = math.ceil(len(sorted_list)/per_page)
        for option in options:
            if str(option).isdigit():
                if page >= 1 and int(option) <= pages:
                    page = int(str(option))
                else:
                    await self.bot.say("**Please enter a valid page number! (1 - {})**".format(str(pages)))
                    return
                break

        msg = ""
        msg += "**Rank              Name (Page {}/{})**\n".format(page, pages)
        rank = 1 + per_page*(page-1)
        start_index = per_page*page - per_page
        end_index = per_page*page

        default_label = "   "
        special_labels = ["♔", "♕", "♖", "♗", "♘", "♙"]

        for single_user in sorted_list[start_index:end_index]:
            if rank-1 < len(special_labels):
                label = special_labels[rank-1]
            else:
                label = default_label

            msg += u'`{:<2}{:<2}{:<2}   # {:<22}'.format(rank, label, u"➤", self._truncate_text(single_user[0],20))
            msg += u'{:>5}{:<2}{:<2}{:<5}`\n'.format(" ", " ", " ", "Total {}: ".format(board_type) + str(single_user[1]))
            rank += 1
        msg +="----------------------------------------------------\n"
        msg += "`{}`".format(footer_text)

        em = discord.Embed(description='', colour=user.colour)
        em.set_author(name=title, icon_url = icon_url)
        em.description = msg

        await self.bot.say(embed = em)

    @commands.cooldown(1, 30, commands.BucketType.user)
    @commands.command(pass_context=True, no_pm=True)
    async def rep(self, ctx, user : discord.Member = None):
        """Gives a reputation point to a designated player."""
        channel = ctx.message.channel
        org_user = ctx.message.author
        server = org_user.server
        # creates user if doesn't exist
        await self._create_user(org_user, server)
        if user:
            await self._create_user(user, server)
        org_userinfo = db.users.find_one({'user_id':org_user.id})
        curr_time = time.time()

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return
        if user and user.id == org_user.id:
            await self.bot.say("**You can't give a rep to yourself!**")
            return
        if user and user.bot:
            await self.bot.say("**You can't give a rep to a bot!**")
            return
        if "rep_block" not in org_userinfo:
            org_userinfo["rep_block"] = 0

        delta = float(curr_time) - float(org_userinfo["rep_block"])
        if user and delta >= 43200.0 and delta>0:
            userinfo = db.users.find_one({'user_id':user.id})
            db.users.update_one({'user_id':org_user.id}, {'$set':{
                    "rep_block": curr_time,
                }})
            db.users.update_one({'user_id':user.id}, {'$set':{
                    "rep":  userinfo["rep"] + 1,
                }})
            await self.bot.say("**You have just given {} a reputation point!**".format(self._is_mention(user)))
        else:
            # calulate time left
            seconds = 43200 - delta
            if seconds < 0:
                await self.bot.say("**You can give a rep!**")
                return

            m, s = divmod(seconds, 60)
            h, m = divmod(m, 60)
            await self.bot.say("**You need to wait {} hours, {} minutes, and {} seconds until you can give reputation again!**".format(int(h), int(m), int(s)))

    @commands.command(pass_context=True, no_pm=True)
    async def lvlinfo(self, ctx, user : discord.Member = None):
        """Gives more specific details about user profile image."""

        if not user:
            user = ctx.message.author
        server = ctx.message.server
        userinfo = db.users.find_one({'user_id':user.id})

        server = ctx.message.server

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        # creates user if doesn't exist
        await self._create_user(user, server)
        msg = ""
        msg += "Name: {}\n".format(user.name)
        msg += "Title: {}\n".format(userinfo["title"])
        msg += "Reps: {}\n".format(userinfo["rep"])
        msg += "Server Level: {}\n".format(userinfo["servers"][server.id]["level"])
        total_server_exp = 0
        for i in range(userinfo["servers"][server.id]["level"]):
            total_server_exp += self._required_exp(i)
        total_server_exp += userinfo["servers"][server.id]["current_exp"]
        msg += "Server Exp: {}\n".format(total_server_exp)
        msg += "Total Exp: {}\n".format(userinfo["total_exp"])
        msg += "Info: {}\n".format(userinfo["info"])
        msg += "Profile background: {}\n".format(userinfo["profile_background"])
        msg += "Rank background: {}\n".format(userinfo["rank_background"])
        msg += "Levelup background: {}\n".format(userinfo["levelup_background"])
        if "profile_info_color" in userinfo.keys() and userinfo["profile_info_color"]:
            msg += "Profile info color: {}\n".format(self._rgb_to_hex(userinfo["profile_info_color"]))
        if "profile_exp_color" in userinfo.keys() and userinfo["profile_exp_color"]:
            msg += "Profile exp color: {}\n".format(self._rgb_to_hex(userinfo["profile_exp_color"]))
        if "rep_color" in userinfo.keys() and userinfo["rep_color"]:
            msg += "Rep section color: {}\n".format(self._rgb_to_hex(userinfo["rep_color"]))
        if "badge_col_color" in userinfo.keys() and userinfo["badge_col_color"]:
            msg += "Badge section color: {}\n".format(self._rgb_to_hex(userinfo["badge_col_color"]))
        if "rank_info_color" in userinfo.keys() and userinfo["rank_info_color"]:
            msg += "Rank info color: {}\n".format(self._rgb_to_hex(userinfo["rank_info_color"]))
        if "rank_exp_color" in userinfo.keys() and userinfo["rank_exp_color"]:
            msg += "Rank exp color: {}\n".format(self._rgb_to_hex(userinfo["rank_exp_color"]))
        if "levelup_info_color" in userinfo.keys() and userinfo["levelup_info_color"]:
            msg += "Level info color: {}\n".format(self._rgb_to_hex(userinfo["levelup_info_color"]))
        msg += "Badges: "
        msg += ", ".join(userinfo["badges"])

        em = discord.Embed(description=msg, colour=user.colour)
        em.set_author(name="Profile Information for {}".format(user.name), icon_url = user.avatar_url)
        await self.bot.say(embed = em)

    def _rgb_to_hex(self, rgb):
        rgb = tuple(rgb[:3])
        return '#%02x%02x%02x' % rgb

    @commands.group(name = "lvlset", pass_context=True)
    async def lvlset(self, ctx):
        """Profile Configuration Options"""
        if ctx.invoked_subcommand is None:
            await send_cmd_help(ctx)
            return

    @lvlset.group(name = "profile", pass_context=True)
    async def profileset(self, ctx):
        """Profile options"""
        if ctx.invoked_subcommand is None or \
                isinstance(ctx.invoked_subcommand, commands.Group):
            await send_cmd_help(ctx)
            return

    @lvlset.group(name = "rank", pass_context=True)
    async def rankset(self, ctx):
        """Rank options"""
        if ctx.invoked_subcommand is None or \
                isinstance(ctx.invoked_subcommand, commands.Group):
            await send_cmd_help(ctx)
            return

    @lvlset.group(name = "levelup", pass_context=True)
    async def levelupset(self, ctx):
        """Level-Up options"""
        if ctx.invoked_subcommand is None or \
                isinstance(ctx.invoked_subcommand, commands.Group):
            await send_cmd_help(ctx)
            return

    @profileset.command(name = "color", pass_context=True, no_pm=True)
    async def profilecolors(self, ctx, section:str, color:str):
        """Set info color. e.g [p]lvlset profile color [exp|rep|badge|info|all] [default|white|hex|auto]"""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        section = section.lower()
        default_info_color = (30, 30 ,30, 200)
        white_info_color = (150, 150, 150, 180)
        default_rep = (92,130,203,230)
        default_badge = (128,151,165,230)
        default_exp = (255, 255, 255, 230)
        default_a = 200

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            await self.bot.say("**Text-only commands allowed.**")
            return

        # get correct section for db query
        if section == "rep":
            section_name = "rep_color"
        elif section == "exp":
            section_name = "profile_exp_color"
        elif section == "badge":
            section_name = "badge_col_color"
        elif section == "info":
            section_name = "profile_info_color"
        elif section == "all":
            section_name = "all"
        else:
            await self.bot.say("**Not a valid section. (rep, exp, badge, info, all)**")
            return

        # get correct color choice
        if color == "auto":
            if section == "exp":
                color_ranks = [random.randint(2,3)]
            elif section == "rep":
                color_ranks = [random.randint(2,3)]
            elif section == "badge":
                color_ranks = [0] # most prominent color
            elif section == "info":
                color_ranks = [random.randint(0,1)]
            elif section == "all":
                color_ranks = [random.randint(2,3), random.randint(2,3), 0, random.randint(0,2)]

            hex_colors = await self._auto_color(userinfo["profile_background"], color_ranks)
            set_color = []
            for hex_color in hex_colors:
                color_temp = self._hex_to_rgb(hex_color, default_a)
                set_color.append(color_temp)

        elif color == "white":
            set_color = [white_info_color]
        elif color == "default":
            if section == "exp":
                set_color = [default_exp]
            elif section == "rep":
                set_color = [default_rep]
            elif section == "badge":
                set_color = [default_badge]
            elif section == "info":
                set_color = [default_info_color]
            elif section == "all":
                set_color = [default_exp, default_rep, default_badge, default_info_color]
        elif self._is_hex(color):
            set_color = [self._hex_to_rgb(color, default_a)]
        else:
            await self.bot.say("**Not a valid color. (default, hex, white, auto)**")
            return

        if section == "all":
            if len(set_color) == 1:
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "profile_exp_color": set_color[0],
                        "rep_color": set_color[0],
                        "badge_col_color": set_color[0],
                        "profile_info_color": set_color[0]
                    }})
            elif color == "default":
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "profile_exp_color": default_exp,
                        "rep_color": default_rep,
                        "badge_col_color": default_badge,
                        "profile_info_color": default_info_color
                    }})
            elif color == "auto":
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "profile_exp_color": set_color[0],
                        "rep_color": set_color[1],
                        "badge_col_color": set_color[2],
                        "profile_info_color": set_color[3]
                    }})
            await self.bot.say("**Colors for profile set.**")
        else:
            print("update one")
            db.users.update_one({'user_id':user.id}, {'$set':{
                    section_name: set_color[0]
                }})
            await self.bot.say("**Color for profile {} set.**".format(section))

    @rankset.command(name = "color", pass_context=True, no_pm=True)
    async def rankcolors(self, ctx, section:str, color:str = None):
        """Set info color. e.g [p]lvlset rank color [exp|info] [default|white|hex|auto]"""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        section = section.lower()
        default_info_color = (30, 30 ,30, 200)
        white_info_color = (150, 150, 150, 180)
        default_exp = (255, 255, 255, 230)
        default_a = 200

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            await self.bot.say("**Text-only commands allowed.**")
            return

        # get correct section for db query
        if section == "exp":
            section_name = "rank_exp_color"
        elif section == "info":
            section_name = "rank_info_color"
        elif section == "all":
            section_name = "all"
        else:
            await self.bot.say("**Not a valid section. (exp, info, all)**")
            return

        # get correct color choice
        if color == "auto":
            if section == "exp":
                color_ranks = [random.randint(2,3)]
            elif section == "info":
                color_ranks = [random.randint(0,1)]
            elif section == "all":
                color_ranks = [random.randint(2,3), random.randint(0,1)]

            hex_colors = await self._auto_color(userinfo["rank_background"], color_ranks)
            set_color = []
            for hex_color in hex_colors:
                color_temp = self._hex_to_rgb(hex_color, default_a)
                set_color.append(color_temp)
        elif color == "white":
            set_color = [white_info_color]
        elif color == "default":
            if section == "exp":
                set_color = [default_exp]
            elif section == "info":
                set_color = [default_info_color]
            elif section == "all":
                set_color = [default_exp, default_rep, default_badge, default_info_color]
        elif self._is_hex(color):
            set_color = [self._hex_to_rgb(color, default_a)]
        else:
            await self.bot.say("**Not a valid color. (default, hex, white, auto)**")
            return

        if section == "all":
            if len(set_color) == 1:
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "rank_exp_color": set_color[0],
                        "rank_info_color": set_color[0]
                    }})
            elif color == "default":
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "rank_exp_color": default_exp,
                        "rank_info_color": default_info_color
                    }})
            elif color == "auto":
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "rank_exp_color": set_color[0],
                        "rank_info_color": set_color[1]
                    }})
            await self.bot.say("**Colors for rank set.**")
        else:
            db.users.update_one({'user_id':user.id}, {'$set':{
                    section_name: set_color[0]
                }})
            await self.bot.say("**Color for rank {} set.**".format(section))

    @levelupset.command(name = "color", pass_context=True, no_pm=True)
    async def levelupcolors(self, ctx, section:str, color:str = None):
        """Set info color. e.g [p]lvlset color [info] [default|white|hex|auto]"""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        section = section.lower()
        default_info_color = (30, 30 ,30, 200)
        white_info_color = (150, 150, 150, 180)
        default_a = 200

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            await self.bot.say("**Text-only commands allowed.**")
            return

        # get correct section for db query
        if section == "info":
            section_name = "levelup_info_color"
        else:
            await self.bot.say("**Not a valid section. (info)**")
            return

        # get correct color choice
        if color == "auto":
            if section == "info":
                color_ranks = [random.randint(0,1)]
            hex_colors = await self._auto_color(userinfo["levelup_background"], color_ranks)
            set_color = []
            for hex_color in hex_colors:
                color_temp = self._hex_to_rgb(hex_color, default_a)
                set_color.append(color_temp)
        elif color == "white":
            set_color = [white_info_color]
        elif color == "default":
            if section == "info":
                set_color = [default_info_color]
        elif self._is_hex(color):
            set_color = [self._hex_to_rgb(color, default_a)]
        else:
            await self.bot.say("**Not a valid color. (default, hex, white, auto)**")
            return

        db.users.update_one({'user_id':user.id}, {'$set':{
                section_name: set_color[0]
            }})
        await self.bot.say("**Color for level-up {} set.**".format(section))

    # uses k-means algorithm to find color from bg, rank is abundance of color, descending
    async def _auto_color(self, url:str, ranks):
        phrases = ["Calculating colors..."] # in case I want more
        #try:
        await self.bot.say("**{}**".format(random.choice(phrases)))
        clusters = 10

        async with aiohttp.get(url) as r:
            image = await r.content.read()
        with open('data/leveler/temp_auto.png','wb') as f:
            f.write(image)

        im = Image.open('data/leveler/temp_auto.png').convert('RGBA')
        im = im.resize((290, 290)) # resized to reduce time
        ar = scipy.misc.fromimage(im)
        shape = ar.shape
        ar = ar.reshape(scipy.product(shape[:2]), shape[2])

        codes, dist = scipy.cluster.vq.kmeans(ar.astype(float), clusters)
        vecs, dist = scipy.cluster.vq.vq(ar, codes)         # assign codes
        counts, bins = scipy.histogram(vecs, len(codes))    # count occurrences

        # sort counts
        freq_index = []
        index = 0
        for count in counts:
            freq_index.append((index, count))
            index += 1
        sorted_list = sorted(freq_index, key=operator.itemgetter(1), reverse=True)

        colors = []
        for rank in ranks:
            color_index = min(rank, len(codes))
            peak = codes[sorted_list[color_index][0]] # gets the original index
            peak = peak.astype(int)

            colors.append(''.join(format(c, '02x') for c in peak))
        return colors # returns array
        #except:
            #await self.bot.say("```Error or no scipy. Install scipy doing 'pip3 install numpy' and 'pip3 install scipy' or read here: https://github.com/AznStevy/Maybe-Useful-Cogs/blob/master/README.md```")

    # converts hex to rgb
    def _hex_to_rgb(self, hex_num: str, a:int):
        h = hex_num.lstrip('#')

        # if only 3 characters are given
        if len(str(h)) == 3:
            expand = ''.join([x*2 for x in str(h)])
            h = expand

        colors = [int(h[i:i+2], 16) for i in (0, 2 ,4)]
        colors.append(a)
        return tuple(colors)

    # dampens the color given a parameter
    def _moderate_color(self, rgb, a, moderate_num):
        new_colors = []
        for color in rgb[:3]:
            if color > 128:
                color -= moderate_num
            else:
                color += moderate_num
            new_colors.append(color)
        new_colors.append(230)

        return tuple(new_colors)


    @profileset.command(pass_context=True, no_pm=True)
    async def info(self, ctx, *, info):
        """Set your user info."""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        max_char = 150

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        if len(info) < max_char:
            db.users.update_one({'user_id':user.id}, {'$set':{"info": info}})
            await self.bot.say("**Your info section has been succesfully set!**")
        else:
            await self.bot.say("**Your description has too many characters! Must be <{}**".format(max_char))

    @levelupset.command(name = "bg", pass_context=True, no_pm=True)
    async def levelbg(self, ctx, *, image_name:str):
        """Set your level background"""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            await self.bot.say("**Text-only commands allowed.**")
            return


        if image_name in self.backgrounds["levelup"].keys():
            if await self._process_purchase(ctx):
                db.users.update_one({'user_id':user.id}, {'$set':{"levelup_background": self.backgrounds["levelup"][image_name]}})
                await self.bot.say("**Your new level-up background has been succesfully set!**")
        else:
            await self.bot.say("That is not a valid bg. See available bgs at `{}backgrounds levelup`".format(prefix[0]))

    @profileset.command(name = "bg", pass_context=True, no_pm=True)
    async def profilebg(self, ctx, *, image_name:str):
        """Set your profile background"""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            await self.bot.say("**Text-only commands allowed.**")
            return


        if image_name in self.backgrounds["profile"].keys():
            if await self._process_purchase(ctx):
                db.users.update_one({'user_id':user.id}, {'$set':{"profile_background": self.backgrounds["profile"][image_name]}})
                await self.bot.say("**Your new profile background has been succesfully set!**")
        else:
            await self.bot.say("That is not a valid bg. See available bgs at `{}backgrounds profile`".format(prefix[0]))

    @rankset.command(name = "bg", pass_context=True, no_pm=True)
    async def rankbg(self, ctx, *, image_name:str):
        """Set your rank background"""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        if "text_only" in self.settings and server.id in self.settings["text_only"]:
            await self.bot.say("**Text-only commands allowed.**")
            return


        if image_name in self.backgrounds["rank"].keys():
            if await self._process_purchase(ctx):
                db.users.update_one({'user_id':user.id}, {'$set':{"rank_background": self.backgrounds["rank"][image_name]}})
                await self.bot.say("**Your new rank background has been succesfully set!**")
        else:
            await self.bot.say("That is not a valid bg. See available bgs at `{}backgrounds rank`".format(prefix[0]))

    @profileset.command(pass_context=True, no_pm=True)
    async def title(self, ctx, *, title):
        """Set your title."""
        user = ctx.message.author
        server = ctx.message.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        max_char = 20

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        if len(title) < max_char:
            userinfo["title"] = title
            db.users.update_one({'user_id':user.id}, {'$set':{"title": title}})
            await self.bot.say("**Your title has been succesfully set!**")
        else:
            await self.bot.say("**Your title has too many characters! Must be <{}**".format(max_char))

    @checks.admin_or_permissions(manage_server=True)
    @commands.group(pass_context=True)
    async def lvladmin(self, ctx):
        """Admin Toggle Features"""
        if ctx.invoked_subcommand is None:
            await send_cmd_help(ctx)
            return

    @checks.admin_or_permissions(manage_server=True)
    @lvladmin.group(pass_context=True)
    async def overview(self, ctx):
        """A list of settings"""
        user = ctx.message.author

        disabled_servers = []
        private_levels = []
        disabled_levels = []
        locked_channels = []

        for server in self.bot.servers:
            if "disabled_servers" in self.settings.keys() and str(server.id) in self.settings["disabled_servers"]:
                disabled_servers.append(server.name)
            if "lvl_msg_lock" in self.settings.keys() and server.id in self.settings["lvl_msg_lock"].keys():
                for channel in server.channels:
                    if self.settings["lvl_msg_lock"][server.id] == channel.id:
                        locked_channels.append("\n{} → #{}".format(server.name,channel.name))
            if "lvl_msg" in self.settings.keys() and server.id in self.settings["lvl_msg"]:
                disabled_levels.append(server.name)
            if "private_lvl_msg" in self.settings.keys() and server.id in self.settings["private_lvl_msg"]:
                private_levels.append(server.name)

        num_users = 0
        for i in db.users.find({}):
            num_users += 1

        msg = ""
        msg += "**Servers:** {}\n".format(len(self.bot.servers))
        msg += "**Unique Users:** {}\n".format(num_users)
        if "mention" in self.settings.keys():
            msg += "**Mentions:** {}\n".format(str(self.settings["mention"]))
        msg += "**Background Price:** {}\n".format(self.settings["bg_price"])
        if "badge_type" in self.settings.keys():
            msg += "**Badge type:** {}\n".format(self.settings["badge_type"])
        msg += "**Disabled Servers:** {}\n".format(", ".join(disabled_servers))
        msg += "**Enabled Level Messages:** {}\n".format(", ".join(disabled_levels))
        msg += "**Private Level Messages:** {}\n".format(", ".join(private_levels))
        msg += "**Channel Locks:** {}\n".format(", ".join(locked_channels))
        em = discord.Embed(description=msg, colour=user.colour)
        em.set_author(name="Settings Overview for {}".format(self.bot.user.name))
        await self.bot.say(embed = em)

    @lvladmin.command(pass_context=True, no_pm=True)
    async def msgcredits(self, ctx, credits:int = 0):
        '''Credits per message logged. Default = 0'''
        channel = ctx.message.channel
        server = ctx.message.server

        if credits < 0 or credits > 1000:
            await self.bot.say("**Please enter a valid number (0 - 1000)**".format(channel.name))
            return

        if "msg_credits" not in self.settings.keys():
            self.settings["msg_credits"] = {}

        self.settings["msg_credits"][server.id] = credits
        await self.bot.say("**Credits per message logged set to `{}`.**".format(str(credits)))

        fileIO('data/leveler/settings.json', "save", self.settings)

    @lvladmin.command(name="lock", pass_context=True, no_pm=True)
    async def lvlmsglock(self, ctx):
        '''Locks levelup messages to one channel. Disable command via locked channel.'''
        channel = ctx.message.channel
        server = ctx.message.server

        if "lvl_msg_lock" not in self.settings.keys():
            self.settings["lvl_msg_lock"] = {}

        if server.id in self.settings["lvl_msg_lock"]:
            if channel.id == self.settings["lvl_msg_lock"][server.id]:
                del self.settings["lvl_msg_lock"][server.id]
                await self.bot.say("**Level-up message lock disabled.**".format(channel.name))
            else:
                self.settings["lvl_msg_lock"][server.id] = channel.id
                await self.bot.say("**Level-up message lock changed to `#{}`.**".format(channel.name))
        else:
            self.settings["lvl_msg_lock"][server.id] = channel.id
            await self.bot.say("**Level-up messages locked to `#{}`**".format(channel.name))

        fileIO('data/leveler/settings.json', "save", self.settings)

    async def _process_purchase(self, ctx):
        user = ctx.message.author
        server = ctx.message.server

        try:
            bank = self.bot.get_cog('Economy').bank
            if bank.account_exists(user) and self.settings["bg_price"] != 0:
                if not bank.can_spend(user, self.settings["bg_price"]):
                    await self.bot.say("**Insufficient funds. Backgrounds changes cost: ${}**".format(self.settings["bg_price"]))
                    return False
                else:
                    await self.bot.say('**{}, you are about to buy a background for `{}`. Confirm by typing `yes`.**'.format(self._is_mention(user), self.settings["bg_price"]))
                    answer = await self.bot.wait_for_message(timeout=15, author=user)
                    if answer is None:
                        await self.bot.say('**Purchase canceled.**')
                        return False
                    elif "yes" not in answer.content.lower():
                        await self.bot.say('**Background not purchased.**')
                        return False
                    else:
                        new_balance = bank.get_balance(user) - self.settings["bg_price"]
                        bank.set_credits(user, new_balance)
                        return True
            else:
                if self.settings["bg_price"] == 0:
                    return True
                else:
                    await self.bot.say("**You don't have an account. Do {}bank register**".format(prefix))
                    return False
        except:
            if self.settings["bg_price"] == 0:
                return True
            else:
                await self.bot.say("**There was an error with economy cog. Fix to allow purchases or set price to $0. Currently ${}**".format(prefix, self.settings["bg_price"]))
                return False

    async def _give_chat_credit(self, user, server):
        try:
            bank = self.bot.get_cog('Economy').bank
            if bank.account_exists(user) and "msg_credits" in self.settings:
                bank.deposit_credits(user, self.settings["msg_credits"][server.id])
        except:
            pass

    @checks.is_owner()
    @lvladmin.command(no_pm=True)
    async def setprice(self, price:int):
        '''Set a price for background changes.'''
        if price < 0:
            await self.bot.say("**That is not a valid background price.**")
        else:
            self.settings["bg_price"] = price
            await self.bot.say("**Background price set to: `{}`!**".format(price))
            fileIO('data/leveler/settings.json', "save", self.settings)

    @checks.is_owner()
    @lvladmin.command(pass_context=True, no_pm=True)
    async def setlevel(self, ctx, user : discord.Member, level:int):
        '''Set a user's level. (What a cheater C:).'''
        org_user = ctx.message.author
        server = user.server
        channel = ctx.message.channel
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        if level < 0:
            await self.bot.say("**Please enter a positive number.**")
            return

        # get rid of old level exp
        old_server_exp = 0
        for i in range(userinfo["servers"][server.id]["level"]):
            old_server_exp += self._required_exp(i)
        userinfo["total_exp"] -= old_server_exp
        userinfo["total_exp"] -= userinfo["servers"][server.id]["current_exp"]

        # add in new exp
        total_exp = self._level_exp(level)
        userinfo["servers"][server.id]["current_exp"] = 0
        userinfo["servers"][server.id]["level"] = level
        userinfo["total_exp"] += total_exp

        db.users.update_one({'user_id':user.id}, {'$set':{
            "servers.{}.level".format(server.id): level,
            "servers.{}.current_exp".format(server.id): 0,
            "total_exp": userinfo["total_exp"]
            }})
        await self.bot.say("**{}'s Level has been set to `{}`.**".format(self._is_mention(user), level))
        await self._handle_levelup(user, userinfo, server, channel)

    @checks.is_owner()
    @lvladmin.command(no_pm=True)
    async def mention(self):
        '''Toggle mentions on messages.'''
        if "mention" not in self.settings.keys() or self.settings["mention"] == True:
            self.settings["mention"] = False
            await self.bot.say("**Mentions disabled.**")
        else:
            self.settings["mention"] = True
            await self.bot.say("**Mentions enabled.**")
        fileIO('data/leveler/settings.json', "save", self.settings)

    async def _valid_image_url(self, url):
        max_byte = 1000

        try:
            async with aiohttp.get(url) as r:
                image = await r.content.read()
            with open('data/leveler/test.png','wb') as f:
                f.write(image)
            image = Image.open('data/leveler/test.png').convert('RGBA')
            os.remove('data/leveler/test.png')
            return True
        except:
            return False

    @checks.admin_or_permissions(manage_server=True)
    @lvladmin.command(pass_context=True, no_pm=True)
    async def toggle(self, ctx):
        """Toggle most leveler commands on the current server."""
        server = ctx.message.server
        if server.id in self.settings["disabled_servers"]:
            self.settings["disabled_servers"] = list(filter(lambda a: a != server.id, self.settings["disabled_servers"]))
            await self.bot.say("**Leveler enabled on `{}`.**".format(server.name))
        else:
            self.settings["disabled_servers"].append(server.id)
            await self.bot.say("**Leveler disabled on `{}`.**".format(server.name))
        fileIO('data/leveler/settings.json', "save", self.settings)

    @checks.admin_or_permissions(manage_server=True)
    @lvladmin.command(pass_context=True, no_pm=True)
    async def textonly(self, ctx, all:str=None):
        """Toggle text-based messages on the server."""
        server = ctx.message.server
        user = ctx.message.author
        # deals with enabled array

        if "text_only" not in self.settings.keys():
            self.settings["text_only"] = []

        if all != None:
            if user.id == self.owner:
                if all == "disableall":
                    self.settings["text_only"] = []
                    await self.bot.say("**Text-only disabled for all servers.**")
                elif all == "enableall":
                    self.settings["lvl_msg"] = []
                    for server in self.bot.servers:
                        self.settings["text_only"].append(server.id)
                    await self.bot.say("**Text-only messages enabled for all servers.**")
            else:
                await self.bot.say("**No Permission.**")
        else:
            if server.id in self.settings["text_only"]:
                self.settings["text_only"].remove(server.id)
                await self.bot.say("**Text-only messages disabled for `{}`.**".format(server.name))
            else:
                self.settings["text_only"].append(server.id)
                await self.bot.say("**Text-only messages enabled for `{}`.**".format(server.name))
        fileIO('data/leveler/settings.json', "save", self.settings)

    @checks.admin_or_permissions(manage_server=True)
    @lvladmin.command(name="alerts", pass_context=True, no_pm=True)
    async def lvlalert(self, ctx, all:str=None):
        """Toggle level-up messages on the server."""
        server = ctx.message.server
        user = ctx.message.author

        # old version was boolean
        if not isinstance(self.settings["lvl_msg"], list):
            self.settings["lvl_msg"] = []

        if all != None:
            if user.id == self.owner:
                if all == "disableall":
                    self.settings["lvl_msg"] = []
                    await self.bot.say("**Level-up messages disabled for all servers.**")
                elif all == "enableall":
                    self.settings["lvl_msg"] = []
                    for server in self.bot.servers:
                        self.settings["lvl_msg"].append(server.id)
                    await self.bot.say("**Level-up messages enabled for all servers.**")
            else:
                await self.bot.say("**No Permission.**")
        else:
            if server.id in self.settings["lvl_msg"]:
                self.settings["lvl_msg"].remove(server.id)
                await self.bot.say("**Level-up alerts disabled for `{}`.**".format(server.name))
            else:
                self.settings["lvl_msg"].append(server.id)
                await self.bot.say("**Level-up alerts enabled for `{}`.**".format(server.name))
        fileIO('data/leveler/settings.json', "save", self.settings)

    @checks.admin_or_permissions(manage_server=True)
    @lvladmin.command(name="private", pass_context=True, no_pm=True)
    async def lvlprivate(self, ctx, all:str=None):
        """Toggles if lvl alert is a private message to the user."""
        server = ctx.message.server
        # deals with ENABLED array, not disabled

        if "private_lvl_msg" not in self.settings.keys():
            self.settings["private_lvl_msg"] = []

        if all != None:
            if user.id == self.owner:
                if all == "disableall":
                    self.settings["private_lvl_msg"] = []
                    await self.bot.say("**Private level-up messages disabled for all servers.**")
                elif all == "enableall":
                    self.settings["private_lvl_msg"] = []
                    for server in self.bot.servers:
                        self.settings["private_lvl_msg"].append(server.id)
                    await self.bot.say("**Private level-up messages enabled for all servers.**")
            else:
                await self.bot.say("**No Permission.**")
        else:
            if server.id in self.settings["private_lvl_msg"]:
                self.settings["private_lvl_msg"].remove(server.id)
                await self.bot.say("**Private level-up alerts disabled for `{}`.**".format(server.name))
            else:
                self.settings["private_lvl_msg"].append(server.id)
                await self.bot.say("**Private level-up alerts enabled for `{}`.**".format(server.name))

        fileIO('data/leveler/settings.json', "save", self.settings)

    @commands.group(pass_context=True)
    async def badge(self, ctx):
        """Badge Configuration Options"""
        if ctx.invoked_subcommand is None:
            await send_cmd_help(ctx)
            return

    @badge.command(name="available", pass_context=True, no_pm=True)
    async def available(self, ctx, global_badge:str = None):
        '''Get a list of available badges for server or 'global'.'''
        user = ctx.message.author
        server = ctx.message.server

        # get server stuff
        ids = [('global','Global',self.bot.user.avatar_url), (server.id, server.name, server.icon_url)]

        title_text = "**Available Badges**"
        index = 0
        for serverid, servername, icon_url in ids:
            em = discord.Embed(description='', colour=user.colour)
            em.set_author(name="{}".format(servername), icon_url = icon_url)
            msg = ""
            server_badge_info = db.badges.find_one({'server_id':serverid})
            if server_badge_info:
                server_badges = server_badge_info['badges']
                for badgename in server_badges:
                    badgeinfo = server_badges[badgename]
                    if badgeinfo['price'] == -1:
                        price = 'Non-purchasable'
                    elif badgeinfo['price'] == 0:
                        price = 'Free'
                    else:
                        price = badgeinfo['price']

                    msg += "**• {}** ({}) - {}\n".format(badgename, price, badgeinfo['description'])
            else:
                msg = "None"

            em.description = msg

            total_pages = 0
            for page in pagify(msg, ["\n"]):
                total_pages +=1

            counter = 1
            for page in pagify(msg, ["\n"]):
                if index == 0:
                    await self.bot.say(title_text, embed = em)
                else:
                    await self.bot.say(embed = em)
                index += 1

                em.set_footer(text = "Page {} of {}".format(counter, total_pages))
                counter += 1


    @badge.command(name="list", pass_context=True, no_pm=True)
    async def listuserbadges(self, ctx, user:discord.Member = None):
        '''Get the badges of a user.'''
        if user == None:
            user = ctx.message.author
        server = ctx.message.server
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        userinfo = self._badge_convert_dict(userinfo)

        # sort
        priority_badges = []
        for badgename in userinfo['badges'].keys():
            badge = userinfo['badges'][badgename]
            priority_num = badge["priority_num"]
            if priority_num != -1:
                priority_badges.append((badge, priority_num))
        sorted_badges = sorted(priority_badges, key=operator.itemgetter(1), reverse=True)

        badge_ranks = ""
        counter = 1
        for badge, priority_num in sorted_badges[:12]:
            badge_ranks += "**{}. {}** ({}) [{}] **—** {}\n".format(counter, badge['badge_name'], badge['server_name'], priority_num, badge['description'])
            counter += 1
        if not badge_ranks:
            badge_ranks = "None"

        em = discord.Embed(description='', colour=user.colour)

        total_pages = 0
        for page in pagify(badge_ranks, ["\n"]):
            total_pages +=1

        counter = 1
        for page in pagify(badge_ranks, ["\n"]):
            em.description = page
            em.set_author(name="Badges for {}".format(user.name), icon_url = user.avatar_url)
            em.set_footer(text = "Page {} of {}".format(counter, total_pages))
            await self.bot.say(embed = em)
            counter += 1

    @badge.command(name="buy", pass_context=True, no_pm=True)
    async def buy(self, ctx, name:str, global_badge:str = None):
        '''Get a badge from repository. optional = "-global"'''
        user = ctx.message.author
        server = ctx.message.server
        if global_badge == '-global':
            serverid = 'global'
        else:
            serverid = server.id
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        userinfo = self._badge_convert_dict(userinfo)
        server_badge_info = db.badges.find_one({'server_id':serverid})

        if server_badge_info:
            server_badges = server_badge_info['badges']
            if name in server_badges:

                if "{}_{}".format(name,str(serverid)) not in userinfo['badges'].keys():
                    badge_info = server_badges[name]
                    if badge_info['price'] == -1:
                        await self.bot.say('**That badge is not purchasable.**'.format(name))
                    elif badge_info['price'] == 0:
                        userinfo['badges']["{}_{}".format(name,str(serverid))] = server_badges[name]
                        db.users.update_one({'user_id':userinfo['user_id']}, {'$set':{
                            "badges":userinfo['badges'],
                            }})
                        await self.bot.say('**`{}` has been obtained.**'.format(name))
                    else:
                        # use the economy cog
                        bank = self.bot.get_cog('Economy').bank
                        await self.bot.say('**{}, you are about to buy the `{}` badge for `{}`. Confirm by typing "yes"**'.format(self._is_mention(user), name, badge_info['price']))
                        answer = await self.bot.wait_for_message(timeout=15, author=user)
                        if answer is None:
                            await self.bot.say('**Purchase canceled.**')
                            return
                        elif "yes" not in answer.content.lower():
                            await self.bot.say('**Badge not purchased.**')
                            return
                        else:
                            if bank.account_exists(user) and badge_info['price'] <= bank.get_balance(user):
                                bank.withdraw_credits(user, badge_info['price'])
                                userinfo['badges']["{}_{}".format(name,str(serverid))] = server_badges[name]
                                db.users.update_one({'user_id':userinfo['user_id']}, {'$set':{
                                    "badges":userinfo['badges'],
                                    }})
                                await self.bot.say('**You have bought the `{}` badge for `{}`.**'.format(name, badge_info['price']))
                            elif bank.account_exists(user) and bank.get_balance(user) < badge_info['price']:
                                await self.bot.say('**Not enough money! Need `{}` more.**'.format(badge_info['price'] - bank.get_balance(user)))
                            else:
                                await self.bot.say('**User does not exist in bank. Do {}bank register**'.format(prefix))
                else:
                    await self.bot.say('**{}, you already have this badge!**'.format(user.name))
            else:
                await self.bot.say('**The badge `{}` does not exist. (try `{}badge available`)**'.format(name, prefix[0]))
        else:
            await self.bot.say('**There are no badges to get! (try `{}badge get [name] -global`).**'.format(prefix[0]))

    @badge.command(name="set", pass_context=True, no_pm=True)
    async def set(self, ctx, name:str, priority_num:int):
        '''Set a badge to profile. -1(invis), 0(not on profile), max: 5000.'''
        user = ctx.message.author
        server = ctx.message.author
        await self._create_user(user, server)

        userinfo = db.users.find_one({'user_id':user.id})
        userinfo = self._badge_convert_dict(userinfo)

        if priority_num < -1 or priority_num > 5000:
            await self.bot.say("**Invalid priority number! -1-5000**")
            return

        for badge in userinfo['badges']:
            if userinfo['badges'][badge]['badge_name'] == name:
                userinfo['badges'][badge]['priority_num'] = priority_num
                db.users.update_one({'user_id':userinfo['user_id']}, {'$set':{
                    "badges":userinfo['badges'],
                    }})
                await self.bot.say("**The `{}` badge priority has been set to `{}`!**".format(userinfo['badges'][badge]['badge_name'], priority_num))
                break
        else:
            await self.bot.say("**You don't have that badge!**")

    def _badge_convert_dict(self, userinfo):
        if 'badges' not in userinfo or not isinstance(userinfo['badges'], dict):
            db.users.update_one({'user_id':userinfo['user_id']}, {'$set':{
                "badges":{},
                }})
        return db.users.find_one({'user_id':userinfo['user_id']})

    @checks.mod_or_permissions(manage_roles=True)
    @badge.command(name="add", pass_context = True, no_pm=True)
    async def addbadge(self, ctx, name:str, bg_img:str, border_color:str, price:int, *, description:str):
        """Add a badge. name = "Use Quotes", Colors = #hex. bg_img = url, price = -1(non-purchasable), 0,..."""

        user = ctx.message.author
        server = ctx.message.server

        # check members
        required_members = 35
        members = 0
        for member in server.members:
            if not member.bot:
                members += 1

        if user.id == self.owner:
            pass
        elif members < required_members:
            await self.bot.say("**You may only add badges in servers with {}+ non-bot members**".format(required_members))
            return

        if '-global' in description and user.id == self.owner:
            description = description.replace('-global', '')
            serverid = 'global'
            servername = 'global'
        else:
            serverid = server.id
            servername = server.name

        if '.' in name:
            await self.bot.say("**Name cannot contain `.`**")
            return

        if not await self._valid_image_url(bg_img):
            await self.bot.say("**Background is not valid. Enter hex or image url!**")
            return

        if not self._is_hex(border_color):
            await self.bot.say("**Border color is not valid!**")
            return

        if price < -1:
            await self.bot.say("**Price is not valid!**")
            return

        if len(description.split(" ")) > 40:
            await self.bot.say("**Description is too long! <=40**")
            return

        badges = db.badges.find_one({'server_id':serverid})
        if not badges:
            db.badges.insert_one({'server_id':serverid,
                'badges': {}})
            badges = db.badges.find_one({'server_id':serverid})

        new_badge = {
                "badge_name": name,
                "bg_img": bg_img,
                "price": price,
                "description": description,
                "border_color": border_color,
                "server_id": serverid,
                "server_name": servername,
                "priority_num": 0
            }

        if name not in badges['badges'].keys():
            # create the badge regardless
            badges['badges'][name] = new_badge
            db.badges.update_one({'server_id':serverid}, {'$set': {
                'badges': badges['badges']
                }})
            await self.bot.say("**`{}` Badge added in `{}` server.**".format(name, servername))
        else:
            # update badge in the server
            badges['badges'][name] = new_badge
            db.badges.update_one({'server_id':serverid}, {'$set': {
                'badges': badges['badges']
                }})

            # go though all users and update the badge. Doing it this way because dynamic does more accesses when doing profile
            for user in db.users.find({}):
                try:
                    user = self._badge_convert_dict(user)
                    userbadges = user['badges']
                    badge_name = "{}_{}".format(name, serverid)
                    if badge_name in userbadges.keys():
                        user_priority_num = userbadges[badge_name]['priority_num']
                        new_badge['priority_num'] = user_priority_num # maintain old priority number set by user
                        userbadges[badge_name] = new_badge
                        db.users.update_one({'user_id':user['user_id']}, {'$set': {
                            'badges': userbadges
                            }})
                except:
                    pass
            await self.bot.say("**The `{}` badge has been updated**".format(name))

    @checks.is_owner()
    @badge.command(no_pm=True)
    async def type(self, name:str):
        """circles or bars."""
        valid_types = ["circles", "bars"]
        if name.lower() not in valid_types:
            await self.bot.say("**That is not a valid badge type!**")
            return

        self.settings["badge_type"] = name.lower()
        await self.bot.say("**Badge type set to `{}`**".format(name.lower()))
        fileIO('data/leveler/settings.json', "save", self.settings)

    def _is_hex(self, color:str):
        if color != None and len(color) != 4 and len(color) != 7:
            return False

        reg_ex = r'^#(?:[0-9a-fA-F]{3}){1,2}$'
        return re.search(reg_ex, str(color))

    @checks.mod_or_permissions(manage_roles=True)
    @badge.command(name="delete", pass_context=True, no_pm=True)
    async def delbadge(self, ctx, *, name:str):
        """Delete a badge and remove from all users."""
        user = ctx.message.author
        channel = ctx.message.channel
        server = user.server

        return

        if '-global' in name and user.id == self.owner:
            name = name.replace(' -global', '')
            serverid = 'global'
        else:
            serverid = server.id

        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        userinfo = self._badge_convert_dict(userinfo)

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        serverbadges = db.badges.find_one({'server_id':serverid})
        if name in serverbadges['badges'].keys():
            del serverbadges['badges'][name]
            db.badges.update_one({'server_id':serverbadges['server_id']}, {'$set':{
                "badges":serverbadges["badges"],
                }})
            # remove the badge if there
            for user_info_temp in db.users.find({}):
                try:
                    user_info_temp = self._badge_convert_dict(user_info_temp)

                    badge_name = "{}_{}".format(name, serverid)
                    if badge_name in user_info_temp["badges"].keys():
                        del user_info_temp["badges"][badge_name]
                        db.users.update_one({'user_id':user_info_temp['user_id']}, {'$set':{
                            "badges":user_info_temp["badges"],
                            }})
                except:
                    pass

            await self.bot.say("**The `{}` badge has been removed.**".format(name))
        else:
            await self.bot.say("**That badge does not exist.**")

    @checks.mod_or_permissions(manage_roles=True)
    @badge.command(pass_context = True, no_pm=True)
    async def give(self, ctx, user : discord.Member, name: str):
        """Give a user a badge with a certain name"""
        org_user = ctx.message.author
        server = org_user.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        userinfo = self._badge_convert_dict(userinfo)

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        serverbadges = db.badges.find_one({'server_id':server.id})
        badges = serverbadges['badges']
        badge_name = "{}_{}".format(name, server.id)

        if name not in badges:
            await self.bot.say("**That badge doesn't exist in this server!**")
            return
        elif badge_name in badges.keys():
            await self.bot.say("**{} already has that badge!**".format(self._is_mention(user)))
            return
        else:
            userinfo["badges"][badge_name] = badges[name]
            db.users.update_one({'user_id':user.id}, {'$set':{"badges": userinfo["badges"]}})
            await self.bot.say("**{} has just given `{}` the `{}` badge!**".format(self._is_mention(org_user), self._is_mention(user), name))

    @checks.mod_or_permissions(manage_roles=True)
    @badge.command(pass_context = True, no_pm=True)
    async def take(self, ctx, user : discord.Member, name: str):
        """Take a user's badge."""
        org_user = ctx.message.author
        server = org_user.server
        # creates user if doesn't exist
        await self._create_user(user, server)
        userinfo = db.users.find_one({'user_id':user.id})
        userinfo = self._badge_convert_dict(userinfo)

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("Leveler commands for this server are disabled.")
            return

        serverbadges = db.badges.find_one({'server_id':server.id})
        badges = serverbadges['badges']
        badge_name = "{}_{}".format(name, server.id)

        if name not in badges:
            await self.bot.say("**That badge doesn't exist in this server!**")
        elif badge_name not in userinfo["badges"]:
            await self.bot.say("**{} does not have that badge!**".format(self._is_mention(user)))
        else:
            if userinfo['badges'][badge_name]['price'] == -1:
                del userinfo["badges"][badge_name]
                db.users.update_one({'user_id':user.id}, {'$set':{"badges": userinfo["badges"]}})
                await self.bot.say("**{} has taken the `{}` badge from {}! :upside_down:**".format(self._is_mention(org_user), name, self._is_mention(user)))
            else:
                await self.bot.say("**You can't take away purchasable badges!**")

    @checks.mod_or_permissions(manage_roles=True)
    @badge.command(name = 'link', no_pm=True, pass_context=True)
    async def linkbadge(self, ctx, badge_name:str, level:int):
        """Associate a role with a level."""
        server = ctx.message.server
        serverbadges = db.badges.find_one({'server_id':server.id})

        if serverbadges == None:
            await self.bot.say("**This server does not have any badges!**")
            return

        if badge_name not in serverbadges['badges'].keys():
            await self.bot.say("**Please make sure the `{}` badge exists!**".format(badge_name))
            return
        else:
            server_linked_badges = db.badgelinks.find_one({'server_id':server.id})
            if not server_linked_badges:
                new_server = {
                    'server_id': server.id,
                    'badges': {
                        badge_name:str(level)
                    }
                }
                db.badgelinks.insert_one(new_server)
            else:
                server_linked_badges['badges'][badge_name] = str(level)
                db.badgelinks.update_one({'server_id':server.id}, {'$set':{'badges':server_linked_badges['badges']}})
            await self.bot.say("**The `{}` badge has been linked to level `{}`**".format(badge_name, level))

    @checks.admin_or_permissions(manage_roles=True)
    @badge.command(name = 'unlink', no_pm=True, pass_context=True)
    async def unlinkbadge(self, ctx, badge_name:str):
        """Delete a role/level association."""
        server = ctx.message.server

        server_linked_badges = db.badgelinks.find_one({'server_id':server.id})
        badge_links = server_linked_badges['badges']

        if badge_name in badge_links.keys():
            await self.bot.say("**Badge/Level association `{}`/`{}` removed.**".format(badge_name, badge_links[badge_name]))
            del badge_links[badge_name]
            db.badgelinks.update_one({'server_id':server.id},{'$set':{'badges':badge_links}})
        else:
            await self.bot.say("**The `{}` badge is not linked to any levels!**".format(badge_name))

    @checks.mod_or_permissions(manage_roles=True)
    @badge.command(name = 'listlinks', no_pm=True, pass_context=True)
    async def listbadge(self, ctx):
        """List level/role associations."""
        server = ctx.message.server
        user = ctx.message.author

        server_badges = db.badgelinks.find_one({'server_id':server.id})

        em = discord.Embed(description='', colour=user.colour)
        em.set_author(name="Current Badge - Level Links for {}".format(server.name), icon_url = server.icon_url)

        if server_badges == None or 'badges' not in server_badges or server_badges['badges'] == {}:
            msg = 'None'
        else:
            badges = server_badges['badges']
            msg = '**Badge** → Level\n'
            for badge in badges.keys():
                msg += '**• {} →** {}\n'.format(badge, badges[badge])

        em.description = msg
        await self.bot.say(embed = em)

    @commands.group(pass_context=True)
    async def role(self, ctx):
        """Admin Background Configuration"""
        if ctx.invoked_subcommand is None:
            await send_cmd_help(ctx)
            return

    @checks.mod_or_permissions(manage_roles=True)
    @role.command(name = 'link', no_pm=True, pass_context=True)
    async def linkrole(self, ctx, role_name:str, level:int, remove_role = None):
        """Associate a role with a level. Removes previous role if given."""
        server = ctx.message.server

        role_obj = discord.utils.find(lambda r: r.name == role_name, server.roles)
        remove_role_obj = discord.utils.find(lambda r: r.name == remove_role, server.roles)
        if role_obj == None or (remove_role != None and remove_role_obj == None):
            if remove_role == None:
                await self.bot.say("**Please make sure the `{}` role exists!**".format(role_name))
            else:
                await self.bot.say("**Please make sure the `{}` and/or `{}` roles exist!**".format(role_name, remove_role))
        else:
            server_roles = db.roles.find_one({'server_id':server.id})
            if not server_roles:
                new_server = {
                    'server_id': server.id,
                    'roles': {
                        role_name: {
                            'level':str(level),
                            'remove_role': remove_role
                            }
                    }
                }
                db.roles.insert_one(new_server)
            else:
                if role_name not in server_roles['roles']:
                    server_roles['roles'][role_name] = {}

                server_roles['roles'][role_name]['level'] = str(level)
                server_roles['roles'][role_name]['remove_role'] = remove_role
                db.roles.update_one({'server_id':server.id}, {'$set':{'roles':server_roles['roles']}})

            if remove_role == None:
                await self.bot.say("**The `{}` role has been linked to level `{}`**".format(role_name, level))
            else:
                await self.bot.say("**The `{}` role has been linked to level `{}`. Will also remove `{}` role.**".format(
                    role_name, level, remove_role))

    @checks.mod_or_permissions(manage_roles=True)
    @role.command(name = 'unlink', no_pm=True, pass_context=True)
    async def unlinkrole(self, ctx, role_name:str):
        """Delete a role/level association."""
        server = ctx.message.server

        server_roles = db.roles.find_one({'server_id':server.id})
        roles = server_roles['roles']

        if role_name in roles:
            await self.bot.say("**Role/Level association `{}`/`{}` removed.**".format(role_name, roles[role_name]['level']))
            del roles[role_name]
            db.roles.update_one({'server_id':server.id},{'$set':{'roles':roles}})
        else:
            await self.bot.say("**The `{}` role is not linked to any levels!**".format(role_name))

    @checks.mod_or_permissions(manage_roles=True)
    @role.command(name = 'listlinks', no_pm=True, pass_context=True)
    async def listrole(self, ctx):
        """List level/role associations."""
        server = ctx.message.server
        user = ctx.message.author

        server_roles = db.roles.find_one({'server_id':server.id})

        em = discord.Embed(description='', colour=user.colour)
        em.set_author(name="Current Role - Level Links for {}".format(server.name), icon_url = server.icon_url)

        if server_roles == None or 'roles' not in server_roles or server_roles['roles'] == {}:
            msg = 'None'
        else:
            roles = server_roles['roles']
            msg = '**Role** → Level\n'
            for role in roles:
                if roles[role]['remove_role'] != None:
                    msg += '**• {} →** {} (Removes: {})\n'.format(role, roles[role]['level'], roles[role]['remove_role'])
                else:
                    msg += '**• {} →** {}\n'.format(role, roles[role]['level'])

        em.description = msg
        await self.bot.say(embed = em)

    @lvladmin.group(name = "bg", pass_context=True)
    async def lvladminbg(self, ctx):
        """Admin Background Configuration"""
        if ctx.invoked_subcommand is None or \
                isinstance(ctx.invoked_subcommand, commands.Group):
            await send_cmd_help(ctx)
            return

    @checks.is_owner()
    @lvladminbg.command(no_pm=True)
    async def addprofilebg(self, name:str, url:str):
        """Add a profile background. Proportions: (290px x 290px)"""
        if name in self.backgrounds["profile"].keys():
            await self.bot.say("**That profile background name already exists!**")
        elif not await self._valid_image_url(url):
            await self.bot.say("**That is not a valid image url!**")
        else:
            self.backgrounds["profile"][name] = url
            fileIO('data/leveler/backgrounds.json', "save", self.backgrounds)
            await self.bot.say("**New profile background(`{}`) added.**".format(name))

    @checks.is_owner()
    @lvladminbg.command(no_pm=True)
    async def addrankbg(self, name:str, url:str):
        """Add a rank background. Proportions: (360px x 100px)"""
        if name in self.backgrounds["rank"].keys():
            await self.bot.say("**That rank background name already exists!**")
        elif not await self._valid_image_url(url):
            await self.bot.say("**That is not a valid image url!**")
        else:
            self.backgrounds["rank"][name] = url
            fileIO('data/leveler/backgrounds.json', "save", self.backgrounds)
            await self.bot.say("**New rank background(`{}`) added.**".format(name))

    @checks.is_owner()
    @lvladminbg.command(no_pm=True)
    async def addlevelbg(self, name:str, url:str):
        '''Add a level-up background. Proportions: (85px x 105px)'''
        if name in self.backgrounds["levelup"].keys():
            await self.bot.say("**That level-up background name already exists!**")
        elif not await self._valid_image_url(url):
            await self.bot.say("**That is not a valid image url!**")
        else:
            self.backgrounds["levelup"][name] = url
            fileIO('data/leveler/backgrounds.json', "save", self.backgrounds)
            await self.bot.say("**New level-up background(`{}`) added.**".format(name))

    @checks.is_owner()
    @lvladminbg.command(no_pm=True, pass_context=True)
    async def setcustombg(self, ctx, bg_type:str, user_id:str, img_url:str):
        """Set one-time custom background"""
        valid_types = ['profile', 'rank', 'levelup']
        type_input = bg_type.lower()

        if type_input not in valid_types:
            await self.bot.say('**Please choose a valid type: `profile`, `rank`, `levelup`.')
            return

        # test if valid user_id
        userinfo = db.users.find_one({'user_id':user_id})
        if not userinfo:
            await self.bot.say("**That is not a valid user id!**")
            return

        if not await self._valid_image_url(img_url):
            await self.bot.say("**That is not a valid image url!**")
            return

        db.users.update_one({'user_id':user_id}, {'$set':{"{}_background".format(type_input): img_url}})
        await self.bot.say("**User {} custom {} background set.**".format(user_id, bg_type))

    @checks.is_owner()
    @lvladminbg.command(no_pm=True)
    async def delprofilebg(self, name:str):
        '''Delete a profile background.'''
        if name in self.backgrounds["profile"].keys():
            del self.backgrounds["profile"][name]
            fileIO('data/leveler/backgrounds.json', "save", self.backgrounds)
            await self.bot.say("**The profile background(`{}`) has been deleted.**".format(name))
        else:
            await self.bot.say("**That profile background name doesn't exist.**")

    @checks.is_owner()
    @lvladminbg.command(no_pm=True)
    async def delrankbg(self, name:str):
        '''Delete a rank background.'''
        if name in self.backgrounds["rank"].keys():
            del self.backgrounds["rank"][name]
            fileIO('data/leveler/backgrounds.json', "save", self.backgrounds)
            await self.bot.say("**The rank background(`{}`) has been deleted.**".format(name))
        else:
            await self.bot.say("**That rank background name doesn't exist.**")

    @checks.is_owner()
    @lvladminbg.command(no_pm=True)
    async def dellevelbg(self, name:str):
        '''Delete a level background.'''
        if name in self.backgrounds["levelup"].keys():
            del self.backgrounds["levelup"][name]
            fileIO('data/leveler/backgrounds.json', "save", self.backgrounds)
            await self.bot.say("**The level-up background(`{}`) has been deleted.**".format(name))
        else:
            await self.bot.say("**That level-up background name doesn't exist.**")

    @commands.command(name = 'backgrounds', pass_context=True, no_pm=True)
    async def disp_backgrounds(self, ctx, type:str = None):
        '''Gives a list of backgrounds. [p]backgrounds [profile|rank|levelup]'''
        server = ctx.message.server
        user = ctx.message.author
        max_all = 18

        if server.id in self.settings["disabled_servers"]:
            await self.bot.say("**Leveler commands for this server are disabled!**")
            return

        em = discord.Embed(description='', colour=user.colour)
        if not type:
            em.set_author(name="All Backgrounds for {}".format(self.bot.user.name), icon_url = self.bot.user.avatar_url)

            for category in self.backgrounds.keys():
                bg_url = []
                for background_name in sorted(self.backgrounds[category].keys()):
                    bg_url.append("[{}]({})".format(background_name, self.backgrounds[category][background_name]))
                max_bg = min(max_all, len(bg_url))
                bgs = ", ".join(bg_url[0:max_bg])
                if len(bg_url) >= max_all:
                    bgs += "..."
                em.add_field(name = category.upper(), value = bgs, inline = False)
            await self.bot.say(embed = em)
        else:
            if type.lower() == "profile":
                em.set_author(name="Profile Backgrounds for {}".format(self.bot.user.name), icon_url = self.bot.user.avatar_url)
                bg_key = "profile"
            elif type.lower() == "rank":
                em.set_author(name="Rank Backgrounds for {}".format(self.bot.user.name), icon_url = self.bot.user.avatar_url)
                bg_key = "rank"
            elif type.lower() == "levelup":
                em.set_author(name="Level Up Backgrounds for {}".format(self.bot.user.name), icon_url = self.bot.user.avatar_url)
                bg_key = "levelup"
            else:
                bg_key = None

            if bg_key:
                bg_url = []
                for background_name in sorted(self.backgrounds[bg_key].keys()):
                    bg_url.append("[{}]({})".format(background_name, self.backgrounds[bg_key][background_name]))
                bgs = ", ".join(bg_url)

                total_pages = 0
                for page in pagify(bgs, [" "]):
                    total_pages +=1

                counter = 1
                for page in pagify(bgs, [" "]):
                    em.description = page
                    em.set_footer(text = "Page {} of {}".format(counter, total_pages))
                    await self.bot.say(embed = em)
                    counter += 1
            else:
                await self.bot.say("**Invalid Background Type. (profile, rank, levelup)**")

    async def draw_profile(self, user, server):
        font_thin_file = 'data/leveler/fonts/Uni_Sans_Thin.ttf'
        font_heavy_file = 'data/leveler/fonts/Uni_Sans_Heavy.ttf'
        font_file = 'data/leveler/fonts/SourceSansPro-Regular.ttf'
        font_bold_file = 'data/leveler/fonts/SourceSansPro-Semibold.ttf'

        name_fnt = ImageFont.truetype(font_heavy_file, 30)
        name_u_fnt = ImageFont.truetype(font_unicode_file, 30)
        title_fnt = ImageFont.truetype(font_heavy_file, 22)
        title_u_fnt = ImageFont.truetype(font_unicode_file, 23)
        label_fnt = ImageFont.truetype(font_bold_file, 18)
        exp_fnt = ImageFont.truetype(font_bold_file, 13)
        large_fnt = ImageFont.truetype(font_thin_file, 33)
        rep_fnt = ImageFont.truetype(font_heavy_file, 26)
        rep_u_fnt = ImageFont.truetype(font_unicode_file, 30)
        text_fnt = ImageFont.truetype(font_file, 14)
        text_u_fnt = ImageFont.truetype(font_unicode_file, 14)
        symbol_u_fnt = ImageFont.truetype(font_unicode_file, 15)

        def _write_unicode(text, init_x, y, font, unicode_font, fill):
            write_pos = init_x

            for char in text:
                if char.isalnum() or char in string.punctuation or char in string.whitespace:
                    draw.text((write_pos, y), char, font=font, fill=fill)
                    write_pos += font.getsize(char)[0]
                else:
                    draw.text((write_pos, y), u"{}".format(char), font=unicode_font, fill=fill)
                    write_pos += unicode_font.getsize(char)[0]
        # get urls
        userinfo = db.users.find_one({'user_id':user.id})
        self._badge_convert_dict(userinfo)
        userinfo = db.users.find_one({'user_id':user.id}) ##############################################
        bg_url = userinfo["profile_background"]
        profile_url = user.avatar_url

        # COLORS
        white_color = (240,240,240,255)
        light_color = (160,160,160,255)
        if "rep_color" not in userinfo.keys() or not userinfo["rep_color"]:
            rep_fill = (92,130,203,230)
        else:
            rep_fill = tuple(userinfo["rep_color"])
        # determines badge section color, should be behind the titlebar
        if "badge_col_color" not in userinfo.keys() or not userinfo["badge_col_color"]:
            badge_fill = (128,151,165,230)
        else:
            badge_fill = tuple(userinfo["badge_col_color"])
        if "profile_info_color" in userinfo.keys():
            info_fill = tuple(userinfo["profile_info_color"])
        else:
            info_fill = (30, 30 ,30, 220)
        info_fill_tx = (info_fill[0], info_fill[1], info_fill[2], 150)
        if "profile_exp_color" not in userinfo.keys() or not userinfo["profile_exp_color"]:
            exp_fill = (255, 255, 255, 230)
        else:
            exp_fill = tuple(userinfo["profile_exp_color"])
        if badge_fill == (128,151,165,230):
            level_fill = white_color
        else:
            level_fill = self._contrast(exp_fill, rep_fill, badge_fill)

        # create image objects
        bg_image = Image
        profile_image = Image

        async with aiohttp.get(bg_url) as r:
            image = await r.content.read()
        with open('data/leveler/temp/{}_temp_profile_bg.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(profile_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/{}_temp_profile_profile.png'.format(user.id),'wb') as f:
            f.write(image)

        bg_image = Image.open('data/leveler/temp/{}_temp_profile_bg.png'.format(user.id)).convert('RGBA')
        profile_image = Image.open('data/leveler/temp/{}_temp_profile_profile.png'.format(user.id)).convert('RGBA')

        # set canvas
        bg_color = (255,255,255,0)
        result = Image.new('RGBA', (340, 390), bg_color)
        process = Image.new('RGBA', (340, 390), bg_color)

        # draw
        draw = ImageDraw.Draw(process)

        # puts in background
        bg_image = bg_image.resize((340, 340), Image.ANTIALIAS)
        bg_image = bg_image.crop((0,0,340, 305))
        result.paste(bg_image,(0,0))

        # draw filter
        draw.rectangle([(0,0),(340, 340)], fill=(0,0,0,10))

        # draw transparent overlay
        vert_pos = 305
        left_pos = 0
        right_pos = 340
        title_height = 30
        gap = 3

        draw.rectangle([(0,134), (340, 325)], fill=info_fill_tx) # general content
        # draw profile circle
        multiplier = 8
        lvl_circle_dia = 116
        circle_left = 14
        circle_top = 48
        raw_length = lvl_circle_dia * multiplier

        # create mask
        mask = Image.new('L', (raw_length, raw_length), 0)
        draw_thumb = ImageDraw.Draw(mask)
        draw_thumb.ellipse((0, 0) + (raw_length, raw_length), fill = 255, outline = 0)

        # border
        lvl_circle = Image.new("RGBA", (raw_length, raw_length))
        draw_lvl_circle = ImageDraw.Draw(lvl_circle)
        draw_lvl_circle.ellipse([0, 0, raw_length, raw_length], fill=(255, 255, 255, 255), outline = (255, 255, 255, 250))
        # put border
        lvl_circle = lvl_circle.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        lvl_bar_mask = mask.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        process.paste(lvl_circle, (circle_left, circle_top), lvl_bar_mask)

        # put in profile picture
        total_gap = 6
        border = int(total_gap/2)
        profile_size = lvl_circle_dia - total_gap
        raw_length = profile_size * multiplier
        output = ImageOps.fit(profile_image, (raw_length, raw_length), centering=(0.5, 0.5))
        output = output.resize((profile_size, profile_size), Image.ANTIALIAS)
        mask = mask.resize((profile_size, profile_size), Image.ANTIALIAS)
        profile_image = profile_image.resize((profile_size, profile_size), Image.ANTIALIAS)
        process.paste(profile_image, (circle_left + border, circle_top + border), mask)

        # write label text
        white_color = (240,240,240,255)
        light_color = (160,160,160,255)
        dark_color = (35, 35, 35, 255)

        head_align = 140
        # determine info text color
        info_text_color = self._contrast(info_fill, white_color, dark_color)
        _write_unicode(self._truncate_text(user.name, 22).upper(), head_align, 142, name_fnt, name_u_fnt, info_text_color) # NAME
        _write_unicode(userinfo["title"].upper(), head_align, 170, title_fnt, title_u_fnt, info_text_color)

        # draw divider
        draw.rectangle([(0,323), (340, 324)], fill=(0,0,0,255)) # box
        # draw text box
        draw.rectangle([(0,324), (340, 390)], fill=(info_fill[0],info_fill[1],info_fill[2],255)) # box

        #rep_text = "{} REP".format(userinfo["rep"])
        rep_text = "{}".format(userinfo["rep"])
        _write_unicode("❤", 257, 9, rep_fnt, rep_u_fnt, info_text_color)
        draw.text((self._center(278, 340, rep_text, rep_fnt), 10), rep_text,  font=rep_fnt, fill=info_text_color) # Exp Text

        lvl_left = 100
        label_align = 362 # vertical
        draw.text((self._center(0, 140, "    RANK", label_fnt), label_align), "    RANK",  font=label_fnt, fill=info_text_color) # Rank
        draw.text((self._center(0, 340, "    LEVEL", label_fnt), label_align), "    LEVEL",  font=label_fnt, fill=info_text_color) # Exp
        draw.text((self._center(200, 340, "BALANCE", label_fnt), label_align), "BALANCE",  font=label_fnt, fill=info_text_color) # Credits

        if "linux" in platform.system().lower():
            global_symbol = u"\U0001F30E "
            fine_adjust = 1
        else:
            global_symbol = "G."
            fine_adjust = 0

        _write_unicode(global_symbol, 36, label_align + 5, label_fnt, symbol_u_fnt, info_text_color) # Symbol
        _write_unicode(global_symbol, 134, label_align + 5, label_fnt, symbol_u_fnt, info_text_color) # Symbol

        # userinfo
        global_rank = "#{}".format(await self._find_global_rank(user))
        global_level = "{}".format(self._find_level(userinfo["total_exp"]))
        draw.text((self._center(0, 140, global_rank, large_fnt), label_align-27), global_rank,  font=large_fnt, fill=info_text_color) # Rank
        draw.text((self._center(0, 340, global_level, large_fnt), label_align-27), global_level,  font=large_fnt, fill=info_text_color) # Exp
        # draw level bar
        exp_font_color = self._contrast(exp_fill, light_color, dark_color)
        exp_frac = int(userinfo["total_exp"] - self._level_exp(int(global_level)))
        exp_total = self._required_exp(int(global_level) + 1)
        bar_length = int(exp_frac/exp_total * 340)
        draw.rectangle([(0, 305), (340, 323)], fill=(level_fill[0],level_fill[1],level_fill[2],245)) # level box
        draw.rectangle([(0, 305), (bar_length, 323)], fill=(exp_fill[0],exp_fill[1],exp_fill[2],255)) # box
        exp_text = "{}/{}".format(exp_frac, exp_total)# Exp
        draw.text((self._center(0, 340, exp_text, exp_fnt), 305), exp_text,  font=exp_fnt, fill=exp_font_color) # Exp Text

        try:
            bank = self.bot.get_cog('Economy').bank
            if bank.account_exists(user):
                credits = bank.get_balance(user)
            else:
                credits = 0
        except:
            credits = 0
        credit_txt = "${}".format(credits)
        draw.text((self._center(200, 340, credit_txt, large_fnt), label_align-27), self._truncate_text(credit_txt, 18),  font=large_fnt, fill=info_text_color) # Credits

        if userinfo["title"] == '':
            offset = 170
        else:
            offset = 195
        margin = 140
        txt_color = self._contrast(info_fill, white_color, dark_color)
        for line in textwrap.wrap(userinfo["info"], width=32):
        # for line in textwrap.wrap('userinfo["info"]', width=200):
            # draw.text((margin, offset), line, font=text_fnt, fill=white_color)
            _write_unicode(line, margin, offset, text_fnt, text_u_fnt, txt_color)
            offset += text_fnt.getsize(line)[1] + 2

        # sort badges
        priority_badges = []

        for badgename in userinfo['badges'].keys():
            badge = userinfo['badges'][badgename]
            priority_num = badge["priority_num"]
            if priority_num != 0 and priority_num != -1:
                priority_badges.append((badge, priority_num))
        sorted_badges = sorted(priority_badges, key=operator.itemgetter(1), reverse=True)

        # TODO: simplify this. it shouldn't be this complicated... sacrifices conciseness for customizability
        if "badge_type" not in self.settings.keys() or self.settings["badge_type"] == "circles":
            # circles require antialiasing
            vert_pos = 172
            right_shift = 0
            left = 9 + right_shift
            right = 52 + right_shift
            size = 38
            total_gap = 4 # /2
            hor_gap = 6
            vert_gap = 6
            border_width = int(total_gap/2)
            multiplier = 6 # for antialiasing
            raw_length = size * multiplier
            mult = [
                (0,0), (1,0), (2,0),
                (0,1), (1,1), (2,1),
                (0,2), (1,2), (2,2)]
            for num in range(9):
                coord = (left + int(mult[num][0])*int(hor_gap+size), vert_pos + int(mult[num][1])*int(vert_gap + size))
                if num < len(sorted_badges[:9]):
                    pair = sorted_badges[num]
                    badge = pair[0]
                    bg_color = badge["bg_img"]
                    border_color = badge["border_color"]
                    # draw mask circle
                    mask = Image.new('L', (raw_length, raw_length), 0)
                    draw_thumb = ImageDraw.Draw(mask)
                    draw_thumb.ellipse((0, 0) + (raw_length, raw_length), fill = 255, outline = 0)

                    # determine image or color for badge bg
                    if await self._valid_image_url(bg_color):
                        # get image
                        async with aiohttp.get(bg_color) as r:
                            image = await r.content.read()
                        with open('data/leveler/temp/{}_temp_badge.png'.format(user.id),'wb') as f:
                            f.write(image)
                        badge_image = Image.open('data/leveler/temp/{}_temp_badge.png'.format(user.id)).convert('RGBA')
                        badge_image = badge_image.resize((raw_length, raw_length), Image.ANTIALIAS)

                        # structured like this because if border = 0, still leaves outline.
                        if border_color:
                            square = Image.new('RGBA', (raw_length, raw_length), border_color)
                            # put border on ellipse/circle
                            output = ImageOps.fit(square, (raw_length, raw_length), centering=(0.5, 0.5))
                            output = output.resize((size, size), Image.ANTIALIAS)
                            outer_mask = mask.resize((size, size), Image.ANTIALIAS)
                            process.paste(output, coord, outer_mask)

                            # put on ellipse/circle
                            output = ImageOps.fit(badge_image, (raw_length, raw_length), centering=(0.5, 0.5))
                            output = output.resize((size - total_gap, size - total_gap), Image.ANTIALIAS)
                            inner_mask = mask.resize((size - total_gap, size - total_gap), Image.ANTIALIAS)
                            process.paste(output, (coord[0] + border_width, coord[1] + border_width), inner_mask)
                        else:
                            # put on ellipse/circle
                            output = ImageOps.fit(badge_image, (raw_length, raw_length), centering=(0.5, 0.5))
                            output = output.resize((size, size), Image.ANTIALIAS)
                            outer_mask = mask.resize((size, size), Image.ANTIALIAS)
                            process.paste(output, coord, outer_mask)
                else:
                    plus_fill = exp_fill
                    # put on ellipse/circle
                    plus_square = Image.new('RGBA', (raw_length, raw_length))
                    plus_draw = ImageDraw.Draw(plus_square)
                    plus_draw.rectangle([(0,0), (raw_length, raw_length)], fill=(info_fill[0],info_fill[1],info_fill[2],245))
                    # draw plus signs
                    margin = 60
                    thickness = 40
                    v_left = int(raw_length/2 - thickness/2)
                    v_right = v_left + thickness
                    v_top = margin
                    v_bottom = raw_length - margin
                    plus_draw.rectangle([(v_left,v_top), (v_right, v_bottom)], fill=(plus_fill[0],plus_fill[1],plus_fill[2],245))
                    h_left = margin
                    h_right = raw_length - margin
                    h_top = int(raw_length/2 - thickness/2)
                    h_bottom = h_top + thickness
                    plus_draw.rectangle([(h_left,h_top), (h_right, h_bottom)], fill=(plus_fill[0],plus_fill[1],plus_fill[2],245))
                    # put border on ellipse/circle
                    output = ImageOps.fit(plus_square, (raw_length, raw_length), centering=(0.5, 0.5))
                    output = output.resize((size, size), Image.ANTIALIAS)
                    outer_mask = mask.resize((size, size), Image.ANTIALIAS)
                    process.paste(output, coord, outer_mask)

                # attempt to remove badge image
                try:
                    os.remove('data/leveler/temp/{}_temp_badge.png'.format(user.id))
                except:
                    pass

        result = Image.alpha_composite(result, process)
        result = self._add_corners(result, 25)
        result.save('data/leveler/temp/{}_profile.png'.format(user.id),'PNG', quality=100)

        # remove images
        try:
            os.remove('data/leveler/temp/{}_temp_profile_bg.png'.format(user.id))
        except:
            pass
        try:
            os.remove('data/leveler/temp/{}_temp_profile_profile.png'.format(user.id))
        except:
            pass

    # returns color that contrasts better in background
    def _contrast(self, bg_color, color1, color2):
        color1_ratio = self._contrast_ratio(bg_color, color1)
        color2_ratio = self._contrast_ratio(bg_color, color2)
        if color1_ratio >= color2_ratio:
            return color1
        else:
            return color2

    def _luminance(self, color):
        # convert to greyscale
        luminance = float((0.2126*color[0]) + (0.7152*color[1]) + (0.0722*color[2]))
        return luminance

    def _contrast_ratio(self, bgcolor, foreground):
        f_lum = float(self._luminance(foreground)+0.05)
        bg_lum = float(self._luminance(bgcolor)+0.05)

        if bg_lum > f_lum:
            return bg_lum/f_lum
        else:
            return f_lum/bg_lum

    # returns a string with possibly a nickname
    def _name(self, user, max_length):
        if user.name == user.display_name:
            return user.name
        else:
            return "{} ({})".format(user.name, self._truncate_text(user.display_name, max_length - len(user.name) - 3), max_length)

    async def _add_dropshadow(self, image, offset=(4,4), background=0x000, shadow=0x0F0, border=3, iterations=5):
        totalWidth = image.size[0] + abs(offset[0]) + 2*border
        totalHeight = image.size[1] + abs(offset[1]) + 2*border
        back = Image.new(image.mode, (totalWidth, totalHeight), background)

        # Place the shadow, taking into account the offset from the image
        shadowLeft = border + max(offset[0], 0)
        shadowTop = border + max(offset[1], 0)
        back.paste(shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]])

        n = 0
        while n < iterations:
            back = back.filter(ImageFilter.BLUR)
            n += 1

        # Paste the input image onto the shadow backdrop
        imageLeft = border - min(offset[0], 0)
        imageTop = border - min(offset[1], 0)
        back.paste(image, (imageLeft, imageTop))
        return back

    """
    async def draw_rank(self, user, server):
        # fonts
        name_fnt = ImageFont.truetype(font_bold_file, 22)
        header_u_fnt = ImageFont.truetype(font_unicode_file, 18)
        sub_header_fnt = ImageFont.truetype(font_bold_file, 14)
        badge_fnt = ImageFont.truetype(font_bold_file, 12)
        large_fnt = ImageFont.truetype(font_bold_file, 33)
        level_label_fnt = ImageFont.truetype(font_bold_file, 22)
        general_info_fnt = ImageFont.truetype(font_bold_file, 15)
        general_info_u_fnt = ImageFont.truetype(font_unicode_file, 11)
        credit_fnt = ImageFont.truetype(font_bold_file, 10)

        def _write_unicode(text, init_x, y, font, unicode_font, fill):
            write_pos = init_x

            for char in text:
                if char.isalnum() or char in string.punctuation or char in string.whitespace:
                    draw.text((write_pos, y), char, font=font, fill=fill)
                    write_pos += font.getsize(char)[0]
                else:
                    draw.text((write_pos, y), u"{}".format(char), font=unicode_font, fill=fill)
                    write_pos += unicode_font.getsize(char)[0]

        userinfo = db.users.find_one({'user_id':user.id})
        # get urls
        bg_url = userinfo["rank_background"]
        profile_url = user.avatar_url
        server_icon_url = server.icon_url

        # create image objects
        bg_image = Image
        profile_image = Image

        async with aiohttp.get(bg_url) as r:
            image = await r.content.read()
        with open('data/leveler/temp/{}_temp_rank_bg.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(profile_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/{}_temp_rank_profile.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(server_icon_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/{}_temp_server_icon.png'.format(user.id),'wb') as f:
            f.write(image)

        bg_image = Image.open('data/leveler/temp/{}_temp_rank_bg.png'.format(user.id)).convert('RGBA')
        profile_image = Image.open('data/leveler/temp/{}_temp_rank_profile.png'.format(user.id)).convert('RGBA')
        server_image = Image.open('data/leveler/temp/{}_temp_server_icon.png'.format(user.id)).convert('RGBA')

        # set canvas
        width = 360
        height = 100
        bg_color = (255,255,255, 0)
        result = Image.new('RGBA', (width, height), bg_color)
        process = Image.new('RGBA', (width, height), bg_color)

        # puts in background
        bg_image = bg_image.resize((width, height), Image.ANTIALIAS)
        bg_image = bg_image.crop((0,0, width, height))
        result.paste(bg_image, (0,0))

        # draw
        draw = ImageDraw.Draw(process)

        # draw transparent overlay
        vert_pos = 5
        left_pos = 70
        right_pos = width - vert_pos
        title_height = 22
        gap = 3

        draw.rectangle([(left_pos - 20,vert_pos), (right_pos, vert_pos + title_height)], fill=(230,230,230,230)) # title box
        content_top = vert_pos + title_height + gap
        content_bottom = 100 - vert_pos

        if "rank_info_color" in userinfo.keys():
            info_color = tuple(userinfo["rank_info_color"])
            info_color = (info_color[0], info_color[1], info_color[2], 160) # increase transparency
        else:
            info_color = (30, 30 ,30, 160)
        draw.rectangle([(left_pos - 20, content_top), (right_pos, content_bottom)], fill=info_color, outline=(180, 180, 180, 180)) # content box

        # stick in credits if needed
        if bg_url in bg_credits.keys():
            credit_text = " ".join("{}".format(bg_credits[bg_url]))
            draw.text((2, 92), credit_text,  font=credit_fnt, fill=(0,0,0,190))

        # draw level circle
        multiplier = 6
        lvl_circle_dia = 94
        circle_left = 15
        circle_top = int((height- lvl_circle_dia)/2)
        raw_length = lvl_circle_dia * multiplier

        # create mask
        mask = Image.new('L', (raw_length, raw_length), 0)
        draw_thumb = ImageDraw.Draw(mask)
        draw_thumb.ellipse((0, 0) + (raw_length, raw_length), fill = 255, outline = 0)

        # drawing level bar calculate angle
        start_angle = -90 # from top instead of 3oclock
        angle = int(360 * (userinfo["servers"][server.id]["current_exp"]/self._required_exp(userinfo["servers"][server.id]["level"]))) + start_angle

        lvl_circle = Image.new("RGBA", (raw_length, raw_length))
        draw_lvl_circle = ImageDraw.Draw(lvl_circle)
        draw_lvl_circle.ellipse([0, 0, raw_length, raw_length], fill=(180, 180, 180, 180), outline = (255, 255, 255, 220))
        # determines exp bar color
        if "rank_exp_color" not in userinfo.keys() or not userinfo["rank_exp_color"]:
            exp_fill = (255, 255, 255, 230)
        else:
            exp_fill = tuple(userinfo["rank_exp_color"])
        draw_lvl_circle.pieslice([0, 0, raw_length, raw_length], start_angle, angle, fill=exp_fill, outline = (255, 255, 255, 230))
        # put on level bar circle
        lvl_circle = lvl_circle.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        lvl_bar_mask = mask.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        process.paste(lvl_circle, (circle_left, circle_top), lvl_bar_mask)

        # draws mask
        total_gap = 10
        border = int(total_gap/2)
        profile_size = lvl_circle_dia - total_gap
        raw_length = profile_size * multiplier
        # put in profile picture
        output = ImageOps.fit(profile_image, (raw_length, raw_length), centering=(0.5, 0.5))
        output = output.resize((profile_size, profile_size), Image.ANTIALIAS)
        mask = mask.resize((profile_size, profile_size), Image.ANTIALIAS)
        profile_image = profile_image.resize((profile_size, profile_size), Image.ANTIALIAS)
        process.paste(profile_image, (circle_left + border, circle_top + border), mask)

        # draw level box
        level_left = 274
        level_right = right_pos
        draw.rectangle([(level_left, vert_pos), (level_right, vert_pos + title_height)], fill="#AAA") # box
        lvl_text = "LEVEL {}".format(userinfo["servers"][server.id]["level"])
        draw.text((self._center(level_left, level_right, lvl_text, level_label_fnt), vert_pos + 3), lvl_text,  font=level_label_fnt, fill=(110,110,110,255)) # Level #

        # labels text colors
        white_text = (240,240,240,255)
        dark_text = (35, 35, 35, 230)
        label_text_color = self._contrast(info_color, white_text, dark_text)

        # draw text
        grey_color = (110,110,110,255)
        white_color = (230,230,230,255)

        # put in server picture
        server_size = content_bottom - content_top - 10
        server_border_size = server_size + 4
        radius = 20
        light_border = (150,150,150,180)
        dark_border = (90,90,90,180)
        border_color = self._contrast(info_color, light_border, dark_border)

        draw_server_border = Image.new('RGBA', (server_border_size*multiplier, server_border_size*multiplier),border_color)
        draw_server_border = self._add_corners(draw_server_border, int(radius*multiplier/2))
        draw_server_border = draw_server_border.resize((server_border_size, server_border_size), Image.ANTIALIAS)
        server_image = server_image.resize((server_size*multiplier, server_size*multiplier), Image.ANTIALIAS)
        server_image = self._add_corners(server_image, int(radius*multiplier/2)-10)
        server_image = server_image.resize((server_size, server_size), Image.ANTIALIAS)
        process.paste(draw_server_border, (circle_left + profile_size + 2*border + 8, content_top + 3), draw_server_border)
        process.paste(server_image, (circle_left + profile_size + 2*border + 10, content_top + 5), server_image)

        # name
        left_text_align = 130
        _write_unicode(self._truncate_text(self._name(user, 20), 20), left_text_align - 12, vert_pos + 3, name_fnt, header_u_fnt, grey_color) # Name

        # divider bar
        draw.rectangle([(187, 45), (188, 85)], fill=(160,160,160,220))

        # labels
        label_align = 200
        draw.text((label_align, 38), "Server Rank:", font=general_info_fnt, fill=label_text_color) # Server Rank
        draw.text((label_align, 58), "Server Exp:", font=general_info_fnt, fill=label_text_color) # Server Exp
        draw.text((label_align, 78), "Credits:", font=general_info_fnt, fill=label_text_color) # Credit
        # info
        right_text_align = 290
        rank_txt = "#{}".format(await self._find_server_rank(user, server))
        draw.text((right_text_align, 38), self._truncate_text(rank_txt, 12) , font=general_info_fnt, fill=label_text_color) # Rank
        exp_txt = "{}".format(await self._find_server_exp(user, server))
        draw.text((right_text_align, 58), self._truncate_text(exp_txt, 12), font=general_info_fnt, fill=label_text_color) # Exp
        try:
            bank = self.bot.get_cog('Economy').bank
            if bank.account_exists(user):
                credits = bank.get_balance(user)
            else:
                credits = 0
        except:
            credits = 0
        credit_txt = "${}".format(credits)
        draw.text((right_text_align, 78), self._truncate_text(credit_txt, 12),  font=general_info_fnt, fill=label_text_color) # Credits

        result = Image.alpha_composite(result, process)
        result = await self._add_dropshadow(result)
        result.save('data/leveler/temp/{}_rank.png'.format(user.id),'PNG', quality=100)
    """

    async def draw_rank(self, user, server):
        # fonts
        font_thin_file = 'data/leveler/fonts/Uni_Sans_Thin.ttf'
        font_heavy_file = 'data/leveler/fonts/Uni_Sans_Heavy.ttf'
        font_file = 'data/leveler/fonts/SourceSansPro-Regular.ttf'
        font_bold_file = 'data/leveler/fonts/SourceSansPro-Semibold.ttf'

        name_fnt = ImageFont.truetype(font_heavy_file, 24)
        name_u_fnt = ImageFont.truetype(font_unicode_file, 24)
        label_fnt = ImageFont.truetype(font_bold_file, 16)
        exp_fnt = ImageFont.truetype(font_bold_file, 9)
        large_fnt = ImageFont.truetype(font_thin_file, 24)
        large_bold_fnt = ImageFont.truetype(font_bold_file, 24)
        symbol_u_fnt = ImageFont.truetype(font_unicode_file, 15)

        def _write_unicode(text, init_x, y, font, unicode_font, fill):
            write_pos = init_x

            for char in text:
                if char.isalnum() or char in string.punctuation or char in string.whitespace:
                    draw.text((write_pos, y), char, font=font, fill=fill)
                    write_pos += font.getsize(char)[0]
                else:
                    draw.text((write_pos, y), u"{}".format(char), font=unicode_font, fill=fill)
                    write_pos += unicode_font.getsize(char)[0]

        userinfo = db.users.find_one({'user_id':user.id})
        # get urls
        bg_url = userinfo["rank_background"]
        profile_url = user.avatar_url
        server_icon_url = server.icon_url

        # create image objects
        bg_image = Image
        profile_image = Image

        async with aiohttp.get(bg_url) as r:
            image = await r.content.read()
        with open('data/leveler/temp/test_temp_rank_bg.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(profile_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/test_temp_rank_profile.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(server_icon_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/test_temp_server_icon.png'.format(user.id),'wb') as f:
            f.write(image)

        bg_image = Image.open('data/leveler/temp/test_temp_rank_bg.png'.format(user.id)).convert('RGBA')
        profile_image = Image.open('data/leveler/temp/test_temp_rank_profile.png'.format(user.id)).convert('RGBA')
        server_image = Image.open('data/leveler/temp/test_temp_server_icon.png'.format(user.id)).convert('RGBA')

        # set canvas
        width = 390
        height = 100
        bg_color = (255,255,255, 0)
        bg_width = width - 50
        result = Image.new('RGBA', (width, height), bg_color)
        process = Image.new('RGBA', (width, height), bg_color)
        draw = ImageDraw.Draw(process)

        # info section
        info_section = Image.new('RGBA', (bg_width, height), bg_color)
        info_section_process = Image.new('RGBA', (bg_width, height), bg_color)
        draw_info = ImageDraw.Draw(info_section)
        # puts in background
        bg_image = bg_image.resize((width, height), Image.ANTIALIAS)
        bg_image = bg_image.crop((0,0, width, height))
        info_section.paste(bg_image, (0,0))

        # draw transparent overlays
        draw_overlay = ImageDraw.Draw(info_section_process)
        draw_overlay.rectangle([(0,0), (bg_width,20)], fill=(230,230,230,200))
        draw_overlay.rectangle([(0,20), (bg_width,30)], fill=(120,120,120,180)) # Level bar
        exp_frac = int(userinfo["servers"][server.id]["current_exp"])
        exp_total = self._required_exp(userinfo["servers"][server.id]["level"])
        exp_width = int(bg_width * (exp_frac/exp_total))
        if "rank_info_color" in userinfo.keys():
            exp_color = tuple(userinfo["rank_info_color"])
            exp_color = (exp_color[0], exp_color[1], exp_color[2], 180) # increase transparency
        else:
            exp_color = (140,140,140,230)
        draw_overlay.rectangle([(0,20), (exp_width,30)], fill=exp_color) # Exp bar
        draw_overlay.rectangle([(0,30), (bg_width,31)], fill=(0,0,0,255)) # Divider
        # draw_overlay.rectangle([(0,35), (bg_width,100)], fill=(230,230,230,0)) # title overlay
        for i in range(0,70):
            draw_overlay.rectangle([(0,height-i), (bg_width,height-i)], fill=(20,20,20,255-i*3)) # title overlay

        # draw corners and finalize
        info_section = Image.alpha_composite(info_section, info_section_process)
        info_section = self._add_corners(info_section, 25)
        process.paste(info_section, (35,0))

        # draw level circle
        multiplier = 6
        lvl_circle_dia = 100
        circle_left = 0
        circle_top = int((height- lvl_circle_dia)/2)
        raw_length = lvl_circle_dia * multiplier

        # create mask
        mask = Image.new('L', (raw_length, raw_length), 0)
        draw_thumb = ImageDraw.Draw(mask)
        draw_thumb.ellipse((0, 0) + (raw_length, raw_length), fill = 255, outline = 0)

        # drawing level border
        lvl_circle = Image.new("RGBA", (raw_length, raw_length))
        draw_lvl_circle = ImageDraw.Draw(lvl_circle)
        draw_lvl_circle.ellipse([0, 0, raw_length, raw_length], fill=(250, 250, 250, 250))
        # determines exp bar color
        """
        if "rank_exp_color" not in userinfo.keys() or not userinfo["rank_exp_color"]:
            exp_fill = (255, 255, 255, 230)
        else:
            exp_fill = tuple(userinfo["rank_exp_color"])"""
        exp_fill = (255, 255, 255, 230)

        # put on profile circle background
        lvl_circle = lvl_circle.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        lvl_bar_mask = mask.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        process.paste(lvl_circle, (circle_left, circle_top), lvl_bar_mask)

        # draws mask
        total_gap = 6
        border = int(total_gap/2)
        profile_size = lvl_circle_dia - total_gap
        raw_length = profile_size * multiplier
        # put in profile picture
        output = ImageOps.fit(profile_image, (raw_length, raw_length), centering=(0.5, 0.5))
        output = output.resize((profile_size, profile_size), Image.ANTIALIAS)
        mask = mask.resize((profile_size, profile_size), Image.ANTIALIAS)
        profile_image = profile_image.resize((profile_size, profile_size), Image.ANTIALIAS)
        process.paste(profile_image, (circle_left + border, circle_top + border), mask)

        # draw text
        grey_color = (100,100,100,255)
        white_color = (220,220,220,255)

        # name
        left_text_align = 130
        name_color = 0
        _write_unicode(self._truncate_text(self._name(user, 20), 20), 100, 0, name_fnt, name_u_fnt, grey_color) # Name

        # labels
        v_label_align = 75
        info_text_color = white_color
        draw.text((self._center(100, 200, "  RANK", label_fnt), v_label_align), "  RANK",  font=label_fnt, fill=info_text_color) # Rank
        draw.text((self._center(100, 360, "  LEVEL", label_fnt), v_label_align), "  LEVEL",  font=label_fnt, fill=info_text_color) # Rank
        draw.text((self._center(260, 360, "BALANCE", label_fnt), v_label_align), "BALANCE",  font=label_fnt, fill=info_text_color) # Rank
        local_symbol = u"\U0001F3E0 "
        if "linux" in platform.system().lower():
            local_symbol = u"\U0001F3E0 "
        else:
            local_symbol = "S. "
        _write_unicode(local_symbol, 117, v_label_align + 4, label_fnt, symbol_u_fnt, info_text_color) # Symbol
        _write_unicode(local_symbol, 195, v_label_align + 4, label_fnt, symbol_u_fnt, info_text_color) # Symbol

        # userinfo
        server_rank = "#{}".format(await self._find_server_rank(user, server))
        draw.text((self._center(100, 200, server_rank, large_fnt), v_label_align - 30), server_rank,  font=large_fnt, fill=info_text_color) # Rank
        level_text = "{}".format(userinfo["servers"][server.id]["level"])
        draw.text((self._center(95, 360, level_text, large_fnt), v_label_align - 30), level_text,  font=large_fnt, fill=info_text_color) # Level
        try:
            bank = self.bot.get_cog('Economy').bank
            if bank.account_exists(user):
                credits = bank.get_balance(user)
            else:
                credits = 0
        except:
            credits = 0
        credit_txt = "${}".format(credits)
        draw.text((self._center(260, 360, credit_txt, large_fnt), v_label_align - 30), credit_txt,  font=large_fnt, fill=info_text_color) # Balance
        exp_text = "{}/{}".format(exp_frac, exp_total)
        draw.text((self._center(80, 360, exp_text, exp_fnt), 19), exp_text,  font=exp_fnt, fill=info_text_color) # Rank


        result = Image.alpha_composite(result, process)
        result.save('data/leveler/temp/{}_rank.png'.format(user.id),'PNG', quality=100)

    def _add_corners(self, im, rad, multiplier = 6):
        raw_length = rad * 2 * multiplier
        circle = Image.new('L', (raw_length, raw_length), 0)
        draw = ImageDraw.Draw(circle)
        draw.ellipse((0, 0, raw_length, raw_length), fill=255)
        circle = circle.resize((rad * 2, rad * 2), Image.ANTIALIAS)

        alpha = Image.new('L', im.size, 255)
        w, h = im.size
        alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0))
        alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad))
        alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0))
        alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad))
        im.putalpha(alpha)
        return im

    """
    async def draw_levelup(self, user, server):
        userinfo = db.users.find_one({'user_id':user.id})
        # get urls
        bg_url = userinfo["levelup_background"]
        profile_url = user.avatar_url

        # create image objects
        bg_image = Image
        profile_image = Image

        async with aiohttp.get(bg_url) as r:
            image = await r.content.read()
        with open('data/leveler/temp/{}_temp_level_bg.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(profile_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/{}_temp_level_profile.png'.format(user.id),'wb') as f:
            f.write(image)

        bg_image = Image.open('data/leveler/temp/{}_temp_level_bg.png'.format(user.id)).convert('RGBA')
        profile_image = Image.open('data/leveler/temp/{}_temp_level_profile.png'.format(user.id)).convert('RGBA')

        # set canvas
        width = 175
        height = 65
        bg_color = (255,255,255, 0)
        result = Image.new('RGBA', (width, height), bg_color)
        process = Image.new('RGBA', (width, height), bg_color)

        # draw
        draw = ImageDraw.Draw(process)

        # puts in background
        bg_image = bg_image.resize((width, height), Image.ANTIALIAS)
        bg_image = bg_image.crop((0,0, width, height))
        result.paste(bg_image, (0,0))

        # draw transparent overlay
        if "levelup_info_color" in userinfo.keys():
            info_color = tuple(userinfo["levelup_info_color"])
            info_color = (info_color[0], info_color[1], info_color[2], 150) # increase transparency
        else:
            info_color = (30, 30 ,30, 150)
        draw.rectangle([(38, 5), (170, 60)], fill=info_color) # info portion

        # draw level circle
        multiplier = 6
        lvl_circle_dia = 60
        circle_left = 4
        circle_top = int((height- lvl_circle_dia)/2)
        raw_length = lvl_circle_dia * multiplier

        # create mask
        mask = Image.new('L', (raw_length, raw_length), 0)
        draw_thumb = ImageDraw.Draw(mask)
        draw_thumb.ellipse((0, 0) + (raw_length, raw_length), fill = 255, outline = 0)

        # drawing level bar calculate angle
        start_angle = -90 # from top instead of 3oclock

        lvl_circle = Image.new("RGBA", (raw_length, raw_length))
        draw_lvl_circle = ImageDraw.Draw(lvl_circle)
        draw_lvl_circle.ellipse([0, 0, raw_length, raw_length], fill=(255, 255, 255, 220), outline = (255, 255, 255, 220))

        # put on level bar circle
        lvl_circle = lvl_circle.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        lvl_bar_mask = mask.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        process.paste(lvl_circle, (circle_left, circle_top), lvl_bar_mask)

        # draws mask
        total_gap = 6
        border = int(total_gap/2)
        profile_size = lvl_circle_dia - total_gap
        raw_length = profile_size * multiplier
        # put in profile picture
        output = ImageOps.fit(profile_image, (raw_length, raw_length), centering=(0.5, 0.5))
        output = output.resize((profile_size, profile_size), Image.ANTIALIAS)
        mask = mask.resize((profile_size, profile_size), Image.ANTIALIAS)
        profile_image = profile_image.resize((profile_size, profile_size), Image.ANTIALIAS)
        process.paste(profile_image, (circle_left + border, circle_top + border), mask)

        # fonts
        level_fnt2 = ImageFont.truetype('data/leveler/fonts/font_bold.ttf', 19)
        level_fnt = ImageFont.truetype('data/leveler/fonts/font_bold.ttf', 26)

        # write label text
        white_text = (240,240,240,255)
        dark_text = (35, 35, 35, 230)
        level_up_text = self._contrast(info_color, white_text, dark_text)
        lvl_text = "LEVEL {}".format(userinfo["servers"][server.id]["level"])
        draw.text((self._center(50, 170, lvl_text, level_fnt), 22), lvl_text, font=level_fnt, fill=level_up_text) # Level Number

        result = Image.alpha_composite(result, process)
        result = await self._add_dropshadow(result)
        filename = 'data/leveler/temp/{}_level.png'.format(user.id)
        result.save(filename,'PNG', quality=100)"""

    async def draw_levelup(self, user, server):
        # fonts
        font_thin_file = 'data/leveler/fonts/Uni_Sans_Thin.ttf'
        level_fnt = ImageFont.truetype(font_thin_file, 23)

        userinfo = db.users.find_one({'user_id':user.id})

        # get urls
        bg_url = userinfo["levelup_background"]
        profile_url = user.avatar_url

        # create image objects
        bg_image = Image
        profile_image = Image

        async with aiohttp.get(bg_url) as r:
            image = await r.content.read()
        with open('data/leveler/temp/{}_temp_level_bg.png'.format(user.id),'wb') as f:
            f.write(image)
        try:
            async with aiohttp.get(profile_url) as r:
                image = await r.content.read()
        except:
            async with aiohttp.get(default_avatar_url) as r:
                image = await r.content.read()
        with open('data/leveler/temp/{}_temp_level_profile.png'.format(user.id),'wb') as f:
            f.write(image)

        bg_image = Image.open('data/leveler/temp/{}_temp_level_bg.png'.format(user.id)).convert('RGBA')
        profile_image = Image.open('data/leveler/temp/{}_temp_level_profile.png'.format(user.id)).convert('RGBA')

        # set canvas
        width = 176
        height = 67
        bg_color = (255,255,255, 0)
        result = Image.new('RGBA', (width, height), bg_color)
        process = Image.new('RGBA', (width, height), bg_color)
        draw = ImageDraw.Draw(process)

        # puts in background
        bg_image = bg_image.resize((width, height), Image.ANTIALIAS)
        bg_image = bg_image.crop((0,0, width, height))
        result.paste(bg_image, (0,0))

        # info section
        lvl_circle_dia = 60
        total_gap = 2
        border = int(total_gap/2)
        info_section = Image.new('RGBA', (165, 55), (230,230,230,20))
        info_section = self._add_corners(info_section, int(lvl_circle_dia/2))
        process.paste(info_section, (border,border))

        # draw transparent overlay
        if "levelup_info_color" in userinfo.keys():
            info_color = tuple(userinfo["levelup_info_color"])
            info_color = (info_color[0], info_color[1], info_color[2], 150) # increase transparency
        else:
            info_color = (30, 30 ,30, 150)

        for i in range(0,height):
            draw.rectangle([(0,height-i), (width,height-i)], fill=(info_color[0],info_color[1],info_color[2],255-i*3)) # title overlay

        # draw circle
        multiplier = 6
        circle_left = 4
        circle_top = int((height- lvl_circle_dia)/2)
        raw_length = lvl_circle_dia * multiplier
        # create mask
        mask = Image.new('L', (raw_length, raw_length), 0)
        draw_thumb = ImageDraw.Draw(mask)
        draw_thumb.ellipse((0, 0) + (raw_length, raw_length), fill = 255, outline = 0)

        # border
        lvl_circle = Image.new("RGBA", (raw_length, raw_length))
        draw_lvl_circle = ImageDraw.Draw(lvl_circle)
        draw_lvl_circle.ellipse([0, 0, raw_length, raw_length], fill=(250, 250, 250, 180))
        lvl_circle = lvl_circle.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        lvl_bar_mask = mask.resize((lvl_circle_dia, lvl_circle_dia), Image.ANTIALIAS)
        process.paste(lvl_circle, (circle_left, circle_top), lvl_bar_mask)

        profile_size = lvl_circle_dia - total_gap
        raw_length = profile_size * multiplier
        # put in profile picture
        output = ImageOps.fit(profile_image, (raw_length, raw_length), centering=(0.5, 0.5))
        output = output.resize((profile_size, profile_size), Image.ANTIALIAS)
        mask = mask.resize((profile_size, profile_size), Image.ANTIALIAS)
        profile_image = profile_image.resize((profile_size, profile_size), Image.ANTIALIAS)
        process.paste(profile_image, (circle_left + border, circle_top + border), mask)

        # write label text
        white_text = (250,250,250,255)
        dark_text = (35, 35, 35, 230)
        level_up_text = self._contrast(info_color, white_text, dark_text)
        lvl_text = "LEVEL {}".format(userinfo["servers"][server.id]["level"])
        draw.text((self._center(60, 170, lvl_text, level_fnt), 23), lvl_text, font=level_fnt, fill=level_up_text) # Level Number

        result = Image.alpha_composite(result, process)
        result = self._add_corners(result, int(height/2))
        filename = 'data/leveler/temp/{}_level.png'.format(user.id)
        result.save(filename,'PNG', quality=100)

    async def _handle_on_message(self, message):
        #try:
        text = message.content
        channel = message.channel
        server = message.server
        user = message.author
        # creates user if doesn't exist, bots are not logged.
        await self._create_user(user, server)
        curr_time = time.time()
        userinfo = db.users.find_one({'user_id':user.id})

        if not server or server.id in self.settings["disabled_servers"]:
            return
        if user.bot:
            return

        # check if chat_block exists
        if "chat_block" not in userinfo:
            userinfo["chat_block"] = 0

        if float(curr_time) - float(userinfo["chat_block"]) >= 120 and not any(text.startswith(x) for x in prefix):
            await self._process_exp(message, userinfo, random.randint(15, 20))
            await self._give_chat_credit(user, server)
        #except AttributeError as e:
            #pass

    async def _process_exp(self, message, userinfo, exp:int):
        server = message.author.server
        channel = message.channel
        user = message.author

        # add to total exp
        try:
            required = self._required_exp(userinfo["servers"][server.id]["level"])
            db.users.update_one({'user_id':user.id}, {'$set':{
                "total_exp": userinfo["total_exp"] + exp,
                }})
        except:
            pass
        print(userinfo["total_exp"] + exp)
        if userinfo["servers"][server.id]["current_exp"] + exp >= required:
            userinfo["servers"][server.id]["level"] += 1
            db.users.update_one({'user_id':user.id}, {'$set':{
                "servers.{}.level".format(server.id): userinfo["servers"][server.id]["level"],
                "servers.{}.current_exp".format(server.id): userinfo["servers"][server.id]["current_exp"] + exp - required,
                "chat_block": time.time()
                }})
            await self._handle_levelup(user, userinfo, server, channel)
        else:
            db.users.update_one({'user_id':user.id}, {'$set':{
                "servers.{}.current_exp".format(server.id): userinfo["servers"][server.id]["current_exp"] + exp,
                "chat_block": time.time()
                }})

    async def _handle_levelup(self, user, userinfo, server, channel):
        if not isinstance(self.settings["lvl_msg"], list):
            self.settings["lvl_msg"] = []
            fileIO("data/leveler/settings.json", "save", self.settings)

        if server.id in self.settings["lvl_msg"]: # if lvl msg is enabled
            # channel lock implementation
            if "lvl_msg_lock" in self.settings.keys() and server.id in self.settings["lvl_msg_lock"].keys():
                channel_id = self.settings["lvl_msg_lock"][server.id]
                channel = find(lambda m: m.id == channel_id, server.channels)

            server_identifier = "" # super hacky
            name = self._is_mention(user) # also super hacky
            # private message takes precedent, of course
            if "private_lvl_msg" in self.settings and server.id in self.settings["private_lvl_msg"]:
                server_identifier = " on {}".format(server.name)
                channel = user
                name = "You"

            new_level = str(userinfo["servers"][server.id]["level"])
            # add to appropriate role if necessary
            try:
                server_roles = db.roles.find_one({'server_id':server.id})
                if server_roles != None:
                    for role in server_roles['roles'].keys():
                        if int(server_roles['roles'][role]['level']) == int(new_level):
                            role_obj = discord.utils.find(lambda r: r.name == role, server.roles)
                            await self.bot.add_roles(user, role_obj)

                            if server_roles['roles'][role]['remove_role'] != None:
                                remove_role_obj = discord.utils.find(
                                    lambda r: r.name == server_roles['roles'][role]['remove_role'], server.roles)
                                if remove_role_obj != None:
                                    await self.bot.remove_roles(user, remove_role_obj)
            except:
                await self.bot.send_message(channel, 'Role was not set. Missing Permissions!')

            # add appropriate badge if necessary
            try:
                server_linked_badges = db.badgelinks.find_one({'server_id':server.id})
                if server_linked_badges != None:
                    for badge_name in server_linked_badges['badges']:
                        if int(server_linked_badges['badges'][badge_name]) == int(new_level):
                            server_badges = db.badges.find_one({'server_id':server.id})
                            if server_badges != None and badge_name in server_badges['badges'].keys():
                                userinfo_db = db.users.find_one({'user_id':user.id})
                                new_badge_name = "{}_{}".format(badge_name, server.id)
                                userinfo_db["badges"][new_badge_name] = server_badges['badges'][badge_name]
                                db.users.update_one({'user_id':user.id}, {'$set':{"badges": userinfo_db["badges"]}})
            except:
                await self.bot.send_message(channel, 'Error. Badge was not given!')

            if "text_only" in self.settings and server.id in self.settings["text_only"]:
                await self.bot.send_typing(channel)
                em = discord.Embed(description='**{} just gained a level{}! (LEVEL {})**'.format(name, server_identifier, new_level), colour=user.colour)
                await self.bot.send_message(channel, '', embed = em)
            else:
                await self.draw_levelup(user, server)
                await self.bot.send_typing(channel)
                await self.bot.send_file(channel, 'data/leveler/temp/{}_level.png'.format(user.id), content='**{} just gained a level{}!**'.format(name, server_identifier))

    async def _find_server_rank(self, user, server):
        targetid = user.id
        users = []

        for userinfo in db.users.find({}):
            try:
                server_exp = 0
                userid = userinfo["user_id"]
                for i in range(userinfo["servers"][server.id]["level"]):
                    server_exp += self._required_exp(i)
                server_exp += userinfo["servers"][server.id]["current_exp"]
                users.append((userid, server_exp))
            except:
                pass

        sorted_list = sorted(users, key=operator.itemgetter(1), reverse=True)

        rank = 1
        for a_user in sorted_list:
            if a_user[0] == targetid:
                return rank
            rank+=1

    async def _find_server_rep_rank(self, user, server):
        targetid = user.id
        users = []
        for userinfo in db.users.find({}):
            userid = userinfo["user_id"]
            if "servers" in userinfo and server.id in userinfo["servers"]:
                users.append((userinfo["user_id"], userinfo["rep"]))

        sorted_list = sorted(users, key=operator.itemgetter(1), reverse=True)

        rank = 1
        for a_user in sorted_list:
            if a_user[0] == targetid:
                return rank
            rank+=1

    async def _find_server_exp(self, user, server):
        server_exp = 0
        userinfo = db.users.find_one({'user_id':user.id})

        try:
            for i in range(userinfo["servers"][server.id]["level"]):
                server_exp += self._required_exp(i)
            server_exp +=  userinfo["servers"][server.id]["current_exp"]
            return server_exp
        except:
            return server_exp

    async def _find_global_rank(self, user):
        users = []

        for userinfo in db.users.find({}):
            try:
                userid = userinfo["user_id"]
                users.append((userid, userinfo["total_exp"]))
            except KeyError:
                pass
        sorted_list = sorted(users, key=operator.itemgetter(1), reverse=True)

        rank = 1
        for stats in sorted_list:
            if stats[0] == user.id:
                return rank
            rank+=1

    async def _find_global_rep_rank(self, user):
        users = []

        for userinfo in db.users.find({}):
            try:
                userid = userinfo["user_id"]
                users.append((userid, userinfo["rep"]))
            except KeyError:
                pass
        sorted_list = sorted(users, key=operator.itemgetter(1), reverse=True)

        rank = 1
        for stats in sorted_list:
            if stats[0] == user.id:
                return rank
            rank+=1

    # handles user creation, adding new server, blocking
    async def _create_user(self, user, server):
        try:
            userinfo = db.users.find_one({'user_id':user.id})
            if not userinfo:
                new_account = {
                    "user_id" : user.id,
                    "username" : user.name,
                    "servers": {},
                    "total_exp": 0,
                    "profile_background": self.backgrounds["profile"]["default"],
                    "rank_background": self.backgrounds["rank"]["default"],
                    "levelup_background": self.backgrounds["levelup"]["default"],
                    "title": "",
                    "info": "I am a mysterious person.",
                    "rep": 0,
                    "badges":{},
                    "active_badges":{},
                    "rep_color": [],
                    "badge_col_color": [],
                    "rep_block": 0,
                    "chat_block": 0,
                    "profile_block": 0,
                    "rank_block": 0
                }
                db.users.insert_one(new_account)

            userinfo = db.users.find_one({'user_id':user.id})

            if "username" not in userinfo or userinfo["username"] != user.name:
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "username": user.name,
                    }}, upsert = True)

            if "servers" not in userinfo or server.id not in userinfo["servers"]:
                db.users.update_one({'user_id':user.id}, {'$set':{
                        "servers.{}.level".format(server.id): 0,
                        "servers.{}.current_exp".format(server.id): 0,
                    }}, upsert = True)
        except AttributeError as e:
            pass

    def _truncate_text(self, text, max_length):
        if len(text) > max_length:
            if text.strip('$').isdigit():
                text = int(text.strip('$'))
                return "${:.2E}".format(text)
            return text[:max_length-3] + "..."
        return text

    # finds the the pixel to center the text
    def _center(self, start, end, text, font):
        dist = end - start
        width = font.getsize(text)[0]
        start_pos = start + ((dist-width)/2)
        return int(start_pos)

    # calculates required exp for next level
    def _required_exp(self, level:int):
        if level < 0:
            return 0
        return 139*level+65

    def _level_exp(self, level: int):
        return level*65 + 139*level*(level-1)//2

    def _find_level(self, total_exp):
        # this is specific to the function above
        return int((1/278)*(9 + math.sqrt(81 + 1112*(total_exp))))

# ------------------------------ setup ----------------------------------------
def check_folders():
    if not os.path.exists("data/leveler"):
        print("Creating data/leveler folder...")
        os.makedirs("data/leveler")

    if not os.path.exists("data/leveler/temp"):
        print("Creating data/leveler/temp folder...")
        os.makedirs("data/leveler/temp")

def transfer_info():
    try:
        users = fileIO("data/leveler/users.json", "load")
        for user_id in users:
            os.makedirs("data/leveler/users/{}".format(user_id))
            # create info.json
            f = "data/leveler/users/{}/info.json".format(user_id)
            if not fileIO(f, "check"):
                fileIO(f, "save", users[user_id])
    except:
        pass

def check_files():
    default = {
        "bg_price": 0,
        "lvl_msg": [], # enabled lvl msg servers
        "disabled_servers": [],
        "badge_type": "circles",
        "mention" : True,
        "text_only": [],
        "server_roles": {},
        "rep_cooldown": 43200,
        "chat_cooldown": 120
        }

    settings_path = "data/leveler/settings.json"
    if not os.path.isfile(settings_path):
        print("Creating default leveler settings.json...")
        fileIO(settings_path, "save", default)

    bgs = {
            "profile": {
                "alice": "http://i.imgur.com/MUSuMao.png",
                "bluestairs": "http://i.imgur.com/EjuvxjT.png",
                "lamp": "http://i.imgur.com/0nQSmKX.jpg",
                "coastline": "http://i.imgur.com/XzUtY47.jpg",
                "redblack": "http://i.imgur.com/74J2zZn.jpg",
                "default": "http://i.imgur.com/8T1FUP5.jpg",
                "iceberg": "http://i.imgur.com/8KowiMh.png",
                "miraiglasses": "http://i.imgur.com/2Ak5VG3.png",
                "miraikuriyama": "http://i.imgur.com/jQ4s4jj.png",
                "mountaindawn": "http://i.imgur.com/kJ1yYY6.jpg",
                "waterlilies": "http://i.imgur.com/qwdcJjI.jpg",
                "greenery": "http://i.imgur.com/70ZH6LX.png"
            },
            "rank": {
                "aurora" : "http://i.imgur.com/gVSbmYj.jpg",
                "default" : "http://i.imgur.com/SorwIrc.jpg",
                "nebula": "http://i.imgur.com/V5zSCmO.jpg",
                "mountain" : "http://i.imgur.com/qYqEUYp.jpg",
                "abstract" : "http://i.imgur.com/70ZH6LX.png",
                "city": "http://i.imgur.com/yr2cUM9.jpg",
            },
            "levelup": {
                "default" : "http://i.imgur.com/eEFfKqa.jpg",
            },
        }

    bgs_path = "data/leveler/backgrounds.json"
    if not os.path.isfile(bgs_path):
        print("Creating default leveler backgrounds.json...")
        fileIO(bgs_path, "save", bgs)

    f = "data/leveler/badges.json"
    if not fileIO(f, "check"):
        print("Creating badges.json...")
        fileIO(f, "save", {})

def setup(bot):
    check_folders()
    check_files()
    n = Leveler(bot)
    bot.add_listener(n._handle_on_message, "on_message")
    bot.add_cog(n)