async def bankset_showsettings(self, ctx: commands.Context): """Show the current bank settings.""" cur_setting = await bank.is_global() if cur_setting: group = bank._config else: if not ctx.guild: return group = bank._config.guild(ctx.guild) group_data = await group.all() bank_name = group_data["bank_name"] bank_scope = _("Global") if cur_setting else _("Server") currency_name = group_data["currency"] default_balance = group_data["default_balance"] max_balance = group_data["max_balance"] settings = _( "Bank settings:\n\nBank name: {bank_name}\nBank scope: {bank_scope}\n" "Currency: {currency_name}\nDefault balance: {default_balance}\n" "Maximum allowed balance: {maximum_bal}\n").format( bank_name=bank_name, bank_scope=bank_scope, currency_name=currency_name, default_balance=humanize_number(default_balance), maximum_bal=humanize_number(max_balance), ) await ctx.send(box(settings))
async def command_equalizer_reset(self, ctx: commands.Context): """Reset the eq to 0 across all bands.""" if not self._player_check(ctx): return await self.send_embed_msg(ctx, title=_("Nothing playing.")) dj_enabled = self._dj_status_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()) if dj_enabled and not await self._can_instaskip(ctx, ctx.author): return await self.send_embed_msg( ctx, title=_("Unable To Modify Preset"), description=_("You need the DJ role to reset the equalizer."), ) player = lavalink.get_player(ctx.guild.id) eq = player.fetch("eq", Equalizer()) for band in range(eq.band_count): eq.set_gain(band, 0.0) await self._apply_gains(ctx.guild.id, eq.bands) await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands) player.store("eq", eq) await self._eq_msg_clear(player.fetch("eq_message")) message = await ctx.send( content=box(eq.visualise(), lang="ini"), embed=discord.Embed(colour=await ctx.embed_colour(), title=_("Equalizer values have been reset.")), ) player.store("eq_message", message)
async def economyset_showsettings(self, ctx: commands.Context): """ Shows the current economy settings """ guild = ctx.guild if await bank.is_global(): conf = self.config else: conf = self.config.guild(guild) await ctx.send( box( _("----Economy Settings---\n" "Minimum slot bid: {slot_min}\n" "Maximum slot bid: {slot_max}\n" "Slot cooldown: {slot_time}\n" "Payday amount: {payday_amount}\n" "Payday cooldown: {payday_time}\n" "Amount given at account registration: {register_amount}\n" "Maximum allowed balance: {maximum_bal}").format( slot_min=humanize_number(await conf.SLOT_MIN()), slot_max=humanize_number(await conf.SLOT_MAX()), slot_time=humanize_number(await conf.SLOT_TIME()), payday_time=humanize_number(await conf.PAYDAY_TIME()), payday_amount=humanize_number(await conf.PAYDAY_CREDITS()), register_amount=humanize_number( await bank.get_default_balance(guild)), maximum_bal=humanize_number(await bank.get_max_balance(guild)), )))
async def custom_trivia_list(self, ctx: commands.Context): """List uploaded custom trivia.""" personal_lists = sorted( [p.resolve().stem for p in cog_data_path(self).glob("*.yaml")]) no_lists_uploaded = _("No custom Trivia lists uploaded.") if not personal_lists: if await ctx.embed_requested(): await ctx.send( embed=discord.Embed(colour=await ctx.embed_colour(), description=no_lists_uploaded)) else: await ctx.send(no_lists_uploaded) return if await ctx.embed_requested(): await ctx.send(embed=discord.Embed( title=_("Uploaded trivia lists"), colour=await ctx.embed_colour(), description=", ".join(sorted(personal_lists)), )) else: msg = box( bold(_("Uploaded trivia lists")) + "\n\n" + ", ".join(sorted(personal_lists))) if len(msg) > 1000: await ctx.author.send(msg) else: await ctx.send(msg)
async def cc_search(self, ctx: commands.Context, *, query): """ Searches through custom commands, according to the query. Uses fuzzywuzzy searching to find close matches. **Arguments:** - `<query>` The query to search for. Can be multiple words. """ cc_commands = await CommandObj.get_commands(self.config.guild(ctx.guild)) extracted = process.extract(query, list(cc_commands.keys())) accepted = [] for entry in extracted: if entry[1] > 60: # Match was decently strong accepted.append((entry[0], cc_commands[entry[0]])) else: # Match wasn't strong enough pass if len(accepted) == 0: return await ctx.send(_("No close matches were found.")) results = self.prepare_command_list(ctx, accepted) if await ctx.embed_requested(): content = " \n".join(map("**{0[0]}** {0[1]}".format, results)) embed = discord.Embed( title=_("Search results"), description=content, colour=await ctx.embed_colour() ) await ctx.send(embed=embed) else: content = "\n".join(map("{0[0]:<12} : {0[1]}".format, results)) await ctx.send(_("The following matches have been found:") + box(content))
async def send_leaderboard(self, ctx: commands.Context, data: dict, key: str, top: int): """Send the leaderboard from the given data. Parameters ---------- ctx : commands.Context The context to send the leaderboard to. data : dict The data for the leaderboard. This must map `discord.Member` -> `dict`. key : str The field to sort the data by. Can be ``wins``, ``total_score``, ``games`` or ``average_score``. top : int The number of members to display on the leaderboard. Returns ------- `list` of `discord.Message` The sent leaderboard messages. """ if not data: await ctx.send(_("There are no scores on record!")) return leaderboard = self._get_leaderboard(data, key, top) ret = [] for page in pagify(leaderboard, shorten_by=10): ret.append(await ctx.send(box(page, lang="py"))) return ret
async def set_cases(self, ctx: commands.Context, action: str = None): """Enable or disable case creation for a mod action.""" guild = ctx.guild if action is None: # No args given casetypes = await modlog.get_all_casetypes(guild) await ctx.send_help() lines = [] for ct in casetypes: enabled = _("enabled") if await ct.is_enabled() else _( "disabled") lines.append(f"{ct.name} : {enabled}") await ctx.send(_("Current settings:\n") + box("\n".join(lines))) return casetype = await modlog.get_casetype(action, guild) if not casetype: await ctx.send(_("That action is not registered.")) else: enabled = await casetype.is_enabled() await casetype.set_enabled(not enabled) await ctx.send( _("Case creation for {action_name} actions is now {enabled}."). format(action_name=action, enabled=_("enabled") if not enabled else _("disabled")))
async def command_equalizer(self, ctx: commands.Context): """Equalizer management.""" if not self._player_check(ctx): ctx.command.reset_cooldown(ctx) return await self.send_embed_msg(ctx, title=_("Nothing playing.")) dj_enabled = self._dj_status_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()) player = lavalink.get_player(ctx.guild.id) eq = player.fetch("eq", Equalizer()) reactions = [ "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}", "\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}", "\N{BLACK UP-POINTING DOUBLE TRIANGLE}", "\N{UP-POINTING SMALL RED TRIANGLE}", "\N{DOWN-POINTING SMALL RED TRIANGLE}", "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}", "\N{BLACK RIGHTWARDS ARROW}\N{VARIATION SELECTOR-16}", "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}", "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}", "\N{INFORMATION SOURCE}\N{VARIATION SELECTOR-16}", ] await self._eq_msg_clear(player.fetch("eq_message")) eq_message = await ctx.send(box(eq.visualise(), lang="ini")) if dj_enabled and not await self._can_instaskip(ctx, ctx.author): with contextlib.suppress(discord.HTTPException): await eq_message.add_reaction( "\N{INFORMATION SOURCE}\N{VARIATION SELECTOR-16}") else: start_adding_reactions(eq_message, reactions) eq_msg_with_reacts = await ctx.fetch_message(eq_message.id) player.store("eq_message", eq_msg_with_reacts) await self._eq_interact(ctx, player, eq, eq_msg_with_reacts, 0)
async def discover_guild( self, author: discord.User, *, mod: bool = False, permissions: Union[discord.Permissions, dict] = None, prompt: str = "", ): """ discovers which of shared guilds between the bot and provided user based on conditions (mod or permissions is an or) prompt is for providing a user prompt for selection """ shared_guilds = [] if permissions is None: perms = discord.Permissions() elif isinstance(permissions, discord.Permissions): perms = permissions else: perms = discord.Permissions(**permissions) async for guild in AsyncIter(self.bot.guilds, steps=100): x = guild.get_member(author.id) if x is not None: if await self.internal_filter(x, mod, perms): shared_guilds.append(guild) if len(shared_guilds) == 0: raise ValueError("No Qualifying Shared Guilds") if len(shared_guilds) == 1: return shared_guilds[0] output = "" guilds = sorted(shared_guilds, key=lambda g: g.name) for i, guild in enumerate(guilds, 1): output += "{}: {}\n".format(i, guild.name) output += "\n{}".format(prompt) for page in pagify(output, delims=["\n"]): await author.send(box(page)) try: message = await self.bot.wait_for( "message", check=MessagePredicate.same_context(channel=author.dm_channel, user=author), timeout=45, ) except asyncio.TimeoutError: await author.send(_("You took too long to select. Try again later.")) return None try: message = int(message.content.strip()) guild = guilds[message - 1] except (ValueError, IndexError): await author.send(_("That wasn't a valid choice.")) return None else: return guild
async def modset_showsettings(self, ctx: commands.Context): """Show the current server administration settings.""" guild = ctx.guild data = await self.config.guild(guild).all() delete_repeats = data["delete_repeats"] warn_mention_spam = data["mention_spam"]["warn"] kick_mention_spam = data["mention_spam"]["kick"] ban_mention_spam = data["mention_spam"]["ban"] strict_mention_spam = data["mention_spam"]["strict"] respect_hierarchy = data["respect_hierarchy"] delete_delay = data["delete_delay"] reinvite_on_unban = data["reinvite_on_unban"] dm_on_kickban = data["dm_on_kickban"] default_days = data["default_days"] default_tempban_duration = data["default_tempban_duration"] msg = "" msg += _("Delete repeats: {num_repeats}\n").format( num_repeats=_("after {num} repeats").format( num=delete_repeats) if delete_repeats != -1 else _("No")) msg += _("Warn mention spam: {num_mentions}\n").format( num_mentions=_("{num} mentions").format( num=warn_mention_spam) if warn_mention_spam else _("No")) msg += _("Kick mention spam: {num_mentions}\n").format( num_mentions=_("{num} mentions").format( num=kick_mention_spam) if kick_mention_spam else _("No")) msg += _("Ban mention spam: {num_mentions}\n").format( num_mentions=_("{num} mentions").format( num=ban_mention_spam) if ban_mention_spam else _("No")) msg += (_( "Mention Spam Strict: All mentions will count including duplicates\n" ) if strict_mention_spam else _("Mention Spam Strict: Only unique mentions will count\n")) msg += _("Respects hierarchy: {yes_or_no}\n").format( yes_or_no=_("Yes") if respect_hierarchy else _("No")) msg += _("Delete delay: {num_seconds}\n").format( num_seconds=_("{num} seconds").format( num=delete_delay) if delete_delay != -1 else _("None")) msg += _("Reinvite on unban: {yes_or_no}\n").format( yes_or_no=_("Yes") if reinvite_on_unban else _("No")) msg += _("Send message to users on kick/ban: {yes_or_no}\n").format( yes_or_no=_("Yes") if dm_on_kickban else _("No")) if default_days: msg += _( "Default message history delete on ban: Previous {num_days} days\n" ).format(num_days=default_days) else: msg += _( "Default message history delete on ban: Don't delete any\n") msg += _("Default tempban duration: {duration}").format( duration=humanize_timedelta(seconds=default_tempban_duration)) await ctx.send(box(msg))
async def selfrole_list(self, ctx: commands.Context): """ Lists all available selfroles. """ selfroles = await self._valid_selfroles(ctx.guild) fmt_selfroles = "\n".join(["+ " + r.name for r in selfroles]) if not fmt_selfroles: await ctx.send("There are currently no selfroles.") return msg = _("Available Selfroles:\n{selfroles}").format( selfroles=fmt_selfroles) await ctx.send(box(msg, "diff"))
async def command_equalizer_list(self, ctx: commands.Context): """List saved eq presets.""" eq_presets = await self.config.custom("EQUALIZER", ctx.guild.id).eq_presets() if not eq_presets.keys(): return await self.send_embed_msg( ctx, title=_("No saved equalizer presets.")) space = "\N{EN SPACE}" header_name = _("Preset Name") header_author = _("Author") header = box( "[{header_name}]{space}[{header_author}]\n".format( header_name=header_name, space=space * 9, header_author=header_author), lang="ini", ) preset_list = "" for preset, bands in eq_presets.items(): try: author = self.bot.get_user(bands["author"]) except TypeError: author = "None" msg = f"{preset}{space * (22 - len(preset))}{author}\n" preset_list += msg page_list = [] colour = await ctx.embed_colour() for page in pagify(preset_list, delims=[", "], page_length=1000): formatted_page = box(page, lang="ini") embed = discord.Embed(colour=colour, description=f"{header}\n{formatted_page}") embed.set_footer(text=_("{num} preset(s)").format( num=humanize_number(len(list(eq_presets.keys()))))) page_list.append(embed) await menu(ctx, page_list, DEFAULT_CONTROLS)
def format_playlist_picker_data(self, pid, pname, ptracks, pauthor, scope) -> str: """Format the values into a prettified codeblock.""" author = self.bot.get_user(pauthor) or pauthor or _("Unknown") line = _(" - Name: <{pname}>\n" " - Scope: < {scope} >\n" " - ID: < {pid} >\n" " - Tracks: < {ptracks} >\n" " - Author: < {author} >\n\n").format( pname=pname, scope=self.humanize_scope(scope), pid=pid, ptracks=ptracks, author=author) return box(line, lang="md")
async def command_equalizer_load(self, ctx: commands.Context, eq_preset: str): """Load a saved eq preset.""" eq_preset = eq_preset.lower() eq_presets = await self.config.custom("EQUALIZER", ctx.guild.id).eq_presets() try: eq_values = eq_presets[eq_preset]["bands"] except KeyError: return await self.send_embed_msg( ctx, title=_("No Preset Found"), description=_( "Preset named {eq_preset} does not exist.".format( eq_preset=eq_preset)), ) except TypeError: eq_values = eq_presets[eq_preset] if not self._player_check(ctx): return await self.send_embed_msg(ctx, title=_("Nothing playing.")) dj_enabled = self._dj_status_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()) player = lavalink.get_player(ctx.guild.id) if dj_enabled and not await self._can_instaskip(ctx, ctx.author): return await self.send_embed_msg( ctx, title=_("Unable To Load Preset"), description=_( "You need the DJ role to load equalizer presets."), ) await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq_values) await self._eq_check(ctx, player) eq = player.fetch("eq", Equalizer()) await self._eq_msg_clear(player.fetch("eq_message")) message = await ctx.send( content=box(eq.visualise(), lang="ini"), embed=discord.Embed( colour=await ctx.embed_colour(), title=_("The {eq_preset} preset was loaded.".format( eq_preset=eq_preset)), ), ) player.store("eq_message", message)
async def trivia_list(self, ctx: commands.Context): """List available trivia categories.""" lists = set(p.stem for p in self._all_lists()) if await ctx.embed_requested(): await ctx.send(embed=discord.Embed( title=_("Available trivia lists"), colour=await ctx.embed_colour(), description=", ".join(sorted(lists)), )) else: msg = box( bold(_("Available trivia lists")) + "\n\n" + ", ".join(sorted(lists))) if len(msg) > 1000: await ctx.author.send(msg) else: await ctx.send(msg)
async def triviaset_showsettings(self, ctx: commands.Context): """Show the current trivia settings.""" settings = self.config.guild(ctx.guild) settings_dict = await settings.all() msg = box( _("Current settings\n" "Bot gains points: {bot_plays}\n" "Answer time limit: {delay} seconds\n" "Lack of response timeout: {timeout} seconds\n" "Points to win: {max_score}\n" "Reveal answer on timeout: {reveal_answer}\n" "Payout multiplier: {payout_multiplier}\n" "Allow lists to override settings: {allow_override}").format( **settings_dict), lang="py", ) await ctx.send(msg)
async def permissions_acl_yaml_example(self, ctx: commands.Context): """Sends an example of the yaml layout for permissions""" await ctx.send( _("Example YAML for setting rules:\n") + box( textwrap.dedent("""\ COMMAND: ping: 12345678901234567: true 56789012345671234: false COG: General: 56789012345671234: true 12345678901234567: false default: false """), lang="yaml", ))
async def paginate_alias_list(self, ctx: commands.Context, alias_list: List[AliasEntry]) -> None: names = sorted(["+ " + a.name for a in alias_list]) message = "\n".join(names) temp = list(pagify(message, delims=["\n"], page_length=1850)) alias_list = [] count = 0 for page in temp: count += 1 page = page.lstrip("\n") page = (_("Aliases:\n") + page + _("\n\nPage {page}/{total}").format(page=count, total=len(temp))) alias_list.append(box("".join(page), "diff")) if len(alias_list) == 1: await ctx.send(alias_list[0]) return await menu(ctx, alias_list, DEFAULT_CONTROLS)
async def format_fuzzy_results( ctx: Context, matched_commands: List[Command], *, embed: Optional[bool] = None) -> Union[str, discord.Embed]: """Format the result of a fuzzy command search. Parameters ---------- ctx : `commands.Context <beastbot.core.commands.Context>` The context in which this result is being displayed. matched_commands : List[`commands.Command <beastbot.core.commands.Command>`] A list of commands which have been matched by the fuzzy search, sorted in order of decreasing similarity. embed : bool Whether or not the result should be an embed. If set to ``None``, this will default to the result of `ctx.embed_requested`. Returns ------- Union[str, discord.Embed] The formatted results. """ if embed is not False and (embed is True or await ctx.embed_requested()): lines = [] for cmd in matched_commands: short_doc = cmd.format_shortdoc_for_context(ctx) lines.append( f"**{ctx.clean_prefix}{cmd.qualified_name}** {short_doc}") return discord.Embed( title="Perhaps you wanted one of these?", colour=await ctx.embed_colour(), description="\n".join(lines), ) else: lines = [] for cmd in matched_commands: short_doc = cmd.format_shortdoc_for_context(ctx) lines.append( f"{ctx.clean_prefix}{cmd.qualified_name} -- {short_doc}") return "Perhaps you wanted one of these? " + box("\n".join(lines), lang="vhdl")
async def command_llsetup_info(self, ctx: commands.Context): """Display Lavalink connection settings.""" configs = await self.config.all() host = configs["host"] password = configs["password"] rest_port = configs["rest_port"] ws_port = configs["ws_port"] msg = "----" + _("Connection Settings") + "---- \n" msg += _("Host: [{host}]\n").format(host=host) msg += _("WS Port: [{port}]\n").format(port=ws_port) if ws_port != rest_port: msg += _("Rest Port: [{port}]\n").format(port=rest_port) msg += _("Password: [{password}]\n").format(password=password) try: await self.send_embed_msg(ctx.author, description=box(msg, lang="ini")) except discord.HTTPException: await ctx.send( _("I need to be able to DM you to send you this info."))
async def command_equalizer_set(self, ctx: commands.Context, band_name_or_position, band_value: float): """Set an eq band with a band number or name and value. Band positions are 1-15 and values have a range of -0.25 to 1.0. Band names are 25, 40, 63, 100, 160, 250, 400, 630, 1k, 1.6k, 2.5k, 4k, 6.3k, 10k, and 16k Hz. Setting a band value to -0.25 nullifies it while +0.25 is double. """ if not self._player_check(ctx): return await self.send_embed_msg(ctx, title=_("Nothing playing.")) dj_enabled = self._dj_status_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()) if dj_enabled and not await self._can_instaskip(ctx, ctx.author): return await self.send_embed_msg( ctx, title=_("Unable To Set Preset"), description=_( "You need the DJ role to set equalizer presets."), ) player = lavalink.get_player(ctx.guild.id) band_names = [ "25", "40", "63", "100", "160", "250", "400", "630", "1k", "1.6k", "2.5k", "4k", "6.3k", "10k", "16k", ] eq = player.fetch("eq", Equalizer()) bands_num = eq.band_count if band_value > 1: band_value = 1 elif band_value <= -0.25: band_value = -0.25 else: band_value = round(band_value, 1) try: band_number = int(band_name_or_position) - 1 except ValueError: band_number = 1000 if band_number not in range( 0, bands_num) and band_name_or_position not in band_names: return await self.send_embed_msg( ctx, title=_("Invalid Band"), description=_( "Valid band numbers are 1-15 or the band names listed in " "the help for this command."), ) if band_name_or_position in band_names: band_pos = band_names.index(band_name_or_position) band_int = False eq.set_gain(int(band_pos), band_value) await self._apply_gain(ctx.guild.id, int(band_pos), band_value) else: band_int = True eq.set_gain(band_number, band_value) await self._apply_gain(ctx.guild.id, band_number, band_value) await self._eq_msg_clear(player.fetch("eq_message")) await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands) player.store("eq", eq) band_name = band_names[ band_number] if band_int else band_name_or_position message = await ctx.send( content=box(eq.visualise(), lang="ini"), embed=discord.Embed( colour=await ctx.embed_colour(), title=_("Preset Modified"), description=_( "The {band_name}Hz band has been set to {band_value}."). format(band_name=band_name, band_value=band_value), ), ) player.store("eq_message", message)
async def send_table(self): """Send a table of scores to the session's channel.""" table = "+ Results: \n\n" for user, score in self.scores.most_common(): table += "+ {}\t{}\n".format(user, score) await self.ctx.send(box(table, lang="diff"))
async def cog_before_invoke(self, ctx: commands.Context) -> None: await self.cog_ready_event.wait() # check for unsupported arch # Check on this needs refactoring at a later date # so that we have a better way to handle the tasks if self.command_llsetup in [ctx.command, ctx.command.root_parent]: pass elif self.lavalink_connect_task and self.lavalink_connect_task.cancelled( ): await ctx.send( _("You have attempted to run Audio's Lavalink server on an unsupported" " architecture. Only settings related commands will be available." )) raise RuntimeError( "Not running audio command due to invalid machine architecture for Lavalink." ) current_perms = ctx.channel.permissions_for(ctx.me) surpass_ignore = (isinstance(ctx.channel, discord.abc.PrivateChannel) or await ctx.bot.is_owner(ctx.author) or await ctx.bot.is_admin(ctx.author)) guild = ctx.guild if guild and not current_perms.is_superset(self.permission_cache): current_perms_set = set(iter(current_perms)) expected_perms_set = set(iter(self.permission_cache)) diff = expected_perms_set - current_perms_set missing_perms = dict((i for i in diff if i[-1] is not False)) missing_perms = OrderedDict(sorted(missing_perms.items())) missing_permissions = missing_perms.keys() log.debug( "Missing the following perms in %d, Owner ID: %d: %s", ctx.guild.id, ctx.guild.owner.id, humanize_list(list(missing_permissions)), ) if not surpass_ignore: text = _("I'm missing permissions in this server, " "Please address this as soon as possible.\n\n" "Expected Permissions:\n") for perm, value in missing_perms.items(): text += "{perm}: [{status}]\n".format( status=_("Enabled") if value else _("Disabled"), perm=HUMANIZED_PERM.get(perm), ) text = text.strip() if current_perms.send_messages and current_perms.read_messages: await ctx.send(box(text=text, lang="ini")) else: log.info( "Missing write permission in %d, Owner ID: %d", ctx.guild.id, ctx.guild.owner.id, ) raise CheckFailure(message=text) with contextlib.suppress(Exception): player = lavalink.get_player(ctx.guild.id) notify_channel = player.fetch("channel") if not notify_channel: player.store("channel", ctx.channel.id) self._daily_global_playlist_cache.setdefault( self.bot.user.id, await self.config.daily_playlists()) if self.local_folder_current_path is None: self.local_folder_current_path = Path(await self.config.localpath()) if not ctx.guild: return dj_enabled = self._dj_status_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_enabled()) self._daily_playlist_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).daily_playlists()) self._persist_queue_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).persist_queue()) if dj_enabled: dj_role = self._dj_role_cache.setdefault( ctx.guild.id, await self.config.guild(ctx.guild).dj_role()) dj_role_obj = ctx.guild.get_role(dj_role) if not dj_role_obj: await self.config.guild(ctx.guild).dj_enabled.set(None) self._dj_status_cache[ctx.guild.id] = None await self.config.guild(ctx.guild).dj_role.set(None) self._dj_role_cache[ctx.guild.id] = None await self.send_embed_msg( ctx, title=_("No DJ role found. Disabling DJ mode."))
async def leaderboard(self, ctx: commands.Context, top: int = 10, show_global: bool = False): """Print the leaderboard. Defaults to top 10. Examples: - `[p]leaderboard` - `[p]leaderboard 50` - Shows the top 50 instead of top 10. - `[p]leaderboard 100 yes` - Shows the top 100 from all servers. **Arguments** - `<top>` How many positions on the leaderboard to show. Defaults to 10 if omitted. - `<show_global>` Whether to include results from all servers. This will default to false unless specified. """ guild = ctx.guild author = ctx.author embed_requested = await ctx.embed_requested() footer_message = _("Page {page_num}/{page_len}.") max_bal = await bank.get_max_balance(ctx.guild) if top < 1: top = 10 base_embed = discord.Embed(title=_("Economy Leaderboard")) if await bank.is_global() and show_global: # show_global is only applicable if bank is global bank_sorted = await bank.get_leaderboard(positions=top, guild=None) base_embed.set_author(name=ctx.bot.user.name, icon_url=ctx.bot.user.avatar_url) else: bank_sorted = await bank.get_leaderboard(positions=top, guild=guild) if guild: base_embed.set_author(name=guild.name, icon_url=guild.icon_url) try: bal_len = len(humanize_number(bank_sorted[0][1]["balance"])) bal_len_max = len(humanize_number(max_bal)) if bal_len > bal_len_max: bal_len = bal_len_max # first user is the largest we'll see except IndexError: return await ctx.send(_("There are no accounts in the bank.")) pound_len = len(str(len(bank_sorted))) header = "{pound:{pound_len}}{score:{bal_len}}{name:2}\n".format( pound="#", name=_("Name"), score=_("Score"), bal_len=bal_len + 6, pound_len=pound_len + 3, ) highscores = [] pos = 1 temp_msg = header for acc in bank_sorted: try: name = guild.get_member(acc[0]).display_name except AttributeError: user_id = "" if await ctx.bot.is_owner(ctx.author): user_id = f"({str(acc[0])})" name = f"{acc[1]['name']} {user_id}" balance = acc[1]["balance"] if balance > max_bal: balance = max_bal await bank.set_balance(MOCK_MEMBER(acc[0], guild), balance) balance = humanize_number(balance) if acc[0] != author.id: temp_msg += (f"{f'{humanize_number(pos)}.': <{pound_len+2}} " f"{balance: <{bal_len + 5}} {name}\n") else: temp_msg += (f"{f'{humanize_number(pos)}.': <{pound_len+2}} " f"{balance: <{bal_len + 5}} " f"<<{author.display_name}>>\n") if pos % 10 == 0: if embed_requested: embed = base_embed.copy() embed.description = box(temp_msg, lang="md") embed.set_footer(text=footer_message.format( page_num=len(highscores) + 1, page_len=ceil(len(bank_sorted) / 10), )) highscores.append(embed) else: highscores.append(box(temp_msg, lang="md")) temp_msg = header pos += 1 if temp_msg != header: if embed_requested: embed = base_embed.copy() embed.description = box(temp_msg, lang="md") embed.set_footer(text=footer_message.format( page_num=len(highscores) + 1, page_len=ceil(len(bank_sorted) / 10), )) highscores.append(embed) else: highscores.append(box(temp_msg, lang="md")) if highscores: await menu( ctx, highscores, DEFAULT_CONTROLS if len(highscores) > 1 else {"\N{CROSS MARK}": close_menu}, ) else: await ctx.send(_("No balances found."))
async def _eq_interact( self, ctx: commands.Context, player: lavalink.Player, eq: Equalizer, message: discord.Message, selected: int, ) -> None: player.store("eq", eq) emoji = { "far_left": "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}", "one_left": "\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}", "max_output": "\N{BLACK UP-POINTING DOUBLE TRIANGLE}", "output_up": "\N{UP-POINTING SMALL RED TRIANGLE}", "output_down": "\N{DOWN-POINTING SMALL RED TRIANGLE}", "min_output": "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}", "one_right": "\N{BLACK RIGHTWARDS ARROW}\N{VARIATION SELECTOR-16}", "far_right": "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}", "reset": "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}", "info": "\N{INFORMATION SOURCE}\N{VARIATION SELECTOR-16}", } selector = f'{" " * 8}{" " * selected}^^' try: await message.edit(content=box(f"{eq.visualise()}\n{selector}", lang="ini")) except discord.errors.NotFound: return try: (react_emoji, react_user) = await self._get_eq_reaction(ctx, message, emoji) except TypeError: return if not react_emoji: await self.config.custom("EQUALIZER", ctx.guild.id).eq_bands.set(eq.bands) await self._clear_react(message, emoji) if react_emoji == "\N{LEFTWARDS BLACK ARROW}\N{VARIATION SELECTOR-16}": await self.remove_react(message, react_emoji, react_user) await self._eq_interact(ctx, player, eq, message, max(selected - 1, 0)) if react_emoji == "\N{BLACK RIGHTWARDS ARROW}\N{VARIATION SELECTOR-16}": await self.remove_react(message, react_emoji, react_user) await self._eq_interact(ctx, player, eq, message, min(selected + 1, 14)) if react_emoji == "\N{UP-POINTING SMALL RED TRIANGLE}": await self.remove_react(message, react_emoji, react_user) _max = float("{:.2f}".format(min(eq.get_gain(selected) + 0.1, 1.0))) eq.set_gain(selected, _max) await self._apply_gain(ctx.guild.id, selected, _max) await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{DOWN-POINTING SMALL RED TRIANGLE}": await self.remove_react(message, react_emoji, react_user) _min = float("{:.2f}".format(max(eq.get_gain(selected) - 0.1, -0.25))) eq.set_gain(selected, _min) await self._apply_gain(ctx.guild.id, selected, _min) await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{BLACK UP-POINTING DOUBLE TRIANGLE}": await self.remove_react(message, react_emoji, react_user) _max = 1.0 eq.set_gain(selected, _max) await self._apply_gain(ctx.guild.id, selected, _max) await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{BLACK DOWN-POINTING DOUBLE TRIANGLE}": await self.remove_react(message, react_emoji, react_user) _min = -0.25 eq.set_gain(selected, _min) await self._apply_gain(ctx.guild.id, selected, _min) await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{BLACK LEFT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}": await self.remove_react(message, react_emoji, react_user) selected = 0 await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{BLACK RIGHT-POINTING TRIANGLE}\N{VARIATION SELECTOR-16}": await self.remove_react(message, react_emoji, react_user) selected = 14 await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{BLACK CIRCLE FOR RECORD}\N{VARIATION SELECTOR-16}": await self.remove_react(message, react_emoji, react_user) for band in range(eq.band_count): eq.set_gain(band, 0.0) await self._apply_gains(ctx.guild.id, eq.bands) await self._eq_interact(ctx, player, eq, message, selected) if react_emoji == "\N{INFORMATION SOURCE}\N{VARIATION SELECTOR-16}": await self.remove_react(message, react_emoji, react_user) await ctx.send_help(self.command_equalizer) await self._eq_interact(ctx, player, eq, message, selected)
async def get_playlist_match( self, context: commands.Context, matches: MutableMapping, scope: str, author: discord.User, guild: discord.Guild, specified_user: bool = False, ) -> Tuple[Optional[Playlist], str, str]: """ Parameters ---------- context: commands.Context The context in which this is being called. matches: dict A dict of the matches found where key is scope and value is matches. scope:str The custom config scope. A value from :code:`PlaylistScope`. author: discord.User The user. guild: discord.Guild The guild. specified_user: bool Whether or not a user ID was specified via argparse. Returns ------- Tuple[Optional[Playlist], str, str] Tuple of Playlist or None if none found, original user input and scope. Raises ------ `TooManyMatches` When more than 10 matches are found or When multiple matches are found but none is selected. """ correct_scope_matches: List[Playlist] original_input = matches.get("arg") lazy_match = False if scope is None: correct_scope_matches_temp: MutableMapping = matches.get("all") lazy_match = True else: correct_scope_matches_temp: MutableMapping = matches.get(scope) guild_to_query = guild.id user_to_query = author.id correct_scope_matches_user = [] correct_scope_matches_guild = [] correct_scope_matches_global = [] if not correct_scope_matches_temp: return None, original_input, scope or PlaylistScope.GUILD.value if lazy_match or (scope == PlaylistScope.USER.value): correct_scope_matches_user = [ p for p in matches.get(PlaylistScope.USER.value) if user_to_query == p.scope_id ] if lazy_match or (scope == PlaylistScope.GUILD.value and not correct_scope_matches_user): if specified_user: correct_scope_matches_guild = [ p for p in matches.get(PlaylistScope.GUILD.value) if guild_to_query == p.scope_id and p.author == user_to_query ] else: correct_scope_matches_guild = [ p for p in matches.get(PlaylistScope.GUILD.value) if guild_to_query == p.scope_id ] if lazy_match or (scope == PlaylistScope.GLOBAL.value and not correct_scope_matches_user and not correct_scope_matches_guild): if specified_user: correct_scope_matches_global = [ p for p in matches.get(PlaylistScope.GLOBAL.value) if p.author == user_to_query ] else: correct_scope_matches_global = [ p for p in matches.get(PlaylistScope.GLOBAL.value) ] correct_scope_matches = [ *correct_scope_matches_global, *correct_scope_matches_guild, *correct_scope_matches_user, ] match_count = len(correct_scope_matches) if match_count > 1: correct_scope_matches2 = [ p for p in correct_scope_matches if p.name == str(original_input).strip() ] if correct_scope_matches2: correct_scope_matches = correct_scope_matches2 elif original_input.isnumeric(): arg = int(original_input) correct_scope_matches3 = [ p for p in correct_scope_matches if p.id == arg ] if correct_scope_matches3: correct_scope_matches = correct_scope_matches3 match_count = len(correct_scope_matches) # We done all the trimming we can with the info available time to ask the user if match_count > 10: if original_input.isnumeric(): arg = int(original_input) correct_scope_matches = [ p for p in correct_scope_matches if p.id == arg ] if match_count > 10: raise TooManyMatches( _("{match_count} playlists match {original_input}: " "Please try to be more specific, or use the playlist ID." ).format(match_count=match_count, original_input=original_input)) elif match_count == 1: return correct_scope_matches[ 0], original_input, correct_scope_matches[0].scope elif match_count == 0: return None, original_input, scope or PlaylistScope.GUILD.value # TODO : Convert this section to a new paged reaction menu when Toby Menus are Merged pos_len = 3 playlists = f"{'#':{pos_len}}\n" number = 0 correct_scope_matches = sorted(correct_scope_matches, key=lambda x: x.name.lower()) async for number, playlist in AsyncIter( correct_scope_matches).enumerate(start=1): author = self.bot.get_user( playlist.author) or playlist.author or _("Unknown") line = _("{number}." " <{playlist.name}>\n" " - Scope: < {scope} >\n" " - ID: < {playlist.id} >\n" " - Tracks: < {tracks} >\n" " - Author: < {author} >\n\n").format( number=number, playlist=playlist, scope=self.humanize_scope(playlist.scope), tracks=len(playlist.tracks), author=author, ) playlists += line embed = discord.Embed( title=_("{playlists} playlists found, which one would you like?"). format(playlists=number), description=box(playlists, lang="md"), colour=await context.embed_colour(), ) msg = await context.send(embed=embed) avaliable_emojis = ReactionPredicate.NUMBER_EMOJIS[1:] avaliable_emojis.append("🔟") emojis = avaliable_emojis[:len(correct_scope_matches)] emojis.append("\N{CROSS MARK}") start_adding_reactions(msg, emojis) pred = ReactionPredicate.with_emojis(emojis, msg, user=context.author) try: await context.bot.wait_for("reaction_add", check=pred, timeout=60) except asyncio.TimeoutError: with contextlib.suppress(discord.HTTPException): await msg.delete() raise TooManyMatches( _("Too many matches found and you did not select which one you wanted." )) if emojis[pred.result] == "\N{CROSS MARK}": with contextlib.suppress(discord.HTTPException): await msg.delete() raise TooManyMatches( _("Too many matches found and you did not select which one you wanted." )) with contextlib.suppress(discord.HTTPException): await msg.delete() return ( correct_scope_matches[pred.result], original_input, correct_scope_matches[pred.result].scope, )
) cooldowns = cmd.get("cooldowns", {}) if cooldowns: cooldown_text = _("Cooldowns:\n") for rate, per in cooldowns.items(): cooldown_text += _("{num} seconds per {period}\n").format(num=per, period=rate) text += cooldown_text text += _("Responses:\n") responses = ["- " + r for r in responses] text += "\n".join(responses) for p in pagify(text): await ctx.send(box(p, lang="yaml")) @commands.Cog.listener() async def on_message_without_command(self, message): is_private = isinstance(message.channel, discord.abc.PrivateChannel) # user_allowed check, will be replaced with self.bot.user_allowed or # something similar once it's added user_allowed = True if len(message.content) < 2 or is_private or not user_allowed or message.author.bot: return if await self.bot.cog_disabled_in_guild(self, message.guild): return