Die Allgemeine Datenschutzverordnung (DSGVO) ist eine europäische Verordnung zum Datenschutz und zur Privatsphäre. Sie gewährt Einzelpersonen das Recht, die Löschung ihrer personenbezogenen Daten zu verlangen, bekannt als das Recht auf Löschung. Wenn Sie personenbezogene Daten (PII) Ihrer Benutzer speichern, wie z. B. deren Benutzer-IDs, müssen Sie die Anforderungen der DSGVO einhalten, indem Sie diese Informationen nach Erhalt einer Anfrage des Benutzers löschen.
Anstatt Anfragen manuell zu bearbeiten, können Sie einen Webhook einrichten und einen Bot innerhalb einer Messaging-Anwendung eines Drittanbieters verwenden, um den Prozess zu automatisieren. Da Datenspeicher die gängigste Möglichkeit sind, PII-Daten zu speichern, bietet dieses Tutorial ein Beispiel dafür, wie man einen Bot in Discord erstellt, der die Open Cloud API für Datenspeicher verwendet, um PII-Daten als Lösung zur Automatisierung zu löschen.
Workflow
Nach Abschluss dieses Tutorials sollten Sie in der Lage sein, ein lokal laufendes benutzerdefiniertes Programm zu erstellen, das die Bearbeitung von Anfragen zum Recht auf Löschung von Benutzern automatisiert. Der Workflow für diesen Prozess ist wie folgt:
- Der Roblox-Support erhält eine Anfrage zum Recht auf Löschung von einem Benutzer.
- Der Roblox-Webhook wird ausgelöst, enthält die Benutzer-ID und eine Liste von Start-Ort-IDs für die Spiele, an denen sie im Payload teilgenommen haben.
- Ihr Bot hört auf diese Webhook-Benachrichtigungen, überprüft deren Authentizität und nutzt die Open Cloud API für Datenspeicher, um die in den Datenspeichern gespeicherten PII-Daten zu löschen.
- Der Bot antwortet auf die Webhook-Nachricht in Discord mit dem Löschstatus.

