Begin work on a data request API (#4045)

[Core] Data Deletion And Disclosure APIs

 - Adds a Data Deletion API
   - Deletion comes in a few forms based on who is requesting
   - Deletion must be handled by 3rd party
 - Adds a Data Collection Disclosure Command
   - Provides a dynamically generated statement from 3rd party
   extensions
 - Modifies the always available commands to be cog compatible
   - Also prevents them from being unloaded accidentally
This commit is contained in:
Michael H
2020-08-03 09:09:07 -04:00
committed by GitHub
parent bb1a256295
commit c0b1e50a5f
38 changed files with 1761 additions and 222 deletions

View File

@@ -138,12 +138,19 @@ class CogOrCommand(NamedTuple):
# noinspection PyArgumentList
@classmethod
async def convert(cls, ctx: commands.Context, arg: str) -> "CogOrCommand":
cog = ctx.bot.get_cog(arg)
if cog:
return cls(type="COG", name=cog.__class__.__name__, obj=cog)
cmd = ctx.bot.get_command(arg)
if cmd:
return cls(type="COMMAND", name=cmd.qualified_name, obj=cmd)
ret = None
if cog := ctx.bot.get_cog(arg):
ret = cls(type="COG", name=cog.qualified_name, obj=cog)
elif cmd := ctx.bot.get_command(arg):
ret = cls(type="COMMAND", name=cmd.qualified_name, obj=cmd)
if ret:
if isinstance(ret.obj, commands.commands._RuleDropper):
raise commands.BadArgument(
"You cannot apply permission rules to this cog or command."
)
return ret
raise commands.BadArgument(
_(

View File

@@ -2,7 +2,7 @@ import asyncio
import io
import textwrap
from copy import copy
from typing import Union, Optional, Dict, List, Tuple, Any, Iterator, ItemsView, cast
from typing import Union, Optional, Dict, List, Tuple, Any, Iterator, ItemsView, Literal, cast
import discord
import yaml
@@ -10,6 +10,7 @@ from schema import And, Or, Schema, SchemaError, Optional as UseOptional
from redbot.core import checks, commands, config
from redbot.core.bot import Red
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils import AsyncIter
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.menus import start_adding_reactions
from redbot.core.utils.predicates import ReactionPredicate, MessagePredicate
@@ -114,6 +115,56 @@ class Permissions(commands.Cog):
self.config.init_custom(COMMAND, 1)
self.config.register_custom(COMMAND)
async def red_delete_data_for_user(
self,
*,
requester: Literal["discord_deleted_user", "owner", "user", "user_strict"],
user_id: int,
):
if requester != "discord_deleted_user":
return
count = 0
_uid = str(user_id)
# The dict as returned here as string keys. Above is for comparison,
# there's a below recast to int where needed for guild ids
for typename, getter in ((COG, self.bot.get_cog), (COMMAND, self.bot.get_command)):
obj_type_rules = await self.config.custom(typename).all()
count += 1
if not count % 100:
await asyncio.sleep(0)
for obj_name, rules_dict in obj_type_rules.items():
count += 1
if not count % 100:
await asyncio.sleep(0)
obj = getter(obj_name)
for guild_id, guild_rules in rules_dict.items():
count += 1
if not count % 100:
await asyncio.sleep(0)
if _uid in guild_rules:
if obj:
# delegate to remove rule here
await self._remove_rule(
CogOrCommand(typename, obj.qualified_name, obj),
user_id,
int(guild_id),
)
else:
grp = self.config.custom(typename, obj_name)
await grp.clear_raw(guild_id, user_id)
async def __permissions_hook(self, ctx: commands.Context) -> Optional[bool]:
"""
Purpose of this hook is to prevent guild owner lockouts of permissions specifically
@@ -345,14 +396,6 @@ class Permissions(commands.Cog):
if not who_or_what:
await ctx.send_help()
return
if isinstance(cog_or_command.obj, commands.commands._AlwaysAvailableCommand):
await ctx.send(
_(
"This command is designated as being always available and "
"cannot be modified by permission rules."
)
)
return
for w in who_or_what:
await self._add_rule(
rule=cast(bool, allow_or_deny),
@@ -388,14 +431,6 @@ class Permissions(commands.Cog):
if not who_or_what:
await ctx.send_help()
return
if isinstance(cog_or_command.obj, commands.commands._AlwaysAvailableCommand):
await ctx.send(
_(
"This command is designated as being always available and "
"cannot be modified by permission rules."
)
)
return
for w in who_or_what:
await self._add_rule(
rule=cast(bool, allow_or_deny),
@@ -473,14 +508,6 @@ class Permissions(commands.Cog):
`<cog_or_command>` is the cog or command to set the default
rule for. This is case sensitive.
"""
if isinstance(cog_or_command.obj, commands.commands._AlwaysAvailableCommand):
await ctx.send(
_(
"This command is designated as being always available and "
"cannot be modified by permission rules."
)
)
return
await self._set_default_rule(
rule=cast(Optional[bool], allow_or_deny),
cog_or_cmd=cog_or_command,
@@ -504,14 +531,6 @@ class Permissions(commands.Cog):
`<cog_or_command>` is the cog or command to set the default
rule for. This is case sensitive.
"""
if isinstance(cog_or_command.obj, commands.commands._AlwaysAvailableCommand):
await ctx.send(
_(
"This command is designated as being always available and "
"cannot be modified by permission rules."
)
)
return
await self._set_default_rule(
rule=cast(Optional[bool], allow_or_deny), cog_or_cmd=cog_or_command, guild_id=GLOBAL
)