Cache prefixes (#3150)

* Cache prefixes

 - This works towards #3148
 - Ends up centralizing some logic
   - Including that prefixes should be a reverse sorted list

* handle global prefix attempts at none

* fix prefix set for server

* cache using guild id
This commit is contained in:
Michael H 2020-01-17 16:49:25 -05:00 committed by GitHub
parent d1b7f836db
commit cd7f4681a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 32 deletions

View File

@ -0,0 +1 @@
Cache prefixes rather than lookup from config each time

View File

@ -36,6 +36,8 @@ from .dev_commands import Dev
from .events import init_events from .events import init_events
from .global_checks import init_global_checks from .global_checks import init_global_checks
from .settings_caches import PrefixManager
from .rpc import RPCMixin from .rpc import RPCMixin
from .utils import common_filters from .utils import common_filters
@ -124,23 +126,13 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
self._config.init_custom(SHARED_API_TOKENS, 2) self._config.init_custom(SHARED_API_TOKENS, 2)
self._config.register_custom(SHARED_API_TOKENS) self._config.register_custom(SHARED_API_TOKENS)
self._prefix_cache = PrefixManager(self._config, cli_flags)
async def prefix_manager(bot, message): async def prefix_manager(bot, message) -> List[str]:
if not cli_flags.prefix: prefixes = await self._prefix_cache.get_prefixes(message.guild)
global_prefix = await bot._config.prefix()
else:
global_prefix = cli_flags.prefix
if message.guild is None:
return global_prefix
server_prefix = await bot._config.guild(message.guild).prefix()
if cli_flags.mentionable: if cli_flags.mentionable:
return ( return when_mentioned_or(*prefixes)(bot, message)
when_mentioned_or(*server_prefix)(bot, message) return prefixes
if server_prefix
else when_mentioned_or(*global_prefix)(bot, message)
)
else:
return server_prefix if server_prefix else global_prefix
if "command_prefix" not in kwargs: if "command_prefix" not in kwargs:
kwargs["command_prefix"] = prefix_manager kwargs["command_prefix"] = prefix_manager
@ -273,15 +265,15 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
""" """
This checks if a user or member is allowed to run things, This checks if a user or member is allowed to run things,
as considered by Red's whitelist and blacklist. as considered by Red's whitelist and blacklist.
If given a user object, this function will check the global lists If given a user object, this function will check the global lists
If given a member, this will additionally check guild lists If given a member, this will additionally check guild lists
If omiting a user or member, you must provide a value for ``who_id`` If omiting a user or member, you must provide a value for ``who_id``
You may also provide a value for ``guild_id`` in this case You may also provide a value for ``guild_id`` in this case
If providing a member by guild and member ids, If providing a member by guild and member ids,
you should supply ``role_ids`` as well you should supply ``role_ids`` as well
@ -289,7 +281,7 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
---------- ----------
who : Optional[Union[discord.Member, discord.User]] who : Optional[Union[discord.Member, discord.User]]
The user or member object to check The user or member object to check
Other Parameters Other Parameters
---------------- ----------------
who_id : Optional[int] who_id : Optional[int]
@ -906,7 +898,7 @@ class RedBase(commands.GroupMixin, commands.bot.BotBase, RPCMixin): # pylint: d
This should realistically only be used for responding using user provided This should realistically only be used for responding using user provided
input. (unfortunately, including usernames) input. (unfortunately, including usernames)
Manually crafted messages which dont take any user input have no need of this Manually crafted messages which dont take any user input have no need of this
Returns Returns
------- -------
discord.Message discord.Message

View File

@ -135,7 +135,9 @@ def parse_cli_flags(args):
"security implications if misused. Can be " "security implications if misused. Can be "
"multiple.", "multiple.",
) )
parser.add_argument("--prefix", "-p", action="append", help="Global prefix. Can be multiple") parser.add_argument(
"--prefix", "-p", action="append", help="Global prefix. Can be multiple", default=[]
)
parser.add_argument( parser.add_argument(
"--no-prompt", "--no-prompt",
action="store_true", action="store_true",

View File

@ -257,10 +257,9 @@ class CoreLogic:
The current (or new) list of prefixes. The current (or new) list of prefixes.
""" """
if prefixes: if prefixes:
prefixes = sorted(prefixes, reverse=True) await self.bot._prefix_cache.set_prefixes(guild=None, prefixes=prefixes)
await self.bot._config.prefix.set(prefixes)
return prefixes return prefixes
return await self.bot._config.prefix() return await self.bot._prefix_cache.get_prefixes(guild=None)
@classmethod @classmethod
async def _version_info(cls) -> Dict[str, str]: async def _version_info(cls) -> Dict[str, str]:
@ -847,15 +846,13 @@ class Core(commands.Cog, CoreLogic):
mod_role_ids = await ctx.bot._config.guild(ctx.guild).mod_role() mod_role_ids = await ctx.bot._config.guild(ctx.guild).mod_role()
mod_role_names = [r.name for r in guild.roles if r.id in mod_role_ids] mod_role_names = [r.name for r in guild.roles if r.id in mod_role_ids]
mod_roles_str = humanize_list(mod_role_names) if mod_role_names else "Not Set." mod_roles_str = humanize_list(mod_role_names) if mod_role_names else "Not Set."
prefixes = await ctx.bot._config.guild(ctx.guild).prefix()
guild_settings = _("Admin roles: {admin}\nMod roles: {mod}\n").format( guild_settings = _("Admin roles: {admin}\nMod roles: {mod}\n").format(
admin=admin_roles_str, mod=mod_roles_str admin=admin_roles_str, mod=mod_roles_str
) )
else: else:
guild_settings = "" guild_settings = ""
prefixes = None # This is correct. The below can happen in a guild.
if not prefixes: prefixes = await ctx.bot._prefix_cache.get_prefixes(ctx.guild)
prefixes = await ctx.bot._config.prefix()
locale = await ctx.bot._config.locale() locale = await ctx.bot._config.locale()
prefix_string = " ".join(prefixes) prefix_string = " ".join(prefixes)
@ -1182,11 +1179,11 @@ class Core(commands.Cog, CoreLogic):
async def serverprefix(self, ctx: commands.Context, *prefixes: str): async def serverprefix(self, ctx: commands.Context, *prefixes: str):
"""Sets Red's server prefix(es)""" """Sets Red's server prefix(es)"""
if not prefixes: if not prefixes:
await ctx.bot._config.guild(ctx.guild).prefix.set([]) await ctx.bot._prefix_cache.set_prefixes(guild=ctx.guild, prefixes=[])
await ctx.send(_("Guild prefixes have been reset.")) await ctx.send(_("Guild prefixes have been reset."))
return return
prefixes = sorted(prefixes, reverse=True) prefixes = sorted(prefixes, reverse=True)
await ctx.bot._config.guild(ctx.guild).prefix.set(prefixes) await ctx.bot._prefix_cache.set_prefixes(guild=ctx.guild, prefixes=prefixes)
await ctx.send(_("Prefix set.")) await ctx.send(_("Prefix set."))
@_set.command() @_set.command()

View File

@ -0,0 +1,53 @@
from __future__ import annotations
from typing import Dict, List, Optional
from argparse import Namespace
import discord
from .config import Config
class PrefixManager:
def __init__(self, config: Config, cli_flags: Namespace):
self._config: Config = config
self._global_prefix_overide: Optional[List[str]] = sorted(
cli_flags.prefix, reverse=True
) or None
self._cached: Dict[Optional[int], List[str]] = {}
async def get_prefixes(self, guild: Optional[discord.Guild] = None) -> List[str]:
ret: List[str]
gid: Optional[int] = guild.id if guild else None
if gid in self._cached:
ret = self._cached[gid].copy()
else:
if gid is not None:
ret = await self._config.guild_from_id(gid).prefix()
if not ret:
ret = await self.get_prefixes(None)
else:
ret = self._global_prefix_overide or (await self._config.prefix())
self._cached[gid] = ret.copy()
return ret
async def set_prefixes(
self, guild: Optional[discord.Guild] = None, prefixes: Optional[List[str]] = None
):
gid: Optional[int] = guild.id if guild else None
prefixes = prefixes or []
if not isinstance(prefixes, list) and not all(isinstance(pfx, str) for pfx in prefixes):
raise TypeError("Prefixes must be a list of strings")
prefixes = sorted(prefixes, reverse=True)
if gid is None:
if not prefixes:
raise ValueError("You must have at least one prefix.")
self._cached.clear()
await self._config.prefix.set(prefixes)
else:
del self._cached[gid]
await self._config.guild_from_id(gid).prefix.set(prefixes)