From ad1fca5784895b655d03cc64879fb3b2b4883d49 Mon Sep 17 00:00:00 2001 From: Sourcery AI <> Date: Sat, 20 May 2023 12:04:35 +0000 Subject: [PATCH] 'Refactored by Sourcery' --- bot.py | 5 +- cli/scripts/help_helper.py | 8 +- cogs/backend/handle/errors/_error_tools.py | 10 +- cogs/backend/handle/events/_event_tools.py | 44 +++--- cogs/botlists/major/discordbotlist.py | 5 +- cogs/ecosystem/bot_info.py | 2 +- cogs/ecosystem/config.py | 131 +++++++++-------- cogs/ecosystem/dev.py | 9 +- cogs/ecosystem/help.py | 7 +- cogs/github/base/org.py | 12 +- cogs/github/base/repo/_list_plugin.py | 23 ++- cogs/github/base/repo/repo.py | 16 ++- cogs/github/base/user.py | 15 +- cogs/github/numbered/commits.py | 31 ++-- cogs/github/numbered/gist.py | 18 ++- cogs/github/other/loc.py | 2 +- cogs/github/other/logs.py | 4 +- cogs/github/other/snippets/_snippet_tools.py | 10 +- cogs/github/other/snippets/snippets.py | 5 +- cogs/python/pypi.py | 10 +- cogs/rust/crates.py | 12 +- lib/api/crates.py | 4 +- lib/api/github.py | 42 +++--- lib/api/pypi.py | 16 ++- lib/manager.py | 135 ++++++++++-------- lib/structs/db/user_collection.py | 6 +- lib/structs/dicts/case_insensitive_dict.py | 10 +- lib/structs/dicts/max_age_dict.py | 8 +- .../discord/components/github_lines_view.py | 7 +- lib/structs/discord/components/view_file.py | 5 +- lib/structs/discord/embed.py | 13 +- lib/utils/decorators.py | 2 +- migrations/2021-03-13_uid_to_oid.py | 9 +- .../2022-02-09_new_release_feed_structure.py | 5 +- 34 files changed, 356 insertions(+), 285 deletions(-) diff --git a/bot.py b/bot.py index 0b7375a7..3531e8f7 100644 --- a/bot.py +++ b/bot.py @@ -62,10 +62,7 @@ async def global_check(ctx: GitBotContext) -> bool: if not isinstance(ctx.channel, discord.DMChannel) and ctx.guild.unavailable: return False - if ctx.author.id in bot.user_id_blacklist: - return False - - return True + return ctx.author.id not in bot.user_id_blacklist @bot.before_invoke diff --git a/cli/scripts/help_helper.py b/cli/scripts/help_helper.py index 5ce04969..b079f21e 100644 --- a/cli/scripts/help_helper.py +++ b/cli/scripts/help_helper.py @@ -84,9 +84,11 @@ def run_help_helper(debug: bool = False): for command_name in commands: underscored_name: str = command_name.replace(' ', '_') if underscored_name not in LOCALE['help']['commands']: - command_data = prompt(command_name, - is_group=(raw_command_list_json.count('"' + command_name + ' ') > 1 - and ' ' not in command_name)) + command_data = prompt( + command_name, + is_group=raw_command_list_json.count(f'"{command_name} ') > 1 + and ' ' not in command_name, + ) while True: if click.confirm(click.style('All good?', blink=True, fg='yellow') + '\n' + '\n'.join([click.style(f'{k}: {v}', fg='cyan') diff --git a/cogs/backend/handle/errors/_error_tools.py b/cogs/backend/handle/errors/_error_tools.py index 082a93fd..b86ba30d 100644 --- a/cogs/backend/handle/errors/_error_tools.py +++ b/cogs/backend/handle/errors/_error_tools.py @@ -7,7 +7,11 @@ def silenced(ctx: GitBotContext, error) -> bool: - return bool(getattr(ctx, f'__silence_{ctx.bot.mgr.to_snake_case(error.__class__.__name__)}_error__', False)) + return getattr( + ctx, + f'__silence_{ctx.bot.mgr.to_snake_case(error.__class__.__name__)}_error__', + False, + ) async def respond_to_command_doesnt_exist(ctx: GitBotContext, error: commands.CommandNotFound) -> discord.Message: @@ -39,10 +43,10 @@ async def log_error_in_discord(ctx: GitBotContext, error: Exception) -> Optional inline=False) elif isinstance(error, commands.CommandNotFound): embed: GitBotEmbed = GitBotEmbed( - color=0x0384fc, + color=0x0384FC, title='Nonexistent command!', description=f'```{(error := str(error))}```', - footer='Closest existing command: ' + closest_existing_command_from_error(ctx.bot, error) + footer=f'Closest existing command: {closest_existing_command_from_error(ctx.bot, error)}', ) else: return diff --git a/cogs/backend/handle/events/_event_tools.py b/cogs/backend/handle/events/_event_tools.py index c71a0ca1..9cd074f8 100644 --- a/cogs/backend/handle/events/_event_tools.py +++ b/cogs/backend/handle/events/_event_tools.py @@ -68,27 +68,29 @@ async def handle_codeblock_message(ctx: GitBotContext) -> Optional[discord.Messa @commands.cooldown(3, 20, commands.BucketType.guild) @commands.max_concurrency(10, wait=True) async def resolve_url_command(ctx: GitBotContext) -> Optional[discord.Message]: - if (await ctx.bot.mgr.get_autoconv_config(ctx)).get('gh_url') and (cmd_data := await ctx.bot.mgr.get_link_reference(ctx)): - ctx.bot.logger.debug('Invoking command(s) "%s" with kwargs: %s', str(cmd_data.command), str(cmd_data.kwargs)) - ctx.__autoinvoked__ = True - if isinstance(cmd_data.command, commands.Command): - return await ctx.invoke(cmd_data.command, **cmd_data.kwargs) - else: - nonce: int = id(ctx) - for command, kwargs in zip(cmd_data.command, cmd_data.kwargs): - ctx.bot.logger.debug('Running output checks with nonce: %d for command "%s"', nonce, str(command)) - ctx.send = functools.partial(ctx.send, nonce=nonce) - await ctx.invoke(command, **kwargs) - try: - message = await ctx.bot.wait_for('message', - check=lambda msg: (msg.channel.id == ctx.channel.id - and msg.author.id == ctx.bot.user.id), - timeout=1.5) - if message.nonce == nonce: - return message - continue - except Exception: # noqa - we really don't care - continue + if not (await ctx.bot.mgr.get_autoconv_config(ctx)).get('gh_url') or not ( + cmd_data := await ctx.bot.mgr.get_link_reference(ctx) + ): + return + ctx.bot.logger.debug('Invoking command(s) "%s" with kwargs: %s', str(cmd_data.command), str(cmd_data.kwargs)) + ctx.__autoinvoked__ = True + if isinstance(cmd_data.command, commands.Command): + return await ctx.invoke(cmd_data.command, **cmd_data.kwargs) + nonce: int = id(ctx) + for command, kwargs in zip(cmd_data.command, cmd_data.kwargs): + ctx.bot.logger.debug('Running output checks with nonce: %d for command "%s"', nonce, str(command)) + ctx.send = functools.partial(ctx.send, nonce=nonce) + await ctx.invoke(command, **kwargs) + try: + message = await ctx.bot.wait_for('message', + check=lambda msg: (msg.channel.id == ctx.channel.id + and msg.author.id == ctx.bot.user.id), + timeout=1.5) + if message.nonce == nonce: + return message + continue + except Exception: # noqa - we really don't care + continue @silence_errors diff --git a/cogs/botlists/major/discordbotlist.py b/cogs/botlists/major/discordbotlist.py index 9bcc27f2..f214cbb9 100644 --- a/cogs/botlists/major/discordbotlist.py +++ b/cogs/botlists/major/discordbotlist.py @@ -11,10 +11,7 @@ def __init__(self, bot: GitBot): @tasks.loop(minutes=30) async def post_dblst_stats(self) -> None: - async with self.bot.session.post(f'https://discordbotlist.com/api/v1/bots/{self.bot.user.id}/stats', - json={'guilds': len(self.bot.guilds), - 'users': int(sum([g.member_count for g in self.bot.guilds]))}, - headers={'Content-Type': 'application/json', 'Authorization': self.token}) as res: + async with self.bot.session.post(f'https://discordbotlist.com/api/v1/bots/{self.bot.user.id}/stats', json={'guilds': len(self.bot.guilds), 'users': int(sum(g.member_count for g in self.bot.guilds))}, headers={'Content-Type': 'application/json', 'Authorization': self.token}) as res: if res.status != 200: res = await res.json() self.bot.logger.error('Discord Bot List API error: %s', str(res)) diff --git a/cogs/ecosystem/bot_info.py b/cogs/ecosystem/bot_info.py index 417ac808..da25ff2d 100644 --- a/cogs/ecosystem/bot_info.py +++ b/cogs/ecosystem/bot_info.py @@ -112,7 +112,7 @@ async def vote_command(self, ctx: GitBotContext) -> None: async def stats_command(self, ctx: GitBotContext) -> None: ctx.fmt.set_prefix('stats') embed: discord.Embed = discord.Embed(color=self.bot.mgr.c.rounded) - users: int = sum([x.member_count for x in self.bot.guilds]) + users: int = sum(x.member_count for x in self.bot.guilds) memory: str = f'**{process.memory_info()[0] / 2. ** 30:.3f}GB** RAM' # memory use in GB... I think cpu: str = f'**{psutil.cpu_percent()}%** CPU,' embed.add_field(name=f"{self.bot.mgr.e.stats} {ctx.lp.title}", value=ctx.lp.body, diff --git a/cogs/ecosystem/config.py b/cogs/ecosystem/config.py index 43d4ec4f..65752435 100644 --- a/cogs/ecosystem/config.py +++ b/cogs/ecosystem/config.py @@ -25,10 +25,20 @@ def construct_release_feed_list(ctx: GitBotContext, rf: ReleaseFeed) -> str: item: str = '' if rf else ctx.l.generic.nonexistent.release_feed for rfi in rf: m: str = '' if not rfi.get('mention') else ' - ' + ctx.bot.mgr.release_feed_mention_to_actual(rfi['mention']) - item += ctx.bot.mgr.e.square + ' ' + f'<#{rfi["cid"]}>{m}\n' + \ - ('\n'.join([f'⠀⠀- [`{rfr["name"]}`](https://github.com/{rfr["name"]})' - for rfr in rfi['repos']]) if rfi['repos'] - else f'⠀⠀- {ctx.l.config.show.feed.no_repos}') + '\n' + item += ( + f'{ctx.bot.mgr.e.square} ' + + f'<#{rfi["cid"]}>{m}\n' + + ( + '\n'.join( + [ + f'⠀⠀- [`{rfr["name"]}`](https://github.com/{rfr["name"]})' + for rfr in rfi['repos'] + ] + ) + if rfi['repos'] + else f'⠀⠀- {ctx.l.config.show.feed.no_repos}' + ) + ) + '\n' return item @staticmethod @@ -59,52 +69,53 @@ async def config_command_group(self, ctx: GitBotContext) -> None: @config_command_group.group(name='show', aliases=['s']) @commands.cooldown(5, 30, commands.BucketType.user) async def config_show_command_group(self, ctx: GitBotContext) -> None: - if ctx.invoked_subcommand is None: - ctx.fmt.set_prefix('config show base') - user: GitBotUser = await self.bot.mgr.db.users.find_one({'_id': ctx.author.id}) or {} - guild: Optional[GitBotGuild] = None - if not isinstance(ctx.channel, discord.DMChannel): - guild: Optional[GitBotGuild] = await self.bot.mgr.db.guilds.find_one({'_id': ctx.guild.id}) - if not user and guild is None or ((guild and len(guild) == 1) and not user): - await ctx.error(ctx.l.generic.nonexistent.qa) - return - lang: str = ctx.fmt('accessibility list locale', f'`{ctx.l.meta.localized_name.capitalize()}`') - user_str, org, repo = (ctx.fmt(f'qa list {item}', self.bot.mgr.to_github_hyperlink(user[item], True) if item in user else - f'`{ctx.l.config.show.base.item_not_set}`') for item in ('user', 'org', 'repo')) - accessibility: list = ctx.l.config.show.base.accessibility.heading + '\n' + '\n'.join([lang]) - qa: list = ctx.l.config.show.base.qa.heading + '\n' + '\n'.join([user_str, org, repo]) - guild_str: str = '' - if not isinstance(ctx.channel, discord.DMChannel): - feed: str = ctx.l.config.show.base.guild.list.feed + '\n' + '\n'.join([f'{self.bot.mgr.e.square} <#{rfi["cid"]}>' - for rfi in guild['feed']]) \ + if ctx.invoked_subcommand is not None: + return + ctx.fmt.set_prefix('config show base') + user: GitBotUser = await self.bot.mgr.db.users.find_one({'_id': ctx.author.id}) or {} + guild: Optional[GitBotGuild] = None + if not isinstance(ctx.channel, discord.DMChannel): + guild: Optional[GitBotGuild] = await self.bot.mgr.db.guilds.find_one({'_id': ctx.guild.id}) + if not user and guild is None or ((guild and len(guild) == 1) and not user): + await ctx.error(ctx.l.generic.nonexistent.qa) + return + lang: str = ctx.fmt('accessibility list locale', f'`{ctx.l.meta.localized_name.capitalize()}`') + user_str, org, repo = (ctx.fmt(f'qa list {item}', self.bot.mgr.to_github_hyperlink(user[item], True) if item in user else + f'`{ctx.l.config.show.base.item_not_set}`') for item in ('user', 'org', 'repo')) + accessibility: list = ctx.l.config.show.base.accessibility.heading + '\n' + '\n'.join([lang]) + qa: list = ctx.l.config.show.base.qa.heading + '\n' + '\n'.join([user_str, org, repo]) + guild_str: str = '' + if not isinstance(ctx.channel, discord.DMChannel): + feed: str = ctx.l.config.show.base.guild.list.feed + '\n' + '\n'.join([f'{self.bot.mgr.e.square} <#{rfi["cid"]}>' + for rfi in guild['feed']]) \ if (guild and guild.get('feed')) else f'{ctx.l.config.show.base.guild.list.feed}' \ f' `{ctx.l.config.show.base.item_not_configured}`' - ctx.fmt.set_prefix('+guild list autoconv') - if not guild: - ac: AutomaticConversionSettings = self.bot.mgr.env.autoconv_default - else: - ac: AutomaticConversionSettings = {k: (v if k not in (_ac := guild.get('autoconv', {})) - else _ac[k]) for k, v in self.bot.mgr.env.autoconv_default.items()} - codeblock: str = ctx.fmt('codeblock', - f'`{ctx.l.enum.generic.switch[str(ac["codeblock"])]}`') - lines: str = ctx.fmt('gh_lines', - f'`{ctx.l.enum.autoconv.gh_lines[str(ac["gh_lines"])]}`') - url: str = ctx.fmt('gh_url', f'`{ctx.l.enum.generic.switch[str(ac["gh_url"])]}`') - autoconv: str = (ctx.l.config.show.base.guild.list.autoconv.heading + '\n' - + '\n'.join([f'{self.bot.mgr.e.square} {aci}' for aci in [codeblock, url, lines]])) - guild_str: str = ctx.l.config.show.base.guild.heading + '\n' + '\n'.join([autoconv, feed]) - shortest_heading_len: int = min(map(len, [ctx.l.config.show.base.accessibility.heading, - ctx.l.config.show.base.guild.heading, - ctx.l.config.show.base.qa.heading])) - linebreak: str = f'\n{self.bot.mgr.gen_separator_line(shortest_heading_len)}\n' - embed = discord.Embed( - color=self.bot.mgr.c.discord.blurple, - title=f"{self.bot.mgr.e.github} {ctx.l.config.show.base.title}", - description=f"{accessibility}{linebreak}{qa}{linebreak if guild_str else ''}{guild_str}" - ) - if guild: - embed.set_footer(text=ctx.fmt('!config show base footer', 'git config show feed')) - await ctx.send(embed=embed) + ctx.fmt.set_prefix('+guild list autoconv') + if not guild: + ac: AutomaticConversionSettings = self.bot.mgr.env.autoconv_default + else: + ac: AutomaticConversionSettings = {k: (v if k not in (_ac := guild.get('autoconv', {})) + else _ac[k]) for k, v in self.bot.mgr.env.autoconv_default.items()} + codeblock: str = ctx.fmt('codeblock', + f'`{ctx.l.enum.generic.switch[str(ac["codeblock"])]}`') + lines: str = ctx.fmt('gh_lines', + f'`{ctx.l.enum.autoconv.gh_lines[str(ac["gh_lines"])]}`') + url: str = ctx.fmt('gh_url', f'`{ctx.l.enum.generic.switch[str(ac["gh_url"])]}`') + autoconv: str = (ctx.l.config.show.base.guild.list.autoconv.heading + '\n' + + '\n'.join([f'{self.bot.mgr.e.square} {aci}' for aci in [codeblock, url, lines]])) + guild_str: str = ctx.l.config.show.base.guild.heading + '\n' + '\n'.join([autoconv, feed]) + shortest_heading_len: int = min(map(len, [ctx.l.config.show.base.accessibility.heading, + ctx.l.config.show.base.guild.heading, + ctx.l.config.show.base.qa.heading])) + linebreak: str = f'\n{self.bot.mgr.gen_separator_line(shortest_heading_len)}\n' + embed = discord.Embed( + color=self.bot.mgr.c.discord.blurple, + title=f"{self.bot.mgr.e.github} {ctx.l.config.show.base.title}", + description=f"{accessibility}{linebreak}{qa}{linebreak if guild_str else ''}{guild_str}" + ) + if guild: + embed.set_footer(text=ctx.fmt('!config show base footer', 'git config show feed')) + await ctx.send(embed=embed) @config_show_command_group.command(name='feed', aliases=['release', 'f', 'releases']) @commands.guild_only() @@ -416,16 +427,17 @@ async def config_autoconv_gh_url_command(self, ctx: GitBotContext) -> None: await self.toggle_autoconv_item(ctx, 'gh_url') def _validate_github_lines_conversion_state(self, state: str | int) -> Optional[int]: - if state is not None: - _int_state: Optional[int] = state if isinstance(state, int) else (int(state) if state.isnumeric() else None) - if _int_state is not None: - if _int_state != 0: - _int_state -= 1 - if _int_state in self.lines_state_map.values(): - return _int_state - for k, v in self.lines_state_map.items(): - if state.lower() in k: - return v + if state is None: + return + _int_state: Optional[int] = state if isinstance(state, int) else (int(state) if state.isnumeric() else None) + if _int_state is not None: + if _int_state != 0: + _int_state -= 1 + if _int_state in self.lines_state_map.values(): + return _int_state + for k, v in self.lines_state_map.items(): + if state.lower() in k: + return v @config_autoconv_group.command('lines', aliases=['line', 'githublines', 'github-lines', 'gh-lines']) @commands.guild_only() @@ -539,8 +551,7 @@ def parse_channel_mention_or_number_response(self, rfis: list[ReleaseFeedItem]) -> list[int]: numbers: list[int] = self.bot.mgr.get_numbers_in_range_in_str(msg.content, len(rfis)) channel_ids: list[int] = [int(g) for g in DISCORD_CHANNEL_MENTION_RE.findall(msg.content)] - for n in numbers: - channel_ids.append(rfis[n - 1]['cid']) + channel_ids.extend(rfis[n - 1]['cid'] for n in numbers) return channel_ids @delete_feed_group.command('repo') diff --git a/cogs/ecosystem/dev.py b/cogs/ecosystem/dev.py index d732cbc4..5d35f53c 100644 --- a/cogs/ecosystem/dev.py +++ b/cogs/ecosystem/dev.py @@ -79,10 +79,11 @@ async def export_commands_command(self, ctx: GitBotContext, format_: str = 'txt' await ctx.error(ctx.fmt('invalid_format', ','.join([f'`{e.value}`' for e in ExportFileType]))) return command: GitBotCommand | GitBotCommandGroup - commands_: list[str] = [] - for command in self.bot.walk_commands(): - if not command.hidden: - commands_.append(command.fullname) + commands_: list[str] = [ + command.fullname + for command in self.bot.walk_commands() + if not command.hidden + ] match format_: case ExportFileType.TEXT: command_strings: str = '\n'.join(commands_) diff --git a/cogs/ecosystem/help.py b/cogs/ecosystem/help.py index feb12eb9..c69f03b2 100644 --- a/cogs/ecosystem/help.py +++ b/cogs/ecosystem/help.py @@ -64,10 +64,7 @@ async def send_command_help(self, ctx: GitBotContext, command: GitBotCommand) -> await ctx.send(embed=self.generate_command_help_embed(ctx, command)) async def send_command_group_help(self, ctx: GitBotContext, command_group: GitBotCommandGroup) -> None: - content: CommandGroupHelp = command_group.get_help_content(ctx) - if not content: - await GitBotEmbed.from_locale_resource(ctx, 'help no_help_for_command', color=self.bot.mgr.c.discord.white).send(ctx) - else: + if content := command_group.get_help_content(ctx): # since a group is basically a command with additional attributes, we can somewhat reuse the same embed embed: GitBotEmbed = self.generate_command_help_embed(ctx, command_group, content=content) embed.title = f'{self.bot.mgr.e.github} {ctx.l.glossary.command_group}: `{command_group.fullname}`' @@ -75,6 +72,8 @@ async def send_command_group_help(self, ctx: GitBotContext, command_group: GitBo value='\n'.join([f':white_small_square: `{self.bot.command_prefix}{c}`' for c in content['commands']])) await embed.send(ctx) + else: + await GitBotEmbed.from_locale_resource(ctx, 'help no_help_for_command', color=self.bot.mgr.c.discord.white).send(ctx) async def send_help(self, ctx: GitBotContext) -> None: pages: EmbedPages = EmbedPages() diff --git a/cogs/github/base/org.py b/cogs/github/base/org.py index 0b53e625..2b0e43a0 100644 --- a/cogs/github/base/org.py +++ b/cogs/github/base/org.py @@ -68,17 +68,17 @@ async def org_info_command(self, ctx: GitBotContext, organization: Optional[GitH created_at: str = ctx.fmt('created_at', self.bot.mgr.github_to_discord_timestamp(org['created_at'])) + '\n' info: str = f"{created_at}{repos}{members}{location}{email}" embed.add_field(name=f":mag_right: {ctx.l.org.info.glossary[1]}:", value=info, inline=False) - blog: tuple = (org['blog'] if 'blog' in org else None, ctx.l.org.info.glossary[3]) + blog: tuple = org.get('blog', None), ctx.l.org.info.glossary[3] twitter: tuple = ( f'https://twitter.com/{org["twitter_username"]}' if "twitter_username" in org and org[ 'twitter_username'] is not None else None, "Twitter") links: list = [blog, twitter] - link_strings: list = [] - for lnk in links: - if lnk[0] is not None and len(lnk[0]) != 0: - link_strings.append(f"- [{lnk[1]}]({lnk[0]})") - if len(link_strings) != 0: + if link_strings := [ + f"- [{lnk[1]}]({lnk[0]})" + for lnk in links + if lnk[0] is not None and len(lnk[0]) != 0 + ]: embed.add_field(name=f":link: {ctx.l.org.info.glossary[2]}:", value='\n'.join(link_strings), inline=False) embed.set_thumbnail(url=org['avatar_url']) await ctx.send(embed=embed, view_on_url=org['html_url']) diff --git a/cogs/github/base/repo/_list_plugin.py b/cogs/github/base/repo/_list_plugin.py index b8d400bd..4d894e80 100644 --- a/cogs/github/base/repo/_list_plugin.py +++ b/cogs/github/base/repo/_list_plugin.py @@ -64,15 +64,24 @@ async def joint_pr_issue_list_command(ctx: GitBotContext, repo: Optional[GitHubR await handle_none(ctx, 'pull request' if type_ == 'pr' else 'issue', stored, state_l) return - item_strings: list[str] = [make_string(ctx, repo, i, 'pull' if type_ == 'pr' else 'issue', - max([len(str(i['number'])) for i in items])) for i in items] + item_strings: list[str] = [ + make_string( + ctx, + repo, + i, + 'pull' if type_ == 'pr' else 'issue', + max(len(str(i['number'])) for i in items), + ) + for i in items + ] embed: GitBotEmbed = GitBotEmbed( - color=ctx.bot.mgr.c.rounded, - title=f'{ctx.bot.mgr.e.github} ' + ctx.bot.mgr.e.get(f'pr_{state_l}') + ' ' + ctx.fmt('title', f'`{state_l}`', f'`{repo}`'), - url=f'https://github.com/{repo}/{"pulls" if type_ == "pr" else "issues"}', - description='\n'.join(item_strings), - footer=ctx.l.repo["pulls" if type_ == 'pr' else 'issues'].footer_tip + color=ctx.bot.mgr.c.rounded, + title=f"{ctx.bot.mgr.e.github} {ctx.bot.mgr.e.get(f'pr_{state_l}')} " + + ctx.fmt('title', f'`{state_l}`', f'`{repo}`'), + url=f'https://github.com/{repo}/{"pulls" if type_ == "pr" else "issues"}', + description='\n'.join(item_strings), + footer=ctx.l.repo["pulls" if type_ == 'pr' else 'issues'].footer_tip, ) async def _callback(_ctx: GitBotContext, selected: dict): diff --git a/cogs/github/base/repo/repo.py b/cogs/github/base/repo/repo.py index 1926275a..c4c45a78 100644 --- a/cogs/github/base/repo/repo.py +++ b/cogs/github/base/repo/repo.py @@ -106,11 +106,11 @@ async def repo_info_command(self, ctx: GitBotContext, repo: Optional[GitHubRepos homepage: tuple = ( r['homepageUrl'] if 'homepageUrl' in r and r['homepageUrl'] else None, ctx.l.repo.info.glossary[4]) links: list = [homepage] - link_strings: list = [] - for lnk in links: - if lnk[0] is not None and len(lnk[0]) != 0: - link_strings.append(f"- [{lnk[1]}]({lnk[0]})") - if len(link_strings) != 0: + if link_strings := [ + f"- [{lnk[1]}]({lnk[0]})" + for lnk in links + if lnk[0] is not None and len(lnk[0]) != 0 + ]: embed.add_field(name=f":link: {ctx.l.repo.info.glossary[2]}:", value='\n'.join(link_strings)) if topics := self.bot.mgr.render_label_like_list(r['topics'][0], @@ -200,7 +200,11 @@ def make_embed(items: list, footer: str | None = None) -> GitBotEmbed: @commands.cooldown(10, 30, commands.BucketType.user) async def repo_files_command_two_arg(self, ctx: GitBotContext, repo: str, ref: str, path: str) -> None: # different order due to how groups are captured in the regex - await self.repo_files_command(ctx, repo_or_path=repo + (path if path.startswith('/') else '/' + path), ref=ref) + await self.repo_files_command( + ctx, + repo_or_path=repo + (path if path.startswith('/') else f'/{path}'), + ref=ref, + ) @repo_command_group.command(name='download', aliases=['dl']) @commands.max_concurrency(10) diff --git a/cogs/github/base/user.py b/cogs/github/base/user.py index c42ccdc9..24b0899e 100644 --- a/cogs/github/base/user.py +++ b/cogs/github/base/user.py @@ -65,7 +65,7 @@ async def user_info_command(self, ctx: GitBotContext, user: Optional[GitHubUser] 'following'] == 0 else ctx.fmt('following plural', u['following'], u['url'] + '?tab=following') if u['following'] == 1: following: str = ctx.fmt('following singular', f'{u["url"]}?tab=following') - follow: str = followers + f' {ctx.l.user.info.linking_word} ' + following + follow: str = f'{followers} {ctx.l.user.info.linking_word} {following}' repos: str = f"{ctx.l.user.info.repos.no_repos}\n" if u[ 'public_repos'] == 0 else ctx.fmt('repos plural', u['public_repos'], f"{u['url']}?tab=repositories") + '\n' @@ -80,19 +80,18 @@ async def user_info_command(self, ctx: GitBotContext, user: Optional[GitHubUser] info: str = f"{joined_at}{repos}{occupation}{orgs}{follow}{contrib}" embed.add_field(name=f":mag_right: {ctx.l.user.info.glossary[1]}:", value=info, inline=False) - w_url: str = u['websiteUrl'] - if w_url: + if w_url := u['websiteUrl']: blog: tuple = (w_url if w_url.startswith(('https://', 'http://')) else f'https://{w_url}', ctx.l.user.info.glossary[3]) else: blog: tuple = (None, ctx.l.glossary.website.capitalize()) twitter: tuple = (( f'https://twitter.com/{u["twitterUsername"]}') if "twitterUsername" in u and u['twitterUsername'] is not None else None, "Twitter") links: list = [blog, twitter] - link_strings: list = [] - for lnk in links: - if lnk[0] is not None and lnk[0] != '': - link_strings.append(f"- [{lnk[1]}]({lnk[0]})") - if len(link_strings) != 0: + if link_strings := [ + f"- [{lnk[1]}]({lnk[0]})" + for lnk in links + if lnk[0] is not None and lnk[0] != '' + ]: embed.add_field(name=f":link: {ctx.l.user.info.glossary[2]}:", value='\n'.join(link_strings), inline=False) embed.set_thumbnail(url=u['avatarUrl']) # for repo in u['pinnedItems']['nodes']: diff --git a/cogs/github/numbered/commits.py b/cogs/github/numbered/commits.py index 1377127c..03e0945d 100644 --- a/cogs/github/numbered/commits.py +++ b/cogs/github/numbered/commits.py @@ -48,13 +48,26 @@ async def commits_command(self, ctx: GitBotContext, repo: Optional[GitHubReposit embed: GitBotEmbed = GitBotEmbed( - title=self.bot.mgr.e.github + ' ' + ctx.fmt('commits embed title', - f'`{parsed.slashname}{f"/{parsed.branch}" if parsed.branch else ""}`'), - description='\n'.join([f'{self._commit_status(c)}[`{c["abbreviatedOid"]}`]({c["url"]}) ' - f'{self.bot.mgr.truncate(c["messageHeadline"], 53)}' for c in commits]), - url=(f'https://github.com/{parsed.slashname}/commits' if not parsed.branch - else f'https://github.com/{parsed.slashname}/commits/{parsed.branch}'), - footer=ctx.l.commits.embed.footer + title=( + f'{self.bot.mgr.e.github} ' + + ctx.fmt( + 'commits embed title', + f'`{parsed.slashname}{f"/{parsed.branch}" if parsed.branch else ""}`', + ) + ), + description='\n'.join( + [ + f'{self._commit_status(c)}[`{c["abbreviatedOid"]}`]({c["url"]}) ' + f'{self.bot.mgr.truncate(c["messageHeadline"], 53)}' + for c in commits + ] + ), + url=( + f'https://github.com/{parsed.slashname}/commits' + if not parsed.branch + else f'https://github.com/{parsed.slashname}/commits/{parsed.branch}' + ), + footer=ctx.l.commits.embed.footer, ) async def _callback(_, _commit): @@ -116,7 +129,7 @@ async def commit_command(self, if commit['messageBody'] and commit['messageBody'] != commit['messageHeadline'] else '') empty: str = ctx.l.commit.fields.message.empty if not full_headline and not message else '' - message: str = '```' + full_headline + message + empty + '```' + message: str = f'```{full_headline}{message}{empty}```' embed.add_field(name=f':notepad_spiral: {ctx.l.commit.fields.message.name}:', value=message) commit_time: str = ctx.fmt('fields info pushed_at' if commit['pushedDate'] else 'fields info committed_at', self.bot.mgr.to_github_hyperlink(commit['author']['user']['login']), @@ -152,7 +165,7 @@ async def commit_command(self, completed=completed, queued=queued, in_progress=in_progress) \ - + self._commit_status(commit, False) + + self._commit_status(commit, False) info: str = f'{commit_time}{signature}{committed_via_web}{checks}' embed.add_field(name=f':mag_right: {ctx.l.commit.fields.info.name}:', value=info) embed.add_field(name=f':gear: {ctx.l.commit.fields.changes.name}:', value=changes) diff --git a/cogs/github/numbered/gist.py b/cogs/github/numbered/gist.py index c36a3aa5..f0ca432d 100644 --- a/cogs/github/numbered/gist.py +++ b/cogs/github/numbered/gist.py @@ -103,10 +103,20 @@ async def get_color_from_files(self, files: list) -> int: most_common: Optional[str] = await self.bot.mgr.get_most_common(extensions) if most_common in ['.md', '']: return self.bot.mgr.c.rounded - for file in files: - if all([file['extension'] == most_common, file['language'], file['language']['color']]): - return int(file['language']['color'][1:], 16) - return self.bot.mgr.c.rounded + return next( + ( + int(file['language']['color'][1:], 16) + for file in files + if all( + [ + file['extension'] == most_common, + file['language'], + file['language']['color'], + ] + ) + ), + self.bot.mgr.c.rounded, + ) @staticmethod def extension(ext: str) -> str: diff --git a/cogs/github/other/loc.py b/cogs/github/other/loc.py index 78045b38..442786fe 100644 --- a/cogs/github/other/loc.py +++ b/cogs/github/other/loc.py @@ -131,7 +131,7 @@ async def prepare_result_sheet(data: dict) -> str: if k not in ('header', 'SUM'): result: str = result.format(f"{k}: {v['code']}\n{{}}") threshold -= 1 - result: str = result[:-5] + '```' + result: str = f'{result[:-5]}```' return result diff --git a/cogs/github/other/logs.py b/cogs/github/other/logs.py index 41adf4dd..96f4c434 100644 --- a/cogs/github/other/logs.py +++ b/cogs/github/other/logs.py @@ -34,9 +34,9 @@ async def logs_command(self, ctx: GitBotContext) -> None: ) try: url_embed: discord.Embed = discord.Embed( - color=0x4287f5, + color=0x4287F5, title=f'{self.bot.mgr.e.github} {ctx.l.logs.dm_title}', - description=f'||{webhook.url + "/github"}||' + description=f'||{webhook.url}/github||', ) await ctx.author.send(embed=url_embed) except discord.errors.HTTPException: diff --git a/cogs/github/other/snippets/_snippet_tools.py b/cogs/github/other/snippets/_snippet_tools.py index 2f762acf..1c7ad962 100644 --- a/cogs/github/other/snippets/_snippet_tools.py +++ b/cogs/github/other/snippets/_snippet_tools.py @@ -7,8 +7,11 @@ async def handle_url(ctx: GitBotContext, url: str, **kwargs) -> tuple: - match_: tuple = ctx.bot.mgr.opt(re.findall(regex.GITHUB_LINES_URL_RE, url) or re.findall(regex.GITLAB_LINES_URL_RE, url), 0) - if match_: + if match_ := ctx.bot.mgr.opt( + re.findall(regex.GITHUB_LINES_URL_RE, url) + or re.findall(regex.GITLAB_LINES_URL_RE, url), + 0, + ): return await get_text_from_url_and_data(ctx, compile_url(match_), match_, **kwargs) return None, ctx.l.snippets.no_lines_mentioned @@ -43,8 +46,7 @@ async def get_text_from_url_and_data(ctx: GitBotContext, continue lines.append(f'{line}\n') - text: str = ''.join(lines) - if text: + if text := ''.join(lines): return f"```{extension}\n{text.rstrip()}\n```" if wrap_in_codeblock else text.rstrip(), None return '', None diff --git a/cogs/github/other/snippets/snippets.py b/cogs/github/other/snippets/snippets.py index b53569be..8af17922 100644 --- a/cogs/github/other/snippets/snippets.py +++ b/cogs/github/other/snippets/snippets.py @@ -17,8 +17,9 @@ def __init__(self, bot: GitBot): async def snippet_command_group(self, ctx: GitBotContext, *, link_or_codeblock: str) -> None: ctx.fmt.set_prefix('snippets') if ctx.invoked_subcommand is None: - codeblock: Optional[str] = self.bot.mgr.extract_content_from_codeblock(link_or_codeblock) - if codeblock: + if codeblock := self.bot.mgr.extract_content_from_codeblock( + link_or_codeblock + ): if len(codeblock.splitlines()) > self.bot.mgr.env.carbon_len_threshold: await ctx.error(ctx.fmt('length_limit_exceeded', self.bot.mgr.env.carbon_len_threshold)) return diff --git a/cogs/python/pypi.py b/cogs/python/pypi.py index d8b90713..94cb2221 100644 --- a/cogs/python/pypi.py +++ b/cogs/python/pypi.py @@ -79,11 +79,11 @@ async def project_info_command(self, ctx: GitBotContext, project: PyPIProject) - docs: tuple = (data['info']['docs_url'] if 'docs_url' in data['info'] and data['info']['docs_url'] else None, ctx.l.pypi.info.glossary[4]) bugs: tuple = (data['info']['bugtrack_url'] if 'bugtrack_url' in data['info'] and data['info']['bugtrack_url'] else None, ctx.l.pypi.info.glossary[4]) links: list = [homepage, docs, bugs] - link_strings: list = [] - for lnk in links: - if lnk[0] is not None and len(lnk[0]) != 0: - link_strings.append(f"- [{lnk[1]}]({lnk[0]})") - if len(link_strings) != 0: + if link_strings := [ + f"- [{lnk[1]}]({lnk[0]})" + for lnk in links + if lnk[0] is not None and len(lnk[0]) != 0 + ]: embed.add_field(name=f":link: {ctx.l.pypi.info.glossary[2]}:", value='\n'.join(link_strings)) if 'license' in data['info'] and data['info']['license']: diff --git a/cogs/rust/crates.py b/cogs/rust/crates.py index 7afb6098..8210f54e 100644 --- a/cogs/rust/crates.py +++ b/cogs/rust/crates.py @@ -67,16 +67,16 @@ async def crate_info_command(self, ctx: GitBotContext, crate: CratesIOCrate) -> '%Y-%m-%dT%H:%M:%S.%f%z')) + '\n' all_time_downloads: str = f'```rust\n{data["crate"]["downloads"]} //' \ - f' {ctx.l.crates.info.all_time_downloads}```\n' + f' {ctx.l.crates.info.all_time_downloads}```\n' info: str = f'{authors}{created_at}{all_time_downloads}' embed.add_field(name=f":mag_right: {ctx.l.crates.info.glossary[1]}:", value=info) links: list = [] - link_strings: list = [] - for lnk in links: - if lnk[0] is not None and len(lnk[0]) != 0: - link_strings.append(f"- [{lnk[1]}]({lnk[0]})") - if len(link_strings) != 0: + if link_strings := [ + f"- [{lnk[1]}]({lnk[0]})" + for lnk in links + if lnk[0] is not None and len(lnk[0]) != 0 + ]: embed.add_field(name=f":link: {ctx.l.pypi.info.glossary[2]}:", value='\n'.join(link_strings)) diff --git a/lib/api/crates.py b/lib/api/crates.py index e59af80a..e99605b4 100644 --- a/lib/api/crates.py +++ b/lib/api/crates.py @@ -15,7 +15,9 @@ def __init__(self, ses: aiohttp.ClientSession): self.ses: aiohttp.ClientSession = ses async def get_crate_data(self, crate: str) -> dict | None: - res: aiohttp.ClientResponse = await self.ses.get(BASE_URL_CRATES + f'/crates/{crate}') + res: aiohttp.ClientResponse = await self.ses.get( + f'{BASE_URL_CRATES}/crates/{crate}' + ) if res.status == 200: return await res.json() diff --git a/lib/api/github.py b/lib/api/github.py index 52a75155..c4c2d847 100644 --- a/lib/api/github.py +++ b/lib/api/github.py @@ -63,7 +63,7 @@ async def ghprofile_stats(self, name: str) -> Optional[GhProfileData]: return None res = await (await self.session.get(f'https://api.ghprofile.me/historic/view?username={name}')).json() period: dict = dict(res['payload']['period']) - if not res['success'] or sum([int(v) for v in period.values()]) == 0: + if not res['success'] or sum(int(v) for v in period.values()) == 0: return None return GhProfileData(*[int(v) for v in period.values()]) @@ -84,7 +84,11 @@ async def getitem(self, resource: str, default: Any = None) -> Any: @github_cached @validate_github_name('user') async def get_user_repos(self, user: GitHubUser) -> Optional[list[dict]]: - return list(r for r in await self.getitem(f'/users/{user}/repos', []) if r['private'] is False) + return [ + r + for r in await self.getitem(f'/users/{user}/repos', []) + if r['private'] is False + ] @github_cached @validate_github_name('org') @@ -94,7 +98,11 @@ async def get_org(self, org: GitHubOrganization) -> Optional[dict]: @github_cached @validate_github_name('org', default=[]) async def get_org_repos(self, org: GitHubOrganization) -> list[dict]: - return list(r for r in await self.getitem(f'/orgs/{org}/repos', []) if r['private'] is False) + return [ + r + for r in await self.getitem(f'/orgs/{org}/repos', []) + if r['private'] is False + ] @normalize_repository async def get_tree_file(self, repo: GitHubRepository, path: str | None = None, ref: str | None = None) -> dict | list | None: @@ -102,7 +110,7 @@ async def get_tree_file(self, repo: GitHubRepository, path: str | None = None, r return None if path: if path[0] != '/': - path = '/' + path + path = f'/{path}' else: path = '' return await self.getitem(f'/repos/{repo}/contents{path}' + (f'?ref={ref}' if ref else '')) @@ -140,9 +148,7 @@ async def get_latest_commit(self, repo: GitHubRepository) -> Optional[dict] | Li try: data: dict = await self.gh.graphql(self.queries.latest_commit, **{'Name': repository, 'Owner': owner}) except QueryError as e: - if 'Repository' in str(e): - return False - return None + return False if 'Repository' in str(e) else None return data['repository']['defaultBranchRef']['target'] @normalize_repository @@ -155,9 +161,7 @@ async def get_commit(self, repo: GitHubRepository, oid: str) -> Optional[dict] | data: dict = await self.gh.graphql(self.queries.commit, **{'Name': repository, 'Owner': owner, 'Oid': oid}) except QueryError as e: - if 'Repository' in str(e): - return False - return None + return False if 'Repository' in str(e) else None return data['repository']['object'] @normalize_repository @@ -176,9 +180,7 @@ async def get_latest_commits(self, repo: GitHubRepository, ref: Optional[str] = data = await self.gh.graphql(self.queries.latest_commits_from_ref, **{'Name': repository, 'Owner': owner, 'RefName': ref, 'First': 10}) except QueryError as e: - if 'Repository' in str(e): - return 'repo' - return 'ref' + return 'repo' if 'Repository' in str(e) else 'ref' if 'defaultBranchRef' not in data.get('repository', {}) and 'ref' not in data['repository']: return 'ref' try: @@ -192,8 +194,10 @@ async def get_repo_zip(self, size_threshold: int = DISCORD_UPLOAD_SIZE_THRESHOLD_BYTES) -> Optional[bool | bytes]: if '/' not in repo or repo.count('/') > 1: return None - res = await self.session.get(BASE_URL + f'/repos/{repo}/zipball', - headers={'Authorization': f'token {self.__token}'}) + res = await self.session.get( + f'{BASE_URL}/repos/{repo}/zipball', + headers={'Authorization': f'token {self.__token}'}, + ) if res.status == 200: try: await res.content.readexactly(size_threshold) @@ -269,9 +273,7 @@ async def get_pull_request(self, 'Owner': owner, 'Number': number}) except QueryError as e: - if 'number' in str(e): - return 'number' - return 'repo' + return 'number' if 'number' in str(e) else 'repo' data: dict = data['repository']['pullRequest'] if 'repository' in data else data data['labels']: list = [lb['node']['name'] for lb in data['labels']['edges']] data['assignees']['users'] = [(u['node']['login'], u['node']['url']) for u in data['assignees']['edges']] @@ -325,9 +327,7 @@ async def get_issue(self, 'Owner': owner, 'Number': number}) except QueryError as e: - if 'number' in str(e): - return 'number' - return 'repo' + return 'number' if 'number' in str(e) else 'repo' if isinstance(data, dict): if not had_keys_removed: data: dict = data['repository']['issue'] diff --git a/lib/api/pypi.py b/lib/api/pypi.py index faa37452..3cfabf71 100644 --- a/lib/api/pypi.py +++ b/lib/api/pypi.py @@ -10,22 +10,30 @@ def __init__(self, ses: aiohttp.ClientSession = aiohttp.ClientSession()): self.ses: aiohttp.ClientSession = ses async def get_project_data(self, project: str) -> Optional[dict]: - res: aiohttp.ClientResponse = await self.ses.get(BASE_URL_PYPI + f'/{project}/json') + res: aiohttp.ClientResponse = await self.ses.get( + f'{BASE_URL_PYPI}/{project}/json' + ) if res.status == 200: return await res.json() async def get_project_version_data(self, project: str, version: str) -> Optional[dict]: # This endpoint doesn't make sense, returns the same data as the non-versioned one - res: aiohttp.ClientResponse = await self.ses.get(BASE_URL_PYPI + f'/{project}/{version}/json') + res: aiohttp.ClientResponse = await self.ses.get( + f'{BASE_URL_PYPI}/{project}/{version}/json' + ) if res.status == 200: return await res.json() async def get_project_overall_downloads(self, project: str, mirrors: bool = False) -> Optional[dict]: - res: aiohttp.ClientResponse = await self.ses.get(BASE_URL_PYPISTATS + f'/packages/{project.lower()}/overall?mirrors={str(mirrors).lower()}') + res: aiohttp.ClientResponse = await self.ses.get( + f'{BASE_URL_PYPISTATS}/packages/{project.lower()}/overall?mirrors={str(mirrors).lower()}' + ) if res.status == 200: return await res.json() async def get_project_recent_downloads(self, project: str) -> Optional[dict]: - res: aiohttp.ClientResponse = await self.ses.get(BASE_URL_PYPISTATS + f'/packages/{project.lower()}/recent') + res: aiohttp.ClientResponse = await self.ses.get( + f'{BASE_URL_PYPISTATS}/packages/{project.lower()}/recent' + ) if res.status == 200: return await res.json() diff --git a/lib/manager.py b/lib/manager.py index a4ff5810..5c4679bc 100644 --- a/lib/manager.py +++ b/lib/manager.py @@ -150,9 +150,9 @@ def render_label_like_list(labels: Collection[str] | list[dict], name_kn, url_kn = name_and_url_slug_knames_if_dict if url_kn_is_slug and not url_fmt: raise ValueError('url_fmt must be specified if urls should be dynamically generated') - is_collection_of_dicts: bool = bool(labels) and isinstance(labels[0], dict) if labels: more: str = f' `+{total_n - max_n}`' if total_n > max_n else '' + is_collection_of_dicts: bool = bool(labels) and isinstance(labels[0], dict) if not is_collection_of_dicts: l_strings: str = ' '.join([f'[`{l_}`]({url_fmt.format(l_)})' for l_ in labels[:max_n]]) else: @@ -197,7 +197,9 @@ def to_snake_case(string: str) -> str: :param string: The string to convert :return: The converted string """ - return ''.join(['_' + i.lower() if i.isupper() else i for i in string]).lstrip('_') + return ''.join( + [f'_{i.lower()}' if i.isupper() else i for i in string] + ).lstrip('_') @staticmethod def to_github_hyperlink(name: str, codeblock: bool = False) -> str: @@ -315,12 +317,17 @@ async def verify_send_perms(channel: discord.TextChannel) -> bool: return False perms: list = list(iter(channel.permissions_for(channel.guild.me))) overwrites: list = list(iter(channel.overwrites_for(channel.guild.me))) # weird inspection, keep an eye on this - if all(req in perms + overwrites for req in [('send_messages', True), - ('read_messages', True), - ('read_message_history', True)]) \ - or ('administrator', True) in perms: - return True - return False + return ( + all( + req in perms + overwrites + for req in [ + ('send_messages', True), + ('read_messages', True), + ('read_message_history', True), + ] + ) + or ('administrator', True) in perms + ) @staticmethod async def get_most_common(items: list | tuple) -> Any: @@ -355,10 +362,7 @@ def regex_get(dict_: dict, pattern: re.Pattern | str, default: Any = None) -> An """ compare: Callable = ((lambda k_: bool(pattern.match(k_))) if isinstance(pattern, re.Pattern) else lambda k_: pattern in k_) - for k, v in dict_.items(): - if compare(k): - return v - return default + return next((v for k, v in dict_.items() if compare(k)), default) @staticmethod def get_nested_key(dict_: AnyDict, key: Iterable[str] | str, sep: str = ' ') -> Any: @@ -373,7 +377,7 @@ def get_nested_key(dict_: AnyDict, key: Iterable[str] | str, sep: str = ' ') -> if isinstance(key, str): key = key.split(sep=sep) - for i, k in enumerate(key): + for k in key: if k.endswith("]"): index_start = k.index("[") index = int(k[index_start + 1:-1]) @@ -447,9 +451,7 @@ def release_feed_mention_to_actual(mention: ReleaseFeedItemMention) -> str: :param mention: The release feed mention value :return: The actual mention """ - if isinstance(mention, str): - return f'@{mention}' - return f'<@&{mention}>' + return f'@{mention}' if isinstance(mention, str) else f'<@&{mention}>' @staticmethod async def just_run(func: Callable, *args, **kwargs) -> Any: @@ -571,20 +573,21 @@ def _handle_env_binding(self, binding: dotenv.parser.Binding) -> None: :param binding: The binding to handle """ - if not self._maybe_set_env_directive(binding.key, binding.value): - try: - if self.env_directives.get('eval_literal'): - if isinstance((parsed := self._eval_bool_literal_safe(binding.value)), bool): - self.env[binding.key] = parsed - else: - self.env[binding.key] = (parsed := self.parse_literal(binding.value)) + if self._maybe_set_env_directive(binding.key, binding.value): + return + try: + if self.env_directives.get('eval_literal'): + if isinstance((parsed := self._eval_bool_literal_safe(binding.value)), bool): + self.env[binding.key] = parsed else: - self.env[binding.key] = (parsed := binding.value) - self.bot.logger.info('env[%s] loaded as "%s"', binding.key, type(parsed).__name__) - return - except (ValueError, SyntaxError): - self.env[binding.key] = binding.value - self.bot.logger.info('env[%s] loaded as "str"', binding.key) + self.env[binding.key] = (parsed := self.parse_literal(binding.value)) + else: + self.env[binding.key] = (parsed := binding.value) + self.bot.logger.info('env[%s] loaded as "%s"', binding.key, type(parsed).__name__) + return + except (ValueError, SyntaxError): + self.env[binding.key] = binding.value + self.bot.logger.info('env[%s] loaded as "str"', binding.key) def load_dotenv(self) -> None: """ @@ -594,8 +597,7 @@ def load_dotenv(self) -> None: - Defaults are loaded from env_defaults.json first, so that .env values take precedence - With the "eval_literal" directive active, binding values are parsed with AST during runtime """ - dotenv_path: str = dotenv.find_dotenv() - if dotenv_path: + if dotenv_path := dotenv.find_dotenv(): self.bot.logger.info('Found .env file, loading environment variables listed inside of it.') with open(dotenv_path, 'r', encoding='utf8') as fp: for binding in dotenv.parser.parse_stream(fp): @@ -728,9 +730,10 @@ def extract_content_from_codeblock(self, codeblock: str) -> Optional[str]: :param codeblock: The codeblock to strip :return: The code extracted from the codeblock """ - match_: re.Match = (re.search(r.MULTILINE_CODEBLOCK_RE, codeblock) or - re.search(r.SINGLE_LINE_CODEBLOCK_RE, codeblock)) - if match_: + if match_ := ( + re.search(r.MULTILINE_CODEBLOCK_RE, codeblock) + or re.search(r.SINGLE_LINE_CODEBLOCK_RE, codeblock) + ): self.bot.logger.debug('Matched codeblock') return match_.group('content').rstrip('\n') self.bot.logger.debug("Couldn't match codeblock") @@ -784,7 +787,7 @@ def load_json(self, then apply recursion until an actionable value (list | str | int | bool) is found in the node :return: The loaded JSON wrapped in DictProxy """ - to_load = './resources/' + str(name).lower() + '.json' if name[-5:] != '.json' else '' + to_load = f'./resources/{name.lower()}.json' if name[-5:] != '.json' else '' with open(to_load, 'r', encoding='utf8') as fp: data: dict | list = json.load(fp) proxy: DictProxy = DictProxy(data) @@ -879,8 +882,7 @@ def validate_index(self, number: str| int, items: list[AnyDict]) -> Optional[dic number: int = int(number) except (TypeError, ValueError): return None - matched = self.opt([i for i in items if i['number'] == number], 0) - if matched: + if matched := self.opt([i for i in items if i['number'] == number], 0): return matched async def reverse(self, seq: Optional[Reversible]) -> Optional[Iterable]: @@ -946,9 +948,8 @@ async def get_locale(self, _id: Identity) -> DictProxy: if cached := self.locale_cache.get(_id): locale: LocaleName = cached self.bot.logger.debug('Returning cached value for identity "%d"', _id) - else: - if stored := await self.db.users.getitem(_id, 'locale'): - locale: str = stored + elif stored := await self.db.users.getitem(_id, 'locale'): + locale: str = stored try: self.locale_cache[_id] = locale return getattr(self.l, locale) @@ -977,14 +978,15 @@ def get_by_key_from_sequence(self, for d in seq: if isinstance(key, str): if (key in d) and (d[key] == value) if not unpack else (d[key] in value): - if not multiple: - return d - matching.append(d) - else: - if (self.get_nested_key(d, key) == value) if not unpack else (self.get_nested_key(d, key) in value): - if not multiple: + if multiple: + matching.append(d) + else: return d + elif (self.get_nested_key(d, key) == value) if not unpack else (self.get_nested_key(d, key) in value): + if multiple: matching.append(d) + else: + return d return matching def populate_generic_numbered_resource(self, @@ -1038,8 +1040,7 @@ def get_missing_keys_for_locale(self, locale: str) -> Optional[tuple[list[str], :param locale: Any meta attribute of the locale :return: The missing keys for the locale and the confidence of the attribute match """ - locale_data: Optional[tuple[DictProxy, bool]] = self.get_locale_meta_by_attribute(locale) - if locale_data: + if locale_data := self.get_locale_meta_by_attribute(locale): missing: list = list( {item for item in self._missing_locale_keys[locale_data[0]['name']] if item is not None}) missing.sort(key=lambda path: len(path) * sum(map(len, path))) @@ -1083,19 +1084,20 @@ def get_localization_percentage(self, locale: str) -> float: :param locale: The locale to get the percentage for :return: The percentage """ - locale: DictProxy | None = getattr(self.l, locale, None) - if locale: - if self.localization_percentages.get(locale.meta['name']) is not None: - return self.localization_percentages[locale.meta['name']] - ml_copy: dict = deepcopy(self.locale.master.actual) - ml_paths: list = self.get_all_dict_paths(ml_copy) - non_localized: int = 0 - for k in ml_paths: - if self.get_nested_key(locale, k) == self.get_nested_key(ml_copy, k): - non_localized += 1 - result: float = round((1 - (non_localized / len(ml_paths))) * 100, 2) - self.localization_percentages[locale.meta['name']] = result - return result + if not (locale := getattr(self.l, locale, None)): + return + if self.localization_percentages.get(locale.meta['name']) is not None: + return self.localization_percentages[locale.meta['name']] + ml_copy: dict = deepcopy(self.locale.master.actual) + ml_paths: list = self.get_all_dict_paths(ml_copy) + non_localized: int = sum( + 1 + for k in ml_paths + if self.get_nested_key(locale, k) == self.get_nested_key(ml_copy, k) + ) + result: float = round((1 - (non_localized / len(ml_paths))) * 100, 2) + self.localization_percentages[locale.meta['name']] = result + return result def fix_dict(self, dict_: AnyDict, ref_: AnyDict, locale: bool = False) -> AnyDict: """ @@ -1139,7 +1141,7 @@ def _replace_emoji(self, match_: re.Match, default: str = '**[?]**') -> str: :param match_: The match to generate the replacement for :return: The replacement string """ - if group := match_.group('emoji_name'): + if group := match_['emoji_name']: return self.e.get(group, default) return match_.string @@ -1186,6 +1188,8 @@ def fmt(self, ctx: 'GitBotContext'): """ self_: Manager = self + + class _Formatter: def __init__(self, ctx_: 'GitBotContext'): self.ctx: 'GitBotContext' = ctx_ @@ -1208,8 +1212,13 @@ def set_prefix(self, prefix: str, absolute: bool = True) -> None: self_.bot.logger.debug('Prefix mode is append, stripping op sign in \'%s\'', prefix) prefix: str = prefix[1:] absolute: bool = False - self.prefix: str = prefix.strip() + ' ' if absolute else self.prefix + prefix.strip() + ' ' + self.prefix: str = ( + f'{prefix.strip()} ' + if absolute + else self.prefix + prefix.strip() + ' ' + ) self_.bot.logger.debug('Locale formatting prefix set to \'%s\' in ' '\'%s\'', self.prefix.strip(), self_.get_last_call_from_callstack()) + return _Formatter(ctx) diff --git a/lib/structs/db/user_collection.py b/lib/structs/db/user_collection.py index 66752b60..ee9ed120 100644 --- a/lib/structs/db/user_collection.py +++ b/lib/structs/db/user_collection.py @@ -42,14 +42,12 @@ async def delitem(self, _id: Identity, field: str) -> bool: @normalize_identity() async def getitem(self, _id: Identity, item: str) -> Optional[str]: query: dict = await self.find_one({'_id': _id}) - if query and item in query: - return query[item] - return None + return query[item] if query and item in query else None @normalize_identity() async def setitem(self, _id: Identity, item: str, value: str) -> bool: valid: bool = True - if item in ('user', 'repo', 'org'): + if item in {'user', 'repo', 'org'}: valid: bool = await ({'user': self._git.get_user, 'repo': self._git.get_repo, 'org': self._git.get_org}[item])(value) is not None elif item == 'locale': valid: bool = any(l_['name'] == value for l_ in self._mgr.locale.languages) diff --git a/lib/structs/dicts/case_insensitive_dict.py b/lib/structs/dicts/case_insensitive_dict.py index ce24ca87..12629160 100644 --- a/lib/structs/dicts/case_insensitive_dict.py +++ b/lib/structs/dicts/case_insensitive_dict.py @@ -8,9 +8,7 @@ class CaseInsensitiveDict(dict): @staticmethod def _casefold(key: Any) -> Any: - if hasattr(key, 'casefold'): - return key.casefold() - return key + return key.casefold() if hasattr(key, 'casefold') else key def __contains__(self, key: Any) -> bool: return super().__contains__(self._casefold(key)) @@ -42,4 +40,8 @@ def __init__(self, mapping: dict = None, **kwargs): super().__init__(mapping, **kwargs) def _casefold(self, key: Any) -> Any: - return super()._casefold(''.join(['_' + i.lower() if i.isupper() else i for i in key]).lstrip('_')) + return super()._casefold( + ''.join([f'_{i.lower()}' if i.isupper() else i for i in key]).lstrip( + '_' + ) + ) diff --git a/lib/structs/dicts/max_age_dict.py b/lib/structs/dicts/max_age_dict.py index a87a6452..c2be7c46 100644 --- a/lib/structs/dicts/max_age_dict.py +++ b/lib/structs/dicts/max_age_dict.py @@ -29,14 +29,10 @@ def valid(self, key: Any, delete: bool = False) -> bool: return True def get(self, key: Any, default: Any = None) -> Any: - if self.valid(key, delete=True): - return super().get(key, default) - return default + return super().get(key, default) if self.valid(key, delete=True) else default def age(self, key: Any, default: Any = 0) -> Any: - if ts := self._age_map.get(key): - return int(time()) - ts - return default + return int(time()) - ts if (ts := self._age_map.get(key)) else default def __setitem__(self, key: Any, value: Any) -> None: self._age_map[key] = int(time()) diff --git a/lib/structs/discord/components/github_lines_view.py b/lib/structs/discord/components/github_lines_view.py index cb32d7f5..111dfba7 100644 --- a/lib/structs/discord/components/github_lines_view.py +++ b/lib/structs/discord/components/github_lines_view.py @@ -91,15 +91,16 @@ async def callback(self, interaction: discord.Interaction): self.view.lines_url = self.view.lines_url.replace(f'#L{[previous_l1]}', f'#L{self.view.l1}-{self.view.l2}') else: self.view.lines_url = self.view.lines_url.replace(f'#L{previous_l1}-L{previous_l2}', f'#L{self.view.l1}-L{self.view.l2}') - new_match = self.view.parsed.groups()[0:4] + (self.view.l1, self.view.l2) + new_match = self.view.parsed.groups()[:4] + (self.view.l1, self.view.l2) new, _ = await get_text_from_url_and_data(ctx, compile_url(new_match), new_match) if new: l_b, l_f = self.get_next_lines(self.view.l1, self.view.l2, False), self.get_next_lines(self.view.l1, self.view.l2, True) self.view.set_labels(l_b, l_f) # TODO make the line numbers injected below into a hyperlink when the discord devs add support for them back await interaction.message.edit( - content=f'`#L{self.view.l1}{"-L" + str(self.view.l2) if self.view.l2 != 1 else ""}`\n{new}', - view=self.view) + content=f'`#L{self.view.l1}{f"-L{str(self.view.l2)}" if self.view.l2 != 1 else ""}`\n{new}', + view=self.view, + ) @staticmethod def get_next_lines(l1: int, l2: int | None, forward: bool) -> tuple[int, int]: diff --git a/lib/structs/discord/components/view_file.py b/lib/structs/discord/components/view_file.py index 7e9922fc..bad0c5ca 100644 --- a/lib/structs/discord/components/view_file.py +++ b/lib/structs/discord/components/view_file.py @@ -31,9 +31,8 @@ async def callback(self, interaction: discord.Interaction): return if (file := io.BytesIO(await res.content.read())).getbuffer().nbytes > int(7.85 * (1024 ** 2)): return - else: - self._file: io.BytesIO = file - self._filename: str = f'{self.file_url.split("/")[2].replace(".", "_")}.{self.filetype}' + self._file: io.BytesIO = file + self._filename: str = f'{self.file_url.split("/")[2].replace(".", "_")}.{self.filetype}' self._file.seek(0) # reset file pointer from potential previous reads by discord.py self._used_by.add(interaction.user.id) await interaction.response.send_message(file=discord.File(self._file, filename=self._filename), ephemeral=True) diff --git a/lib/structs/discord/embed.py b/lib/structs/discord/embed.py index 8d2de15a..452a2949 100644 --- a/lib/structs/discord/embed.py +++ b/lib/structs/discord/embed.py @@ -209,7 +209,12 @@ async def confirmation(self, ctx: 'GitBotContext', callback: GitBotEmbedResponse response_callback=callback, init_message=initial_message ) - if (result and result[0] and isinstance(result[0][0], discord.Reaction) - and result[0][0].is_custom_emoji() and result[0][0].emoji.id == 770244084727283732): - return True - return False + return bool( + ( + result + and result[0] + and isinstance(result[0][0], discord.Reaction) + and result[0][0].is_custom_emoji() + and result[0][0].emoji.id == 770244084727283732 + ) + ) diff --git a/lib/utils/decorators.py b/lib/utils/decorators.py index 90d1c81f..48343239 100644 --- a/lib/utils/decorators.py +++ b/lib/utils/decorators.py @@ -18,7 +18,7 @@ def gen_aliases(_name: str) -> tuple: return _name, f'-{_name}', f'--{_name}', f'—{_name}', f'——{_name}' aliases: list[str] = attrs.get('aliases') or [] - to_add: list[str] = list(sum([gen_aliases(alias) for alias in aliases], ())) + to_add: list[str] = list(sum((gen_aliases(alias) for alias in aliases), ())) aliases.extend([*to_add, *(gen_aliases(name)[1:])]) attrs['aliases'] = list(set(aliases)) return attrs diff --git a/migrations/2021-03-13_uid_to_oid.py b/migrations/2021-03-13_uid_to_oid.py index 53c7313d..8ef6c4d6 100644 --- a/migrations/2021-03-13_uid_to_oid.py +++ b/migrations/2021-03-13_uid_to_oid.py @@ -3,6 +3,7 @@ with ones overwriting the _id field. """ + from pymongo import MongoClient, InsertOne, DeleteOne from dotenv import load_dotenv from os import getenv @@ -17,9 +18,7 @@ if 'user_id' in u: uid: int = u['user_id'] del u['_id'], u['user_id'] - ops.append(DeleteOne({'user_id': uid})) - ops.append(InsertOne(dict(_id=uid, **u))) - -print("Writing " + str(len(ops))) + ops.extend((DeleteOne({'user_id': uid}), InsertOne(dict(_id=uid, **u)))) +print(f"Writing {len(ops)}") db.bulk_write(ops, ordered=False) -print("Updated " + str(len(ops) >> 1)) +print(f"Updated {str(len(ops) >> 1)}") diff --git a/migrations/2022-02-09_new_release_feed_structure.py b/migrations/2022-02-09_new_release_feed_structure.py index d56a3b2e..4ae31f94 100644 --- a/migrations/2022-02-09_new_release_feed_structure.py +++ b/migrations/2022-02-09_new_release_feed_structure.py @@ -2,6 +2,7 @@ A one-use script to change old-style guild documents into ones with new Multi-Release-Feed functionality. """ + import discord import requests from pymongo import MongoClient, UpdateOne @@ -24,6 +25,6 @@ ops.append(UpdateOne(g, {'$set': {'feed': [new_rfi]}, '$unset': {'hook': ''}})) -print("Writing " + str(len(ops))) +print(f"Writing {len(ops)}") db.bulk_write(ops, ordered=False) -print("Updated " + str(len(ops))) +print(f"Updated {len(ops)}")