Konfigurieren Sie einen Webhook mit Integration von Drittanbietern
Bevor Sie einen Bot erstellen, richten Sie einen Server mit Webhook-Integration in der Messaging-Anwendung eines Drittanbieters ein. Verwenden Sie dann den Server, um einen Webhook im Creator-Dashboard zu konfigurieren.
Richten Sie einen Server ein
Die folgenden Schritte zeigen, wie Sie den Server mit Discord einrichten.
Erstellen Sie einen neuen Discord-Server. Wenn Sie mit dem Prozess nicht vertraut sind, siehe Discord-Support.
Der Server erstellt automatisch einen Kanal #general als Ihren Standardkanal. Klicken Sie auf das Kanal bearbeiten-Symbol des #general-Kanals.
Setzen Sie unter Berechtigungen den Kanal auf privat.
Erstellen Sie eine Webhook-Integration mit dem neuen Server und benennen Sie sie in GDPR Hook. Wenn Sie mit dem Prozess nicht vertraut sind, siehe Discord-Support.
Kopieren Sie die Webhook-URL und bewahren Sie sie an einem sicheren Ort auf. Nur vertrauenswürdigen Teammitgliedern sollte der Zugriff gewährt werden, da das Leaking der URL es böswilligen Akteuren ermöglichen kann, gefälschte Nachrichten zu senden und möglicherweise Ihre Benutzerdaten zu löschen.
Konfigurieren Sie einen Webhook in Roblox
Nachdem Sie die URL des Drittanbieterservers erhalten haben, verwenden Sie sie, um einen Webhook zu konfigurieren im Creator-Dashboard. Stellen Sie sicher, dass Sie die folgenden Einstellungen vornehmen:
- Fügen Sie die Discord-Server-URL als Webhook-URL hinzu.
- Fügen Sie ein benutzerdefiniertes Secret hinzu. Obwohl ein Secret optional ist, um die Konfiguration abzuschließen, sollten Sie eines hinzufügen, um zu verhindern, dass böswillige Akteure sich als Roblox ausgeben und Ihre Daten löschen. Weitere Informationen zur Verwendung eines Secrets finden Sie unter Webhook-Sicherheit überprüfen.
- Wählen Sie Anfrage zum Recht auf Löschung unter Trigger aus.
Sie können den Webhook mit der Testantwort-Schaltfläche testen, um zu sehen, ob Sie eine Benachrichtigung im #general-Kanal Ihres Servers von Roblox erhalten. Wenn Sie die Benachrichtigung nicht erhalten, versuchen Sie es erneut oder überprüfen Sie Ihre Servereinstellungen zur Fehlersuche.
Konfigurieren Sie einen Bot
Nachdem Sie den Webhook hinzugefügt haben, verwenden Sie ihn, um den Bot mit den folgenden Schritten zu konfigurieren. Für weitere Informationen siehe die Discord-Dokumentation.
- Navigieren Sie zur Anwendungsseite.
- Erstellen Sie eine neue Anwendung und benennen Sie sie in GDPR Bot.
- Das System leitet Sie zu den Einstellungen für Allgemeine Informationen des Bots weiter. Kopieren Sie die Anwendungs-ID und bewahren Sie sie an einem sicheren Ort auf.
- Wählen Sie im Einstellungsmenü OAuth2 aus.
- Navigieren Sie zu den OAuth2-Einstellungen.
- Aktivieren Sie den Bot-Scope. Eine Liste zusätzlicher Bot-Berechtigungen wird angezeigt.
- Fügen Sie die Berechtigung Administrator hinzu. Speichern Sie Ihre Änderungen.
- Speichern Sie die generierte URL.
- Kopieren Sie die generierte URL. Lassen Sie die Anwendungsseiteneinstellungen offen.
- Navigieren Sie zur generierten URL. Wählen Sie den Zielserver aus. Klicken Sie auf die Schaltfläche Weiter und dann auf die Schaltfläche Autorisieren.
- Navigieren Sie zurück zur Anwendungsseiteneinstellungen und dann zu den Bot-Einstellungen.
- Aktivieren Sie im Abschnitt Privileged Gateway Intents die Message Content Intent.
- Speichern Sie im Bot-Einstellungen > Build-A-Bot-Abschnitt das Bot-Token an einem sicheren Ort für spätere Schritte. Wenn Sie das Token nicht sehen, klicken Sie auf die Schaltfläche Token zurücksetzen, um ein neues zu generieren.
Erstellen Sie einen Open Cloud API-Schlüssel
Um Ihrem Bot von Drittanbietern den Zugriff auf Ihre Datenspeicher zur Speicherung von PII-Daten der Benutzer zu ermöglichen, erstellen Sie einen Open Cloud API-Schlüssel, der auf Ihre Spiele zugreifen kann, und fügen Sie die Berechtigung Eintrag löschen der Datenspeicher für die Datenlöschung hinzu. Wenn Sie geordnete Datenspeicher zur Speicherung von PII verwenden, müssen Sie auch die Berechtigung Schreiben der geordneten Datenspeicher hinzufügen. Nach Abschluss kopieren und speichern Sie den API-Schlüssel an einem sicheren Ort, um ihn in späteren Schritten zu verwenden.
Beschaffen Sie die Identifikatoren von Spielen und Orten
Damit der Bot die PII-Daten lokalisieren kann, die von Benutzern zur Löschung angefordert werden, beschaffen Sie die folgenden Identifikatoren aller Spiele, die Sie mit dem Bot verwenden möchten:
- Die Universums-ID, die eindeutige Kennung Ihres Spiels.
- Die Start-Ort-ID, die eindeutige Kennung des Startorts eines Spiels.
Um diese Identifikatoren zu erhalten:
Navigieren Sie zum Creator-Dashboard.
Fahren Sie mit der Maus über das Miniaturbild eines Spiels, klicken Sie auf die Schaltfläche ⋯ und wählen Sie Universums-ID kopieren und Start-Ort-ID kopieren aus.

