From 963fd37a7df29a3a6dd4077fc32c0a013b45d756 Mon Sep 17 00:00:00 2001 From: Fixator10 Date: Tue, 25 Aug 2020 16:39:08 +0400 Subject: [PATCH] and... completed? i guess --- leveler/abc.py | 6 +- leveler/commands/__init__.py | 11 +- leveler/commands/lvladmin/__init__.py | 7 +- leveler/commands/lvladmin/badge.py | 369 +++++++++ leveler/commands/lvlset/__init__.py | 10 + leveler/commands/lvlset/badge.py | 238 ++++++ leveler/commands/lvlset/basecmd.py | 10 + leveler/commands/lvlset/levelup.py | 107 +++ leveler/commands/lvlset/profile.py | 227 +++++ leveler/commands/lvlset/rank.py | 149 ++++ leveler/commands/other.py | 56 +- leveler/leveler.py | 1096 +------------------------ 12 files changed, 1186 insertions(+), 1100 deletions(-) create mode 100644 leveler/commands/lvladmin/badge.py create mode 100644 leveler/commands/lvlset/__init__.py create mode 100644 leveler/commands/lvlset/badge.py create mode 100644 leveler/commands/lvlset/basecmd.py create mode 100644 leveler/commands/lvlset/levelup.py create mode 100644 leveler/commands/lvlset/profile.py create mode 100644 leveler/commands/lvlset/rank.py diff --git a/leveler/abc.py b/leveler/abc.py index 16a72ae1..804c7f52 100644 --- a/leveler/abc.py +++ b/leveler/abc.py @@ -2,7 +2,7 @@ from io import BytesIO from logging import Logger from re import Match -from typing import Optional +from typing import List, Optional from aiohttp import ClientSession from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase @@ -126,6 +126,10 @@ async def _give_chat_credit(self, user, server): async def _valid_image_url(self, url): raise NotImplementedError + @abstractmethod + async def _auto_color(self, ctx, url: str, ranks) -> List[str]: + raise NotImplementedError + class CompositeMetaClass(type(commands.Cog), type(ABC)): """ diff --git a/leveler/commands/__init__.py b/leveler/commands/__init__.py index 03d00998..71ea7b6d 100644 --- a/leveler/commands/__init__.py +++ b/leveler/commands/__init__.py @@ -2,11 +2,20 @@ from .database import DataBase from .db_converters import DBConverters from .lvladmin import LevelAdmin +from .lvlset import LevelSet +from .other import Other from .profiles import Profiles from .top import Top class LevelerCommands( - Profiles, DataBase, Top, LevelAdmin, DBConverters, metaclass=CompositeMetaClass + Profiles, + DataBase, + Top, + LevelAdmin, + LevelSet, + DBConverters, + Other, + metaclass=CompositeMetaClass, ): """Class joining all command subclasses""" diff --git a/leveler/commands/lvladmin/__init__.py b/leveler/commands/lvladmin/__init__.py index 65fd14b5..7aebe3b5 100644 --- a/leveler/commands/lvladmin/__init__.py +++ b/leveler/commands/lvladmin/__init__.py @@ -1,11 +1,14 @@ from leveler.abc import CompositeMetaClass from .backgrounds import Backgrounds +from .badge import Badge from .economy import Economy from .roles import Roles from .settings import Settings from .users import Users -class LevelAdmin(Backgrounds, Economy, Roles, Settings, Users, metaclass=CompositeMetaClass): - """Database converters commands""" +class LevelAdmin( + Backgrounds, Economy, Roles, Settings, Users, Badge, metaclass=CompositeMetaClass +): + """Leveler administration commands""" diff --git a/leveler/commands/lvladmin/badge.py b/leveler/commands/lvladmin/badge.py new file mode 100644 index 00000000..c3aacf72 --- /dev/null +++ b/leveler/commands/lvladmin/badge.py @@ -0,0 +1,369 @@ +from collections import OrderedDict + +import discord +from redbot.core import commands +from redbot.core.utils import chat_formatting as chat +from redbot.core.utils.menus import DEFAULT_CONTROLS, menu + +from leveler.abc import MixinMeta + +from .basecmd import LevelAdminBaseCMD + + +class Badge(MixinMeta): + """Badge administration commands""" + + lvladmin = getattr(LevelAdminBaseCMD, "lvladmin") + + @lvladmin.group() + async def badge(self, ctx): + """Badge Configuration Options.""" + + @commands.mod_or_permissions(manage_roles=True) + @badge.command(name="add") + @commands.guild_only() + async def addbadge( + self, ctx, name: str, bg_img: str, border_color: str, price: int, *, description: str, + ): + """Add a badge. + + Options : + `name`: Indicate badge's name. If the badge has space, use quote. + `bg_img`: Indicate the image of the badge. (Only URL supported) + `border_color`: Indicate color of the badge's border. (HEX color) + `price`: Indicate the badge's price. (Indicate `-1` and it won't be purchasable, `0` for free.) + `description`: Indicate a description for your badge. + eg: `[p]lvlset badge add Leveler [my_url] #b60047 0 My super badge!` + + If you are the bot owner, you can `-global` to the description to make the badge available everywhere.""" + + user = ctx.author + server = ctx.guild + + # check members + required_members = 35 + members = len([member for member in server.members if not member.bot]) + + if await self.bot.is_owner(user): + pass + elif members < required_members: + await ctx.send( + "**You may only add badges in servers with {}+ non-bot members**".format( + required_members + ) + ) + return + + if "-global" in description and await self.bot.is_owner(user): + description = description.replace("-global", "") + serverid = "global" + servername = "global" + else: + serverid = server.id + servername = server.name + + if "." in name: + await ctx.send("**Name cannot contain `.`**") + return + + if not await self._valid_image_url(bg_img): + await ctx.send("**Background is not valid. Enter HEX color or image URL!**") + return + + if not await self._is_hex(border_color): + await ctx.send("**Border color is not valid!**") + return + + if price < -1: + await ctx.send("**Price is not valid!**") + return + + if len(description.split(" ")) > 40: + await ctx.send("**Description is too long! Must be 40 or less.**") + return + + badges = await self.db.badges.find_one({"server_id": str(serverid)}) + if not badges: + await self.db.badges.insert_one({"server_id": str(serverid), "badges": {}}) + badges = await self.db.badges.find_one({"server_id": str(serverid)}) + + new_badge = { + "badge_name": name, + "bg_img": bg_img, + "price": price, + "description": description, + "border_color": border_color, + "server_id": str(serverid), + "server_name": servername, + "priority_num": 0, + } + + if name not in badges["badges"].keys(): + # create the badge regardless + badges["badges"][name] = new_badge + await self.db.badges.update_one( + {"server_id": str(serverid)}, {"$set": {"badges": badges["badges"]}} + ) + await ctx.send("**`{}` Badge added in `{}` server.**".format(name, servername)) + else: + # update badge in the server + badges["badges"][name] = new_badge + await self.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 + async for user in self.db.users.find({}): + try: + user = await 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 + await self.db.users.update_one( + {"user_id": user["user_id"]}, {"$set": {"badges": userbadges}}, + ) + except Exception as exc: + self.log.error(f"Unable to update badge {name} for {user['user_id']}: {exc}") + await ctx.send("**The `{}` badge has been updated**".format(name)) + + @commands.is_owner() + @badge.command() + @commands.guild_only() + async def type(self, ctx, name: str): + """Define if badge must be circles or bars.""" + valid_types = ["circles", "bars"] + if name.lower() not in valid_types: + await ctx.send("**That is not a valid badge type!**") + return + + await self.config.badge_type.set(name.lower()) + await ctx.send("**Badge type set to `{}`**".format(name.lower())) + + @commands.mod_or_permissions(manage_roles=True) + @badge.command(name="delete") + @commands.guild_only() + async def delbadge(self, ctx, *, name: str): + """Delete a badge and remove from all users. + + Option : `-global`.""" + user = ctx.author + server = ctx.guild + + # return + + if "-global" in name and await self.bot.is_owner(user): + name = name.replace(" -global", "") + serverid = "global" + else: + serverid = server.id + + if await self.config.guild(server).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + serverbadges = await self.db.badges.find_one({"server_id": str(serverid)}) + if name in serverbadges["badges"].keys(): + del serverbadges["badges"][name] + await self.db.badges.update_one( + {"server_id": serverbadges["server_id"]}, + {"$set": {"badges": serverbadges["badges"]}}, + ) + # remove the badge if there + async for user_info_temp in self.db.users.find({}): + try: + user_info_temp = await 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] + await self.db.users.update_one( + {"user_id": user_info_temp["user_id"]}, + {"$set": {"badges": user_info_temp["badges"]}}, + ) + except Exception as exc: + self.log.error( + f"Unable to delete badge {name} from {user_info_temp['user_id']}: {exc}" + ) + + await ctx.send("**The `{}` badge has been removed.**".format(name)) + else: + await ctx.send("**That badge does not exist.**") + + @commands.mod_or_permissions(manage_roles=True) + @badge.command() + @commands.guild_only() + async def give(self, ctx, user: discord.Member, name: str): + """Give a user a badge with a certain name + + Indicate the user and the badge's name.""" + org_user = ctx.message.author + server = ctx.guild + # creates user if doesn't exist + if user.bot: + await ctx.send_help() + return + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + userinfo = await self._badge_convert_dict(userinfo) + + if await self.config.guild(server).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + serverbadges = await self.db.badges.find_one({"server_id": str(server.id)}) + badges = serverbadges["badges"] + badge_name = "{}_{}".format(name, server.id) + + if name not in badges: + await ctx.send("**That badge doesn't exist in this server!**") + return + if badge_name in badges.keys(): + await ctx.send("**{} already has that badge!**".format(await self._is_mention(user))) + return + userinfo["badges"][badge_name] = badges[name] + await self.db.users.update_one( + {"user_id": str(user.id)}, {"$set": {"badges": userinfo["badges"]}} + ) + await ctx.send( + "**{} has just given `{}` the `{}` badge!**".format( + await self._is_mention(org_user), await self._is_mention(user), name + ) + ) + + @commands.mod_or_permissions(manage_roles=True) + @badge.command() + @commands.guild_only() + async def take(self, ctx, user: discord.Member, name: str): + """Take a user's badge. + + Indicate the user and the badge's name.""" + if user.bot: + await ctx.send_help() + return + org_user = ctx.author + server = ctx.guild + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + userinfo = await self._badge_convert_dict(userinfo) + + if await self.config.guild(server).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + serverbadges = await self.db.badges.find_one({"server_id": str(server.id)}) + badges = serverbadges["badges"] + badge_name = "{}_{}".format(name, server.id) + + if name not in badges: + await ctx.send("**That badge doesn't exist in this server!**") + elif badge_name not in userinfo["badges"]: + await ctx.send("**{} does not have that badge!**".format(await self._is_mention(user))) + else: + if userinfo["badges"][badge_name]["price"] == -1: + del userinfo["badges"][badge_name] + await self.db.users.update_one( + {"user_id": str(user.id)}, {"$set": {"badges": userinfo["badges"]}} + ) + await ctx.send( + "**{} has taken the `{}` badge from {}! :upside_down:**".format( + await self._is_mention(org_user), name, await self._is_mention(user), + ) + ) + else: + await ctx.send("**You can't take away purchasable badges!**") + + @commands.mod_or_permissions(manage_roles=True) + @badge.command(name="link") + @commands.guild_only() + async def linkbadge(self, ctx, badge_name: str, level: int): + """Associate a badge with a level. + + Indicate the badge's name and the level.""" + server = ctx.guild + serverbadges = await self.db.badges.find_one({"server_id": str(server.id)}) + + if serverbadges is None: + await ctx.send("**This server does not have any badges!**") + return + + if badge_name not in serverbadges["badges"].keys(): + await ctx.send("**Please make sure the `{}` badge exists!**".format(badge_name)) + return + server_linked_badges = await self.db.badgelinks.find_one({"server_id": str(server.id)}) + if not server_linked_badges: + new_server = { + "server_id": str(server.id), + "badges": {badge_name: str(level)}, + } + await self.db.badgelinks.insert_one(new_server) + else: + server_linked_badges["badges"][badge_name] = str(level) + await self.db.badgelinks.update_one( + {"server_id": str(server.id)}, + {"$set": {"badges": server_linked_badges["badges"]}}, + ) + await ctx.send( + "**The `{}` badge has been linked to level `{}`**".format(badge_name, level) + ) + + @commands.admin_or_permissions(manage_roles=True) + @badge.command(name="unlink") + @commands.guild_only() + async def unlinkbadge(self, ctx, badge_name: str): + """Delete a badge/level association.""" + server = ctx.guild + + server_linked_badges = await self.db.badgelinks.find_one({"server_id": str(server.id)}) + badge_links = server_linked_badges["badges"] + + if badge_name in badge_links.keys(): + await ctx.send( + "**Badge/Level association `{}`/`{}` removed.**".format( + badge_name, badge_links[badge_name] + ) + ) + del badge_links[badge_name] + await self.db.badgelinks.update_one( + {"server_id": str(server.id)}, {"$set": {"badges": badge_links}} + ) + else: + await ctx.send("**The `{}` badge is not linked to any levels!**".format(badge_name)) + + @commands.mod_or_permissions(manage_roles=True) + @badge.command(name="listlinks") + @commands.guild_only() + async def listbadge(self, ctx): + """List level/badge associations.""" + server = ctx.guild + + server_badges = await self.db.badgelinks.find_one({"server_id": str(server.id)}) + + if server_badges is None or not server_badges.get("badges"): + msg = "None" + else: + sortorder = sorted( + server_badges["badges"], key=lambda b: int(server_badges["badges"][b]) + ) + badges = OrderedDict(server_badges["badges"]) + for k in sortorder: + badges.move_to_end(k) + msg = "**Badge** → Level\n" + for badge in badges.keys(): + msg += "**• {} →** {}\n".format(badge, badges[badge]) + + pages = list(chat.pagify(msg, page_length=2048)) + embeds = [] + for i, page in enumerate(pages, start=1): + em = discord.Embed(colour=await ctx.embed_color()) + em.set_author( + name="Current Badge - Level Links for {}".format(server.name), + icon_url=server.icon_url, + ) + em.set_footer(text=f"Page {i}/{len(pages)}") + em.description = msg + embeds.append(em) + await menu(ctx, embeds, DEFAULT_CONTROLS) diff --git a/leveler/commands/lvlset/__init__.py b/leveler/commands/lvlset/__init__.py new file mode 100644 index 00000000..9c41f260 --- /dev/null +++ b/leveler/commands/lvlset/__init__.py @@ -0,0 +1,10 @@ +from leveler.abc import CompositeMetaClass + +from .badge import Badge +from .levelup import Levelup +from .profile import Profile +from .rank import Rank + + +class LevelSet(Badge, Levelup, Profile, Rank, metaclass=CompositeMetaClass): + """Leveler user settings commands""" diff --git a/leveler/commands/lvlset/badge.py b/leveler/commands/lvlset/badge.py new file mode 100644 index 00000000..9533a232 --- /dev/null +++ b/leveler/commands/lvlset/badge.py @@ -0,0 +1,238 @@ +import operator +from abc import ABC +from asyncio import TimeoutError as AsyncTimeoutError + +import discord +from redbot.core import bank, commands +from redbot.core.utils import chat_formatting as chat +from redbot.core.utils.menus import DEFAULT_CONTROLS, menu +from redbot.core.utils.predicates import MessagePredicate + +from leveler.abc import MixinMeta + +from .basecmd import LevelSetBaseCMD + + +class Badge(MixinMeta, ABC): + """Badge commands""" + + lvlset = getattr(LevelSetBaseCMD, "lvlset") + + @lvlset.group(name="badge") + async def lvlset_badge(self, ctx): + """Badge Configuration Options.""" + + @lvlset_badge.command() + @commands.guild_only() + async def available(self, ctx, badge_type: str = "server"): + """Get a list of available badges. + + Options: `server` or `global`. + Defaults for server.""" + server = ctx.guild + if any([badge_type.casefold() == btype for btype in ["server", "guild"]]): + servername = server.name + icon_url = server.icon_url + serverid = server.id + elif badge_type.casefold() == "global": + servername = "Global" + icon_url = self.bot.user.avatar_url + serverid = "global" + else: + await ctx.send("**Invalid Badge Type. Must be `server` or `global`.**") + return + em = discord.Embed(title="Badges available", colour=await ctx.embed_color()) + em.set_author(name="{}".format(servername), icon_url=icon_url) + msg = "" + server_badge_info = await self.db.badges.find_one({"server_id": str(serverid)}) + if server_badge_info and server_badge_info["badges"]: + 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" + + pages = [ + discord.Embed( + title="Badges available", description=page, colour=await ctx.embed_color(), + ) + for page in chat.pagify(msg, page_length=2048) + ] + pagenum = 1 + for page in pages: + page.set_author(name=servername, icon_url=icon_url) + page.set_footer(text="Page {}/{}".format(pagenum, len(pages))) + pagenum += 1 + await menu(ctx, pages, DEFAULT_CONTROLS) + + @lvlset_badge.command(name="list") + @commands.guild_only() + async def listuserbadges(self, ctx, user: discord.Member = None): + """Get all badges of a user.""" + if user is None: + user = ctx.author + if user.bot: + await ctx.send_help() + return + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + userinfo = await 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(colour=user.colour) + + total_pages = len(list(chat.pagify(badge_ranks))) + embeds = [] + + counter = 1 + for page in chat.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)) + embeds.append(em) + counter += 1 + await menu(ctx, embeds, DEFAULT_CONTROLS) + + @lvlset_badge.command(name="buy") + @commands.guild_only() + async def buy(self, ctx, name: str, global_badge: str = None): + """Buy a badge. + + Option: `-global`.""" + user = ctx.author + server = ctx.guild + if global_badge == "-global": + serverid = "global" + else: + serverid = server.id + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + userinfo = await self._badge_convert_dict(userinfo) + server_badge_info = await self.db.badges.find_one({"server_id": str(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 ctx.send("**That badge is not purchasable.**".format(name)) + elif badge_info["price"] == 0: + userinfo["badges"]["{}_{}".format(name, str(serverid))] = server_badges[ + name + ] + await self.db.users.update_one( + {"user_id": userinfo["user_id"]}, + {"$set": {"badges": userinfo["badges"]}}, + ) + await ctx.send("**`{}` has been obtained.**".format(name)) + else: + await ctx.send( + "**{}, you are about to buy the `{}` badge for `{}`. Confirm by typing `yes`.**".format( + await self._is_mention(user), name, badge_info["price"] + ) + ) + pred = MessagePredicate.yes_or_no(ctx) + try: + await self.bot.wait_for("message", timeout=15, check=pred) + except AsyncTimeoutError: + pass + if not pred.result: + await ctx.send("**Purchase canceled.**") + return + if badge_info["price"] <= await bank.get_balance(user): + await bank.withdraw_credits(user, badge_info["price"]) + userinfo["badges"][ + "{}_{}".format(name, str(serverid)) + ] = server_badges[name] + await self.db.users.update_one( + {"user_id": userinfo["user_id"]}, + {"$set": {"badges": userinfo["badges"]}}, + ) + await ctx.send( + "**You have bought the `{}` badge for `{}`.**".format( + name, badge_info["price"] + ) + ) + elif await bank.get_balance(user) < badge_info["price"]: + await ctx.send( + "**Not enough money! Need `{}` more.**".format( + badge_info["price"] - await bank.get_balance(user) + ) + ) + else: + await ctx.send("**{}, you already have this badge!**".format(user.name)) + else: + await ctx.send( + "**The badge `{}` does not exist. Try `{}badge available`**".format( + name, ctx.clean_prefix + ) + ) + else: + await ctx.send( + "**There are no badges to get! Try `{}badge get [badge name] -global`.**".format( + ctx.clean_prefix + ) + ) + + @lvlset_badge.command(name="set") + @commands.guild_only() + async def set_badge(self, ctx, name: str, priority_num: int): + """Set a badge to profile. + + Options for priority number : + `-1`: The badge will be invisible. + `0`: The badge won't be show on your profile. + Maximum to `5000`.""" + user = ctx.author + + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + userinfo = await self._badge_convert_dict(userinfo) + + if priority_num < -1 or priority_num > 5000: + await ctx.send("**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 + await self.db.users.update_one( + {"user_id": userinfo["user_id"]}, {"$set": {"badges": userinfo["badges"]}}, + ) + await ctx.send( + "**The `{}` badge priority has been set to `{}`!**".format( + userinfo["badges"][badge]["badge_name"], priority_num + ) + ) + break + else: + await ctx.send("**You don't have that badge!**") diff --git a/leveler/commands/lvlset/basecmd.py b/leveler/commands/lvlset/basecmd.py new file mode 100644 index 00000000..a829d3c1 --- /dev/null +++ b/leveler/commands/lvlset/basecmd.py @@ -0,0 +1,10 @@ +from redbot.core import commands + +from leveler.abc import CompositeMetaClass + + +class LevelSetBaseCMD(metaclass=CompositeMetaClass): + @commands.group(name="lvlset") + async def lvlset(self, ctx): + """Profile configuration Options.""" + pass diff --git a/leveler/commands/lvlset/levelup.py b/leveler/commands/lvlset/levelup.py new file mode 100644 index 00000000..d703b5e4 --- /dev/null +++ b/leveler/commands/lvlset/levelup.py @@ -0,0 +1,107 @@ +import random + +from redbot.core import commands + +from leveler.abc import MixinMeta + +from .basecmd import LevelSetBaseCMD + + +class Levelup(MixinMeta): + """Levelup commands""" + + lvlset = getattr(LevelSetBaseCMD, "lvlset") + + @lvlset.group(name="levelup", pass_context=True) + async def levelupset(self, ctx): + """Level-Up options.""" + pass + + @levelupset.command(name="color", alias=["colour"]) + @commands.guild_only() + async def levelupcolors(self, ctx, section: str, color: str = None): + """Set levelup color. + + Section can only be `info`. + Color can be : `default`, `white`, `HEX code` (#000000) or `auto`. + e.g: `[p]lvlset color info default`""" + user = ctx.author + server = ctx.guild + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + + section = section.lower() + default_info_color = (30, 30, 30, 200) + white_info_color = (150, 150, 150, 180) + default_a = 200 + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if await self.config.guild(ctx.guild).text_only(): + await ctx.send("**Text-only commands allowed.**") + return + + # get correct section for db query + if section == "info": + section_name = "levelup_info_color" + else: + await ctx.send("**Not a valid section. Must be `info`.**") + return + + # get correct color choice + if color == "auto": + if not all(lib in globals().keys() for lib in ["numpy", "cluster"]): + await ctx.send("**Missing required package. Autocolor feature unavailable**") + return + if section == "info": + color_ranks = [random.randint(0, 1)] + hex_colors = await self._auto_color(ctx, userinfo["levelup_background"], color_ranks) + set_color = [] + for hex_color in hex_colors: + color_temp = await 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 await self._is_hex(color): + set_color = [await self._hex_to_rgb(color, default_a)] + else: + await ctx.send( + "**Not a valid color. Must be `default` `HEX color`, `white` or `auto`.**" + ) + return + + await self.db.users.update_one( + {"user_id": str(user.id)}, {"$set": {section_name: set_color[0]}} + ) + await ctx.send("**Color for level-up {} set.**".format(section)) + + @levelupset.command(name="bg") + @commands.guild_only() + async def levelbg(self, ctx, *, image_name: str): + """Set your level-up background.""" + user = ctx.author + backgrounds = await self.config.backgrounds() + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if await self.config.guild(ctx.guild).text_only(): + await ctx.send("**Text-only commands allowed.**") + return + + if image_name in backgrounds["levelup"].keys(): + if await self._process_purchase(ctx): + await self.db.users.update_one( + {"user_id": str(user.id)}, + {"$set": {"levelup_background": backgrounds["levelup"][image_name]}}, + ) + await ctx.send("**Your new level-up background has been succesfully set!**") + else: + await ctx.send( + f"That is not a valid background. See available backgrounds at `{ctx.clean_prefix}backgrounds levelup`." + ) diff --git a/leveler/commands/lvlset/profile.py b/leveler/commands/lvlset/profile.py new file mode 100644 index 00000000..a5d84e51 --- /dev/null +++ b/leveler/commands/lvlset/profile.py @@ -0,0 +1,227 @@ +import random + +from redbot.core import commands + +from leveler.abc import MixinMeta + +from .basecmd import LevelSetBaseCMD + + +class Profile(MixinMeta): + """Profile commands""" + + lvlset = getattr(LevelSetBaseCMD, "lvlset") + + @lvlset.group(name="profile") + async def profileset(self, ctx): + """Profile options.""" + + @profileset.command(name="color", alias=["colour"]) + @commands.guild_only() + async def profilecolors(self, ctx, section: str, color: str): + """Set profile color. + + For section, you can choose: `exp`, `rep`, `badge`, `info` or `all`. + For color, you can use: `default`, `white`, `HEX code` (#000000) or `auto`. + e.g: `[p]lvlset profile color all #eb4034`""" + user = ctx.author + userinfo = await self.db.users.find_one({"user_id": str(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 await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if await self.config.guild(ctx.guild).text_only(): + await ctx.send("**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 ctx.send( + "**Not a valid section. Must be `rep`, `exp`, `badge`, `info` or `all`.**" + ) + return + + # get correct color choice + if color == "auto": + if not all(lib in globals().keys() for lib in ["numpy", "cluster"]): + await ctx.send("**Missing required package. Autocolor feature unavailable**") + return + 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(ctx, userinfo["profile_background"], color_ranks) + set_color = [] + for hex_color in hex_colors: + color_temp = await 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 await self._is_hex(color): + set_color = [await self._hex_to_rgb(color, default_a)] + else: + await ctx.send( + "**Not a valid color. Must be `default`, `HEX color`, `white` or `auto`.**" + ) + return + + if section == "all": + if len(set_color) == 1: + await self.db.users.update_one( + {"user_id": str(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": + await self.db.users.update_one( + {"user_id": str(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": + await self.db.users.update_one( + {"user_id": str(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 ctx.send("**Colors for profile set.**") + else: + await self.db.users.update_one( + {"user_id": str(user.id)}, {"$set": {section_name: set_color[0]}} + ) + await ctx.send("**Color for profile {} set.**".format(section)) + + @profileset.command(name="bg") + @commands.guild_only() + async def profilebg(self, ctx, *, image_name: str): + """Set your profile background.""" + user = ctx.author + backgrounds = await self.config.backgrounds() + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if await self.config.guild(ctx.guild).text_only(): + await ctx.send("**Text-only commands allowed.**") + return + + if image_name in backgrounds["profile"].keys(): + if await self._process_purchase(ctx): + await self.db.users.update_one( + {"user_id": str(user.id)}, + {"$set": {"profile_background": backgrounds["profile"][image_name]}}, + ) + await ctx.send("**Your new profile background has been succesfully set!**") + else: + await ctx.send( + f"That is not a valid background. See available backgrounds at `{ctx.clean_prefix}backgrounds profile`." + ) + + @profileset.command() + @commands.guild_only() + async def title(self, ctx, *, title): + """Set your title.""" + user = ctx.author + userinfo = await self.db.users.find_one({"user_id": str(user.id)}) + max_char = 20 + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if len(title) < max_char: + userinfo["title"] = title + await self.db.users.update_one({"user_id": str(user.id)}, {"$set": {"title": title}}) + await ctx.send("**Your title has been succesfully set!**") + else: + await ctx.send( + "**Your title has too many characters! Must be {} or less.**".format(max_char) + ) + + @profileset.command() + @commands.guild_only() + async def info(self, ctx, *, info): + """Set your user info.""" + user = ctx.author + max_char = 150 + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if len(info) < max_char: + await self.db.users.update_one({"user_id": str(user.id)}, {"$set": {"info": info}}) + await ctx.send("**Your info section has been succesfully set!**") + else: + await ctx.send( + "**Your description has too many characters! Must be {} or less.**".format( + max_char + ) + ) diff --git a/leveler/commands/lvlset/rank.py b/leveler/commands/lvlset/rank.py new file mode 100644 index 00000000..62217781 --- /dev/null +++ b/leveler/commands/lvlset/rank.py @@ -0,0 +1,149 @@ +import random + +from redbot.core import commands + +from leveler.abc import MixinMeta + +from .basecmd import LevelSetBaseCMD + + +class Rank(MixinMeta): + """Rank commands""" + + lvlset = getattr(LevelSetBaseCMD, "lvlset") + + @lvlset.group(name="rank", pass_context=True) + async def rankset(self, ctx): + """Rank options.""" + + @rankset.command(name="color", alias=["colour"]) + @commands.guild_only() + async def rankcolors(self, ctx, section: str, color: str = None): + """Set rank color. + + For section, you can choose: `exp`, `info` or `all`. + For color, you can use: `default`, `white`, `HEX code` (#000000) or `auto`. + e.g: `[p]lvlset rank color info white`""" + user = ctx.author + userinfo = await self.db.users.find_one({"user_id": str(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_rep = (92, 130, 203, 230) + default_badge = (128, 151, 165, 230) + default_a = 200 + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + if await self.config.guild(ctx.guild).text_only(): + await ctx.send("**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 ctx.send("**Not a valid section. Must be `exp`, `info` or `all`**") + return + + # get correct color choice + if color == "auto": + if not all(lib in globals().keys() for lib in ["numpy", "cluster"]): + await ctx.send("**Missing required package. Autocolor feature unavailable**") + return + 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(ctx, userinfo["rank_background"], color_ranks) + set_color = [] + for hex_color in hex_colors: + color_temp = await 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 await self._is_hex(color): + set_color = [await self._hex_to_rgb(color, default_a)] + else: + await ctx.send( + "**Not a valid color. Must be `default`, `HEX color`, `white or `auto`.**" + ) + return + + if section == "all": + if len(set_color) == 1: + await self.db.users.update_one( + {"user_id": str(user.id)}, + {"$set": {"rank_exp_color": set_color[0], "rank_info_color": set_color[0],}}, + ) + elif color == "default": + await self.db.users.update_one( + {"user_id": str(user.id)}, + { + "$set": { + "rank_exp_color": default_exp, + "rank_info_color": default_info_color, + } + }, + ) + elif color == "auto": + await self.db.users.update_one( + {"user_id": str(user.id)}, + {"$set": {"rank_exp_color": set_color[0], "rank_info_color": set_color[1],}}, + ) + await ctx.send("**Colors for rank set.**") + else: + await self.db.users.update_one( + {"user_id": str(user.id)}, {"$set": {section_name: set_color[0]}} + ) + await ctx.send("**Color for rank {} set.**".format(section)) + + @rankset.command(name="bg") + @commands.guild_only() + async def rankbg(self, ctx, *, image_name: str): + """Set your rank background.""" + user = ctx.author + backgrounds = await self.config.backgrounds() + + if await self.config.guild(ctx.guild).disabled(): + await ctx.send("Leveler commands for this server are disabled.") + return + + if await self.config.guild(ctx.guild).text_only(): + await ctx.send("**Text-only commands allowed.**") + return + + if image_name in backgrounds["rank"].keys(): + if await self._process_purchase(ctx): + await self.db.users.update_one( + {"user_id": str(user.id)}, + {"$set": {"rank_background": backgrounds["rank"][image_name]}}, + ) + await ctx.send("**Your new rank background has been succesfully set!**") + else: + await ctx.send( + f"That is not a valid background. See available backgrounds at `{ctx.clean_prefix}backgrounds rank`." + ) diff --git a/leveler/commands/other.py b/leveler/commands/other.py index 94e5dd8a..95b48246 100644 --- a/leveler/commands/other.py +++ b/leveler/commands/other.py @@ -3,11 +3,12 @@ import discord from redbot.core import commands from redbot.core.utils import chat_formatting as chat +from redbot.core.utils.menus import DEFAULT_CONTROLS, menu from ..abc import CompositeMetaClass, MixinMeta -class Other(MixinMeta, CompositeMetaClass): +class Other(MixinMeta, metaclass=CompositeMetaClass): """Other commands""" @commands.command() @@ -59,3 +60,56 @@ async def rep(self, ctx, *, user: discord.Member = None): chat.humanize_timedelta(seconds=seconds) ) ) + + @commands.command(name="backgrounds", usage="") + @commands.guild_only() + async def disp_backgrounds(self, ctx, bg_type: str): + """Gives a list of backgrounds. + + type can be: `profile`, `rank` or `levelup`.""" + server = ctx.guild + backgrounds = await self.config.backgrounds() + + if await self.config.guild(server).disabled(): + await ctx.send("**Leveler commands for this server are disabled!**") + return + + em = discord.Embed(colour=await ctx.embed_color()) + if bg_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 bg_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 bg_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: + embeds = [] + total = len(backgrounds[bg_key]) + cnt = 1 + for bg in sorted(backgrounds[bg_key].keys()): + em = discord.Embed( + title=bg, + color=await ctx.embed_color(), + url=backgrounds[bg_key][bg], + description=f"Background {cnt}/{total}", + ) + em.set_image(url=backgrounds[bg_key][bg]) + embeds.append(em) + cnt += 1 + await menu(ctx, embeds, DEFAULT_CONTROLS) + else: + await ctx.send("**Invalid background type. Must be `profile`, `rank` or `levelup`.**") diff --git a/leveler/leveler.py b/leveler/leveler.py index f58973ed..651b9df2 100644 --- a/leveler/leveler.py +++ b/leveler/leveler.py @@ -1,25 +1,8 @@ -import operator -import random -import time -from asyncio import TimeoutError as AsyncTimeoutError -from collections import OrderedDict from logging import getLogger import aiohttp -import discord -from redbot.core import Config, bank, checks, commands +from redbot.core import Config, commands from redbot.core.bot import Red -from redbot.core.utils import chat_formatting as chat -from redbot.core.utils.menus import DEFAULT_CONTROLS, menu -from redbot.core.utils.predicates import MessagePredicate - -try: - import numpy - from scipy import cluster -except Exception as e: - print( - f"{__file__}: numpy/scipy is unable to import: {e}\nAutocolor feature will be unavailable" - ) from .abc import CompositeMetaClass from .commands import LevelerCommands @@ -123,1080 +106,3 @@ def cog_unload(self): async def red_delete_data_for_user(self, *, requester, user_id: int): await self.db.users.delete_one({"user_id": str(user_id)}) - - @commands.group(name="lvlset", pass_context=True) - async def lvlset(self, ctx): - """Profile configuration Options.""" - pass - - @lvlset.group(name="profile", pass_context=True) - async def profileset(self, ctx): - """Profile options.""" - pass - - @lvlset.group(name="rank", pass_context=True) - async def rankset(self, ctx): - """Rank options.""" - pass - - @lvlset.group(name="levelup", pass_context=True) - async def levelupset(self, ctx): - """Level-Up options.""" - pass - - @profileset.command(name="color", alias=["colour"], pass_context=True, no_pm=True) - async def profilecolors(self, ctx, section: str, color: str): - """Set profile color. - - For section, you can choose: `exp`, `rep`, `badge`, `info` or `all`. - For color, you can use: `default`, `white`, `HEX code` (#000000) or `auto`. - e.g: `[p]lvlset profile color all #eb4034`""" - user = ctx.author - userinfo = await self.db.users.find_one({"user_id": str(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 await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if await self.config.guild(ctx.guild).text_only(): - await ctx.send("**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 ctx.send( - "**Not a valid section. Must be `rep`, `exp`, `badge`, `info` or `all`.**" - ) - return - - # get correct color choice - if color == "auto": - if not all(lib in globals().keys() for lib in ["numpy", "cluster"]): - await ctx.send("**Missing required package. Autocolor feature unavailable**") - return - 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(ctx, userinfo["profile_background"], color_ranks) - set_color = [] - for hex_color in hex_colors: - color_temp = await 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 await self._is_hex(color): - set_color = [await self._hex_to_rgb(color, default_a)] - else: - await ctx.send( - "**Not a valid color. Must be `default`, `HEX color`, `white` or `auto`.**" - ) - return - - if section == "all": - if len(set_color) == 1: - await self.db.users.update_one( - {"user_id": str(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": - await self.db.users.update_one( - {"user_id": str(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": - await self.db.users.update_one( - {"user_id": str(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 ctx.send("**Colors for profile set.**") - else: - await self.db.users.update_one( - {"user_id": str(user.id)}, {"$set": {section_name: set_color[0]}} - ) - await ctx.send("**Color for profile {} set.**".format(section)) - - @rankset.command(name="color", alias=["colour"]) - @commands.guild_only() - async def rankcolors(self, ctx, section: str, color: str = None): - """Set rank color. - - For section, you can choose: `exp`, `info` or `all`. - For color, you can use: `default`, `white`, `HEX code` (#000000) or `auto`. - e.g: `[p]lvlset rank color info white`""" - user = ctx.author - server = ctx.guild - userinfo = await self.db.users.find_one({"user_id": str(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_rep = (92, 130, 203, 230) - default_badge = (128, 151, 165, 230) - default_a = 200 - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if await self.config.guild(ctx.guild).text_only(): - await ctx.send("**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 ctx.send("**Not a valid section. Must be `exp`, `info` or `all`**") - return - - # get correct color choice - if color == "auto": - if not all(lib in globals().keys() for lib in ["numpy", "cluster"]): - await ctx.send("**Missing required package. Autocolor feature unavailable**") - return - 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(ctx, userinfo["rank_background"], color_ranks) - set_color = [] - for hex_color in hex_colors: - color_temp = await 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 await self._is_hex(color): - set_color = [await self._hex_to_rgb(color, default_a)] - else: - await ctx.send( - "**Not a valid color. Must be `default`, `HEX color`, `white or `auto`.**" - ) - return - - if section == "all": - if len(set_color) == 1: - await self.db.users.update_one( - {"user_id": str(user.id)}, - {"$set": {"rank_exp_color": set_color[0], "rank_info_color": set_color[0],}}, - ) - elif color == "default": - await self.db.users.update_one( - {"user_id": str(user.id)}, - { - "$set": { - "rank_exp_color": default_exp, - "rank_info_color": default_info_color, - } - }, - ) - elif color == "auto": - await self.db.users.update_one( - {"user_id": str(user.id)}, - {"$set": {"rank_exp_color": set_color[0], "rank_info_color": set_color[1],}}, - ) - await ctx.send("**Colors for rank set.**") - else: - await self.db.users.update_one( - {"user_id": str(user.id)}, {"$set": {section_name: set_color[0]}} - ) - await ctx.send("**Color for rank {} set.**".format(section)) - - @levelupset.command(name="color", alias=["colour"]) - @commands.guild_only() - async def levelupcolors(self, ctx, section: str, color: str = None): - """Set levelup color. - - Section can only be `info`. - Color can be : `default`, `white`, `HEX code` (#000000) or `auto`. - e.g: `[p]lvlset color info default`""" - user = ctx.author - server = ctx.guild - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - - section = section.lower() - default_info_color = (30, 30, 30, 200) - white_info_color = (150, 150, 150, 180) - default_a = 200 - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if await self.config.guild(ctx.guild).text_only(): - await ctx.send("**Text-only commands allowed.**") - return - - # get correct section for db query - if section == "info": - section_name = "levelup_info_color" - else: - await ctx.send("**Not a valid section. Must be `info`.**") - return - - # get correct color choice - if color == "auto": - if not all(lib in globals().keys() for lib in ["numpy", "cluster"]): - await ctx.send("**Missing required package. Autocolor feature unavailable**") - return - if section == "info": - color_ranks = [random.randint(0, 1)] - hex_colors = await self._auto_color(ctx, userinfo["levelup_background"], color_ranks) - set_color = [] - for hex_color in hex_colors: - color_temp = await 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 await self._is_hex(color): - set_color = [await self._hex_to_rgb(color, default_a)] - else: - await ctx.send( - "**Not a valid color. Must be `default` `HEX color`, `white` or `auto`.**" - ) - return - - await self.db.users.update_one( - {"user_id": str(user.id)}, {"$set": {section_name: set_color[0]}} - ) - await ctx.send("**Color for level-up {} set.**".format(section)) - - @profileset.command() - @commands.guild_only() - async def info(self, ctx, *, info): - """Set your user info.""" - user = ctx.author - max_char = 150 - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if len(info) < max_char: - await self.db.users.update_one({"user_id": str(user.id)}, {"$set": {"info": info}}) - await ctx.send("**Your info section has been succesfully set!**") - else: - await ctx.send( - "**Your description has too many characters! Must be {} or less.**".format( - max_char - ) - ) - - @levelupset.command(name="bg") - @commands.guild_only() - async def levelbg(self, ctx, *, image_name: str): - """Set your level-up background.""" - user = ctx.author - backgrounds = await self.config.backgrounds() - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if await self.config.guild(ctx.guild).text_only(): - await ctx.send("**Text-only commands allowed.**") - return - - if image_name in backgrounds["levelup"].keys(): - if await self._process_purchase(ctx): - await self.db.users.update_one( - {"user_id": str(user.id)}, - {"$set": {"levelup_background": backgrounds["levelup"][image_name]}}, - ) - await ctx.send("**Your new level-up background has been succesfully set!**") - else: - await ctx.send( - f"That is not a valid background. See available backgrounds at `{ctx.clean_prefix}backgrounds levelup`." - ) - - @profileset.command(name="bg") - @commands.guild_only() - async def profilebg(self, ctx, *, image_name: str): - """Set your profile background.""" - user = ctx.author - server = ctx.guild - backgrounds = await self.config.backgrounds() - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if await self.config.guild(ctx.guild).text_only(): - await ctx.send("**Text-only commands allowed.**") - return - - if image_name in backgrounds["profile"].keys(): - if await self._process_purchase(ctx): - await self.db.users.update_one( - {"user_id": str(user.id)}, - {"$set": {"profile_background": backgrounds["profile"][image_name]}}, - ) - await ctx.send("**Your new profile background has been succesfully set!**") - else: - await ctx.send( - f"That is not a valid background. See available backgrounds at `{ctx.clean_prefix}backgrounds profile`." - ) - - @rankset.command(name="bg") - @commands.guild_only() - async def rankbg(self, ctx, *, image_name: str): - """Set your rank background.""" - user = ctx.author - server = ctx.guild - backgrounds = await self.config.backgrounds() - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("Leveler commands for this server are disabled.") - return - - if await self.config.guild(ctx.guild).text_only(): - await ctx.send("**Text-only commands allowed.**") - return - - if image_name in backgrounds["rank"].keys(): - if await self._process_purchase(ctx): - await self.db.users.update_one( - {"user_id": str(user.id)}, - {"$set": {"rank_background": backgrounds["rank"][image_name]}}, - ) - await ctx.send("**Your new rank background has been succesfully set!**") - else: - await ctx.send( - f"That is not a valid background. See available backgrounds at `{ctx.clean_prefix}backgrounds rank`." - ) - - @profileset.command() - @commands.guild_only() - async def title(self, ctx, *, title): - """Set your title.""" - user = ctx.author - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - max_char = 20 - - if await self.config.guild(ctx.guild).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - if len(title) < max_char: - userinfo["title"] = title - await self.db.users.update_one({"user_id": str(user.id)}, {"$set": {"title": title}}) - await ctx.send("**Your title has been succesfully set!**") - else: - await ctx.send( - "**Your title has too many characters! Must be {} or less.**".format(max_char) - ) - - @lvlset.group(autohelp=True) - async def badge(self, ctx): - """Badge Configuration Options.""" - pass - - @badge.command(name="available") - @commands.guild_only() - async def available(self, ctx, badge_type: str = "server"): - """Get a list of available badges. - - Options: `server` or `global`. - Defaults for server.""" - server = ctx.guild - if any([badge_type.casefold() == btype for btype in ["server", "guild"]]): - servername = server.name - icon_url = server.icon_url - serverid = server.id - elif badge_type.casefold() == "global": - servername = "Global" - icon_url = self.bot.user.avatar_url - serverid = "global" - else: - await ctx.send("**Invalid Badge Type. Must be `server` or `global`.**") - return - em = discord.Embed(title="Badges available", colour=await ctx.embed_color()) - em.set_author(name="{}".format(servername), icon_url=icon_url) - msg = "" - server_badge_info = await self.db.badges.find_one({"server_id": str(serverid)}) - if server_badge_info and server_badge_info["badges"]: - 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" - - pages = [ - discord.Embed( - title="Badges available", description=page, colour=await ctx.embed_color(), - ) - for page in chat.pagify(msg, page_length=2048) - ] - pagenum = 1 - for page in pages: - page.set_author(name=servername, icon_url=icon_url) - page.set_footer(text="Page {}/{}".format(pagenum, len(pages))) - pagenum += 1 - await menu(ctx, pages, DEFAULT_CONTROLS) - - @badge.command(name="list") - @commands.guild_only() - async def listuserbadges(self, ctx, user: discord.Member = None): - """Get all badges of a user.""" - if user is None: - user = ctx.author - if user.bot: - await ctx.send_help() - return - server = ctx.guild - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - userinfo = await 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(colour=user.colour) - - total_pages = len(list(chat.pagify(badge_ranks))) - embeds = [] - - counter = 1 - for page in chat.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)) - embeds.append(em) - counter += 1 - await menu(ctx, embeds, DEFAULT_CONTROLS) - - @badge.command(name="buy") - @commands.guild_only() - async def buy(self, ctx, name: str, global_badge: str = None): - """Buy a badge. - - Option: `-global`.""" - user = ctx.author - server = ctx.guild - if global_badge == "-global": - serverid = "global" - else: - serverid = server.id - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - userinfo = await self._badge_convert_dict(userinfo) - server_badge_info = await self.db.badges.find_one({"server_id": str(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 ctx.send("**That badge is not purchasable.**".format(name)) - elif badge_info["price"] == 0: - userinfo["badges"]["{}_{}".format(name, str(serverid))] = server_badges[ - name - ] - await self.db.users.update_one( - {"user_id": userinfo["user_id"]}, - {"$set": {"badges": userinfo["badges"]}}, - ) - await ctx.send("**`{}` has been obtained.**".format(name)) - else: - await ctx.send( - "**{}, you are about to buy the `{}` badge for `{}`. Confirm by typing `yes`.**".format( - await self._is_mention(user), name, badge_info["price"] - ) - ) - pred = MessagePredicate.yes_or_no(ctx) - try: - await self.bot.wait_for("message", timeout=15, check=pred) - except AsyncTimeoutError: - pass - if not pred.result: - await ctx.send("**Purchase canceled.**") - return - if badge_info["price"] <= await bank.get_balance(user): - await bank.withdraw_credits(user, badge_info["price"]) - userinfo["badges"][ - "{}_{}".format(name, str(serverid)) - ] = server_badges[name] - await self.db.users.update_one( - {"user_id": userinfo["user_id"]}, - {"$set": {"badges": userinfo["badges"]}}, - ) - await ctx.send( - "**You have bought the `{}` badge for `{}`.**".format( - name, badge_info["price"] - ) - ) - elif await bank.get_balance(user) < badge_info["price"]: - await ctx.send( - "**Not enough money! Need `{}` more.**".format( - badge_info["price"] - await bank.get_balance(user) - ) - ) - else: - await ctx.send("**{}, you already have this badge!**".format(user.name)) - else: - await ctx.send( - "**The badge `{}` does not exist. Try `{}badge available`**".format( - name, ctx.clean_prefix - ) - ) - else: - await ctx.send( - "**There are no badges to get! Try `{}badge get [badge name] -global`.**".format( - ctx.clean_prefix - ) - ) - - @badge.command(name="set") - @commands.guild_only() - async def set_badge(self, ctx, name: str, priority_num: int): - """Set a badge to profile. - - Options for priority number : - `-1`: The badge will be invisible. - `0`: The badge won't be show on your profile. - Maximum to `5000`.""" - user = ctx.author - server = ctx.guild - - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - userinfo = await self._badge_convert_dict(userinfo) - - if priority_num < -1 or priority_num > 5000: - await ctx.send("**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 - await self.db.users.update_one( - {"user_id": userinfo["user_id"]}, {"$set": {"badges": userinfo["badges"]}}, - ) - await ctx.send( - "**The `{}` badge priority has been set to `{}`!**".format( - userinfo["badges"][badge]["badge_name"], priority_num - ) - ) - break - else: - await ctx.send("**You don't have that badge!**") - - @checks.mod_or_permissions(manage_roles=True) - @badge.command(name="add") - @commands.guild_only() - async def addbadge( - self, ctx, name: str, bg_img: str, border_color: str, price: int, *, description: str, - ): - """Add a badge. - - Options : - `name`: Indicate badge's name. If the badge has space, use quote. - `bg_img`: Indicate the image of the badge. (Only URL supported) - `border_color`: Indicate color of the badge's border. (HEX color) - `price`: Indicate the badge's price. (Indicate `-1` and it won't be purchasable, `0` for free.) - `description`: Indicate a description for your badge. - eg: `[p]lvlset badge add Leveler [my_url] #b60047 0 My super badge!` - - If you are the bot owner, you can `-global` to the description to make the badge available everywhere.""" - - user = ctx.author - server = ctx.guild - - # check members - required_members = 35 - members = len([member for member in server.members if not member.bot]) - - if await self.bot.is_owner(user): - pass - elif members < required_members: - await ctx.send( - "**You may only add badges in servers with {}+ non-bot members**".format( - required_members - ) - ) - return - - if "-global" in description and await self.bot.is_owner(user): - description = description.replace("-global", "") - serverid = "global" - servername = "global" - else: - serverid = server.id - servername = server.name - - if "." in name: - await ctx.send("**Name cannot contain `.`**") - return - - if not await self._valid_image_url(bg_img): - await ctx.send("**Background is not valid. Enter HEX color or image URL!**") - return - - if not await self._is_hex(border_color): - await ctx.send("**Border color is not valid!**") - return - - if price < -1: - await ctx.send("**Price is not valid!**") - return - - if len(description.split(" ")) > 40: - await ctx.send("**Description is too long! Must be 40 or less.**") - return - - badges = await self.db.badges.find_one({"server_id": str(serverid)}) - if not badges: - await self.db.badges.insert_one({"server_id": str(serverid), "badges": {}}) - badges = await self.db.badges.find_one({"server_id": str(serverid)}) - - new_badge = { - "badge_name": name, - "bg_img": bg_img, - "price": price, - "description": description, - "border_color": border_color, - "server_id": str(serverid), - "server_name": servername, - "priority_num": 0, - } - - if name not in badges["badges"].keys(): - # create the badge regardless - badges["badges"][name] = new_badge - await self.db.badges.update_one( - {"server_id": str(serverid)}, {"$set": {"badges": badges["badges"]}} - ) - await ctx.send("**`{}` Badge added in `{}` server.**".format(name, servername)) - else: - # update badge in the server - badges["badges"][name] = new_badge - await self.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 - async for user in self.db.users.find({}): - try: - user = await 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 - await self.db.users.update_one( - {"user_id": user["user_id"]}, {"$set": {"badges": userbadges}}, - ) - except Exception as exc: - self.log.error(f"Unable to update badge {name} for {user['user_id']}: {exc}") - await ctx.send("**The `{}` badge has been updated**".format(name)) - - @checks.is_owner() - @badge.command() - @commands.guild_only() - async def type(self, ctx, name: str): - """Define if badge must be circles or bars.""" - valid_types = ["circles", "bars"] - if name.lower() not in valid_types: - await ctx.send("**That is not a valid badge type!**") - return - - await self.config.badge_type.set(name.lower()) - await ctx.send("**Badge type set to `{}`**".format(name.lower())) - - @checks.mod_or_permissions(manage_roles=True) - @badge.command(name="delete") - @commands.guild_only() - async def delbadge(self, ctx, *, name: str): - """Delete a badge and remove from all users. - - Option : `-global`.""" - user = ctx.author - server = ctx.guild - - # return - - if "-global" in name and await self.bot.is_owner(user): - name = name.replace(" -global", "") - serverid = "global" - else: - serverid = server.id - - if await self.config.guild(server).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - serverbadges = await self.db.badges.find_one({"server_id": str(serverid)}) - if name in serverbadges["badges"].keys(): - del serverbadges["badges"][name] - await self.db.badges.update_one( - {"server_id": serverbadges["server_id"]}, - {"$set": {"badges": serverbadges["badges"]}}, - ) - # remove the badge if there - async for user_info_temp in self.db.users.find({}): - try: - user_info_temp = await 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] - await self.db.users.update_one( - {"user_id": user_info_temp["user_id"]}, - {"$set": {"badges": user_info_temp["badges"]}}, - ) - except Exception as exc: - self.log.error( - f"Unable to delete badge {name} from {user_info_temp['user_id']}: {exc}" - ) - - await ctx.send("**The `{}` badge has been removed.**".format(name)) - else: - await ctx.send("**That badge does not exist.**") - - @checks.mod_or_permissions(manage_roles=True) - @badge.command() - @commands.guild_only() - async def give(self, ctx, user: discord.Member, name: str): - """Give a user a badge with a certain name - - Indicate the user and the badge's name.""" - org_user = ctx.message.author - server = ctx.guild - # creates user if doesn't exist - if user.bot: - await ctx.send_help() - return - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - userinfo = await self._badge_convert_dict(userinfo) - - if await self.config.guild(server).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - serverbadges = await self.db.badges.find_one({"server_id": str(server.id)}) - badges = serverbadges["badges"] - badge_name = "{}_{}".format(name, server.id) - - if name not in badges: - await ctx.send("**That badge doesn't exist in this server!**") - return - if badge_name in badges.keys(): - await ctx.send("**{} already has that badge!**".format(await self._is_mention(user))) - return - userinfo["badges"][badge_name] = badges[name] - await self.db.users.update_one( - {"user_id": str(user.id)}, {"$set": {"badges": userinfo["badges"]}} - ) - await ctx.send( - "**{} has just given `{}` the `{}` badge!**".format( - await self._is_mention(org_user), await self._is_mention(user), name - ) - ) - - @checks.mod_or_permissions(manage_roles=True) - @badge.command() - @commands.guild_only() - async def take(self, ctx, user: discord.Member, name: str): - """Take a user's badge. - - Indicate the user and the badge's name.""" - if user.bot: - await ctx.send_help() - return - org_user = ctx.author - server = ctx.guild - userinfo = await self.db.users.find_one({"user_id": str(user.id)}) - userinfo = await self._badge_convert_dict(userinfo) - - if await self.config.guild(server).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - serverbadges = await self.db.badges.find_one({"server_id": str(server.id)}) - badges = serverbadges["badges"] - badge_name = "{}_{}".format(name, server.id) - - if name not in badges: - await ctx.send("**That badge doesn't exist in this server!**") - elif badge_name not in userinfo["badges"]: - await ctx.send("**{} does not have that badge!**".format(await self._is_mention(user))) - else: - if userinfo["badges"][badge_name]["price"] == -1: - del userinfo["badges"][badge_name] - await self.db.users.update_one( - {"user_id": str(user.id)}, {"$set": {"badges": userinfo["badges"]}} - ) - await ctx.send( - "**{} has taken the `{}` badge from {}! :upside_down:**".format( - await self._is_mention(org_user), name, await self._is_mention(user), - ) - ) - else: - await ctx.send("**You can't take away purchasable badges!**") - - @checks.mod_or_permissions(manage_roles=True) - @badge.command(name="link") - @commands.guild_only() - async def linkbadge(self, ctx, badge_name: str, level: int): - """Associate a badge with a level. - - Indicate the badge's name and the level.""" - server = ctx.guild - serverbadges = await self.db.badges.find_one({"server_id": str(server.id)}) - - if serverbadges is None: - await ctx.send("**This server does not have any badges!**") - return - - if badge_name not in serverbadges["badges"].keys(): - await ctx.send("**Please make sure the `{}` badge exists!**".format(badge_name)) - return - server_linked_badges = await self.db.badgelinks.find_one({"server_id": str(server.id)}) - if not server_linked_badges: - new_server = { - "server_id": str(server.id), - "badges": {badge_name: str(level)}, - } - await self.db.badgelinks.insert_one(new_server) - else: - server_linked_badges["badges"][badge_name] = str(level) - await self.db.badgelinks.update_one( - {"server_id": str(server.id)}, - {"$set": {"badges": server_linked_badges["badges"]}}, - ) - await ctx.send( - "**The `{}` badge has been linked to level `{}`**".format(badge_name, level) - ) - - @checks.admin_or_permissions(manage_roles=True) - @badge.command(name="unlink") - @commands.guild_only() - async def unlinkbadge(self, ctx, badge_name: str): - """Delete a badge/level association.""" - server = ctx.guild - - server_linked_badges = await self.db.badgelinks.find_one({"server_id": str(server.id)}) - badge_links = server_linked_badges["badges"] - - if badge_name in badge_links.keys(): - await ctx.send( - "**Badge/Level association `{}`/`{}` removed.**".format( - badge_name, badge_links[badge_name] - ) - ) - del badge_links[badge_name] - await self.db.badgelinks.update_one( - {"server_id": str(server.id)}, {"$set": {"badges": badge_links}} - ) - else: - await ctx.send("**The `{}` badge is not linked to any levels!**".format(badge_name)) - - @checks.mod_or_permissions(manage_roles=True) - @badge.command(name="listlinks") - @commands.guild_only() - async def listbadge(self, ctx): - """List level/badge associations.""" - server = ctx.guild - - server_badges = await self.db.badgelinks.find_one({"server_id": str(server.id)}) - - if server_badges is None or not server_badges.get("badges"): - msg = "None" - else: - sortorder = sorted( - server_badges["badges"], key=lambda b: int(server_badges["badges"][b]) - ) - badges = OrderedDict(server_badges["badges"]) - for k in sortorder: - badges.move_to_end(k) - msg = "**Badge** → Level\n" - for badge in badges.keys(): - msg += "**• {} →** {}\n".format(badge, badges[badge]) - - pages = list(chat.pagify(msg, page_length=2048)) - embeds = [] - for i, page in enumerate(pages, start=1): - em = discord.Embed(colour=await ctx.embed_color()) - em.set_author( - name="Current Badge - Level Links for {}".format(server.name), - icon_url=server.icon_url, - ) - em.set_footer(text=f"Page {i}/{len(pages)}") - em.description = msg - embeds.append(em) - await menu(ctx, embeds, DEFAULT_CONTROLS) - - @commands.command(name="backgrounds", usage="") - @commands.guild_only() - async def disp_backgrounds(self, ctx, bg_type: str): - """Gives a list of backgrounds. - - type can be: `profile`, `rank` or `levelup`.""" - server = ctx.guild - backgrounds = await self.config.backgrounds() - - if await self.config.guild(server).disabled(): - await ctx.send("**Leveler commands for this server are disabled!**") - return - - em = discord.Embed(colour=await ctx.embed_color()) - if bg_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 bg_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 bg_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: - embeds = [] - total = len(backgrounds[bg_key]) - cnt = 1 - for bg in sorted(backgrounds[bg_key].keys()): - em = discord.Embed( - title=bg, - color=await ctx.embed_color(), - url=backgrounds[bg_key][bg], - description=f"Background {cnt}/{total}", - ) - em.set_image(url=backgrounds[bg_key][bg]) - embeds.append(em) - cnt += 1 - await menu(ctx, embeds, DEFAULT_CONTROLS) - else: - await ctx.send("**Invalid background type. Must be `profile`, `rank` or `levelup`.**")