async def economyset(self, ctx: commands.Context): """Manage Economy settings.""" guild = ctx.guild if ctx.invoked_subcommand is None: if await bank.is_global(): conf = self.config else: conf = self.config.guild(ctx.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}" ).format( slot_min=await conf.SLOT_MIN(), slot_max=await conf.SLOT_MAX(), slot_time=await conf.SLOT_TIME(), payday_time=await conf.PAYDAY_TIME(), payday_amount=await conf.PAYDAY_CREDITS(), register_amount=await bank.get_default_balance(guild), ) ) )
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 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) for guild in self.bot.guilds: 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 _list_global_alias(self, ctx: commands.Context): """List the available global aliases on this bot.""" names = [_("Aliases:")] + sorted( ["+ " + a.name for a in await self.unloaded_global_aliases()] ) if len(names) == 0: await ctx.send(_("There are no aliases on this server.")) else: await ctx.send(box("\n".join(names), "diff"))
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]) msg = _("Available Selfroles:\n{selfroles}").format(selfroles=fmt_selfroles) await ctx.send(box(msg, "diff"))
async def _repo_info(self, ctx, repo: Repo): """Show information about a repo.""" if repo is None: await ctx.send(_("Repo `{repo.name}` not found.").format(repo=repo)) return msg = _("Information on {repo.name}:\n{description}").format( repo=repo, description=repo.description or "" ) await ctx.send(box(msg))
async def _repo_list(self, ctx): """List all installed repos.""" repos = self._repo_manager.get_all_repo_names() repos = sorted(repos, key=str.lower) joined = _("Installed Repos:\n\n") for repo_name in repos: repo = self._repo_manager.get_repo(repo_name) joined += "+ {}: {}\n".format(repo.name, repo.short or "") for page in pagify(joined, ["\n"], shorten_by=16): await ctx.send(box(page.lstrip(" "), lang="diff"))
async def __msg_list(self, ctx: commands.Context, event: str): """Handler for listing message formats.""" guild = ctx.guild msg = "{} message formats:\n".format(event.capitalize()) async with self.config.guild(guild).get_attr(event).messages() as messages: for n, m in enumerate(messages, start=1): msg += " {}. {}\n".format(n, m) for page in pagify(msg, ["\n", " "], shorten_by=20): await ctx.send(box(page))
async def build_menu(self, groups, page=1): footer = "You are viewing page {} of {}.".format(page if page > 0 else 1, len(groups)) if self.shop is None and self.mode == 0: output = ["{} - {}".format(idx, ele) for idx, ele in enumerate(groups[page], 1)] elif self.mode == 0: header = ( f"{'#':<3} {'Name':<29} {'Qty':<7} {'Cost':<8}\n" f"{'--':<3} {'-'*29:<29} {'-'*4:<7} {'-'*8:<8}" ) fmt = [header] for idx, x in enumerate(self.sorter(groups[page]), 1): line_one = ( f"{f'{idx}.': <{3}} {x[0]: <{29}s} {x[1]['Qty']:<{8}}" f"{x[1]['Cost']: < {7}}" ) fmt.append(line_one) fmt.append( f'< {x[1]["Info"][:50]} >' if len(x[1]["Info"]) < 50 else f'< {x[1]["Info"][:47]}... >' ) fmt.append("") output = box("\n".join(fmt), "md") elif self.mode == 1 and self.user is None: headers = ("#", "User", "Pending Items") fmt = [ (idx, discord.utils.get(self.ctx.bot.users, id=int(x[0])).name, len(x[1])) for idx, x in enumerate(groups[page], 1) ] output = box(tabulate(fmt, headers=headers, numalign="left"), lang="md") elif self.mode == 1: headers = ("#", "Item", "Order ID", "Timestamp") fmt = [ (idx, x[1]["Item"], x[0], x[1]["Timestamp"]) for idx, x in enumerate(groups[page], 1) ] output = box(tabulate(fmt, headers=headers, numalign="left"), lang="md") else: output = None return self.build_embed(output, footer)
async def dataconversioncommand(self, ctx: commands.Context, v2path: str): """Interactive prompt for importing data from Red V2. Takes the path where the V2 install is, and overwrites values which have entries in both V2 and v3; use with caution. """ resolver = SpecResolver(Path(v2path.strip())) if not resolver.available: return await ctx.send( _( "There don't seem to be any data files I know how to " "handle here. Are you sure you gave me the base " "installation path?" ) ) while resolver.available: menu = _("Please select a set of data to import by number, or 'exit' to exit") for index, entry in enumerate(resolver.available, 1): menu += "\n{}. {}".format(index, entry) menu_message = await ctx.send(box(menu)) try: message = await self.bot.wait_for( "message", check=MessagePredicate.same_context(ctx), timeout=60 ) except asyncio.TimeoutError: return await ctx.send(_("Try this again when you are ready.")) else: if message.content.strip().lower() in ["quit", "exit", "-1", "q", "cancel"]: return await ctx.tick() try: message = int(message.content.strip()) to_conv = resolver.available[message - 1] except (ValueError, IndexError): await ctx.send(_("That wasn't a valid choice.")) continue else: async with ctx.typing(): await resolver.convert(self.bot, to_conv) await ctx.send(_("{} converted.").format(to_conv)) await menu_message.delete() else: return await ctx.send( _( "There isn't anything else I know how to convert here.\n" "There might be more things I can convert in the future." ) )
async def ccrole_list(self, ctx): """Shows custom commands list""" guild = ctx.guild cmd_list = await self.config.guild(guild).cmdlist() cmd_list = {k: v for k, v in cmd_list.items() if v} if not cmd_list: await ctx.send( "There are no custom commands in this server. Use `{}ccrole add` to start adding some.".format( ctx.prefix ) ) return cmd_list = ", ".join([ctx.prefix + c for c in sorted(cmd_list.keys())]) cmd_list = "Custom commands:\n\n" + cmd_list if ( len(cmd_list) < 1500 ): # I'm allowed to have arbitrary numbers for when it's too much to dm dammit await ctx.send(box(cmd_list)) else: for page in pagify(cmd_list, delims=[" ", "\n"]): await ctx.author.send(box(page)) await ctx.send("Command list DM'd")
def update(self, groups, page=0): header = ( f"{'#':<3} {'Items':<29} {'Qty':<7} {'Type':<8}\n" f"{'--':<3} {'-'*29:<29} {'-'*4:<7} {'-'*8:<8}" ) fmt = [header] for idx, x in enumerate(groups[page], 1): line_one = ( f"{f'{idx}.': <{3}} {x[0]: <{28}s} {x[1]['Qty']: < {9}}" f"{x[1]['Type']: <{7}s}" ) fmt.append(line_one) fmt.append( f'< {x[1]["Info"][:50]} >' if len(x[1]["Info"]) < 50 else f'< {x[1]["Info"][:47]}... >' ) fmt.append("") return box("\n".join(fmt), lang="md")
async def cc_show(self, ctx, command_name: str): """Shows a custom command's reponses and its settings.""" try: cmd = await self.commandobj.get_full(ctx.message, command_name) except NotFound: ctx.send(_("I could not not find that custom command.")) return responses = cmd["response"] if isinstance(responses, str): responses = [responses] author = ctx.guild.get_member(cmd["author"]["id"]) # If the author is still in the server, show their current name if author: author = "{} ({})".format(author, cmd["author"]["id"]) else: author = "{} ({})".format(cmd["author"]["name"], cmd["author"]["id"]) _type = _("Random") if len(responses) > 1 else _("Normal") text = _( "Command: {}\n" "Author: {}\n" "Created: {}\n" "Type: {}\n".format(command_name, author, cmd["created_at"], _type) ) cooldowns = cmd["cooldowns"] if cooldowns: cooldown_text = _("Cooldowns:\n") for rate, per in cooldowns.items(): cooldown_text += _("{} seconds per {}\n".format(per, 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"))
async def _cog_info(self, ctx, repo: Repo, cog_name: str): """List information about a single cog.""" cog = discord.utils.get(repo.available_cogs, name=cog_name) if cog is None: await ctx.send( _("There is no cog `{cog_name}` in the repo `{repo.name}`").format( cog_name=cog_name, repo=repo ) ) return msg = _( "Information on {cog_name}:\n{description}\n\nRequirements: {requirements}" ).format( cog_name=cog.name, description=cog.description or "", requirements=", ".join(cog.requirements) or "None", ) await ctx.send(box(msg))
async def findcog(self, ctx: commands.Context, command_name: str): """Find which cog a command comes from. This will only work with loaded cogs. """ command = ctx.bot.all_commands.get(command_name) if command is None: await ctx.send(_("That command doesn't seem to exist.")) return # Check if in installed cogs cog_name = self.cog_name_from_instance(command.instance) installed, cog_installable = await self.is_installed(cog_name) if installed: msg = self.format_findcog_info(command_name, cog_installable) else: # Assume it's in a base cog msg = self.format_findcog_info(command_name, command.instance) await ctx.send(box(msg))
async def bankset(self, ctx: commands.Context): """Base command for bank settings.""" if ctx.invoked_subcommand is None: if await bank.is_global(): bank_name = await bank._conf.bank_name() currency_name = await bank._conf.currency() default_balance = await bank._conf.default_balance() else: if not ctx.guild: return bank_name = await bank._conf.guild(ctx.guild).bank_name() currency_name = await bank._conf.guild(ctx.guild).currency() default_balance = await bank._conf.guild(ctx.guild).default_balance() settings = _( "Bank settings:\n\nBank name: {bank_name}\nCurrency: {currency_name}\n" "Default balance: {default_balance}" ).format( bank_name=bank_name, currency_name=currency_name, default_balance=default_balance ) await ctx.send(box(settings))
async def permissions_acl(self, ctx: commands.Context): """Manage permissions with YAML files.""" if ctx.invoked_subcommand is None or ctx.invoked_subcommand == self.permissions_acl: # Send a little guide on YAML formatting 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 _cog_list(self, ctx, repo: Repo): """List all available cogs from a single repo.""" installed = await self.installed_cogs() installed_str = "" if installed: installed_str = _("Installed Cogs:\n") + "\n".join( [ "- {}{}".format(i.name, ": {}".format(i.short) if i.short else "") for i in installed if i.repo_name == repo.name ] ) cogs = repo.available_cogs cogs = _("Available Cogs:\n") + "\n".join( [ "+ {}: {}".format(c.name, c.short or "") for c in cogs if not (c.hidden or c in installed) ] ) cogs = cogs + "\n\n" + installed_str for page in pagify(cogs, ["\n"], shorten_by=16): await ctx.send(box(page.lstrip(" "), lang="diff"))
async def uptime(self, ctx: commands.Context): """Shows [botname]'s uptime.""" def format_timedelta(delta: timedelta, unit: str): mapper = { "seconds": 1, "minutes": 60, "hours": 3600, "days": 86400 } return humanize_number( math.floor(delta.total_seconds() / mapper[unit])) delta = datetime.utcnow() - self.bot.uptime description = f"{self.bot.user} has been up for {bold(humanize_timedelta(timedelta=delta) or 'less than one second')}.\n\n" embed = discord.Embed( title="\N{LARGE GREEN CIRCLE} Uptime Information", description=description, color=await ctx.embed_colour(), timestamp=datetime.now(), ) data = {} units = ("seconds", "minutes", "hours", "days") for unit in units: data[unit] = str(format_timedelta(delta, unit)) plural_unit = lambda x: f"{data[x]} {x[:-1]}" if data[ x] == 1 else f"{data[x]} {x}" unit_details = "\n".join(f"+ {plural_unit(x)}" for x in units) embed.add_field(name="Unit Details", value=box(unit_details, lang="diff"), inline=False) app_info = await self.bot.application_info() bot_stats = { "users": humanize_number(len(self.bot.users)), "servers": humanize_number(len(self.bot.guilds)), "commands_available": humanize_number(len(set(self.bot.walk_commands()))), "owner": app_info.team.name if app_info.team else app_info.owner, } format_key = lambda x: x.replace("_", " ").capitalize() sorted_bot_stats = sorted(bot_stats.items(), key=lambda x: len(x[0])) embed.add_field( name="Bot Stats", value=box("\n".join(f"{format_key(k)}: {v}" for k, v in sorted_bot_stats), lang="yaml"), inline=False, ) if self.commands_run: most_run_command = sorted(self.commands_run.items(), key=lambda x: x[1], reverse=True) format_time = lambda x: "once" if x == 1 else f"{x} times" command_usage = f"The most used command whilst the bot has been online is `{most_run_command[0][0]}`, which has been used {format_time(most_run_command[0][1])}." if len(self.commands_run) != 1: command_usage += f"\n\nThe least used command is `{most_run_command[-1][0]}`, which has been used {format_time(most_run_command[-1][1])}." embed.add_field( name="Command usage since this cog has been loaded", value=command_usage, inline=False, ) embed.set_author(name=self.bot.user, icon_url=self.bot.user.avatar_url) await ctx.send(embed=embed)
async def report_cookie(self, ctx): """Signaler le contenu du dernier cookie obtenu""" author, guild = ctx.author, ctx.guild last_cookie = await self.config.member(author).last_cookie() config = await self.config.guild(guild).all() confirm, cancel = self.bot.get_emoji( 812451214037221439), self.bot.get_emoji(812451214179434551) if not last_cookie: return await ctx.send( "**Aucun cookie acheté** › Vous n'avez aucun cookie dans votre historique d'achats." ) cookies = await self.config.guild(guild).Cookies() try: cookie_id = last_cookie['cookie_id'] _c = cookies[cookie_id] except: return await ctx.send( "**Cookie inexistant** › Le cookie en question n'existe déjà plus dans la base de données, il a peut-être expiré ou a déjà été supprimé." ) em = discord.Embed(title=f"Signaler le cookie *{cookie_id}*", color=author.color) em.add_field(name="Texte du cookie", value=box(last_cookie['text'])) em.set_footer(text=f"Voulez-vous signaler ce cookie aux modérateurs ?") msg = await ctx.reply(embed=em, mention_author=False) start_adding_reactions(msg, [confirm, cancel]) try: react, _ = await self.bot.wait_for( "reaction_add", check=lambda m, u: u == ctx.author and m.message.id == msg.id, timeout=30) except asyncio.TimeoutError: return await msg.delete() if react.emoji == confirm: await msg.clear_reactions() if config['report_channel']: chan = self.bot.get_channel(config['report_channel']) if chan: r = discord.Embed(title="Signalement d'un cookie", description=f"**ID :** `{cookie_id}`", color=discord.Color.red()) r.add_field(name="Texte du cookie signalé", value=box(last_cookie['text'])) r.set_footer( text= f"Supprimer : \";cookieset delete {cookie_id}\"\nVoir tous les signalements : \";reports\"" ) await chan.send(embed=r) async with self.config.guild(guild).reports() as reports: if cookie_id not in reports: reports.append(cookie_id) em.set_footer(text=f"Votre signalement a bien été enregistré") await msg.edit(embed=em, mention_author=False) await msg.delete(delay=20) else: return await msg.delete()
async def test_cookie_formating(self, ctx, *, text: str): """Permet de tester le formattage d'un cookie (balises et fonctions) __Balises__ Vous pouvez mettre les balises suivantes dans vos cookies pour exploiter les objets renvoyés directement dans le texte /!\\ Mettre plusieurs fois la même balise renvoie le même objet ! *{buyer}* = Acheteur du cookie *{guild}* / *{server}* = Serveur où vous êtes *{cookie_author}* = Créateur du cookie (vous-même) *{random_member}* = Membre aléatoire du serveur *{date}* = Date au moment de l'ouverture du cookie au format dd/mm/aaaa *{hour}* = Heure au moment de l'ouverture du cookie au format hh:mm *{random_ten}* / *{random_hundred}* = Nombre aléatoire entre 0 et 10 / entre 0 et 100 *{random_bool}* = Booléen au hasard (Vrai ou Faux) __Fonctions__ n = ID de la fonction si vous comptez en mettre plusieures identiques <number|X_Y|n> = Génère un nombre aléatoire entre X et Y <member|n> = Génère une mention de membre aléatoire <bool|n> = Génère un emoji booléen au hasard <random|A_B_C...> = Choisir une réponse aléatoire parmi les options (délim. par _) Il est possible d'utiliser `:` à la place de `|`""" guild, author = ctx.guild, ctx.author original = copy(text) def special_formatter(string: str): scan = re.compile(r"<([\w\s:'\-|]*)>", re.DOTALL | re.IGNORECASE).findall(string) for b in scan: chunk = f'<{b}>' if len(chunk) > 200: continue b, *p = re.split(':|\|', b) b = b.lower() if b == 'number': seuils = [int(i) for i in p[0].split('_')] if p else (0, 10) try: string = string.replace(chunk, str(random.randint(*seuils))) except: pass if b == 'member': mem = random.choice(guild.members) string = string.replace(chunk, mem.mention) if b == 'bool': string = string.replace(chunk, random.choice(('✅', '❎'))) if b == 'random' and p: c = random.choice(list(p[0].split('_'))) string = string.replace(chunk, c) return string cookie_author = '`Auteur du cookie`' random_member = random.choice(guild.members) date, hour = datetime.now().strftime( '%d/%m/%Y'), datetime.now().strftime('%H:%M') rdm_ten = random.randint(0, 10) rdm_hundred = random.randint(0, 100) rdm_bool = random.choice(("Vrai", "Faux")) text = special_formatter(text).format(buyer=author, guild=guild, server=guild, cookie_author=cookie_author, random_member=random_member, date=date, hour=hour, random_ten=rdm_ten, random_hundred=rdm_hundred, random_bool=rdm_bool) em = discord.Embed(description=box(original), color=author.color) if 'http' in text: scan = re.compile(r'(https?://\S*\.\S*)', re.DOTALL | re.IGNORECASE).findall(text) if scan: em.set_image(url=scan[0]) name = scan[0].split('/')[-1] if "?" in name: name = name.split('?')[0] if not name: name = "URL" txt = text.replace(scan[0], f"[[{name}]]({scan[0]})") em.description = txt em.add_field(name="Résultat", value=text, inline=False) em.set_footer( text= "Ceci est une démonstration de ce que donnerait votre texte s'il était obtenu par quelqu'un" ) await ctx.reply(embed=em, mention_author=False)
async def server(self, ctx, server_ip: str): """Get info about server""" try: server: MinecraftServer = await self.bot.loop.run_in_executor( None, MinecraftServer.lookup, server_ip) except Exception as e: await ctx.send(chat.error(_("Unable to resolve IP: {}").format(e))) return async with ctx.channel.typing(): try: status = await server.async_status() except OSError as e: await ctx.send( chat.error( _("Unable to get server's status: {}").format(e))) return except AsyncTimeoutError: await ctx.send( chat.error(_("Unable to get server's status: Timed out"))) return # TODO: Reimplement on async query in mcstatus # NOTE: Possibly, make query optional # try: # query = await server.async_query() # except (ConnectionResetError, OSError): # query = None icon_file = None icon = (discord.File( icon_file := BytesIO( base64.b64decode(status.favicon.split(",", 1)[1])), filename="icon.png", ) if status.favicon else None) embed = discord.Embed( title=f"{server.host}:{server.port}", description=chat.box(await self.clear_mcformatting(status.description)), color=await ctx.embed_color(), ) if icon: embed.set_thumbnail(url="attachment://icon.png") embed.add_field(name=_("Latency"), value=f"{status.latency} ms") embed.add_field( name=_("Players"), value="{0.players.online}/{0.players.max}\n{1}".format( status, chat.box( list( chat.pagify( await self.clear_mcformatting("\n".join( [p.name for p in status.players.sample])), page_length=992, ))[0]) if status.players.sample else "", ), ) embed.add_field( name=_("Version"), value=_("{}\nProtocol: {}").format(status.version.name, status.version.protocol), ) # if query: # embed.add_field(name=_("World"), value=f"{query.map}") # embed.add_field( # name=_("Software"), # value=_("{}\nVersion: {}").format(query.software.brand, query.software.version) # # f"Plugins: {query.software.plugins}" # ) await ctx.send(file=icon, embed=embed) if icon_file: icon_file.close()
def _multiply(self, mul): return box(self * mul)
def _shuffle(self): data = self.split() random.shuffle(data) return box(" ".join(data))
async def invite_filter(self, message): author = message.author guild = author.guild EMBED_TITLE = "🔥📧 • Invite filter" EMBED_FIELDS = [{ "name": "Username", "value": f"`{author}`" }, { "name": "ID", "value": f"`{author.id}`" }, { "name": "Channel", "value": message.channel.mention }] result = INVITE_URL_RE.findall(message.content) if not result: return exclude_own_invites = await self.config.guild( guild).invite_filter_exclude_own_invites() if exclude_own_invites: external_invite = await get_external_invite(guild, result) if external_invite is None: return False else: external_invite = result[0][1] if len(message.content) > 1000: content = box(f"{message.content[:1000]}(...)") else: content = box(message.content) action = Action(await self.config.guild(guild).invite_filter_action()) if action == Action.Ban: reason = "Posting an invite link (Defender autoban)" await guild.ban(author, reason=reason, delete_message_days=0) self.dispatch_event("member_remove", author, Action.Ban.value, reason) elif action == Action.Kick: reason = "Posting an invite link (Defender autokick)" await guild.kick(author, reason=reason) self.dispatch_event("member_remove", author, Action.Kick.value, reason) elif action == Action.Softban: reason = "Posting an invite link (Defender autokick)" await guild.ban(author, reason=reason, delete_message_days=1) await guild.unban(author) self.dispatch_event("member_remove", author, Action.Softban.value, reason) elif action == Action.Punish: punish_role = guild.get_role( await self.config.guild(guild).punish_role()) punish_message = await self.format_punish_message(author) if punish_role and not self.is_role_privileged(punish_role): await author.add_roles( punish_role, reason="Defender: punish role assignation") if punish_message: await message.channel.send(punish_message) else: self.send_to_monitor( guild, "[InviteFilter] Failed to punish user. Is the punish role " "still present and with *no* privileges?") return try: await message.delete() except discord.Forbidden: self.send_to_monitor( guild, "[InviteFilter] Failed to delete message: " f"no permissions in #{message.channel}") except discord.NotFound: pass except Exception as e: log.error("Unexpected error in invite filter's message deletion", exc_info=e) invite_data = f"**About [discord.gg/{external_invite}](https://discord.gg/{external_invite})**\n" try: invite = await self.bot.fetch_invite(external_invite) except (discord.NotFound, discord.HTTPException): invite_data += f"I could not gather more information about the invite." else: if invite.guild: invite_data += f"This invite leads to the server `{invite.guild.name}` (`{invite.guild.id}`)\n" if invite.approximate_presence_count is not None and invite.approximate_member_count is not None: invite_data += ( f"It has **{invite.approximate_member_count}** members " f"({invite.approximate_presence_count} online)\n") icon_url = invite.guild.icon_url_as() banner_url = invite.guild.banner_url_as() is_partner = "PARTNERED" in invite.guild.features is_verified = "VERIFIED" in invite.guild.features chars = [] chars.append( f"It was created {timestamp(invite.guild.created_at, relative=True)}" ) if is_partner: chars.append("it is a **partner** server") if is_verified: chars.append("it is **verified**") if icon_url: chars.append(f"it has an [icon set]({icon_url})") if banner_url: chars.append(f"it has a [banner set]({banner_url})") if invite.guild.description: chars.append( f"the following is its description:\n{box(invite.guild.description)}" ) invite_data += f"{humanize_list(chars)}" else: invite_data += f"I have failed to retrieve the server's data. Possibly a group DM invite?\n" if action == Action.NoAction: notif_text = f"I have deleted a message with this content:\n{content}\n{invite_data}" else: notif_text = f"I have {ACTIONS_VERBS[action]} a user for posting this message:\n{content}\n{invite_data}" quick_action = QuickAction(author.id, "Posting an invite link") heat_key = f"core-if-{author.id}-{message.channel.id}" await self.send_notification(guild, notif_text, title=EMBED_TITLE, fields=EMBED_FIELDS, jump_to=message, no_repeat_for=timedelta(minutes=1), heat_key=heat_key, quick_action=quick_action) await self.create_modlog_case( self.bot, guild, message.created_at, action.value, author, guild.me, "Posting an invite link", until=None, channel=None, ) return True
async def message_leaderboard(self, ctx): """ Message Tracker Leaderboard, displays all the members. [Credit to Core Economy](https://github.com/Cog-Creators/Red-DiscordBot/blob/dc68bc5d373c69aa1307ecef8118da14379ac67a/redbot/cogs/economy/economy.py#L545-L663) """ try: self.counted_message[ctx.guild.id] update_config = await self.update_guild_config_from_cache( ctx.guild) # updates from cache await self.remove_non_members_from_config() # clears non_members if update_config is False: return await ctx.send( "Something happened with updating. Please try again.") except KeyError: # If it fails to have anything, we may assume that no messages have been sent. pass config_info = await self.config.all_members(ctx.guild) if not config_info: return await ctx.send( "There is no tracked messages for this server.") if await self.config.guild(ctx.guild).enabled_system() is False: await ctx.send( "This server does not have this system enabled. We won't be tracking any new messages." ) # Even if it's turned off, we should still allow it to be shown. async with ctx.typing(): sorting_list = sorted(config_info.items(), key=lambda x: x[1]["counter"], reverse=True) pound_len = len(str(len(sorting_list))) top_message_len = len(str(sorting_list[0][1]["counter"])) embed_message = "" embed_header = "{pound:{pound_len}}{score:{bal_len}}{name:2}\n".format( pound="#", name="Name", score="Messages", bal_len=top_message_len + 8, pound_len=pound_len + 2, ) footer_message = "Page {page_num}/{page_len}." # Work this in somehow. base = discord.Embed( title="{guild}'s leaderboard".format(guild=ctx.guild.name), description="") embed_list = [] pos = 1 new_embed = base.copy() embed_message = embed_header for user_id, counter in sorting_list: user = ctx.guild.get_member(user_id).display_name if user is None: user = user_id if user_id != ctx.author.id: embed_message += ( f"{f'{pos}.': <{pound_len+2}} " f"{humanize_number(counter['counter']): <{top_message_len + 6}} {user}\n" ) else: embed_message += ( f"{f'{pos}.': <{pound_len+2}} " f"{humanize_number(counter['counter']): <{top_message_len + 6}} <<{ctx.author.display_name}>>\n" ) if pos % 10 == 0: new_embed = base.copy() new_embed.description = box(embed_message, lang="md") new_embed.set_footer(text=footer_message.format( page_num=ceil(len(embed_list) + 1), page_len=ceil(len(sorting_list) / 10), )) embed_list.append(new_embed) embed_message = embed_header pos += 1 if embed_message != embed_header: new_embed = base.copy() new_embed.description = box(embed_message, lang="md") new_embed.set_footer(text=footer_message.format( page_num=ceil(len(embed_list) + 1), page_len=ceil(len(sorting_list) / 10))) embed_list.append(new_embed) if not embed_list: return await ctx.send("Sorry, no leaderboard to display.") await menu( ctx, embed_list, DEFAULT_CONTROLS if len(embed_list) > 1 else {"\N{CROSS MARK}": close_menu}, )
async def advusagecount(self, ctx): avatar = self.bot.user.avatar_url_as(static_format="png") uptime = str(self.get_bot_uptime()) errors_count = "{:,}".format(self.counter["command_error"]) messages_read = "{:,}".format(self.counter["messages_read"]) messages_sent = "{:,}".format(self.counter["msg_sent"]) dms_received = "{:,}".format(self.counter["dms_received"]) guild_join = "{:,}".format(self.counter["guild_join"]) guild_leave = "{:,}".format(self.counter["guild_remove"]) resumed_sessions = "{:,}".format(self.counter["sessions_resumed"]) commands_count = "{:,}".format(self.counter["processed_commands"]) new_mem = "{:,}".format(self.counter["new_members"]) left_mem = "{:,}".format(self.counter["members_left"]) msg_deleted = "{:,}".format(self.counter["messages_deleted"]) msg_edited = "{:,}".format(self.counter["messages_edited"]) react_added = "{:,}".format(self.counter["reactions_added"]) react_removed = "{:,}".format(self.counter["reactions_removed"]) roles_add = "{:,}".format(self.counter["roles_added"]) roles_rem = "{:,}".format(self.counter["roles_removed"]) roles_up = "{:,}".format(self.counter["roles_updated"]) mem_ban = "{:,}".format(self.counter["members_banned"]) mem_unban = "{:,}".format(self.counter["members_unbanned"]) emoji_add = "{:,}".format(self.counter["emojis_added"]) emoji_rem = "{:,}".format(self.counter["emojis_removed"]) emoji_up = "{:,}".format(self.counter["emojis_updated"]) vc_joins = "{:,}".format(self.counter["users_joined_bot_music_room"]) tracks_played = "{:,}".format(self.counter["tracks_played"]) #streams_played = "{:,}".format(self.counter["streams_played"]) #yt_streams = "{:,}".format(self.counter["yt_streams_played"]) #mixer_streams = "{:,}".format(self.counter["mixer_streams_played"]) #ttv_streams = "{:,}".format(self.counter["ttv_streams_played"]) #other_streams = "{:,}".format(self.counter["other_streams_played"]) #youtube_tracks = "{:,}".format(self.counter["youtube_tracks"]) #soundcloud_tracks = "{:,}".format(self.counter["soundcloud_tracks"]) #bandcamp_tracks = "{:,}".format(self.counter["bandcamp_tracks"]) #vimeo_tracks = "{:,}".format(self.counter["vimeo_tracks"]) #mixer_tracks = "{:,}".format(self.counter["mixer_tracks"]) #twitch_tracks = "{:,}".format(self.counter["twitch_tracks"]) #other_tracks = "{:,}".format(self.counter["other_tracks"]) try: total_num = "{:,}/{:,}".format(len(lavalink.active_players()), len(lavalink.all_players())) except AttributeError: total_num = "{:,}/{:,}".format( len([p for p in lavalink.players if p.current is not None]), len([p for p in lavalink.players]), ) em = discord.Embed( title=_("Usage count of {} since last restart:").format( ctx.bot.user.name), color=await ctx.embed_colour()) em.add_field(name=_("Message Stats"), value=box(_(""" Messages Read: {} Messages Sent: {} Messages Deleted: {} Messages Edited {} DMs Recieved: {}""").format(messages_read, messages_sent, msg_deleted, msg_edited, dms_received), lang="prolog"), inline=False) em.add_field(name=_("Commands Stats"), value=box(_(""" Commands Processed: {} Errors Occured: {} Sessions Resumed: {}""").format(commands_count, errors_count, resumed_sessions), lang="prolog"), inline=False) em.add_field(name=_("Guild Stats"), value=box(_(""" Guilds Joined: {} Guilds Left: {}""").format(guild_join, guild_leave), lang="prolog"), inline=False) em.add_field(name=_("User Stats"), value=box(_(""" New Users: {} Left Users: {} Banned Users: {} Unbanned Users: {}""").format(new_mem, left_mem, mem_ban, mem_unban), lang="prolog"), inline=False) em.add_field(name=_("Role Stats"), value=box(_(""" Roles Added: {} Roles Removed: {} Roles Updated: {}""").format(roles_add, roles_rem, roles_up), lang="prolog"), inline=False) em.add_field(name=_("Emoji Stats"), value=box(_(""" Reacts Added: {} Reacts Removed: {} Emoji Added: {} Emoji Removed: {} Emoji Updated: {}""").format(react_added, react_removed, emoji_add, emoji_rem, emoji_up), lang="prolog"), inline=False) em.add_field(name=_("Audio Stats"), value=box(_(""" Users Who Joined VC: {} Tracks Played: {} Number Of Players: {}""").format(vc_joins, tracks_played, total_num), lang="prolog"), inline=False) em.set_thumbnail(url=avatar) em.set_footer(text=_("Since {}").format(uptime)) await ctx.send(embed=em)
async def repoinfo(self, ctx, repo_name): DLCOG = self.bot.get_cog("Downloader") if DLCOG is None: await ctx.send(inline("Downloader cog not loaded.")) return repo = DLCOG._repo_manager.get_repo(repo_name) if repo is None: await ctx.send(box("Repo not found.\n\nAvaliable Repos:\n" + "\n".join( DLCOG._repo_manager.get_all_repo_names()))) return extensions = [i.name for i in repo.available_cogs] cogs = filter(lambda x: x.__module__.split(".")[0] in extensions, self.bot.cogs.values()) hs = await commands.help.HelpSettings.from_context(ctx) rhf = commands.help.RedHelpFormatter() coms = [( cog.__cog_name__, await commands.help.RedHelpFormatter().get_cog_help_mapping(ctx, cog, hs) ) for cog in cogs] if not coms: await ctx.send(inline("There are no loaded cogs on the repo!")) return if await ctx.embed_requested(): emb = {"embed": {"title": "", "description": ""}, "footer": {"text": ""}, "fields": []} for cog_name, data in coms: if cog_name: title = f"**__{cog_name}:__**" else: title = "**__No Category:__**" def shorten_line(a_line: str) -> str: if len(a_line) < 70: return a_line return a_line[:67] + "..." cog_text = "\n".join( shorten_line( f"**{name}** {command.format_shortdoc_for_context(ctx)}") for name, command in sorted(data.items()) ) for i, page in enumerate( pagify(cog_text, page_length=1000, shorten_by=0)): title = title if i < 1 else f"{title} (continued)" field = EmbedField(title, page, False) emb["fields"].append(field) await rhf.make_and_send_embeds(ctx, emb, help_settings=hs) else: to_join = ["Commands for {}:\n".format(repo_name)] names = [] for k, v in coms: names.extend(list(v.name for v in v.values())) max_width = max( discord.utils._string_width(name or "No Category:") for name in names) def width_maker(cmds): doc_max_width = 80 - max_width for nm, com in cmds: width_gap = discord.utils._string_width(nm) - len(nm) doc = com.format_shortdoc_for_context(ctx) if len(doc) > doc_max_width: doc = doc[: doc_max_width - 3] + "..." yield nm, doc, max_width - width_gap for cog_name, data in coms: title = f"{cog_name}:" if cog_name else "No Category:" to_join.append(title) for name, doc, width in width_maker(sorted(data.items())): to_join.append(f" {name:<{width}} {doc}") to_page = "\n".join(to_join) pages = [box(p) for p in pagify(to_page)] await rhf.send_pages(ctx, pages, embed=False, help_settings=hs)
async def debugid(self, ctx, *, query): padinfo_cog = self.bot.get_cog('PadInfo') # m is a named monster m, err, debug_info = lookup_named_monster(query) if m is None: await ctx.send(box('No match: ' + err)) return msg = "{}. {}".format(m.monster_no_na, m.name_na) msg += "\nLookup type: {}".format(debug_info) def list_or_none(l): if len(l) == 1: return '\n\t{}'.format(''.join(l)) elif len(l): return '\n\t' + '\n\t'.join(sorted(l)) else: return 'NONE' msg += "\n\nNickname original components:" msg += "\n monster_basename: {}".format(m.monster_basename) msg += "\n group_computed_basename: {}".format( m.group_computed_basename) msg += "\n extra_nicknames: {}".format(list_or_none(m.extra_nicknames)) msg += "\n\nNickname final components:" msg += "\n basenames: {}".format(list_or_none(m.group_basenames)) msg += "\n prefixes: {}".format(list_or_none(m.prefixes)) msg += "\n\nAccepted nickname entries:" accepted_nn = list( filter( lambda nn: m.monster_id == padinfo_cog.index_all.all_entries[ nn].monster_id, m.final_nicknames)) accepted_twnn = list( filter( lambda nn: m.monster_id == padinfo_cog.index_all. two_word_entries[nn].monster_id, m.final_two_word_nicknames)) msg += "\n nicknames: {}".format(list_or_none(accepted_nn)) msg += "\n two_word_nicknames: {}".format(list_or_none(accepted_twnn)) msg += "\n\nOverwritten nickname entries:" replaced_nn = list( filter(lambda nn: nn not in accepted_nn, m.final_nicknames)) replaced_twnn = list( filter(lambda nn: nn not in accepted_twnn, m.final_two_word_nicknames)) replaced_nn_info = map( lambda nn: (nn, padinfo_cog.index_all.all_entries[nn]), replaced_nn) replaced_twnn_info = map( lambda nn: (nn, padinfo_cog.index_all.two_word_entries[nn]), replaced_twnn) replaced_nn_text = list( map( lambda nn_info: '{} : {}. {}'.format(nn_info[0], nn_info[ 1].monster_no_na, nn_info[1].name_na), replaced_nn_info)) replaced_twnn_text = list( map( lambda nn_info: '{} : {}. {}'.format(nn_info[0], nn_info[ 1].monster_no_na, nn_info[1].name_na), replaced_twnn_info)) msg += "\n nicknames: {}".format(list_or_none(replaced_nn_text)) msg += "\n two_word_nicknames: {}".format( list_or_none(replaced_twnn_text)) msg += "\n\nNickname entry sort parts:" msg += "\n (is_low_priority, group_size, monster_no_na) : ({}, {}, {})".format( m.is_low_priority, m.group_size, m.monster_no_na) msg += "\n\nMatch selection sort parts:" msg += "\n (is_low_priority, rarity, monster_no_na) : ({}, {}, {})".format( m.is_low_priority, m.rarity, m.monster_no_na) sent_messages = [] for page in pagify(msg): sent_messages.append(await ctx.send(box(page))) await rpadutils.await_and_remove(self.bot, sent_messages[-1], ctx.author, delete_msgs=sent_messages, timeout=30)
async def game(self, channel): """Runs a quiz game on a channel.""" channelinfo = self.playing_channels[channel.id] category = channelinfo["CategoryID"] category_name = channelinfo["Category"] try: response = await self.get_questions( channel.guild, category=channelinfo["CategoryID"]) except RuntimeError: await channel.send( "An error occurred in retrieving questions. Please try again.") self.playing_channels.pop(channel.id) raise # Introduction intro = ( f"Welcome to the quiz game! Your category is `{category_name}`.\n" "Remember to answer correctly as quickly as you can for more points.\n" "You have 10 seconds per question: the timer is shown in reactions on each question.\n" "The game will begin shortly.") await channel.send(intro) await asyncio.sleep(4) # Question and Answer afk_questions = 0 for index, dictionary in enumerate(response["results"]): answers = [dictionary["correct_answer"] ] + dictionary["incorrect_answers"] # Display question and countdown if len(answers) == 2: # true/false question answers = ["True", "False", "", ""] else: answers = [html.unescape(answer) for answer in answers] random.shuffle(answers) message = "" message += html.unescape(dictionary["question"]) + "\n" message += f"A. {answers[0]}\n" message += f"B. {answers[1]}\n" message += f"C. {answers[2]}\n" message += f"D. {answers[3]}\n" message_obj = await channel.send(box(message)) await message_obj.add_reaction("0⃣") channelinfo["Answers"].clear( ) # clear the previous question's answers start_time = time.perf_counter() numbers = [ "1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣", "🔟" ] for i in range(10): if len(channelinfo["Answers"]) == len(channelinfo["Players"]): break await asyncio.sleep(1) await message_obj.add_reaction(numbers[i]) # Organize answers user_answers = channelinfo["Answers"] # snapshot channelinfo["Answers"] at this point in time # to ignore new answers that are added to it answerdict = {["a", "b", "c", "d"][num]: answers[num] for num in range(4)} # Check for AFK if len(user_answers) < 2: afk_questions += 1 afk_count = await self.config.guild(channel.guild).afk() if afk_questions == int(afk_count): await channel.send( "The game has been cancelled due to lack of participation." ) self.playing_channels.pop(channel.id) return else: afk_questions = 0 # Find and display correct answer correct_letter = "" for letter, answer in answerdict.items(): if answer == html.unescape(dictionary["correct_answer"]): correct_letter = letter break assert answerdict[correct_letter] == html.unescape( dictionary["correct_answer"]) if await self.config.guild(channel.guild).show_answer(): message = f"Correct answer:```{correct_letter.upper()}. {answerdict[correct_letter]}```" await channel.send(message) # Assign scores for playerid in user_answers: if user_answers[playerid]["Choice"] == correct_letter: time_taken = user_answers[playerid]["Time"] - start_time assert time_taken > 0 if time_taken < 1: channelinfo["Players"][playerid] += 1000 else: # the 20 in the formula below is 2 * 10s (max answer time) channelinfo["Players"][playerid] += round( 1000 * (1 - (time_taken / 20))) # Display top 5 players and their points message = self.scoreboard(channel) await channel.send("Scoreboard:\n" + message) await asyncio.sleep(4) questions = await self.config.guild(channel.guild).questions() if index < (int(questions) - 1): await channel.send("Next question...") await asyncio.sleep(1) await self.end_game(channel)
async def helpsearch(self, ctx): """Help info for the search command.""" await ctx.author.send(box(HELP_MSG.format(ctx)))
def _trim(self, trimmer): return box(self.strip(trimmer).strip())
async def godvillegame(self, ctx, *, godname: str): """Get data about godville's god by name""" async with self.session.get("{}/{}".format(BASE_API_GLOBAL, godname.casefold())) as sg: if sg.status == 404: await ctx.send( chat.error( "404 — Sorry, but there is nothing here\nCheck god name and try again" )) return elif sg.status != 200: await ctx.send( chat.error( "Something went wrong. Server returned {}.".format( sg.status))) return profile = await sg.json() profile = GodvilleUser(profile) text_header = "{} and his {}\n{}\n" \ .format(chat.bold(profile.god), chat.bold(profile.name), chat.italics(chat.escape(profile.motto.strip(), formatting=True)) if profile.motto else chat.inline("Nothing here")) if profile.arena_is_in_fight: text_header += "In fight: {}\n".format(profile.fight_type_rus) if profile.town: text_header += "In city: {}\n".format(profile.town) if profile.need_update: text_header += chat.bold("! INFO OUTDATED !") + "\n" text = "" pet = "" times = "" if profile.gold_approximately: text += "Gold: {}\n".format(profile.gold_approximately) if profile.distance: text += "Milestone: {}\n".format(profile.distance) if profile.quest_progress: text += "Quest: {} ({}%)\n".format(profile.quest, profile.quest_progress) if profile.experience: text += "Exp for next level: {}%\n".format(profile.experience) text += "Level: {}\n".format(profile.level) if profile.godpower: text += "Godpower: {}/{}\n".format( profile.godpower, 200 if profile.savings_date else 100) text += "Personality: {}\n".format(profile.alignment) text += "Gender: {}\n".format(profile.gender) text += "Wins / Losses: {}/{}\n".format(profile.arena_won, profile.arena_lost) text += "Guild: {} ({})\n".format(profile.clan, profile.clan_position) if profile.clan \ else "Guild: Not in guild\n" text += "Bricks: {} ({}%)\n".format(profile.bricks, profile.bricks / 10) if profile.inventory: text += "Inventory: {}/{} ({}%)\n".format( profile.inventory, profile.inventory_max, int(profile.inventory / profile.inventory_max * 100)) else: text += "Inventory max: {}\n".format(profile.inventory_max) if profile.health: text += "Health: {}/{} ({}%)\n".format( profile.health, profile.health_max, int(profile.health / profile.health_max * 100)) else: text += "Health maximum: {}\n".format(profile.health_max) if profile.ark_male: text += "Manimals: {} ({}%)\n".format(profile.ark_male, profile.ark_male / 10) if profile.ark_female: text += "Fenimals: {} ({}%)\n".format(profile.ark_female, profile.ark_female / 10) if profile.savings: text += "Savings: {}\n".format(profile.savings) if profile.trading_level: text += "Trading Level: {}\n".format(profile.trading_level) if profile.wood: text += "Wood: {} ({}%)\n".format(profile.wood, profile.wood / 10) # private (api only) if profile.diary_last: text += "Diary: {}\n".format(profile.diary_last) if profile.activatables: text += "Activatables in inv: {}\n".format(", ".join( profile.activatables)) if profile.aura: text += "Aura: {}\n".format(profile.aura) # pet if profile.pet.name: pet += "Name: {}\n".format(profile.pet.name) pet += "Level: {}\n".format(profile.pet.level or "No level") if profile.pet.type: pet += "Type: {}\n".format(profile.pet.type) if profile.pet.wounded: pet += "❌ — Knocked out" # times if profile.temple_date: times += "Temple completed: {}\n".format( profile.date_string("temple")) if profile.ark_date: times += "Ark completed: {}\n".format(profile.date_string("ark")) if profile.savings_date: times += "Pension collected: {}\n".format( profile.date_string("savings")) # ? finaltext = "" finaltext += text_header finaltext += chat.box(text) if pet: finaltext += "Pet:\n" finaltext += chat.box(pet) if times: finaltext += chat.box(times) await ctx.send(finaltext)
def _reverse(self): return box(self[::-1])
async def boxPagifySay(say_fn, msg): for page in pagify(msg, delims=["\n"]): await say_fn(box(page))
async def _version_msg(self, ctx, version=None): msg = box(_("Nsfw cog version: ") + version, lang="py") return await ctx.send(msg)
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
async def staff(self, ctx: commands.Context, *, reason: str = None): """ Alert for the staff. """ channel = await self.config.guild(ctx.guild).channel() role = await self.config.guild(ctx.guild).role() if not channel: return await ctx.send( error("The staff have not yet setup a staff channel.")) channel = self.bot.get_channel(channel) role = ctx.guild.get_role(role) now = datetime.now() date = now.strftime("%d/%m/%y") message_list = [] backslash = '\n' async for message in ctx.channel.history(limit=6): author, msg = message.author, message.content.replace('`', '') if len(msg) > 30: msg = msg[:30].strip(' ') + '...' elif not len(msg): msg = "[Embed, Attachment or File]" message_list.append( f"{str(author.display_name)}: {msg.replace(backslash, ' ')}") context = box('\n'.join(message_list), lang='yaml') reason = reason or "No reason was provided." embed = discord.Embed( title=warning("Staff Attention Pending | Conspicuous Activity"), description="[Click here for context]({})".format( ctx.message.jump_url), color=await ctx.embed_colour(), ) embed.add_field(name="Member", value=ctx.author.mention, inline=True) embed.add_field(name="Channel", value=ctx.channel.mention, inline=True) embed.add_field(name="Date", value=date, inline=True) embed.add_field(name="Reason", value=reason, inline=False) embed.add_field(name="Context", value=context, inline=False) if await ctx.embed_requested(): try: await channel.send( allowed_mentions=discord.AllowedMentions(roles=True), content=role.mention if role else None, embed=embed, ) await ctx.send( "I have alerted the authorities, please remain calm.") except discord.Forbidden: return await ctx.send( "I do not have permissions to alert the staff.") else: return await ctx.send( "I do not have permissions to send embeds in the staff's channel." )
def _snake(self): return box(self.replace(" ", "_"))
async def tip_cookie_author(self, ctx, somme: int = None): """Permet de donner un tip à l'auteur du dernier cookie acheté Par défaut le tip prendra la valeur définie comme récompense lors des like de cookie""" author, guild = ctx.author, ctx.guild if not somme: somme = await self.config.guild(guild).reward( ) if await self.config.guild(guild).reward() > 0 else 1 last_cookie = await self.config.member(author).last_cookie() eco = self.bot.get_cog('XPay') currency = await eco.get_currency(guild) confirm, cancel = self.bot.get_emoji( 812451214037221439), self.bot.get_emoji(812451214179434551) if not last_cookie: return await ctx.send( "**Aucun cookie acheté** › Vous n'avez aucun cookie dans votre historique d'achats." ) cookie_price = await self.config.guild(guild).price() if somme <= 0 or somme > cookie_price: return await ctx.send( f"**Valeur invalide** › Le tip doit être compris entre 1 et la valeur d'achat ({cookie_price}{currency})." ) if not await eco.check_balance(author, somme): return await ctx.send( "**Solde insuffisant** › Vous n'avez pas les moyens de tipper cette somme." ) if last_cookie['tipped']: return await ctx.send( "**Déjà tippé** › Vous ne pouvez pas donner plus d'un seul tip par cookie." ) if last_cookie['author']: lc_author = guild.get_member(last_cookie['author']) em = discord.Embed(title=f"Envoyer un tip à **{lc_author.name}**", color=author.color) em.add_field(name="Texte du cookie", value=box(last_cookie['text'])) em.set_footer( text= f"Voulez-vous envoyer {somme}{currency} à l'auteur de ce cookie pour le récompenser ?" ) msg = await ctx.reply(embed=em, mention_author=False) start_adding_reactions(msg, [confirm, cancel]) try: react, _ = await self.bot.wait_for( "reaction_add", check=lambda m, u: u == ctx.author and m.message.id == msg. id, timeout=30) except asyncio.TimeoutError: return await msg.delete() if react.emoji == confirm: await msg.clear_reactions() await self.config.member(author).last_cookie.set_raw( 'tipped', value=True) try: await eco.withdraw_credits(author, somme, reason="Tip de fortune cookie") except: em.set_footer(text=f"Erreur dans l'envoi du tips") return await msg.edit(embed=em) else: await eco.deposit_credits( lc_author, somme, reason="Tip reçu pour un fortune cookie") em.set_footer( text= f"Vous avez envoyé {somme}{currency} à {lc_author.name}") return await msg.edit(embed=em, mention_author=False) else: return await msg.delete() else: await ctx.send( "**Auteur inconnu** › L'auteur de votre dernier cookie acheté ne semble plus être sur ce serveur et ne peut donc recevoir de tips." )
def _alternating(self): text = list(self) text[0::2] = map(str.upper, text[0::2]) text[1::2] = map(str.lower, text[1::2]) return box(text)
async def _negaverse( self, ctx: commands.Context, offering: int = None, roll: Optional[int] = -1, nega: discord.Member = None ): """This will send you to fight a nega-member!""" if self.in_adventure(ctx): ctx.command.reset_cooldown(ctx) return await smart_embed( ctx, _("You tried to teleport to another dimension but the monster ahead did not give you a chance."), ) bal = await bank.get_balance(ctx.author) currency_name = await bank.get_currency_name( ctx.guild, ) if offering is None: ctx.command.reset_cooldown(ctx) return await smart_embed( ctx, _( "**{author}**, you need to specify how many " "{currency_name} you are willing to offer to the gods for your success." ).format(author=escape(ctx.author.display_name), currency_name=currency_name), ) if offering <= 500 or bal <= 500: ctx.command.reset_cooldown(ctx) return await smart_embed(ctx, _("The gods refuse your pitiful offering.")) if offering > bal: offering = int(bal) admin_roll = -1 nega_set = False if (roll >= 0 or nega) and await self.bot.is_owner(ctx.author): if not is_dev(ctx.author): if not await self.no_dev_prompt(ctx): ctx.command.reset_cooldown(ctx) return nega_set = True admin_roll = roll offering_value = 0 winning_state = False loss_state = False xp_won_final = 0 lock = self.get_lock(ctx.author) await lock.acquire() try: nv_msg = await ctx.send( _( "**{author}**, this will cost you at least {offer} {currency_name}.\n" "You currently have {bal}. Do you want to proceed?" ).format( author=escape(ctx.author.display_name), offer=humanize_number(offering), currency_name=currency_name, bal=humanize_number(bal), ) ) start_adding_reactions(nv_msg, ReactionPredicate.YES_OR_NO_EMOJIS) pred = ReactionPredicate.yes_or_no(nv_msg, ctx.author) try: await ctx.bot.wait_for("reaction_add", check=pred, timeout=60) except asyncio.TimeoutError: ctx.command.reset_cooldown(ctx) await self._clear_react(nv_msg) lock.release() return if not pred.result: with contextlib.suppress(discord.HTTPException): ctx.command.reset_cooldown(ctx) await nv_msg.edit( content=_("**{}** decides against visiting the negaverse... for now.").format( escape(ctx.author.display_name) ) ) lock.release() return await self._clear_react(nv_msg) percentage_offered = (offering / bal) * 100 min_roll = int(percentage_offered / 10) entry_roll = max(random.randint(max(1, min_roll), 20), 0) if admin_roll == -1 else admin_roll if entry_roll == 1: tax_mod = random.randint(4, 8) tax = round(bal / tax_mod) if tax > offering: loss = tax else: loss = offering offering_value += loss loss_state = True await bank.withdraw_credits(ctx.author, loss) entry_msg = _( "A swirling void slowly grows and you watch in horror as it rushes to " "wash over you, leaving you cold... and your coin pouch significantly lighter. " "The portal to the negaverse remains closed." ) lock.release() return await nv_msg.edit(content=entry_msg) else: entry_msg = _( "Shadowy hands reach out to take your offering from you and a swirling " "black void slowly grows and engulfs you, transporting you to the negaverse." ) await nv_msg.edit(content=entry_msg) await self._clear_react(nv_msg) await bank.withdraw_credits(ctx.author, offering) if nega_set: nega_member = nega negachar = _("The Almighty Nega-{c}").format(c=escape(nega_member.display_name)) else: nega_member = random.choice(ctx.message.guild.members) negachar = _("Nega-{c}").format(c=escape(nega_member.display_name)) nega_msg = await ctx.send( _("**{author}** enters the negaverse and meets **{negachar}**.").format( author=escape(ctx.author.display_name), negachar=negachar ) ) try: character = await Character.from_json(ctx, self.config, ctx.author, self._daily_bonus) except Exception as exc: log.exception("Error with the new character sheet", exc_info=exc) lock.release() ctx.command.reset_cooldown(ctx) return roll = random.randint(max(1, min_roll * 2), 50) if admin_roll == -1 else admin_roll if is_dev(nega_member): roll = -2 versus = random.randint(10, 60) xp_mod = random.randint(1, 10) daymult = self._daily_bonus.get(str(datetime.today().isoweekday()), 0) xp_won = int((offering / xp_mod)) xp_to_max = int((character.maxlevel + 1) ** 3.5) ten_percent = xp_to_max * 0.1 xp_won = ten_percent if xp_won > ten_percent else xp_won xp_won = int(xp_won * (min(max(random.randint(0, character.rebirths), 1), 50) / 100 + 1)) xp_won = int(xp_won * (character.gear_set_bonus.get("xpmult", 1) + daymult)) if roll == -2: looted = "" curr_balance = character.bal await bank.set_balance(ctx.author, 0) offering_value += curr_balance loss_string = _("all of their") loss_state = True items = await character.looted(how_many=max(int(10 - roll) // 2, 1)) if items: item_string = "\n".join([f"{v} x{i}" for v, i in items]) looted = box(f"{item_string}", lang="css") await self.config.user(ctx.author).set(await character.to_json(ctx, self.config)) loss_msg = _( ", losing {loss} {currency_name} as **{negachar}** rifled through their belongings." ).format(loss=loss_string, currency_name=currency_name, negachar=negachar) if looted: loss_msg += _(" **{negachar}** also stole the following items:\n\n{items}").format( items=looted, negachar=negachar ) await nega_msg.edit( content=_("{content}\n**{author}** fumbled and died to **{negachar}'s** savagery{loss_msg}").format( content=nega_msg.content, author=escape(ctx.author.display_name), negachar=negachar, loss_msg=loss_msg, ) ) ctx.command.reset_cooldown(ctx) elif roll < 10: loss = round(bal // 3) looted = "" curr_balance = character.bal try: await bank.withdraw_credits(ctx.author, loss) offering_value += loss loss_string = humanize_number(loss) except ValueError: await bank.set_balance(ctx.author, 0) offering_value += curr_balance loss_string = _("all of their") loss_state = True if character.bal < loss: items = await character.looted(how_many=max(int(10 - roll) // 2, 1)) if items: item_string = "\n".join([f"{v} {i}" for v, i in items]) looted = box(f"{item_string}", lang="css") await self.config.user(ctx.author).set(await character.to_json(ctx, self.config)) loss_msg = _( ", losing {loss} {currency_name} as **{negachar}** rifled through their belongings." ).format(loss=loss_string, currency_name=currency_name, negachar=negachar) if looted: loss_msg += _(" **{negachar}** also stole the following items:\n\n{items}").format( items=looted, negachar=negachar ) await nega_msg.edit( content=_("{content}\n**{author}** fumbled and died to **{negachar}'s** savagery{loss_msg}").format( content=nega_msg.content, author=escape(ctx.author.display_name), negachar=negachar, loss_msg=loss_msg, ) ) ctx.command.reset_cooldown(ctx) elif roll == 50 and versus < 50: await nega_msg.edit( content=_( "{content}\n**{author}** decapitated **{negachar}**. " "You gain {xp_gain} xp and take " "{offering} {currency_name} back from the shadowy corpse." ).format( content=nega_msg.content, author=escape(ctx.author.display_name), negachar=negachar, xp_gain=humanize_number(xp_won), offering=humanize_number(offering), currency_name=currency_name, ) ) with contextlib.suppress(Exception): lock.release() msg = await self._add_rewards(ctx, ctx.author, xp_won, offering, False) xp_won_final += xp_won offering_value += offering winning_state = True if msg: await smart_embed(ctx, msg, success=True) elif roll > versus: await nega_msg.edit( content=_( "{content}\n**{author}** " "{dice}({roll}) bravely defeated **{negachar}** {dice}({versus}). " "You gain {xp_gain} xp." ).format( dice=self.emojis.dice, content=nega_msg.content, author=escape(ctx.author.display_name), roll=roll, negachar=negachar, versus=versus, xp_gain=humanize_number(xp_won), ) ) with contextlib.suppress(Exception): lock.release() msg = await self._add_rewards(ctx, ctx.author, xp_won, 0, False) xp_won_final += xp_won offering_value += offering winning_state = True if msg: await smart_embed(ctx, msg, success=True) elif roll == versus: ctx.command.reset_cooldown(ctx) await nega_msg.edit( content=_( "{content}\n**{author}** {dice}({roll}) almost killed **{negachar}** {dice}({versus})." ).format( dice=self.emojis.dice, content=nega_msg.content, author=escape(ctx.author.display_name), roll=roll, negachar=negachar, versus=versus, ) ) else: loss = round(bal / (random.randint(10, 25))) curr_balance = character.bal looted = "" try: await bank.withdraw_credits(ctx.author, loss) offering_value += loss loss_string = humanize_number(loss) except ValueError: await bank.set_balance(ctx.author, 0) loss_string = _("all of their") offering_value += curr_balance loss_state = True if character.bal < loss: items = await character.looted(how_many=max(int(10 - roll) // 2, 1)) if items: item_string = "\n".join([f"{i} - {v}" for v, i in items]) looted = box(f"{item_string}", lang="css") await self.config.user(ctx.author).set(await character.to_json(ctx, self.config)) loss_msg = _(", losing {loss} {currency_name} as **{negachar}** looted their backpack.").format( loss=loss_string, currency_name=currency_name, negachar=negachar, ) if looted: loss_msg += _(" **{negachar}** also stole the following items:\n\n{items}").format( items=looted, negachar=negachar ) await nega_msg.edit( content=_( "**{author}** {dice}({roll}) was killed by **{negachar}** {dice}({versus}){loss_msg}" ).format( dice=self.emojis.dice, author=escape(ctx.author.display_name), roll=roll, negachar=negachar, versus=versus, loss_msg=loss_msg, ) ) ctx.command.reset_cooldown(ctx) finally: lock = self.get_lock(ctx.author) with contextlib.suppress(Exception): lock.release() try: character = await Character.from_json(ctx, self.config, ctx.author, self._daily_bonus) except Exception as exc: log.exception("Error with the new character sheet", exc_info=exc) else: changed = False if character.last_currency_check + 600 < time.time() or character.bal > character.last_known_currency: character.last_known_currency = await bank.get_balance(ctx.author) character.last_currency_check = time.time() changed = True if offering_value > 0: current_gold__losses_value = character.nega.get("gold__losses", 0) character.nega.update({"gold__losses": int(current_gold__losses_value + offering_value)}) changed = True if xp_won_final > 0: current_xp__earnings_value = character.nega.get("xp__earnings", 0) character.nega.update({"xp__earnings": current_xp__earnings_value + xp_won_final}) changed = True if winning_state is not False: current_wins_value = character.nega.get("wins", 0) character.nega.update({"wins": current_wins_value + 1}) changed = True if loss_state is not False: current_loses_value = character.nega.get("loses", 0) character.nega.update({"loses": current_loses_value + 1}) changed = True if changed: await self.config.user(ctx.author).set(await character.to_json(ctx, self.config))
def _replace(self, text_to_replace, replacement): replace = lambda x: x.replace(text_to_replace, replacement) return box(replace(self))
async def dblinfo(self, ctx, *, bot: Union[int, discord.Member, discord.User, None] = None): """ Show information of a chosen bot on discordbots.org. `[bot]`: Can be a mention or ID of a bot. """ key = await ctx.bot.db.api_tokens.get_raw("dbl", default=None) if key is None: return await ctx.send( _("Owner of this bot needs to set an API key first !")) if bot is None: return await ctx.send_help() if isinstance(bot, int): try: bot = await self.bot.fetch_user(bot) except discord.NotFound: return await ctx.send(str(bot) + _(" is not a Discord user.")) if not bot.bot: return await ctx.send( _("This is not a bot user, please try again with a bot.")) try: async with ctx.typing(): try: info = await self._get_data(ctx, bot=bot.id) if info is None: return stats = await self._get_data(ctx, endpoint="/stats", bot=bot.id) except TypeError: return emoji = (discord.utils.get(self.bot.emojis, id=392249976639455232) if self.bot.get_guild(264445053596991498) is not None else "`\N{WHITE HEAVY CHECK MARK}`") format_kwargs = { "description": (bold(_("Description:")) + box("\n{}\n").format(info["shortdesc"]) if info["tags"] else ""), "tags": (bold(_("Tags:")) + box("\n{}\n\n").format(", ".join(info["tags"])) if info["tags"] else ""), "if_cert": (bold(_("\nCertified !")) + f" {emoji}\n" if info["certifiedBot"] else "\n"), "prefix": (bold(_("Prefix:")) + " {}\n".format(info["prefix"]) if info.get("prefix", "") else ""), "lib": (bold(_("Library:")) + " {}\n".format(info["lib"]) if info.get("lib", "") else ""), "servs": (bold(_("Server count:")) + " {:,}\n".format(stats["server_count"]) if stats.get("server_count", "") else ""), "shards": (bold(_("Shard count:")) + " {:,}\n".format(stats["shard_count"]) if stats.get("shard_count", "") else ""), "m_votes": (bold(_("Monthly votes:")) + (" {:,}\n".format(info["monthlyPoints"]) if info.get("monthlyPoints", "") else "0\n")), "t_votes": (bold(_("Total votes:")) + (" {:,}\n".format( info["points"]) if info.get("points", "") else "0\n")), "owners": ( bold( _("Owner{}: ").format( "s" if len(info["owners"]) > 1 else "")) + ", ".join([ str((await self.bot.fetch_user(i))) for i in info["owners"] ]) + "\n" # Thanks Slime :ablobcatsipsweats: ), "approval_date": (bold(_("Approval date:")) + " {}\n\n".format(info["date"].replace("T", " ")[:-5])), "dbl_page": (_("[DBL Page]({})").format( f"https://discordbots.org/bot/{bot.id}")), "if_inv": (_(" • [Invitation link]({})").format( info["invite"]) if info["invite"] else ""), "if_supp": (_(" • [Support](https://discord.gg/{})").format( info["support"]) if info["support"] else ""), "if_gh": (_(" • [GitHub]({})").format(info["github"]) if info["github"] else ""), "if_wsite": (_(" • [Website]({})").format(info["website"]) if info["website"] else ""), } description = ( "{description}" "{tags}" "{if_cert}" "{prefix}" "{lib}" "{servs}{shards}" "{m_votes}" "{t_votes}" "{owners}" "{approval_date}" "{dbl_page}{if_inv}{if_supp}{if_gh}{if_wsite}").format( **format_kwargs) em = discord.Embed(color=(await ctx.embed_colour()), description=description) em.set_author( name=_("DBL Info about {}:").format(info["username"]), icon_url= "https://cdn.discordapp.com/emojis/393548388664082444.gif", ) em.set_thumbnail(url=bot.avatar_url_as(static_format="png")) return await ctx.send(embed=em) except Exception as error: return await ctx.send( _("Something went wrong when trying to get bot information.\n") + inline(str(error)))
def _squash(self): return box(self.replace(" ", ""))
async def print_cmdlist(self, ctx, cmdlist, inline=False): #TODO: Write a docstring once I figure out what this does if not cmdlist: await ctx.send("There are no padglobal commands yet") return prefix = ctx.prefix prefixes = defaultdict(int) for c in cmdlist: m = re.match(r'^([a-zA-Z]+)\d+$', c) if m: grp = m.group(1) prefixes[grp] = prefixes[grp] + 1 good_prefixes = [cmd for cmd, cnt in prefixes.items() if cnt > 1] prefix_to_suffix = defaultdict(list) prefix_to_other = defaultdict(list) i = 0 msg = PAD_CMD_HEADER.format(ctx.prefix) + "\n" if inline: for cmd in sorted([cmd for cmd in cmdlist.keys()]): msg += " {} : {}\n".format(cmd, cmdlist[cmd]) for page in pagify(msg): await ctx.author.send(box(page)) return for cmd in sorted([cmd for cmd in cmdlist.keys()]): m = re.match(r'^([a-zA-Z]+)(\d+)$', cmd) if m: prefix = m.group(1) if prefix in good_prefixes: suffix = m.group(2) prefix_to_suffix[prefix].append(suffix) continue should_skip = False for good_prefix in good_prefixes: if cmd.startswith(good_prefix): prefix_to_other[prefix].append(cmd) should_skip = True break if should_skip: continue msg += " {}{}\n".format(ctx.prefix, cmd) if prefix_to_suffix: msg += "\nThe following commands are indexed:\n" for prefix in sorted(prefix_to_suffix.keys()): msg += " {}{}[n]:\n ".format(ctx.prefix, prefix) for suffix in sorted(map(int, prefix_to_suffix[prefix])): msg += " {}{}".format(prefix, suffix) if len(prefix_to_other[prefix]): msg += "\n" for cmd in sorted(prefix_to_other[prefix]): msg += " {}{}".format(ctx.prefix, cmd) msg += "\n\n" for page in pagify(msg): await ctx.author.send(box(page))
def _remove(self, remove): return box(self.replace(remove, ""))
async def godville(self, ctx, *, godname: str): """Get data about godville's god by name""" async with self.session.get("{}/{}/{}".format( BASE_API, godname.casefold(), await self.api_by_god(godname.casefold(), "godville") or "")) as sg: if sg.status == 404: await ctx.send( chat.error( "404 — Sorry, but there is nothing here\nCheck god name and try again" )) return elif sg.status != 200: await ctx.send( chat.error( "Something went wrong. Server returned {}.".format( sg.status))) return profile = await sg.json() profile = GodvilleUser(profile) text_header = "{} и его {}\n{}\n" \ .format(chat.bold(profile.god), chat.bold(profile.name), chat.italics(chat.escape(profile.motto.strip(), formatting=True)) if profile.motto else chat.inline("Здесь ничего нет")) if profile.arena_is_in_fight: text_header += "В сражении: {}\n".format(profile.fight_type_rus) if profile.town: text_header += "В городе: {}\n".format(profile.town) if profile.need_update: text_header += chat.bold("! УСТАРЕВШАЯ ИНФОРМАЦИЯ !") + "\n" text = "" pet = "" times = "" if profile.gold_approximately: text += "Золота: {}\n".format(profile.gold_approximately) if profile.distance: text += "Столбов от столицы: {}\n".format(profile.distance) if profile.quest_progress: text += "Задание: {} ({}%)\n".format(profile.quest, profile.quest_progress) if profile.experience: text += "Опыта до следующего уровня: {}%\n".format( profile.experience) text += "Уровень: {}\n".format(profile.level) if profile.godpower: text += "Праны: {}/{}\n".format( profile.godpower, 200 if profile.savings_date else 100) text += "Характер: {}\n".format(profile.alignment) text += "Пол: {}\n".format(profile.gender) text += "Побед/Поражений: {}/{}\n".format(profile.arena_won, profile.arena_lost) text += "Гильдия: {} ({})\n".format(profile.clan, profile.clan_position) if profile.clan \ else "Гильдия: Не состоит\n" text += "Кирпичей: {} ({}%)\n".format(profile.bricks, profile.bricks / 10) if profile.inventory: text += "Инвентарь: {}/{} ({}%)\n".format( profile.inventory, profile.inventory_max, int(profile.inventory / profile.inventory_max * 100)) else: text += "Вместимость инвентаря: {}\n".format(profile.inventory_max) if profile.health: text += "Здоровье: {}/{} ({}%)\n".format( profile.health, profile.health_max, int(profile.health / profile.health_max * 100)) else: text += "Максимум здоровья: {}\n".format(profile.health_max) if profile.ark_male: text += "Тварей ♂: {} ({}%)\n".format(profile.ark_male, profile.ark_male / 10) if profile.ark_female: text += "Тварей ♀: {} ({}%)\n".format(profile.ark_female, profile.ark_female / 10) if profile.savings: text += "Сбережений: {}\n".format(profile.savings) if profile.trading_level: text += "Уровень торговли: {}\n".format(profile.trading_level) if profile.wood: text += "Поленьев: {} ({}%)\n".format(profile.wood, profile.wood / 10) # private (api only) if profile.diary_last: text += "Дневник: {}\n".format(profile.diary_last) if profile.activatables: text += "Активируемое в инвентаре: {}\n".format(", ".join( profile.activatables)) if profile.aura: text += "Аура: {}\n".format(profile.aura) # pet if profile.pet.name: pet += "Имя: {}\n".format(profile.pet.name) pet += "Уровень: {}\n".format(profile.pet.level or "Без уровня") if profile.pet.type: pet += "Тип: {}\n".format(profile.pet.type) if profile.pet.wounded: pet += "❌ — Контужен" # times if profile.temple_date: times += "Храм достроен: {}\n".format( profile.date_string("temple")) if profile.ark_date: times += "Ковчег достроен: {}\n".format(profile.date_string("ark")) if profile.savings_date: times += "Пенсия собрана: {}\n".format( profile.date_string("savings")) finaltext = "" finaltext += text_header finaltext += chat.box(text) if pet: finaltext += "Питомец:\n" finaltext += chat.box(pet) if times: finaltext += chat.box(times) await ctx.send(finaltext)
async def welcomeset(self, ctx: commands.Context): """Change Welcome settings.""" await ctx.trigger_typing() if ctx.invoked_subcommand is None: guild = ctx.guild c = await self.config.guild(guild).all() channel = await self.__get_channel(ctx.guild) j = c["join"] jw = j["whisper"] v = c["leave"] b = c["ban"] u = c["unban"] msg = box( ( " Enabled: {}\n" " Channel: {}\n" " Join:\n" " Enabled: {}\n" " Delete previous: {}\n" " Whisper:\n" " State: {}\n" " Message: {}\n" " Messages: {}; do '{prefix}welcomeset join msg list' for a list\n" " Bot message: {}\n" " Leave:\n" " Enabled: {}\n" " Delete previous: {}\n" " Messages: {}; do '{prefix}welcomeset leave msg list' for a list\n" " Ban:\n" " Enabled: {}\n" " Delete previous: {}\n" " Messages: {}; do '{prefix}welcomeset ban msg list' for a list\n" " Unban:\n" " Enabled: {}\n" " Delete previous: {}\n" " Messages: {}; do '{prefix}welcomeset unban msg list' for a list\n" "" ).format( c["enabled"], channel, j["enabled"], j["delete"], jw["state"], jw["message"], len(j["messages"]), j["bot"], v["enabled"], v["delete"], len(v["messages"]), b["enabled"], b["delete"], len(b["messages"]), u["enabled"], u["delete"], len(u["messages"]), prefix=ctx.prefix, ), "Current Welcome settings:", ) await ctx.send(msg)