1359 lines
55 KiB
Python
1359 lines
55 KiB
Python
import asyncio
|
|
import typing as t
|
|
from io import BytesIO
|
|
from time import perf_counter
|
|
|
|
import discord
|
|
from redbot.core import commands
|
|
from redbot.core.i18n import Translator, cog_i18n
|
|
from redbot.core.utils.chat_formatting import (
|
|
box,
|
|
humanize_number,
|
|
humanize_timedelta,
|
|
pagify,
|
|
)
|
|
|
|
from ..abc import MixinMeta
|
|
from ..common import const, utils
|
|
from ..common.models import Emojis, Prestige
|
|
|
|
_ = Translator("LevelUp", __file__)
|
|
|
|
|
|
@cog_i18n(_)
|
|
class Admin(MixinMeta):
|
|
@commands.group(name="levelset", aliases=["lvlset", "lset"])
|
|
@commands.guild_only()
|
|
@commands.admin_or_permissions(manage_guild=True)
|
|
async def levelset(self, ctx: commands.Context):
|
|
"""Configure LevelUp Settings"""
|
|
pass
|
|
|
|
@levelset.command(name="view")
|
|
@commands.bot_has_permissions(embed_links=True)
|
|
async def view_settings(self, ctx: commands.Context):
|
|
"""View all LevelUP settings"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
txt = _(
|
|
"**Main**\n"
|
|
"`System Enabled: `{}\n"
|
|
"`Profile Type: `{}\n"
|
|
"`Style Override: `{}\n"
|
|
"`Include Balance: `{}\n"
|
|
"**Messages**\n"
|
|
"`Message XP: `{}\n"
|
|
"`Min Msg Length: `{}\n"
|
|
"`Cooldown: `{}\n"
|
|
"`Command XP: `{}\n"
|
|
"**Voice**\n"
|
|
"`Voice XP: `{} per minute\n"
|
|
"`Ignore Muted: `{}\n"
|
|
"`Ignore Solo: `{}\n"
|
|
"`Ignore Deafened: `{}\n"
|
|
"`Ignore Invisible: `{}\n"
|
|
"**Level Algorithm**\n"
|
|
"`Base Multiplier: `{}\n"
|
|
"`Exp Multiplier: `{}\n"
|
|
"`Equation: `{}\n"
|
|
"**LevelUps**\n"
|
|
"`Notify In Channel: `{}\n"
|
|
"• Send levelup message in the channel the user is typing in\n"
|
|
"`Notify in DMs: `{}\n"
|
|
"• Send levelup message in DMs\n"
|
|
"`Notify Channel: `{}\n"
|
|
"• Log channel for levelup messages\n"
|
|
"`Mention User: `{}\n"
|
|
"• This will mention the user in the levelup message\n"
|
|
"`AutoRemove Roles: `{}\n"
|
|
"• Remove the previous level role when a user levels up\n"
|
|
).format(
|
|
_("Yes") if conf.enabled else _("NO!⚠️"),
|
|
_("Embeds") if conf.use_embeds else _("Images"),
|
|
str(conf.style_override).title(),
|
|
_("Yes") if conf.showbal else _("No"),
|
|
f"{conf.xp[0]} - {conf.xp[1]}",
|
|
conf.min_length,
|
|
utils.humanize_delta(conf.cooldown),
|
|
conf.command_xp,
|
|
humanize_number(conf.voicexp),
|
|
conf.ignore_muted,
|
|
conf.ignore_solo,
|
|
conf.ignore_deafened,
|
|
conf.ignore_invisible,
|
|
conf.algorithm.base,
|
|
conf.algorithm.exp,
|
|
f"{conf.algorithm.base} x (level ^ {conf.algorithm.exp}) = XP",
|
|
conf.notify,
|
|
conf.notifydm,
|
|
f"<#{conf.notifylog}>" if conf.notifylog else _("None"),
|
|
conf.notifymention,
|
|
conf.autoremove,
|
|
)
|
|
embed = discord.Embed(
|
|
title=_("LevelUp Settings"),
|
|
description=txt.strip(),
|
|
color=await self.bot.get_embed_color(ctx),
|
|
)
|
|
if conf.levelroles:
|
|
joined = "\n".join(
|
|
_("• Level {}: {}").format(level, f"<@&{role_id}>") for level, role_id in conf.levelroles.items()
|
|
)
|
|
chunks = list(pagify(joined, page_length=1024))
|
|
if len(chunks) == 1:
|
|
embed.add_field(name=_("Level Roles"), value=joined, inline=False)
|
|
else:
|
|
for i, chunk in enumerate(chunks):
|
|
embed.add_field(
|
|
name=_("Level Roles") if i == 0 else _("Level Roles Continued"), value=chunk, inline=False
|
|
)
|
|
if conf.prestigelevel and conf.prestigedata:
|
|
roles = _("➣ Prestige roles will {}").format(
|
|
_("**Stack**") if conf.stackprestigeroles else _("**Not Stack**")
|
|
)
|
|
req = _("➣ Requires reaching level {} to activate").format(conf.prestigelevel)
|
|
if conf.keep_level_roles:
|
|
req += _("\n➣ Level roles will be kept after prestiging")
|
|
else:
|
|
req += _("\n➣ Level roles will be reset after prestiging")
|
|
joined = "\n".join(
|
|
_("• Prestige {}: {}").format(level, f"<@&{prestige.role}>")
|
|
for level, prestige in conf.prestigedata.items()
|
|
)
|
|
embed.add_field(name=_("Prestige"), value=f"{roles}\n{req}\n{joined}", inline=False)
|
|
if conf.rolebonus.voice:
|
|
joined = "\n".join(
|
|
_("• {}: `{}`").format(f"<@&{role_id}>", xp_range) for role_id, xp_range in conf.rolebonus.voice.items()
|
|
)
|
|
embed.add_field(name=_("Voice XP Bonus Roles"), value=joined, inline=False)
|
|
if conf.channelbonus.voice:
|
|
joined = "\n".join(
|
|
_("• {}: `{}`").format(f"<#{channel_id}>", xp_range)
|
|
for channel_id, xp_range in conf.channelbonus.voice.items()
|
|
)
|
|
embed.add_field(name=_("Voice XP Bonus Channels"), value=joined, inline=False)
|
|
if conf.streambonus:
|
|
embed.add_field(
|
|
name=_("Stream Bonus"),
|
|
value=_("Bonus for streaming: {}").format(f"`{conf.streambonus}`"),
|
|
inline=False,
|
|
)
|
|
if conf.rolebonus.msg:
|
|
joined = "\n".join(
|
|
_("• {}: `{}`").format(f"<@&{role_id}>", xp_range) for role_id, xp_range in conf.rolebonus.msg.items()
|
|
)
|
|
embed.add_field(name=_("Message XP Bonus Roles"), value=joined, inline=False)
|
|
if conf.channelbonus.msg:
|
|
joined = "\n".join(
|
|
_("• {}: `{}`").format(f"<#{channel_id}>", xp_range)
|
|
for channel_id, xp_range in conf.channelbonus.msg.items()
|
|
)
|
|
embed.add_field(name=_("Message XP Bonus Channels"), value=joined, inline=False)
|
|
if conf.allowedroles:
|
|
joined = ", ".join([f"<@&{role_id}>" for role_id in conf.allowedroles if ctx.guild.get_role(role_id)])
|
|
embed.add_field(name=_("Allowed Roles"), value=joined, inline=False)
|
|
if conf.allowedchannels:
|
|
joined = ", ".join(
|
|
[f"<#{channel_id}>" for channel_id in conf.allowedchannels if ctx.guild.get_channel(channel_id)]
|
|
)
|
|
embed.add_field(name=_("Allowed Channels"), value=joined, inline=False)
|
|
if conf.ignoredroles:
|
|
joined = ", ".join([f"<@&{role_id}>" for role_id in conf.ignoredroles if ctx.guild.get_role(role_id)])
|
|
embed.add_field(name=_("Ignored Roles"), value=joined, inline=False)
|
|
if conf.ignoredchannels:
|
|
joined = ", ".join(
|
|
[f"<#{channel_id}>" for channel_id in conf.ignoredchannels if ctx.guild.get_channel(channel_id)]
|
|
)
|
|
embed.add_field(name=_("Ignored Channels"), value=joined, inline=False)
|
|
if conf.ignoredusers:
|
|
joined = ", ".join([f"<@{user_id}>" for user_id in conf.ignoredusers if ctx.guild.get_member(user_id)])
|
|
embed.add_field(name=_("Ignored Users"), value=joined, inline=False)
|
|
if dm_role := conf.role_awarded_dm:
|
|
embed.add_field(name=_("LevelUp DM Role Message"), value=dm_role, inline=False)
|
|
if dm_msg := conf.levelup_dm:
|
|
embed.add_field(name=_("LevelUp DM Message"), value=dm_msg, inline=False)
|
|
if msg := conf.levelup_msg:
|
|
embed.add_field(name=_("LevelUp Message"), value=msg, inline=False)
|
|
if msg_role := conf.role_awarded_msg:
|
|
embed.add_field(name=_("LevelUp Role Message"), value=msg_role, inline=False)
|
|
if roles := conf.role_groups:
|
|
joined = ", ".join([f"<@&{role_id}>" for role_id in roles if ctx.guild.get_role(role_id)])
|
|
txt = _("The following roles gain exp as a group:\n{}").format(joined)
|
|
embed.add_field(name=_("Role Exp Groups"), value=txt, inline=False)
|
|
if ctx.author.id not in self.bot.owner_ids:
|
|
txt = _("➣ Profile Cache Time\n")
|
|
if self.db.cache_seconds:
|
|
txt += _("Profiles will be cached for {}\n").format(utils.humanize_delta(self.db.cache_seconds))
|
|
else:
|
|
txt += _("Profiles are not cached\n")
|
|
txt += _("➣ Profile Rendering\n")
|
|
if self.db.render_gifs:
|
|
txt += _("Users with animated profiles will render as a GIF")
|
|
else:
|
|
txt += _("Profiles will always be static images")
|
|
embed.add_field(
|
|
name=_("Bot Owner Settings"),
|
|
value=txt,
|
|
inline=False,
|
|
)
|
|
await ctx.send(embed=embed)
|
|
|
|
@levelset.command(name="forcestyle")
|
|
async def force_profile_style(self, ctx: commands.Context, style: t.Literal["default", "runescape", "none"]):
|
|
"""
|
|
Force a profile style for all users
|
|
|
|
Specify `none` to disable the forced style
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if conf.use_embeds:
|
|
return await ctx.send(
|
|
_("LevelUp is using embeds, use the {} command to toggle between embed and image profiles.").format(
|
|
f"`{ctx.clean_prefix}levelset embeds`"
|
|
)
|
|
)
|
|
if style == "none":
|
|
conf.style_override = None
|
|
self.save()
|
|
return await ctx.send(_("Style override has been **disabled**!"))
|
|
conf.style_override = style
|
|
self.save()
|
|
await ctx.send(_("Style override has been set to **{}**").format(style))
|
|
|
|
@levelset.command(name="toggle")
|
|
async def toggle_levelup(self, ctx: commands.Context):
|
|
"""Toggle the LevelUp system"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.enabled else _("**Enabled**")
|
|
conf.enabled = not conf.enabled
|
|
self.save()
|
|
await ctx.send(_("LevelUp has been {}").format(status))
|
|
|
|
@levelset.command(name="rolegroup")
|
|
async def add_remove_role_group(self, ctx: commands.Context, role: t.Union[discord.Role, int]):
|
|
"""
|
|
Add or remove a role to the role group
|
|
|
|
These roles gain their own experience points as a group
|
|
When a member gains xp while having this role, the xp they earn is also added to the role group
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
role_id = role if isinstance(role, int) else role.id
|
|
if role_id in conf.role_groups:
|
|
del conf.role_groups[role_id]
|
|
txt = _("The role {} will no longer gain experience points.").format(f"<@&{role_id}>")
|
|
else:
|
|
if not ctx.guild.get_role(role_id):
|
|
return await ctx.send(_("Role not found!"))
|
|
conf.role_groups[role_id] = 0
|
|
txt = _("The role {} will now gain expecience points from all members that have it.").format(
|
|
f"<@&{role_id}>"
|
|
)
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@levelset.command(name="addxp")
|
|
async def add_xp_to_user(
|
|
self,
|
|
ctx: commands.Context,
|
|
user_or_role: t.Union[discord.Member, discord.Role],
|
|
xp: int,
|
|
):
|
|
"""Add XP to a user or role"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if isinstance(user_or_role, discord.Member):
|
|
profile = conf.get_profile(user_or_role)
|
|
profile.xp += xp
|
|
txt = _("Added {} XP to {}").format(xp, user_or_role.name)
|
|
self.save()
|
|
return await ctx.send(txt)
|
|
for user in user_or_role.members:
|
|
profile = conf.get_profile(user)
|
|
profile.xp += xp
|
|
txt = _("Added {} XP {} member(s) with the {} role").format(
|
|
xp,
|
|
len(user_or_role.members),
|
|
user_or_role.mention,
|
|
)
|
|
await ctx.send(txt)
|
|
self.save()
|
|
|
|
@levelset.command(name="removexp")
|
|
async def remove_xp_from_user(
|
|
self,
|
|
ctx: commands.Context,
|
|
user_or_role: t.Union[discord.Member, discord.Role],
|
|
xp: int,
|
|
):
|
|
"""Remove XP from a user or role"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if isinstance(user_or_role, discord.Member):
|
|
profile = conf.get_profile(user_or_role)
|
|
profile.xp -= min(profile.xp, xp)
|
|
txt = _("Removed {} XP from {}").format(min(profile.xp, xp), user_or_role.name)
|
|
self.save()
|
|
return await ctx.send(txt)
|
|
for user in user_or_role.members:
|
|
profile = conf.get_profile(user)
|
|
profile.xp -= min(profile.xp, xp)
|
|
txt = _("Removed {} XP from {} member(s) with the {} role").format(
|
|
xp,
|
|
len(user_or_role.members),
|
|
user_or_role.mention,
|
|
)
|
|
await ctx.send(txt)
|
|
self.save()
|
|
|
|
@levelset.command(name="algorithm", aliases=["algo"])
|
|
async def set_level_algorithm(
|
|
self,
|
|
ctx: commands.Context,
|
|
part: t.Literal["base", "exp"],
|
|
value: t.Union[float, int],
|
|
):
|
|
"""
|
|
Customize the leveling algorithm for your server
|
|
• Default base is 100
|
|
• Default exp is 2
|
|
|
|
**Equation**
|
|
➣ Getting required XP for a level
|
|
• `base * (level ^ exp) = XP`
|
|
➣ Getting required level for an XP value
|
|
• `level = (XP / base) ^ (1 / exp)`
|
|
|
|
**Arguments**
|
|
➣ `part` - The part of the algorithm to change
|
|
➣ `value` - The value to set it to
|
|
"""
|
|
if part == "exp":
|
|
value = float(value)
|
|
if value <= 0:
|
|
return await ctx.send(_("Exponent must be greater than 0"))
|
|
if value > 10:
|
|
return await ctx.send(_("Exponent must be less than 10"))
|
|
else:
|
|
value = round(value)
|
|
if value < 0:
|
|
return await ctx.send(_("Base must be greater than 0"))
|
|
conf = self.db.get_conf(ctx.guild)
|
|
setattr(conf.algorithm, part, value)
|
|
self.save()
|
|
await ctx.send(_("Algorithm {} has been set to {}").format(part, value))
|
|
|
|
@levelset.command(name="commandxp")
|
|
async def set_command_xp(self, ctx: commands.Context):
|
|
"""Toggle whether users can gain Exp from running commands"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.command_xp else _("**Enabled**")
|
|
conf.command_xp = not conf.command_xp
|
|
self.save()
|
|
await ctx.send(_("Command XP has been {}").format(status))
|
|
|
|
@levelset.command(name="dm")
|
|
async def toggle_dm(self, ctx: commands.Context):
|
|
"""
|
|
Toggle DM notifications
|
|
|
|
Determines whether LevelUp messages are DM'd to the user
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.notifydm else _("**Enabled**")
|
|
conf.notifydm = not conf.notifydm
|
|
self.save()
|
|
await ctx.send(_("DM notifications have been {}").format(status))
|
|
|
|
@levelset.command(name="resetemojis")
|
|
async def reset_emojis(self, ctx: commands.Context):
|
|
"""Reset the emojis to default"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.emojis = Emojis()
|
|
self.save()
|
|
await ctx.send(_("Emojis have been reset to default"))
|
|
|
|
@levelset.command(name="emojis")
|
|
@commands.bot_has_permissions(embed_links=True)
|
|
async def set_emojis(
|
|
self,
|
|
ctx: commands.Context,
|
|
level: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
prestige: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
star: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
chat: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
voicetime: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
experience: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
balance: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
):
|
|
"""Set the emojis used to represent each stat type"""
|
|
|
|
async def test_reactions(
|
|
ctx: commands.Context,
|
|
emojis: t.List[t.Union[discord.Emoji, discord.PartialEmoji, str]],
|
|
) -> bool:
|
|
try:
|
|
[await ctx.message.add_reaction(e) for e in emojis]
|
|
return True
|
|
except Exception as e:
|
|
await ctx.send(f"Cannot add reactions: {e}")
|
|
return False
|
|
|
|
reactions = [level, prestige, star, chat, voicetime, experience, balance]
|
|
if not await test_reactions(ctx, reactions):
|
|
return
|
|
|
|
def get_emoji_value(emoji: t.Union[discord.Emoji, discord.PartialEmoji, str]):
|
|
if isinstance(emoji, str):
|
|
return emoji
|
|
if emoji.id:
|
|
return emoji.id
|
|
return str(emoji)
|
|
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.emojis.level = get_emoji_value(level)
|
|
conf.emojis.trophy = get_emoji_value(prestige)
|
|
conf.emojis.star = get_emoji_value(star)
|
|
conf.emojis.chat = get_emoji_value(chat)
|
|
conf.emojis.mic = get_emoji_value(voicetime)
|
|
conf.emojis.bulb = get_emoji_value(experience)
|
|
conf.emojis.money = get_emoji_value(balance)
|
|
self.save()
|
|
await ctx.send(_("Emojis have been set"))
|
|
|
|
@levelset.command(name="embeds")
|
|
async def toggle_embeds(self, ctx: commands.Context):
|
|
"""Toggle using embeds or generated pics"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if self.db.force_embeds:
|
|
txt = _("Profile rendering is locked to Embeds only by the bot owner!")
|
|
conf.use_embeds = False
|
|
self.save()
|
|
return await ctx.send(txt)
|
|
status = _("**Images**") if conf.use_embeds else _("**Embeds**")
|
|
conf.use_embeds = not conf.use_embeds
|
|
self.save()
|
|
await ctx.send(_("Profile rendering has been set to {}").format(status))
|
|
|
|
@levelset.command(name="levelchannel")
|
|
async def set_level_channel(
|
|
self,
|
|
ctx: commands.Context,
|
|
channel: t.Union[discord.TextChannel, None] = None,
|
|
):
|
|
"""
|
|
Set LevelUp log channel
|
|
|
|
Set a channel for all level up messages to send to.
|
|
|
|
If level notify is off and mention is on, the bot will mention the user in the channel
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if not channel and not conf.notifylog:
|
|
return await ctx.send_help()
|
|
if not channel and conf.notifylog:
|
|
conf.notifylog = 0
|
|
self.save()
|
|
return await ctx.send(_("LevelUp messages will no longer be sent to a specific channel"))
|
|
conf.notifylog = channel.id
|
|
self.save()
|
|
await ctx.send(_("LevelUp messages will now be sent to {}").format(channel.mention))
|
|
|
|
@levelset.command(name="levelnotify")
|
|
async def toggle_levelup_notifications(self, ctx: commands.Context):
|
|
"""
|
|
Send levelup message in the channel the user is typing in
|
|
|
|
Send a message in the channel a user is typing in when they level up
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.notify else _("**Enabled**")
|
|
conf.notify = not conf.notify
|
|
self.save()
|
|
await ctx.send(_("LevelUp notifications have been {}").format(status))
|
|
|
|
@levelset.command(name="mention")
|
|
async def toggle_mention(self, ctx: commands.Context):
|
|
"""
|
|
Toggle whether to mention the user in the level up message
|
|
|
|
If level notify is on AND a log channel is set, the user will only be mentioned in the channel they are in.
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.notifymention else _("**Enabled**")
|
|
conf.notifymention = not conf.notifymention
|
|
self.save()
|
|
await ctx.send(_("Mentioning user in LevelUp messages has been {}").format(status))
|
|
|
|
@levelset.command(name="seelevels")
|
|
@commands.bot_has_permissions(attach_files=True, embed_links=True)
|
|
async def see_levels(self, ctx: commands.Context):
|
|
"""
|
|
Test the level algorithm
|
|
View the first 20 levels using the current algorithm to test experience curve
|
|
"""
|
|
async with ctx.typing():
|
|
conf = self.db.get_conf(ctx.guild)
|
|
txt, filebytes = await asyncio.to_thread(
|
|
utils.plot_levels,
|
|
base=conf.algorithm.base,
|
|
exponent=conf.algorithm.exp,
|
|
cooldown=conf.cooldown,
|
|
xp_range=conf.xp,
|
|
)
|
|
file = discord.File(BytesIO(filebytes), filename="levels.png")
|
|
img = "attachment://levels.png"
|
|
example = _(
|
|
"XP required for a level = Base * Level^ᵉˣᵖ\n\n"
|
|
"Approx time is the time it would take for a user to reach a level with randomized breaks"
|
|
)
|
|
desc = _("`Base Multiplier: `") + f"{conf.algorithm.base}\n"
|
|
desc += _("`Exp Multiplier: `") + f"{conf.algorithm.exp}\n"
|
|
desc += _("`Experience Range: `") + f"{conf.xp}\n"
|
|
desc += _("`Message Cooldown: `") + f"{conf.cooldown}\n"
|
|
desc += f"{box(example)}\n{box(txt, lang='python')}"
|
|
embed = discord.Embed(
|
|
title=_("Leveling Algorithm"),
|
|
description=desc,
|
|
color=await self.bot.get_embed_color(ctx),
|
|
)
|
|
embed.set_image(url=img)
|
|
await ctx.send(file=file, embed=embed)
|
|
|
|
@levelset.command(name="setlevel")
|
|
async def set_level(self, ctx: commands.Context, user: discord.Member, level: int):
|
|
"""
|
|
Set a user's level
|
|
|
|
**Arguments**
|
|
• `user` - The user to set the level for
|
|
• `level` - The level to set the user to
|
|
"""
|
|
async with ctx.typing():
|
|
conf = self.db.get_conf(ctx.guild)
|
|
profile = conf.get_profile(user)
|
|
profile.level = level
|
|
profile.xp = conf.algorithm.get_xp(level)
|
|
# Make sure xp is a valid number that python can actually handle
|
|
if profile.xp > 1e308:
|
|
return await ctx.send(_("That level is too high!"))
|
|
self.save()
|
|
reason = _("{} set {}'s level to {}").format(ctx.author.name, user.name, level)
|
|
added, removed = await self.ensure_roles(user, conf, reason)
|
|
if added or removed:
|
|
txt = _("{}'s level has been set to {} and their roles have been updated").format(user.name, level)
|
|
else:
|
|
txt = _("{}'s level has been set to {}").format(user.name, level)
|
|
await ctx.send(txt)
|
|
|
|
@levelset.command(name="setprestige")
|
|
async def set_user_prestige(self, ctx: commands.Context, user: discord.Member, prestige: int):
|
|
"""
|
|
Set a user to a specific prestige level
|
|
|
|
Prestige roles will need to be manually added/removed when using this command
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if user.id not in conf.users:
|
|
return await ctx.send(_("User has not been registered yet!"))
|
|
if not conf.prestigedata:
|
|
return await ctx.send(_("Prestige levels have not been set!"))
|
|
if prestige and prestige not in conf.prestigedata:
|
|
return await ctx.send(_("That prestige level does not exist!"))
|
|
profile = conf.get_profile(user)
|
|
profile.prestige = prestige
|
|
self.save()
|
|
await ctx.send(_("{} has been set to prestige level {}").format(user.name, prestige))
|
|
|
|
@levelset.command(name="showbalance", aliases=["showbal"])
|
|
async def toggle_profile_balance(self, ctx: commands.Context):
|
|
"""Toggle whether to show user's economy credit balance in their profile"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.showbal else _("**Enabled**")
|
|
conf.showbal = not conf.showbal
|
|
self.save()
|
|
await ctx.send(_("Including economy balance in profiles has been {}").format(status))
|
|
|
|
@levelset.command(name="starcooldown")
|
|
async def set_star_cooldown(self, ctx: commands.Context, seconds: int):
|
|
"""
|
|
Set the star cooldown
|
|
|
|
Users can give another user a star every X seconds
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.starcooldown = seconds
|
|
self.save()
|
|
await ctx.send(_("Star cooldown has been set to {} seconds").format(seconds))
|
|
|
|
@levelset.command(name="starmention")
|
|
async def toggle_star_mention(self, ctx: commands.Context):
|
|
"""
|
|
Toggle star reaction mentions
|
|
Toggle whether the bot mentions that a user reacted to a message with a star
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.starmention else _("**Enabled**")
|
|
conf.starmention = not conf.starmention
|
|
self.save()
|
|
await ctx.send(_("Mentioning user when they receive a star has been {}").format(status))
|
|
|
|
@levelset.command(name="starmentiondelete")
|
|
async def toggle_starmention_autodelete(self, ctx: commands.Context, deleted_after: int):
|
|
"""
|
|
Toggle whether the bot auto-deletes the star mentions
|
|
Set to 0 to disable auto-delete
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.starmentionautodelete = deleted_after
|
|
if deleted_after:
|
|
await ctx.send(_("Star mentions will be deleted after {} seconds").format(deleted_after))
|
|
else:
|
|
await ctx.send(_("Star mentions will not be auto-deleted"))
|
|
self.save()
|
|
|
|
@levelset.group(name="allowed")
|
|
async def allowed(self, ctx: commands.Context):
|
|
"""Base command for all allowed lists"""
|
|
pass
|
|
|
|
@allowed.command(name="channel")
|
|
async def allowed_channel(
|
|
self,
|
|
ctx: commands.Context,
|
|
*,
|
|
channel: t.Union[discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel, discord.ForumChannel],
|
|
):
|
|
"""
|
|
Add/Remove a channel in the allowed list
|
|
If the allow list is not empty, only channels in the list will gain XP
|
|
|
|
Use the command with a channel already in the allowed list to remove it
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if channel.id in conf.allowedchannels:
|
|
conf.allowedchannels.remove(channel.id)
|
|
txt = _("Channel {} has been removed from the allowed list").format(channel.mention)
|
|
else:
|
|
conf.allowedchannels.append(channel.id)
|
|
txt = _("Channel {} has been added to the allowed list").format(channel.mention)
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@allowed.command(name="role")
|
|
async def allowed_role(
|
|
self,
|
|
ctx: commands.Context,
|
|
*,
|
|
role: discord.Role,
|
|
):
|
|
"""
|
|
Add/Remove a role in the allowed list
|
|
If the allow list is not empty, only roles in the list will gain XP
|
|
|
|
Use the command with a role already in the allowed list to remove it
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if role.id in conf.allowedroles:
|
|
conf.allowedroles.remove(role.id)
|
|
txt = _("Role {} has been removed from the allowed list").format(role.mention)
|
|
else:
|
|
conf.allowedroles.append(role.id)
|
|
txt = _("Role {} has been added to the allowed list").format(role.mention)
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@levelset.group(name="ignore")
|
|
async def ignore(self, ctx: commands.Context):
|
|
"""Base command for all ignore lists"""
|
|
pass
|
|
|
|
@ignore.command(name="channel")
|
|
async def ignore_channel(
|
|
self,
|
|
ctx: commands.Context,
|
|
*,
|
|
channel: t.Union[discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel, discord.ForumChannel],
|
|
):
|
|
"""
|
|
Add/Remove a channel in the ignore list
|
|
Channels in the ignore list don't gain XP
|
|
|
|
Use the command with a channel already in the ignore list to remove it
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if channel.id in conf.ignoredchannels:
|
|
conf.ignoredchannels.remove(channel.id)
|
|
txt = _("Channel {} has been removed from the ignore list").format(channel.mention)
|
|
else:
|
|
conf.ignoredchannels.append(channel.id)
|
|
txt = _("Channel {} has been added to the ignore list").format(channel.mention)
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@ignore.command(name="role")
|
|
async def ignore_role(
|
|
self,
|
|
ctx: commands.Context,
|
|
*,
|
|
role: discord.Role,
|
|
):
|
|
"""
|
|
Add/Remove a role in the ignore list
|
|
Members with roles in the ignore list don't gain XP
|
|
|
|
Use the command with a role already in the ignore list to remove it
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if role.id in conf.ignoredroles:
|
|
conf.ignoredroles.remove(role.id)
|
|
txt = _("Role {} has been removed from the ignore list").format(role.mention)
|
|
else:
|
|
conf.ignoredroles.append(role.id)
|
|
txt = _("Role {} has been added to the ignore list").format(role.mention)
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@ignore.command(name="user")
|
|
async def ignore_user(
|
|
self,
|
|
ctx: commands.Context,
|
|
*,
|
|
user: discord.Member,
|
|
):
|
|
"""
|
|
Add/Remove a user in the ignore list
|
|
Members in the ignore list don't gain XP
|
|
|
|
Use the command with a user already in the ignore list to remove them
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if user.id in conf.ignoredusers:
|
|
conf.ignoredusers.remove(user.id)
|
|
txt = _("User {} has been removed from the ignore list").format(user.name)
|
|
else:
|
|
conf.ignoredusers.append(user.id)
|
|
txt = _("User {} has been added to the ignore list").format(user.name)
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@levelset.group(name="levelupmessages", aliases=["lvlalerts", "levelalerts", "lvlmessages", "lvlmsg"])
|
|
async def set_levelup_alerts(self, ctx: commands.Context):
|
|
"""Level up alert messages
|
|
|
|
**Arguments**
|
|
The following placeholders can be used:
|
|
• `{username}`: The user's name
|
|
• `{mention}`: Mentions the user
|
|
• `{displayname}`: The user's display name
|
|
• `{level}`: The level the user just reached
|
|
• `{server}`: The server the user is in
|
|
|
|
**If using dmrole or msgrole**
|
|
• `{role}`: The role the user just recieved
|
|
"""
|
|
|
|
@set_levelup_alerts.command(name="view")
|
|
async def view_levelup_alerts(self, ctx: commands.Context):
|
|
"""View the current level up alert messages"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
color = await self.bot.get_embed_color(ctx)
|
|
desc = _("Current LevelUp Alert Messages\n-# None means the default will be used")
|
|
embed = discord.Embed(description=desc, color=color)
|
|
value = _("-# When a user levels up\n{}").format(str(conf.levelup_dm or None))
|
|
embed.add_field(name=_("LevelUp DM"), value=value, inline=False)
|
|
value = _("-# When a user levels up and receives a role\n{}").format(str(conf.role_awarded_dm or None))
|
|
embed.add_field(name=_("LevelUp DM Role"), value=value, inline=False)
|
|
value = _("-# When a user levels up\n{}").format(str(conf.levelup_msg or None))
|
|
embed.add_field(name=_("LevelUp Message"), value=value, inline=False)
|
|
value = _("-# When a user levels up and receives a role\n{}").format(str(conf.role_awarded_msg or None))
|
|
embed.add_field(name=_("LevelUp Role Message"), value=value, inline=False)
|
|
await ctx.send(embed=embed)
|
|
|
|
@set_levelup_alerts.command(name="dm")
|
|
async def set_levelup_dm(self, ctx: commands.Context, *, message: str = None):
|
|
"""
|
|
Set the DM a user gets when they level up (Without recieving a role).
|
|
|
|
**Arguments**
|
|
The following placeholders can be used:
|
|
• `{username}`: The user's name
|
|
• `{mention}`: Mentions the user
|
|
• `{displayname}`: The user's display name
|
|
• `{level}`: The level the user just reached
|
|
• `{server}`: The server the user is in
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if not message and not conf.levelup_dm:
|
|
return await ctx.send_help()
|
|
if not message and conf.levelup_dm:
|
|
conf.levelup_dm = None
|
|
self.save()
|
|
return await ctx.send(_("LevelUp DM message has been removed"))
|
|
kwargs = {
|
|
"username": ctx.author.name,
|
|
"mention": ctx.author.mention,
|
|
"displayname": ctx.author.display_name,
|
|
"level": 1,
|
|
"server": ctx.guild.name,
|
|
}
|
|
try:
|
|
msg = message.format(**kwargs)
|
|
except KeyError as e:
|
|
return await ctx.send(_("Invalid placeholder used: {}").format(e))
|
|
conf.levelup_dm = message
|
|
self.save()
|
|
embed = discord.Embed(description=msg, color=await self.bot.get_embed_color(ctx))
|
|
await ctx.send(_("LevelUp DM message has been set"), embed=embed)
|
|
|
|
@set_levelup_alerts.command(name="dmrole")
|
|
async def set_levelup_dmrole(self, ctx: commands.Context, *, message: str = None):
|
|
"""
|
|
Set the DM a user gets when they level up and recieve a role.
|
|
|
|
**Arguments**
|
|
The following placeholders can be used:
|
|
• `{username}`: The user's name
|
|
• `{mention}`: Mentions the user
|
|
• `{displayname}`: The user's display name
|
|
• `{level}`: The level the user just reached
|
|
• `{server}`: The server the user is in
|
|
• `{role}`: The role the user just recieved
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if not message and not conf.role_awarded_dm:
|
|
return await ctx.send_help()
|
|
if not message and conf.role_awarded_dm:
|
|
conf.role_awarded_dm = None
|
|
self.save()
|
|
return await ctx.send(_("LevelUp DM role message has been removed"))
|
|
kwargs = {
|
|
"username": ctx.author.name,
|
|
"mention": ctx.author.mention,
|
|
"displayname": ctx.author.display_name,
|
|
"level": 1,
|
|
"server": ctx.guild.name,
|
|
"role": "Example Role",
|
|
}
|
|
try:
|
|
msg = message.format(**kwargs)
|
|
except KeyError as e:
|
|
return await ctx.send(_("Invalid placeholder used: {}").format(e))
|
|
conf.role_awarded_dm = message
|
|
self.save()
|
|
embed = discord.Embed(description=msg, color=await self.bot.get_embed_color(ctx))
|
|
await ctx.send(_("LevelUp DM role message has been set"), embed=embed)
|
|
|
|
@set_levelup_alerts.command(name="msg")
|
|
async def set_levelup_msg(self, ctx: commands.Context, *, message: str = None):
|
|
"""
|
|
Set the message sent when a user levels up.
|
|
|
|
**Arguments**
|
|
The following placeholders can be used:
|
|
• `{username}`: The user's name
|
|
• `{mention}`: Mentions the user
|
|
• `{displayname}`: The user's display name
|
|
• `{level}`: The level the user just reached
|
|
• `{server}`: The server the user is in
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if not message and not conf.levelup_msg:
|
|
return await ctx.send_help()
|
|
if not message and conf.levelup_msg:
|
|
conf.levelup_msg = ""
|
|
self.save()
|
|
return await ctx.send(_("LevelUp message has been removed"))
|
|
kwargs = {
|
|
"username": ctx.author.name,
|
|
"mention": ctx.author.mention,
|
|
"displayname": ctx.author.display_name,
|
|
"level": 1,
|
|
"server": ctx.guild.name,
|
|
}
|
|
try:
|
|
msg = message.format(**kwargs)
|
|
except KeyError as e:
|
|
return await ctx.send(_("Invalid placeholder used: {}").format(e))
|
|
conf.levelup_msg = message
|
|
self.save()
|
|
embed = discord.Embed(description=msg, color=await self.bot.get_embed_color(ctx))
|
|
await ctx.send(_("LevelUp message has been set"), embed=embed)
|
|
|
|
@set_levelup_alerts.command(name="msgrole")
|
|
async def set_levelup_msgrole(self, ctx: commands.Context, *, message: str = None):
|
|
"""
|
|
Set the message sent when a user levels up and recieves a role.
|
|
|
|
**Arguments**
|
|
The following placeholders can be used:
|
|
• `{username}`: The user's name
|
|
• `{mention}`: Mentions the user
|
|
• `{displayname}`: The user's display name
|
|
• `{level}`: The level the user just reached
|
|
• `{server}`: The server the user is in
|
|
• `{role}`: The role the user just recieved
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if not message and not conf.role_awarded_msg:
|
|
return await ctx.send_help()
|
|
if not message and conf.role_awarded_msg:
|
|
conf.role_awarded_msg = ""
|
|
self.save()
|
|
return await ctx.send(_("LevelUp role message has been removed"))
|
|
kwargs = {
|
|
"username": ctx.author.name,
|
|
"mention": ctx.author.mention,
|
|
"displayname": ctx.author.display_name,
|
|
"level": 1,
|
|
"server": ctx.guild.name,
|
|
"role": "Example Role",
|
|
}
|
|
try:
|
|
msg = message.format(**kwargs)
|
|
except KeyError as e:
|
|
return await ctx.send(_("Invalid placeholder used: {}").format(e))
|
|
conf.role_awarded_msg = message
|
|
self.save()
|
|
embed = discord.Embed(description=msg, color=await self.bot.get_embed_color(ctx))
|
|
await ctx.send(_("LevelUp role message has been set"), embed=embed)
|
|
|
|
@levelset.group(name="messages", aliases=["message", "msg"])
|
|
async def message_group(self, ctx: commands.Context):
|
|
"""Message settings"""
|
|
|
|
@message_group.command(name="channelbonus")
|
|
async def msg_chan_bonus(
|
|
self,
|
|
ctx: commands.Context,
|
|
channel: t.Union[discord.TextChannel, discord.CategoryChannel],
|
|
min_xp: commands.positive_int,
|
|
max_xp: commands.positive_int,
|
|
):
|
|
"""
|
|
Add a range of bonus XP to apply to certain channels
|
|
|
|
This bonus applies to message xp
|
|
|
|
Set both min and max to 0 to remove the role bonus
|
|
"""
|
|
if min_xp > max_xp:
|
|
return await ctx.send(_("Min XP value cannot be greater than Max XP value"))
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if channel.id in conf.channelbonus.msg:
|
|
if min_xp == 0 and max_xp == 0:
|
|
del conf.channelbonus.msg[channel.id]
|
|
self.save()
|
|
return await ctx.send(_("Channel bonus has been removed"))
|
|
conf.channelbonus.msg[channel.id] = [min_xp, max_xp]
|
|
self.save()
|
|
return await ctx.send(_("Channel bonus has been updated"))
|
|
|
|
if min_xp == 0 and max_xp == 0:
|
|
return await ctx.send(_("XP range cannot be 0"))
|
|
conf.channelbonus.msg[channel.id] = [min_xp, max_xp]
|
|
self.save()
|
|
await ctx.send(_("Channel bonus has been set"))
|
|
|
|
@message_group.command(name="cooldown")
|
|
async def set_cooldown(self, ctx: commands.Context, cooldown: commands.positive_int):
|
|
"""
|
|
Cooldown threshold for message XP
|
|
|
|
When a user sends a message they will have to wait X seconds before their message
|
|
counts as XP gained
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.cooldown = cooldown
|
|
self.save()
|
|
await ctx.send(_("Cooldown has been set to {} seconds").format(cooldown))
|
|
|
|
@message_group.command(name="length")
|
|
async def set_length(self, ctx: commands.Context, length: commands.positive_int):
|
|
"""
|
|
Set minimum message length for XP
|
|
Minimum length a message must be to count towards XP gained
|
|
|
|
Set to 0 to disable
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.min_length = length
|
|
self.save()
|
|
await ctx.send(_("Minimum message length has been set to {}").format(length))
|
|
|
|
@message_group.command(name="rolebonus")
|
|
async def msg_role_bonus(
|
|
self,
|
|
ctx: commands.Context,
|
|
role: discord.Role,
|
|
min_xp: commands.positive_int,
|
|
max_xp: commands.positive_int,
|
|
):
|
|
"""
|
|
Add a range of bonus XP to apply to certain roles
|
|
|
|
This bonus applies to message xp
|
|
|
|
Set both min and max to 0 to remove the role bonus
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if min_xp > max_xp:
|
|
return await ctx.send(_("Min XP value cannot be greater than Max XP value"))
|
|
if role.id in conf.rolebonus.msg:
|
|
if min_xp == 0 and max_xp == 0:
|
|
del conf.rolebonus.msg[role.id]
|
|
self.save()
|
|
return await ctx.send(_("Role bonus has been removed"))
|
|
conf.rolebonus.msg[role.id] = [min_xp, max_xp]
|
|
self.save()
|
|
return await ctx.send(_("Role bonus has been updated"))
|
|
conf.rolebonus.msg[role.id] = [min_xp, max_xp]
|
|
self.save()
|
|
await ctx.send(_("Role bonus has been set"))
|
|
|
|
@message_group.command(name="xp")
|
|
async def set_xp(self, ctx: commands.Context, min_xp: commands.positive_int, max_xp: commands.positive_int):
|
|
"""
|
|
Set message XP range
|
|
|
|
Set the Min and Max amount of XP that a message can gain
|
|
Default is 3 min and 6 max
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if min_xp > max_xp:
|
|
return await ctx.send(_("Min XP value cannot be greater than Max XP value"))
|
|
if min_xp == 0 and max_xp == 0:
|
|
return await ctx.send(_("XP range cannot be 0"))
|
|
conf.xp = [min_xp, max_xp]
|
|
self.save()
|
|
await ctx.send(_("Message XP range has been set to {} - {}").format(min_xp, max_xp))
|
|
|
|
@levelset.group(name="roles")
|
|
async def level_roles(self, ctx: commands.Context):
|
|
"""Level role assignment"""
|
|
|
|
@level_roles.command(name="autoremove")
|
|
async def toggle_autoremove(self, ctx: commands.Context):
|
|
"""Automatic removal of previous level roles"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.autoremove else _("**Enabled**")
|
|
conf.autoremove = not conf.autoremove
|
|
self.save()
|
|
await ctx.send(_("Automatic removal of previous level roles has been {}").format(status))
|
|
|
|
@level_roles.command(name="add")
|
|
async def add_level_role(self, ctx: commands.Context, level: int, role: discord.Role):
|
|
"""Assign a role to a level"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if role >= ctx.guild.me.top_role:
|
|
return await ctx.send(_("I cannot assign roles higher than my top role!"))
|
|
if role >= ctx.author.top_role:
|
|
return await ctx.send(_("You cannot assign roles higher than your top role!"))
|
|
if level in conf.levelroles:
|
|
txt = _("The role associated with level {} has been updated").format(level)
|
|
else:
|
|
txt = _("The role associated with level {} has been added").format(level)
|
|
conf.levelroles[level] = role.id
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@level_roles.command(name="remove", aliases=["rem", "del"])
|
|
async def del_level_role(self, ctx: commands.Context, level: int):
|
|
"""Unassign a role from a level"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if level not in conf.levelroles:
|
|
return await ctx.send(_("There is no role associated with level {}").format(level))
|
|
del conf.levelroles[level]
|
|
self.save()
|
|
await ctx.send(_("The role associated with level {} has been removed").format(level))
|
|
|
|
@level_roles.command(name="initialize", aliases=["init"])
|
|
@commands.bot_has_permissions(manage_roles=True, embed_links=True)
|
|
@commands.cooldown(1, 240, commands.BucketType.guild)
|
|
async def init_roles(self, ctx: commands.Context):
|
|
"""
|
|
Initialize level roles
|
|
|
|
This command is for if you added level roles after users have achieved that level,
|
|
it will apply all necessary roles to a user according to their level and prestige
|
|
"""
|
|
start = perf_counter()
|
|
roles_added = 0
|
|
roles_removed = 0
|
|
embed = discord.Embed(
|
|
description=_("Synchronizing level roles, this may take a while..."),
|
|
color=discord.Color.magenta(),
|
|
)
|
|
embed.set_thumbnail(url=const.LOADING)
|
|
msg = await ctx.send(embed=embed)
|
|
last_update = perf_counter()
|
|
conf = self.db.get_conf(ctx.guild)
|
|
reason = _("Level role initialization")
|
|
member_count = len(ctx.guild.members)
|
|
async with ctx.typing():
|
|
for idx, user in enumerate(ctx.guild.members):
|
|
added, removed = await self.ensure_roles(user, conf, reason)
|
|
roles_added += len(added)
|
|
roles_removed += len(removed)
|
|
|
|
# Update message every 5% of the way if there are more than 40 users
|
|
if member_count > 40 and idx % (member_count // 20) == 0 and perf_counter() - last_update > 5:
|
|
desc = _("Synchronizing level roles, this may take a while...\n{:.0%} complete").format(
|
|
idx / member_count
|
|
)
|
|
embed.description = desc
|
|
asyncio.create_task(msg.edit(embed=embed))
|
|
last_update = perf_counter()
|
|
|
|
if not roles_added and not roles_removed:
|
|
return await msg.edit(
|
|
content=_("No roles were added or removed"),
|
|
embed=None,
|
|
)
|
|
desc = _("Role initialization complete\nRoles added: {}\nRoles removed: {}").format(roles_added, roles_removed)
|
|
embed = discord.Embed(description=desc, color=discord.Color.green())
|
|
td = round(perf_counter() - start)
|
|
delta = humanize_timedelta(seconds=td)
|
|
foot = _("Initialization took {} to complete.").format(delta)
|
|
embed.set_footer(text=foot)
|
|
await msg.edit(embed=embed)
|
|
|
|
@levelset.group(name="voice")
|
|
async def voice_group(self, ctx: commands.Context):
|
|
"""Voice settings"""
|
|
|
|
@voice_group.command(name="channelbonus")
|
|
async def voice_chan_bonus(
|
|
self,
|
|
ctx: commands.Context,
|
|
channel: discord.VoiceChannel,
|
|
min_xp: commands.positive_int,
|
|
max_xp: commands.positive_int,
|
|
):
|
|
"""
|
|
Add a range of bonus XP to apply to certain channels
|
|
|
|
This bonus applies to voice time xp per minute.
|
|
Example: 2 minutes in a channel with a 1-2 bonus will give 2-4 XP
|
|
|
|
Set both min and max to 0 to remove the channel bonus.
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if min_xp > max_xp:
|
|
return await ctx.send(_("Min XP value cannot be greater than Max XP value"))
|
|
if channel.id in conf.channelbonus.voice:
|
|
if min_xp == 0 and max_xp == 0:
|
|
del conf.channelbonus.voice[channel.id]
|
|
self.save()
|
|
return await ctx.send(_("Channel bonus has been removed"))
|
|
conf.channelbonus.voice[channel.id] = [min_xp, max_xp]
|
|
self.save()
|
|
return await ctx.send(_("Channel bonus has been updated"))
|
|
if min_xp == 0 and max_xp == 0:
|
|
return await ctx.send(_("XP range cannot be 0"))
|
|
conf.channelbonus.voice[channel.id] = [min_xp, max_xp]
|
|
self.save()
|
|
await ctx.send(_("Channel bonus has been set"))
|
|
|
|
@voice_group.command(name="streambonus")
|
|
async def voice_stream_bonus(
|
|
self, ctx: commands.Context, min_xp: commands.positive_int, max_xp: commands.positive_int
|
|
):
|
|
"""
|
|
Add a range of bonus XP to users who are Discord streaming
|
|
|
|
This bonus applies to voice time xp
|
|
|
|
Set both min and max to 0 to remove the bonus
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if min_xp > max_xp:
|
|
return await ctx.send(_("Min XP value cannot be greater than Max XP value"))
|
|
if min_xp == 0 and max_xp == 0:
|
|
conf.streambonus = None
|
|
self.save()
|
|
return await ctx.send(_("Stream bonus has been removed"))
|
|
conf.streambonus = [min_xp, max_xp]
|
|
self.save()
|
|
await ctx.send(_("Stream bonus has been set"))
|
|
|
|
@voice_group.command(name="rolebonus")
|
|
async def voice_role_bonus(
|
|
self,
|
|
ctx: commands.Context,
|
|
role: discord.Role,
|
|
min_xp: commands.positive_int,
|
|
max_xp: commands.positive_int,
|
|
):
|
|
"""
|
|
Add a range of bonus XP to apply to certain roles
|
|
|
|
This bonus applies to voice time xp
|
|
|
|
Set both min and max to 0 to remove the role bonus
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if min_xp > max_xp:
|
|
return await ctx.send(_("Min XP value cannot be greater than Max XP value"))
|
|
if role.id in conf.rolebonus.voice:
|
|
if min_xp == 0 and max_xp == 0:
|
|
del conf.rolebonus.voice[role.id]
|
|
self.save()
|
|
return await ctx.send(_("Role bonus has been removed"))
|
|
conf.rolebonus.voice[role.id] = [min_xp, max_xp]
|
|
self.save()
|
|
return await ctx.send(_("Role bonus has been updated"))
|
|
if min_xp == 0 and max_xp == 0:
|
|
return await ctx.send(_("XP range cannot be 0"))
|
|
conf.rolebonus.voice[role.id] = [min_xp, max_xp]
|
|
self.save()
|
|
await ctx.send(_("Role bonus has been set"))
|
|
|
|
@voice_group.command(name="deafened")
|
|
async def ignore_deafened(self, ctx: commands.Context):
|
|
"""
|
|
Ignore deafened voice users
|
|
Toggle whether deafened users in a voice channel can gain voice XP
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if conf.ignore_deafened:
|
|
txt = _("Deafened users can now gain XP while in a voice channel")
|
|
conf.ignore_deafened = False
|
|
else:
|
|
txt = _("Deafened users will no longer gain XP while in a voice channel")
|
|
conf.ignore_deafened = True
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@voice_group.command(name="invisible")
|
|
async def ignore_invisible(self, ctx: commands.Context):
|
|
"""
|
|
Ignore invisible voice users
|
|
Toggle whether invisible users in a voice channel can gain voice XP
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if conf.ignore_invisible:
|
|
txt = _("Invisible users can now gain XP while in a voice channel")
|
|
conf.ignore_invisible = False
|
|
else:
|
|
txt = _("Invisible users will no longer gain XP while in a voice channel")
|
|
conf.ignore_invisible = True
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@voice_group.command(name="muted")
|
|
async def ignore_muted(self, ctx: commands.Context):
|
|
"""
|
|
Ignore muted voice users
|
|
Toggle whether self-muted users in a voice channel can gain voice XP
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if conf.ignore_muted:
|
|
txt = _("Muted users can now gain XP while in a voice channel")
|
|
conf.ignore_muted = False
|
|
else:
|
|
txt = _("Muted users will no longer gain XP while in a voice channel")
|
|
conf.ignore_muted = True
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@voice_group.command(name="solo")
|
|
async def ignore_solo(self, ctx: commands.Context):
|
|
"""
|
|
Ignore solo voice users
|
|
Toggle whether solo users in a voice channel can gain voice XP
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if conf.ignore_solo:
|
|
txt = _("Solo users can now gain XP while in a voice channel")
|
|
conf.ignore_solo = False
|
|
else:
|
|
txt = _("Solo users will no longer gain XP while in a voice channel")
|
|
conf.ignore_solo = True
|
|
self.save()
|
|
await ctx.send(txt)
|
|
|
|
@voice_group.command(name="xp")
|
|
async def set_voice_xp(self, ctx: commands.Context, voice_xp: commands.positive_int):
|
|
"""
|
|
Set voice XP gain
|
|
Sets the amount of XP gained per minute in a voice channel (default is 2)
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.voicexp = voice_xp
|
|
self.save()
|
|
await ctx.send(_("Voice XP has been set to {} per minute").format(voice_xp))
|
|
|
|
@levelset.group(name="prestige")
|
|
async def prestige_group(self, ctx: commands.Context):
|
|
"""Prestige settings"""
|
|
|
|
@prestige_group.command(name="keeproles")
|
|
async def toggle_keep_roles(self, ctx: commands.Context):
|
|
"""Keep level roles after prestiging"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.keep_level_roles else _("**Enabled**")
|
|
conf.keep_level_roles = not conf.keep_level_roles
|
|
self.save()
|
|
await ctx.send(_("Keeping roles after prestiging has been {}").format(status))
|
|
|
|
@prestige_group.command(name="level")
|
|
async def prestige_level(self, ctx: commands.Context, level: commands.positive_int):
|
|
"""
|
|
Set the level required to prestige
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
conf.prestigelevel = level
|
|
self.save()
|
|
await ctx.send(_("Prestige level has been set to {}").format(level))
|
|
|
|
@prestige_group.command(name="stack")
|
|
async def toggle_stack_roles(self, ctx: commands.Context):
|
|
"""
|
|
Toggle stacking roles on prestige
|
|
|
|
For example each time you prestige, you keep the previous prestige roles
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
status = _("**Disabled**") if conf.stackprestigeroles else _("**Enabled**")
|
|
conf.stackprestigeroles = not conf.stackprestigeroles
|
|
self.save()
|
|
await ctx.send(_("Stacking roles on prestige has been {}").format(status))
|
|
|
|
@prestige_group.command(name="add")
|
|
@commands.bot_has_guild_permissions(manage_roles=True)
|
|
async def add_prestige_level(
|
|
self,
|
|
ctx: commands.Context,
|
|
prestige: int,
|
|
role: discord.Role,
|
|
emoji: t.Union[discord.Emoji, discord.PartialEmoji, str],
|
|
):
|
|
"""
|
|
Add a role to a prestige level
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if role >= ctx.guild.me.top_role:
|
|
return await ctx.send(_("I cannot assign roles higher than my top role!"))
|
|
if role >= ctx.author.top_role:
|
|
return await ctx.send(_("You cannot assign roles higher than your top role!"))
|
|
if prestige in conf.prestigedata:
|
|
return await ctx.send(_("This prestige level has already been set!"))
|
|
url = utils.get_twemoji(emoji) if isinstance(emoji, str) else emoji.url
|
|
prestige_obj = Prestige(
|
|
role=role.id,
|
|
emoji_string=str(emoji),
|
|
emoji_url=url,
|
|
)
|
|
conf.prestigedata[prestige] = prestige_obj
|
|
self.save()
|
|
await ctx.send(_("Role and emoji have been set for prestige level {}").format(prestige))
|
|
|
|
@prestige_group.command(name="remove", aliases=["rem", "del"])
|
|
async def remove_prestige_level(self, ctx: commands.Context, prestige: int):
|
|
"""
|
|
Remove a prestige level
|
|
"""
|
|
conf = self.db.get_conf(ctx.guild)
|
|
if prestige not in conf.prestigedata:
|
|
return await ctx.send(_("That prestige level does not exist!"))
|
|
del conf.prestigedata[prestige]
|
|
self.save()
|
|
await ctx.send(_("Prestige level {} has been removed").format(prestige))
|