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

@@ -1,9 +1,10 @@
import asyncio
import re
import random
from datetime import datetime, timedelta
from inspect import Parameter
from collections import OrderedDict
from typing import Iterable, List, Mapping, Tuple, Dict, Set
from typing import Iterable, List, Mapping, Tuple, Dict, Set, Literal
from urllib.parse import quote_plus
import discord
@@ -11,7 +12,7 @@ from fuzzywuzzy import process
from redbot.core import Config, checks, commands
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils import menus
from redbot.core.utils import menus, AsyncIter
from redbot.core.utils.chat_formatting import box, pagify, escape, humanize_list
from redbot.core.utils.predicates import MessagePredicate
@@ -40,15 +41,35 @@ class OnCooldown(CCError):
class CommandObj:
def __init__(self, **kwargs):
config = kwargs.get("config")
self.config = kwargs.get("config")
self.bot = kwargs.get("bot")
self.db = config.guild
self.db = self.config.guild
@staticmethod
async def get_commands(config) -> dict:
_commands = await config.commands()
return {k: v for k, v in _commands.items() if _commands[k]}
async def redact_author_ids(self, user_id: int):
all_guilds = await self.config.all_guilds()
for guild_id in all_guilds.keys():
await asyncio.sleep(0)
async with self.config.guild_from_id(guild_id).commands() as all_commands:
async for com_name, com_info in AsyncIter(all_commands.items(), steps=100):
if not com_info:
continue
if com_info.get("author", {}).get("id", 0) == user_id:
com_info["author"]["id"] = 0xDE1
com_info["author"]["name"] = "Deleted User"
if editors := com_info.get("editors", None):
for index, editor_id in enumerate(editors):
if editor_id == user_id:
editors[index] = 0xDE1
async def get_responses(self, ctx):
intro = _(
"Welcome to the interactive random {cc} maker!\n"
@@ -200,6 +221,17 @@ class CustomCommands(commands.Cog):
self.commandobj = CommandObj(config=self.config, bot=self.bot)
self.cooldowns = {}
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
await self.commandobj.redact_author_ids(user_id)
@commands.group(aliases=["cc"])
@commands.guild_only()
async def customcom(self, ctx: commands.Context):
@@ -209,7 +241,7 @@ class CustomCommands(commands.Cog):
@customcom.command(name="raw")
async def cc_raw(self, ctx: commands.Context, command: str.lower):
"""Get the raw response of a custom command, to get the proper markdown.
This is helpful for copy and pasting."""
commands = await self.config.guild(ctx.guild).commands()
if command not in commands:
@@ -472,12 +504,14 @@ class CustomCommands(commands.Cog):
if isinstance(responses, str):
responses = [responses]
author = ctx.guild.get_member(cmd["author"]["id"])
# If the author is still in the server, show their current name
if author:
author = "{} ({})".format(author, cmd["author"]["id"])
_aid = cmd["author"]["id"]
if _aid == 0xDE1:
author = _("Deleted User")
elif member := ctx.guild.get_member(_aid):
author = f"{member} ({_aid})"
else:
author = "{} ({})".format(cmd["author"]["name"], cmd["author"]["id"])
author = f"{cmd['author']['name']} ({_aid})"
_type = _("Random") if len(responses) > 1 else _("Normal")