async def mywarnings(self, ctx: commands.Context): """List warnings for yourself.""" user = ctx.author msg = "" member_settings = self.config.member(user) async with member_settings.warnings() as user_warnings: if not user_warnings.keys(): # no warnings for the user await ctx.send(_("You have no warnings!")) else: for key in user_warnings.keys(): mod_id = user_warnings[key]["mod"] if mod_id == 0xDE1: mod = _("Deleted Moderator") else: bot = ctx.bot mod = bot.get_user(mod_id) or _( "Unknown Moderator ({})").format(mod_id) msg += _( "{num_points} point warning {reason_name} issued by {user} for " "{description}\n").format( num_points=user_warnings[key]["points"], reason_name=key, user=mod, description=user_warnings[key]["description"], ) await ctx.send_interactive( pagify(msg, shorten_by=58), box_lang=_("Warnings for {user}").format(user=user), )
async def listcases(self, ctx: commands.Context, *, member: Union[discord.Member, int]): """List cases for the specified member.""" async with ctx.typing(): try: if isinstance(member, int): cases = await modlog.get_cases_for_member(bot=ctx.bot, guild=ctx.guild, member_id=member) else: cases = await modlog.get_cases_for_member(bot=ctx.bot, guild=ctx.guild, member=member) except discord.NotFound: return await ctx.send(_("That user does not exist.")) except discord.HTTPException: return await ctx.send( _("Something unexpected went wrong while fetching that user by ID." )) if not cases: return await ctx.send(_("That user does not have any cases.")) rendered_cases = [] message = "" for case in cases: message += _("{case}\n**Timestamp:** {timestamp}\n\n").format( case=await case.message_content(embed=False), timestamp=datetime.utcfromtimestamp( case.created_at).strftime("%Y-%m-%d %H:%M:%S UTC"), ) for page in pagify(message, ["\n\n", "\n"], priority=True): rendered_cases.append(page) await menu(ctx, rendered_cases, DEFAULT_CONTROLS)
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 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 cc_list(self, ctx: commands.Context): """List all available custom commands. The list displays a preview of each command's response, with markdown escaped and newlines replaced with spaces. """ cc_dict = await CommandObj.get_commands(self.config.guild(ctx.guild)) if not cc_dict: await ctx.send( _( "There are no custom commands in this server." " Use `{command}` to start adding some." ).format(command=f"{ctx.clean_prefix}customcom create") ) return results = self.prepare_command_list(ctx, sorted(cc_dict.items(), key=lambda t: t[0])) if await ctx.embed_requested(): # We need a space before the newline incase the CC preview ends in link (GH-2295) content = " \n".join(map("**{0[0]}** {0[1]}".format, results)) pages = list(pagify(content, page_length=1024)) embed_pages = [] for idx, page in enumerate(pages, start=1): embed = discord.Embed( title=_("Custom Command List"), description=page, colour=await ctx.embed_colour(), ) embed.set_footer(text=_("Page {num}/{total}").format(num=idx, total=len(pages))) embed_pages.append(embed) await menus.menu(ctx, embed_pages, menus.DEFAULT_CONTROLS) else: content = "\n".join(map("{0[0]:<12} : {0[1]}".format, results)) pages = list(map(box, pagify(content, page_length=2000, shorten_by=10))) await menus.menu(ctx, pages, menus.DEFAULT_CONTROLS)
async def message_forwarder( *, destination: discord.abc.Messageable, content: str = None, embed=None, files: Optional[List[discord.File]] = None, ) -> List[discord.Message]: """ This does the actual sending, use this instead of a full tunnel if you are using command initiated reactions instead of persistent event based ones Parameters ---------- destination: discord.abc.Messageable Where to send content: str The message content embed: discord.Embed The embed to send files: Optional[List[discord.File]] A list of files to send. Returns ------- List[discord.Message] The messages sent as a result. Raises ------ discord.Forbidden see `discord.abc.Messageable.send` discord.HTTPException see `discord.abc.Messageable.send` """ rets = [] if content: for page in pagify(content): rets.append(await destination.send(page, files=files, embed=embed)) if files: del files if embed: del embed elif embed or files: rets.append(await destination.send(files=files, embed=embed)) return rets
async def _channel_list(self, ctx: commands.Context): """Send a list of the channel's filtered words.""" channel = ctx.channel author = ctx.author word_list = await self.config.channel(channel).filter() if not word_list: await ctx.send( _("There is no current words setup to be filtered in this channel." )) return words = humanize_list(word_list) words = _("Filtered in this channel:") + "\n\n" + words try: for page in pagify(words, delims=[" ", "\n"], shorten_by=8): await author.send(page) except discord.Forbidden: await ctx.send(_("I can't send direct messages to you."))
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 command_audiostats(self, ctx: commands.Context): """Audio stats.""" server_num = len(lavalink.active_players()) total_num = len(lavalink.all_players()) msg = "" async for p in AsyncIter(lavalink.all_players()): connect_start = p.fetch("connect") connect_dur = self.get_time_string( int((datetime.datetime.utcnow() - connect_start).total_seconds())) try: if not p.current: raise AttributeError current_title = await self.get_track_description( p.current, self.local_folder_current_path) msg += "{} [`{}`]: {}\n".format(p.channel.guild.name, connect_dur, current_title) except AttributeError: msg += "{} [`{}`]: **{}**\n".format(p.channel.guild.name, connect_dur, _("Nothing playing.")) if total_num == 0: return await self.send_embed_msg( ctx, title=_("Not connected anywhere.")) servers_embed = [] pages = 1 for page in pagify(msg, delims=["\n"], page_length=1500): em = discord.Embed( colour=await ctx.embed_colour(), title=_("Playing in {num}/{total} servers:").format( num=humanize_number(server_num), total=humanize_number(total_num)), description=page, ) em.set_footer(text=_("Page {}/{}").format( humanize_number(pages), humanize_number((math.ceil(len(msg) / 1500))))) pages += 1 servers_embed.append(em) await menu(ctx, servers_embed, DEFAULT_CONTROLS)
async def streamalert_list(self, ctx: commands.Context): """List all active stream alerts in this server.""" streams_list = defaultdict(list) guild_channels_ids = [c.id for c in ctx.guild.channels] msg = _("Active alerts:\n\n") for stream in self.streams: for channel_id in stream.channels: if channel_id in guild_channels_ids: streams_list[channel_id].append(stream.name.lower()) if not streams_list: await ctx.send(_("There are no active alerts in this server.")) return for channel_id, streams in streams_list.items(): channel = ctx.guild.get_channel(channel_id) msg += "** - #{}**\n{}\n".format(channel, ", ".join(streams)) for page in pagify(msg): await ctx.send(page)
async def warnings(self, ctx: commands.Context, user: Union[discord.Member, int]): """List the warnings for the specified user.""" try: userid: int = user.id except AttributeError: userid: int = user user = ctx.guild.get_member(userid) user = user or namedtuple("Member", "id guild")(userid, ctx.guild) msg = "" member_settings = self.config.member(user) async with member_settings.warnings() as user_warnings: if not user_warnings.keys(): # no warnings for the user await ctx.send(_("That user has no warnings!")) else: for key in user_warnings.keys(): mod_id = user_warnings[key]["mod"] if mod_id == 0xDE1: mod = _("Deleted Moderator") else: bot = ctx.bot mod = bot.get_user(mod_id) or _( "Unknown Moderator ({})").format(mod_id) msg += _( "{num_points} point warning {reason_name} issued by {user} for " "{description}\n").format( num_points=user_warnings[key]["points"], reason_name=key, user=mod, description=user_warnings[key]["description"], ) await ctx.send_interactive( pagify(msg, shorten_by=58), box_lang=_("Warnings for {user}").format(user=user), )
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)
command_name=command_name, author=author, created_at=cmd["created_at"], type=_type ) 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 voice_mute( self, ctx: commands.Context, users: commands.Greedy[discord.Member], *, time_and_reason: MuteTime = {}, ): """Mute a user in their current voice channel. `<users...>` is a space separated list of usernames, ID's, or mentions. `[time_and_reason]` is the time to mute for and reason. Time is any valid time length such as `30 minutes` or `2 days`. If nothing is provided the mute will use the set default time or indefinite if not set. Examples: `[p]voicemute @member1 @member2 spam 5 hours` `[p]voicemute @member1 3 days`""" if not users: return await ctx.send_help() if ctx.me in users: return await ctx.send(_("You cannot mute me.")) if ctx.author in users: return await ctx.send(_("You cannot mute yourself.")) async with ctx.typing(): success_list = [] issue_list = [] for user in users: user_voice_state = user.voice can_move, perm_reason = await self._voice_perm_check( ctx, user_voice_state, mute_members=True, manage_permissions=True) if not can_move: issue_list.append((user, perm_reason)) continue duration = time_and_reason.get("duration", None) reason = time_and_reason.get("reason", None) time = "" until = None if duration: until = datetime.now(timezone.utc) + duration time = _(" for {duration}").format( duration=humanize_timedelta(timedelta=duration)) else: default_duration = await self.config.guild( ctx.guild).default_time() if default_duration: until = datetime.now( timezone.utc) + timedelta(seconds=default_duration) time = _(" for {duration}").format( duration=humanize_timedelta(timedelta=timedelta( seconds=default_duration))) guild = ctx.guild author = ctx.author channel = user_voice_state.channel audit_reason = get_audit_reason(author, reason, shorten=True) success = await self.channel_mute_user(guild, channel, author, user, until, audit_reason) if success["success"]: if "reason" in success and success["reason"]: issue_list.append((user, success["reason"])) else: success_list.append(user) await modlog.create_case( self.bot, guild, ctx.message.created_at.replace(tzinfo=timezone.utc), "vmute", user, author, reason, until=until, channel=channel, ) await self._send_dm_notification(user, author, guild, _("Voice mute"), reason, duration) async with self.config.member(user).perms_cache() as cache: cache[channel.id] = success["old_overs"] else: issue_list.append((user, success["reason"])) if success_list: msg = _("{users} has been muted in this channel{time}.") if len(success_list) > 1: msg = _("{users} have been muted in this channel{time}.") await ctx.send( msg.format(users=humanize_list([f"{u}" for u in success_list]), time=time)) if issue_list: msg = _("The following users could not be muted\n") for user, issue in issue_list: msg += f"{user}: {issue}\n" await ctx.send_interactive(pagify(msg))
async def unmute_voice( self, ctx: commands.Context, users: commands.Greedy[discord.Member], *, reason: Optional[str] = None, ): """Unmute a user in their current voice channel. `<users...>` is a space separated list of usernames, ID's, or mentions. `[reason]` is the reason for the unmute.""" if not users: return await ctx.send_help() if ctx.me in users: return await ctx.send(_("You cannot unmute me.")) if ctx.author in users: return await ctx.send(_("You cannot unmute yourself.")) async with ctx.typing(): issue_list = [] success_list = [] for user in users: user_voice_state = user.voice can_move, perm_reason = await self._voice_perm_check( ctx, user_voice_state, mute_members=True, manage_permissions=True) if not can_move: issue_list.append((user, perm_reason)) continue guild = ctx.guild author = ctx.author channel = user_voice_state.channel audit_reason = get_audit_reason(author, reason, shorten=True) success = await self.channel_unmute_user( guild, channel, author, user, audit_reason) if success["success"]: if "reason" in success and success["reason"]: issue_list.append((user, success["reason"])) else: success_list.append(user) await modlog.create_case( self.bot, guild, ctx.message.created_at.replace(tzinfo=timezone.utc), "vunmute", user, author, reason, until=None, channel=channel, ) await self._send_dm_notification(user, author, guild, _("Voice unmute"), reason) else: issue_list.append((user, success["reason"])) if success_list: if channel.id in self._channel_mutes and self._channel_mutes[ channel.id]: await self.config.channel(channel).muted_users.set( self._channel_mutes[channel.id]) else: await self.config.channel(channel).muted_users.clear() await ctx.send( _("{users} unmuted in this channel.").format( users=humanize_list([f"{u}" for u in success_list]))) if issue_list: msg = _("The following users could not be unmuted\n") for user, issue in issue_list: msg += f"{user}: {issue}\n" await ctx.send_interactive(pagify(msg))