Ogólne rozporządzenie o ochronie danych (dane osobowe) jest europejskim rozporządzeniem w sprawie ochrony danych i prywatność, poufność. Daje osobom prawo do żądania usunięcia ich osobistych danych, znanych jako prawo do usunięcia. Jeśli przechowujesz jakiekolwiek osobiste dane identyfikujące Zamiast wysyłać wnioski ręcznie, możesz ustawić webhook i użyć botu w aplikacji czatu stron trzecich, aby automatyzować ten proces. Gdy przechowywanie danych jest najczęstszym sposobem na przechowywanie danych PII, ten samouczek pokazuje przykład na to, jak utworzyć bot w Guilded
Przepływ
Po ukończeniu tego samouczka powinieneś być w stanie stworzyć lokalnie uruchomiony program personalizowany, który automatyzuje zarządzanie wnioskami o usunięcie od użytkowników. Workflow dla tego procesu jest następujący:
- Roblox Support otrzymuje prawo do wniosku o usunięcie od użytkownika.
- Roblox webhook jest uruchomiony, zawierający ID użytkownika i listę ID miejsca startowego dla doświadczeń, które dołączyły do ładowania.
- Twój bot słucha tych powiadomień o webhookach, weryfikuje ich autentyczność i wykorzystuje Open Cloud API for Data Stores do usuwania przechowywanych danych w magazynach danych.
- Bot odpowiada na wiadomość webhook w Discord lub Guilded z statusem usunięcia.
Konfiguracja w Webhook z integracją stron trzecich
Zanim stworzysz Bota, skonfiguruj serwer z wbudowaną integracją wiadomości na aplikacji wiadomości trzeciej strony. Następnie użyj serwera, aby skonfigurować wiadomość w Bocie Creatora.
Uruchomienie serwera
Poniższe kroki pokazują, jak skonfigurować serwer używając Guilded lub Discord.
- Utwórz nowy serwer Guilded. Jeśli nie jesteś znajomy z procesem, zobacz Guilded Support.
- Pod ustawieniami prywatności, ustaw serwer na prywatne. Serwer automatycznie tworzy prywatny kanał #general jako twoją domyślną kamerę.
- Utwórz integrację webhook z nowym serwerem i nadaj mu imię, które możesz łatwo zrozumieć, takie jak GDPR Hook. Jeśli nie jesteś znajomy z procesem, zobacz Guilded Support.
- Kopiuj URL webhook i przechowywaj go w bezpiecznym miejsce. Umożliwь tylko zaufanym członkom zespołu dostępu do niego, ponieważ ujawnienie URL może umożliwić fałszywym aktorom wysyłanie fałszywych wiadomości i potencjalnie usunięcie twoich danych użytkownika.
Konfiguracja wtyczki w Roblox
Po uzyskaniu trzeciego adresu URL serwera użyj go, aby skonfigurować webhook na Dashboardu Twórcy. upewnij się, że wykonujesz następujące ustawienia:
- Dodaj URL serwera Guilded lub Discord jako Webhook URL .
- Włącz niestandardowy tajny . Chociaż tajny jest opcjonalny do ukończenia konfiguracji, powinieneś go włączyć, aby zapobiec złym aktom na impersonifikacji Roblox i usunięciu Twoich danych. Dla więcej informacji o użyciu tajnego, zobacz Zabezpieczanie bezpieczeństwa Webhook.
- Wybierz Prawo do żądania usunięcia pod Spowodami .
Możesz testować webhook używając przycisku Testuj odpowiedź, aby zobaczyć, czy otrzymujesz powiadomienie w kanałach # general Roblox. Jeśli nie otrzymujesz powiadomienia, spróbuj ponownie lub sprawdź ustawienia serwera, aby rozwiązać problem.
Konfiguracja Bota
Po dodaniu webhook'a użyj go, aby skonfigurować Bota za pomocą następujących kroków:
Otwórz listę Wszystkie serwery poprzez kliknięcie jego ikony lub użyj skrótu:
- CtrlS na Windows.
- ⌘S na Mac.
Wybierz swój serwer, aby otrzymywać wiadomości o prawie do usunięcia.
Rozwój listy pod Strona główna serwera i wybierz Zarządzaj Botami.
Serwer automatycznie tworzy prywatny kanał #general jako twój domyślny kanał.
Kliknij przycisk Utwórz bota i dodaj imię bota. Gilded przekieruje cię na stronę konfiguracji bota.
Wybierz sekcję API na stronie konfiguracji botów.
W sekcji Tokeny, kliknij przycisk Generuj Token.
Zapisz i przechowuj generowany token w bezpiecznym miejsce.
Tworzenie klucza API Open Cloud
Aby umożliwić swojemu trzeciemu botowi dostęp do twoich magazynów danych, aby przechowywać dane osobowe użytkowników, utwórz klucz API, który może uzyskać dostęp do twoich doświadczeń i dodać permisję Usuń wpis dla magazynów danych dla celu usunięcia
Zdobywanie identyfikatorów doświadczeń i miejsc
Aby Bot lokalizował dane PII wymagane przez użytkowników do usunięcia, uzyskaj następujące identyfikatory wszystkich doświadczeń, które chcesz użyć dla Bot:
- Uniwersalny identyfikator Twojego doświadczenia , unikalny identyfikator Twojego doświadczenia.
- Start Place ID, unikalny identyfikator początkowego miejsca doświadczenia.
Aby uzyskać te identyfikatory, otwórz stronę Kreacje na Panelu twórcy. Następnie wybierz doświadczenie i skopiuj Identyfikator uniwersum i 2>Początek miejsca2>.
Dodawanie Skryptów
Po ukończeniu ustawień webhook, bot i klucz API dla magazynów danych dodaj je do skryptów, które implementują logikę automatyzacji bot'a. Poniższy przykład używa Python 3:
Zainstaluj biblioteki Python za pomocą następujących komend:
Zainstaluj bibliotekipip3 install discordpip3 install guilded.py==1.8.0pip3 install requestspip3 install urllib3==1.26.6Kopiuj i zapisz następujące skrypty odpowiadające poszczególnym częściom logiki botów w tym samym katalogu:
bot_konfiguruj.pyDISCORD_BOT_TOKEN = ""GUILDED_BOT_TOKEN = ""OPEN_CLOUD_API_KEY = ""ROBLOX_WEBHOOK_SECRET = ""# Słownik ID miejsca startowego do# (identyfikator wszechświata, lista (nazwa magazynów danych, zakres i klucz wpisu)) dla# Standardowe magazyny danych# Dane użytkownika przechowywane pod tymi wpisami zostaną usunięteSTANDARD_DATA_STORE_ENTRIES = {# ID Miejsce Inicjacyjne111111111: (# ID wszechświata222222222,[("StandardDataStore1", "Scope1", "Key1_{user_id}"),("StandardDataStore1", "Scope1", "Key2_{user_id}"),("StandardDataStore2", "Scope1", "Key3_{user_id}")]),33333333: (444444444,[("StandardDataStore3", "Scope1", "Key1_{user_id}")])}# Słownik ID miejsca startowego do# (identyfikator wszechświata, lista (nazwa magazynów danych, zakres i klucz wpisu)) dla# Przechowywanie danych w porządku# Dane użytkownika przechowywane pod tymi wpisami zostaną usunięteORDERED_DATA_STORE_ENTRIES = {111111111: (222222222,[("OrderedDataStore1", "Scope2", "Key4_{user_id}")])}api_przechowywania_danych.pyimport requestsimport bot_configfrom collections import defaultdict"""Calls Data Stores Open Cloud API to delete all entries for a user_id configured inSTANDARD_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete."""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"""Calls Ordered Data Stores Open Cloud API to delete all entries for a user_id configured inORDERED_DATA_STORE_ENTRIES. Returns a list of successful deletions and failures to delete."""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"""Parses received message for Roblox signature and timestamp, the footer is only set if youconfigured webhook secret"""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"""Verifies Roblox signature with configured secret to check for validity"""def validate_signature(message, signature, timestamp):if not message or not signature or not timestamp:return False# Zapobiega atakowi odtwarzalności w ciągu 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# Zweryfikowuje 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# Prawidłowa podpisywaniereturn True"""Parses a received webhook messaged on Discord or Guilded. Extracts user ID, prevents replay attackbased on timestamp received, and verifies Roblox signature with configured secret to check forvalidity."""def parse_message(message):# Otrzymuje wiadomość dla użytkownika ID i gry IDif len(message.embeds) != 1 or \not message.embeds[0].description:return "", []description_match = re.match(r"You have received a new notification for Right to Erasure for the User Id: (.*) in " +r"the game\(s\) with 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 "", []guilded_bot.pyimport guildedimport jsonimport bot_configimport data_stores_apiimport message_parserdef run():client = guilded.Client()@client.eventasync def on_ready():print(f"{client.user} is listening to Right to Erasure messages")"""Handler for webhook messages from Roblox"""@client.eventasync def on_message(message):# P Parses i waliduje wiadomośćuser_id, start_place_ids = message_parser.parse_message(message)if not user_id or not start_place_ids:return# Usuwa standardowe magazyny danych użytkowników[successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Deleted standard data stores data for " +f"user ID: {user_id}, data: {dict(successes)}")if failures:await message.reply(f"Failed to delete standard data stores data for " +f"user ID: {user_id}, data: {dict(failures)}")# Usuwa przechowywane dane użytkownika[successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Deleted ordered data stores data for " +f"user ID: {user_id}, data: {dict(successes)}")if failures:await message.reply(f"Failed to delete ordered data stores data for " +f"user ID: {user_id}, data: {dict(failures)}")client.run(bot_config.GUILDED_BOT_TOKEN)if __name__ == "__main__":run()Na pliku bot_config.py dla głównej konfiguracji botu:
- Ustaw DISCORD_BOT_TOKEN lub GUILDED_BOT_TOKEN do generowanego przez twojego bota tokenu.
- Ustaw OPEN_CLOUD_API_KEY jako klucz API, który stworzyłeś.
- Ustaw ROBLOX_WEBHOOK_SECRET jako sekret, który ustawiłeś podczas konfiguracji webhook na Dashboardze Twórcy.
- W STANDARD_DATA_STORE_ENTRIES i ORDERED_DATA_STORE_ENTRIES słownikach do lokalizacji magazynu danych każdej rekordu do usuwać:
- Dodaj swoje skopiowane ID Start Place jako klucze.
- Dodaj ID wszechświata jako pierwszy element wartości zapisu.
- Zastąp drugi element tuplu imieniem, zakresem, nazwą klucza wpisu i związanym ID użytkownika twoich magazynów danych. Jeśli używasz innego schematu danych, modyfikuj zgodnie z własnym schematem danych.
Wykonaj następujące polecenie, aby uruchomić Bota:
Zacznij bota z guildąpython3 guilded_bot.pyBot wtedy zaczyna słuchać i weryfikować wszystkie wiadomości w Roblox Webhook, aby mieć prawo do usuwania wniosków o usunięcie i wezwywania Open Cloud Endpoint, aby usunąć odpowiednią przestrzeń dyskową.
Testowanie
Możesz utworzyć i z実行ować testową wiadomość, aby zweryfikować, że twój niestandardowy program może prawidłowo zarządzać wnioskami o usunięcie i usuwania danych PII:
Wyślij prośbę HTTP POST na swojego guilded lub Discord webhook server z następującym ciałoprośby:
Przykładowy zapiscurl -X POST {serverUrl}-H 'Content-Type: application/json'-d '{"embeds":[{"title":"RightToErasureRequest","description":"You have received a new notification for Right to Erasure for the User Id: {userIds} in the game(s) with Ids: {gameIds}","footer":{"icon_url":"https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png","text":"Roblox-Signature: {robloxSignature}, Timestamp: {timestamp}"}}]}'Jeśli masz sekret wtyczki witryny:
- Generuj Roblox-Signature poprzez zastosowanie szyfrowania HMAC-SHA256 do twojego klucza sekretnego wtyczki.
- Ustaw obecny czas używając czasu UTC w sekundach jako Timestamp .
Połącz description w następującej formie:
Description Field Format{Timestamp}. You have received a new notification for Right to Erasure for the User Id: {userId} in the game(s) with Ids: {gameIds}`.Na przykład:
Example Description Field1683927229. You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955
Twój program powinien być w stanie zidentyfikować, że wiadomość pochodzi od oficjalnego źródła Roblox, ponieważ zaszyfrowałeś wiadomość swoim sekretem. Następnie powinien usunąć dane PII związane z twoim prośba.
Przykładowy organizm
{
"embeds": [
{
"title": "RightToErasureRequest",
"description": "You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955",
"footer": {
"icon_url": "https://create.roblox.com/dash/ assets/webhook/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}