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

@@ -3,12 +3,12 @@ from __future__ import annotations
import asyncio
import logging
from datetime import datetime, timedelta
from typing import List, Union, Optional, cast, TYPE_CHECKING
from typing import List, Literal, Union, Optional, cast, TYPE_CHECKING
import discord
from redbot.core import Config
from .utils import AsyncIter
from .utils.common_filters import (
filter_invites,
filter_mass_mentions,
@@ -47,10 +47,41 @@ _CASETYPES = "CASETYPES"
_CASES = "CASES"
_SCHEMA_VERSION = 4
_data_deletion_lock = asyncio.Lock()
_ = Translator("ModLog", __file__)
async def _process_data_deletion(
*, requester: Literal["discord_deleted_user", "owner", "user", "user_strict"], user_id: int
):
if requester != "discord_deleted_user":
return
# Oh, how I wish it was as simple as I wanted...
key_paths = []
async with _data_deletion_lock:
all_cases = await _config.custom(_CASES).all()
async for guild_id_str, guild_cases in AsyncIter(all_cases.items(), steps=100):
async for case_num_str, case in AsyncIter(guild_cases.items(), steps=100):
for keyname in ("user", "moderator", "amended_by"):
if (case.get(keyname, 0) or 0) == user_id: # this could be None...
key_paths.append((guild_id_str, case_num_str))
async with _config.custom(_CASES).all() as all_cases:
for guild_id_str, case_num_str in key_paths:
case = all_cases[guild_id_str][case_num_str]
if (case.get("user", 0) or 0) == user_id:
case["user"] = 0xDE1
case.pop("last_known_username", None)
if (case.get("moderator", 0) or 0) == user_id:
case["moderator"] = 0xDE1
if (case.get("amended_by", 0) or 0) == user_id:
case["amended_by"] = 0xDE1
async def _init(bot: Red):
global _config
global _bot_ref
@@ -310,8 +341,11 @@ class Case:
moderator = _("Unknown")
elif isinstance(self.moderator, int):
# can't use _() inside f-string expressions, see bpo-36310 and red#3818
translated = _("Unknown or Deleted User")
moderator = f"[{translated}] ({self.moderator})"
if self.moderator == 0xDE1:
moderator = _("Deleted User.")
else:
translated = _("Unknown or Deleted User")
moderator = f"[{translated}] ({self.moderator})"
else:
moderator = escape_spoilers(f"{self.moderator} ({self.moderator.id})")
until = None
@@ -329,8 +363,11 @@ class Case:
amended_by = None
elif isinstance(self.amended_by, int):
# can't use _() inside f-string expressions, see bpo-36310 and red#3818
translated = _("Unknown or Deleted User")
amended_by = f"[{translated}] ({self.amended_by})"
if self.amended_by == 0xDE1:
amended_by = _("Deleted User.")
else:
translated = _("Unknown or Deleted User")
amended_by = f"[{translated}] ({self.amended_by})"
else:
amended_by = escape_spoilers(f"{self.amended_by} ({self.amended_by.id})")
@@ -341,7 +378,9 @@ class Case:
)
if isinstance(self.user, int):
if self.last_known_username is None:
if self.user == 0xDE1:
user = _("Deleted User.")
elif self.last_known_username is None:
# can't use _() inside f-string expressions, see bpo-36310 and red#3818
translated = _("Unknown or Deleted User")
user = f"[{translated}] ({self.user})"