Michael H 3d6020b9cf [V3/permissions] Performance improvements (#1885)
* basic caching layer

* bit more work, now with an upper size to the cache

* cache fix

* smarter cache invalidation

* One more cache case

* Put in a bare skeleton of something else still needed

* more logic handling improvements

* more work, still not finished

* mass-resolve is done in theory, but needs testing

* small bugfixin + comments

* add note about before/after hooks

* LRU-dict fix

* when making comments about optimizations, provide historical context

* fmt pass
2018-07-12 02:56:08 +02:00

82 lines
2.4 KiB
Python

import types
import contextlib
import asyncio
import logging
from redbot.core import commands
log = logging.getLogger("redbot.cogs.permissions.resolvers")
def entries_from_ctx(ctx: commands.Context) -> tuple:
voice_channel = None
with contextlib.suppress(Exception):
voice_channel = ctx.author.voice.voice_channel
entries = [x.id for x in (ctx.author, voice_channel, ctx.channel) if x]
roles = sorted(ctx.author.roles, reverse=True) if ctx.guild else []
entries.extend([x.id for x in roles])
# entries now contains the following (in order) (if applicable)
# author.id
# author.voice.voice_channel.id
# channel.id
# role.id for each role (highest to lowest)
# (implicitly) guild.id because
# the @everyone role shares an id with the guild
return tuple(entries)
async def val_if_check_is_valid(*, ctx: commands.Context, check: object, level: str) -> bool:
"""
Returns the value from a check if it is valid
"""
val = None
# let's not spam the console with improperly made 3rd party checks
try:
if asyncio.iscoroutinefunction(check):
val = await check(ctx, level=level)
else:
val = check(ctx, level=level)
except Exception as e:
# but still provide a way to view it (run with debug flag)
log.debug(str(e))
return val
def resolve_models(*, ctx: commands.Context, models: dict) -> bool:
"""
Resolves models in order.
"""
cmd_name = ctx.command.qualified_name
cog_name = ctx.cog.__class__.__name__
resolved = None
to_iter = (("commands", cmd_name), ("cogs", cog_name))
for model_name, ctx_attr in to_iter:
if ctx_attr in models.get(model_name, {}):
blacklist = models[model_name][ctx_attr].get("deny", [])
whitelist = models[model_name][ctx_attr].get("allow", [])
resolved = resolve_lists(ctx=ctx, whitelist=whitelist, blacklist=blacklist)
if resolved is not None:
return resolved
resolved = models[model_name][ctx_attr].get("default", None)
if resolved is not None:
return resolved
return None
def resolve_lists(*, ctx: commands.Context, whitelist: list, blacklist: list) -> bool:
"""
resolves specific lists
"""
for entry in entries_from_ctx(ctx):
if entry in whitelist:
return True
if entry in blacklist:
return False
return None