Fügen Sie Skripte hinzu
Nachdem Sie den Webhook, den Bot und den API-Schlüssel für Datenspeicher eingerichtet haben, fügen Sie diese in die Skripte ein, die die Automatisierungslogik des Bots implementieren. Das folgende Beispiel verwendet Python 3.
Installieren Sie die Python-Bibliotheken mit den folgenden Befehlen:
Bibliotheken installierenpip3 install discordpip3 install requestspip3 install urllib3==1.26.6Kopieren und speichern Sie die folgenden Skripte, die verschiedenen Teilen der Bot-Logik entsprechen, im selben Verzeichnis:
bot_config.pyBOT_TOKEN = ""OPEN_CLOUD_API_KEY = ""ROBLOX_WEBHOOK_SECRET = ""# Wörterbuch der Start-Ort-ID an# (Universums-ID, Liste von (Name der Datenspeicher, Scope und Einstiegsschlüssel)) für# Standard-Datenspeicher# Benutzerdaten, die unter diesen Einträgen gespeichert sind, werden gelöschtSTANDARD_DATA_STORE_ENTRIES = {# Start-Ort-ID111111111: (# Universums-ID222222222,[("StandardDataStore1", "Scope1", "Key1_{user_id}"),("StandardDataStore1", "Scope1", "Key2_{user_id}"),("StandardDataStore2", "Scope1", "Key3_{user_id}")]),33333333: (444444444,[("StandardDataStore3", "Scope1", "Key1_{user_id}")])}# Wörterbuch der Start-Ort-ID an# (Universums-ID, Liste von (Name der Datenspeicher, Scope und Einstiegsschlüssel)) für# Geordnete Datenspeicher# Benutzerdaten, die unter diesen Einträgen gespeichert sind, werden gelöschtORDERED_DATA_STORE_ENTRIES = {111111111: (222222222,[("OrderedDataStore1", "Scope2", "Key4_{user_id}")])}data_stores_api.pyimport requestsimport bot_configfrom collections import defaultdict"""Ruft die Data Stores Open Cloud API auf, um alle Einträge für eine benutzer_id zu löschen, die inSTANDARD_DATA_STORE_ENTRIES konfiguriert ist. Gibt eine Liste erfolgreicher Löschungen und fehlgeschlagener Löschungen zurück."""def delete_standard_data_stores(user_id, start_place_ids):successes = defaultdict(list)failures = defaultdict(list)for owned_start_place_id in bot_config.STANDARD_DATA_STORE_ENTRIES:if owned_start_place_id not in start_place_ids:continueuniverse_id, universe_entries = bot_config.STANDARD_DATA_STORE_ENTRIES[owned_start_place_id]for (data_store_name, scope, entry_key) in universe_entries:entry_key = entry_key.replace("{user_id}", user_id)response = requests.delete(f"https://apis.roblox.com/datastores/v1/universes/{universe_id}/standard-datastores/datastore/entries/entry",headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY},params={"datastoreName": data_store_name,"scope": scope,"entryKey": entry_key})if response.status_code in [200, 204]:successes[owned_start_place_id].append((data_store_name, scope, entry_key))else:failures[owned_start_place_id].append((data_store_name, scope, entry_key))return successes, failures"""Ruft die Ordered Data Stores Open Cloud API auf, um alle Einträge für eine benutzer_id zu löschen, die inORDERED_DATA_STORE_ENTRIES konfiguriert ist. Gibt eine Liste erfolgreicher Löschungen und fehlgeschlagener Löschungen zurück."""def delete_ordered_data_stores(user_id, start_place_ids):successes = defaultdict(list)failures = defaultdict(list)for owned_start_place_id in bot_config.ORDERED_DATA_STORE_ENTRIES:if owned_start_place_id not in start_place_ids:continueuniverse_id, universe_entries = bot_config.ORDERED_DATA_STORE_ENTRIES[owned_start_place_id]for (data_store_name, scope, entry_key) in universe_entries:entry_key = entry_key.replace("{user_id}", user_id)response = requests.delete(f"https://apis.roblox.com/ordered-data-stores/v1/universes/{universe_id}/orderedDatastores/{data_store_name}/scopes/{scope}/entries/{entry_key}",headers={"x-api-key": bot_config.OPEN_CLOUD_API_KEY})if response.status_code in [200, 204, 404]:successes[owned_start_place_id].append((data_store_name, scope, entry_key))else:failures[owned_start_place_id].append((data_store_name, scope, entry_key))return successes, failuresmessage_parser.pyimport timeimport hmacimport hashlibimport reimport base64import bot_config"""Analysiert die empfangene Nachricht nach der Roblox-Signatur und dem Zeitstempel, der Footer wird nur gesetzt, wenn Siedas Webhook-Secret konfiguriert haben."""def parse_footer(message):if not message.embeds[0].footer or \not message.embeds[0].footer.text:return "", 0footer_match = re.match(r"Roblox-Signature: (.*), Timestamp: (.*)",message.embeds[0].footer.text)if not footer_match:return "", 0else:signature = footer_match.group(1)timestamp = int(footer_match.group(2))return signature, timestamp"""Überprüft die Roblox-Signatur mit dem konfigurierten Secret auf ihre Gültigkeit."""def validate_signature(message, signature, timestamp):if not message or not signature or not timestamp:return False# Verhindert Wiederholungsangriffe im 300 Sekundenfensterrequest_timestamp_ms = timestamp * 1000window_time_ms = 300 * 1000oldest_timestamp_allowed = round(time.time() * 1000) - window_time_msif request_timestamp_ms < oldest_timestamp_allowed:return False# Validiert die Signaturtimestamp_message = "{}.{}".format(timestamp, message.embeds[0].description)digest = hmac.new(bot_config.ROBLOX_WEBHOOK_SECRET.encode(),msg=timestamp_message.encode(),digestmod=hashlib.sha256).digest()validated_signature = base64.b64encode(digest).decode()if signature != validated_signature:return False# Gültige Signaturreturn True"""Analysiert eine empfangene Webhook-Nachricht auf Discord. Extrahiert die Benutzer-ID, verhindert Wiederholungsangriffeauf der Grundlage des empfangenen Zeitstempels und überprüft die Roblox-Signatur mit dem konfigurierten Secret auf ihre Gültigkeit."""def parse_message(message):# Analysiert die empfangene Nachricht nach Benutzer-ID und Spiel-IDif len(message.embeds) != 1 or \not message.embeds[0].description:return "", []description_match = re.match(r"Sie haben eine neue Benachrichtigung für das Recht auf Löschung für die Benutzer-ID: (.*) in " +r"den Spiel(en) mit IDs: (.*)",message.embeds[0].description)if not description_match:return "", []user_id = description_match.group(1)start_place_ids = set(int(item.strip()) for item in description_match.group(2).split(","))signature, timestamp = parse_footer(message)if validate_signature(message, signature, timestamp):return user_id, start_place_idselse:return "", []discord_bot.pyimport discordimport bot_configimport data_stores_apiimport message_parserdef run():intents = discord.Intents.default()intents.message_content = Trueclient = discord.Client(intents=intents)@client.eventasync def on_ready():print(f"{client.user} hört auf Nachrichten zum Recht auf Löschung")"""Handler für Webhook-Nachrichten von Roblox"""@client.eventasync def on_message(message):# Analysiert und validiert die Nachrichtuser_id, start_place_ids = message_parser.parse_message(message)if not user_id or not start_place_ids:return# Löscht die Benutzer Daten im Standard-Datenspeicher[successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Gelöschte Daten im Standard-Datenspeicher für " +f"Benutzer-ID: {user_id}, Daten: {dict(successes)}")if failures:await message.reply(f"Fehler beim Löschen von Daten im Standard-Datenspeicher für " +f"Benutzer-ID: {user_id}, Daten: {dict(failures)}")# Löscht die Benutzer Daten im geordneten Datenspeicher[successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Gelöschte Daten im geordneten Datenspeicher für " +f"Benutzer-ID: {user_id}, Daten: {dict(successes)}")if failures:await message.reply(f"Fehler beim Löschen von Daten im geordneten Datenspeicher für " +f"Benutzer-ID: {user_id}, Daten: {dict(failures)}")client.run(bot_config.BOT_TOKEN)if __name__ == "__main__":run()In der Datei bot_config.py für die Hauptkonfiguration des Bots:
- Setzen Sie BOT_TOKEN auf das Token, das von Ihrem Bot generiert wurde.
- Setzen Sie OPEN_CLOUD_API_KEY auf den von Ihnen erstellten API-Schlüssel.
- Setzen Sie ROBLOX_WEBHOOK_SECRET auf das Secret, das Sie bei der Konfiguration des Webhooks im Creator-Dashboard festgelegt haben.
- In den Wörterbüchern STANDARD_DATA_STORE_ENTRIES und ORDERED_DATA_STORE_ENTRIES zum Auffinden des Datenspeichers für jeden zu löschenden Eintrag:
- Fügen Sie Ihre kopierten Start-Ort-IDs als Schlüssel hinzu.
- Fügen Sie Universums-IDs als erstes Element des Tupelwerts hinzu.
- Ersetzen Sie das zweite Element des Tupels durch den Namen, den Scope, den Einstiegsschlüssel und die zugehörige Benutzer-ID Ihrer Datenspeicher. Wenn Sie ein anderes Datenschema verwenden, passen Sie das entsprechend an.
Führen Sie den folgenden Befehl aus, um den Bot zu starten:
Discord Bot ausführenpython3 discord_bot.pyDer Bot beginnt dann, Roblox-Webhook-Nachrichten für Anfragen zum Recht auf Löschung zu hören und ruft den Open Cloud-Endpunkt zum Löschen des entsprechenden Datenspeichers auf.
Testen
Sie können eine Testnachricht erstellen und senden, um zu überprüfen, ob Ihr benutzerdefiniertes Programm Anfragen zum Recht auf Löschung ordnungsgemäß bearbeiten und PII-Daten löschen kann:
Senden Sie eine HTTP POST-Anfrage an Ihren Discord-Webhooks-Server mit folgendem Anfragekörper:
Beispielanfragecurl -X POST {serverUrl}-H 'Content-Type: application/json'-d '{"embeds":[{"title":"RightToErasureRequest","description":"Sie haben eine neue Benachrichtigung für das Recht auf Löschung für die Benutzer-ID: {userIds} in den Spiel(en) mit IDs: {gameIds}","footer":{"icon_url":"https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png","text":"Roblox-Signature: {robloxSignature}, Timestamp: {timestamp}"}}]}'Wenn Sie ein Webhook-Secret haben:
- Generieren Sie eine Roblox-Signature, indem Sie HMAC-SHA256-Encoding auf Ihren Webhook-Secret-Schlüssel anwenden.
- Setzen Sie die aktuelle Zeit unter Verwendung des UTC-Zeitstempels in Sekunden als Timestamp.
Stellen Sie den description-Text in folgendem Format zusammen:
Format des Beschreibungsfeldes{Timestamp}. Sie haben eine neue Benachrichtigung für das Recht auf Löschung für die Benutzer-ID: {userId} in den Spiel(en) mit IDs: {gameIds}`.Zum Beispiel:
Beispiel für das Beschreibungsfeld1683927229. Sie haben eine neue Benachrichtigung für das Recht auf Löschung für die Benutzer-ID: 2425654247 in den Spiel(en) mit IDs: 10539205763, 13260950955
Ihr Programm sollte in der Lage sein zu erkennen, dass Ihre Nachricht von der offiziellen Roblox-Quelle stammt, da Sie die Nachricht mit Ihrem Secret codiert haben. Es sollte dann die mit Ihrer Anfrage verbundenen PII-Daten löschen.
Beispielkörper
{
"embeds": [
{
"title": "RightToErasureRequest",
"description": "Sie haben eine neue Benachrichtigung für das Recht auf Löschung für die Benutzer-ID: 2425654247 in den Spiel(en) mit IDs: 10539205763, 13260950955",
"footer": {
"icon_url": "https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}