Genel Veri Koruma Yönetmeliği (GDPR), veri koruma ve gizlilik hakkında Avrupa düzenlemesidir. Bireylere, kişisel verilerinin silinmesini talep etme hakkı verir, bu hak silme hakkı olarak bilinir. Kullanıcılarınızın Kişisel Olarak Tanınabilir Bilgilerini (PII) depoluyorsanız, bir kullanıcının talebi alındığında bu bilgilerin silinmesi yoluyla GDPR gerekliliklerine uymalısınız.
Talepleri elle yönetmek yerine, bir webhook ayarlayabilir ve süreci otomatikleştirmek için bir üçüncü taraf mesajlaşma uygulamasında bir bot kullanabilirsiniz. PII verilerini depolamanın en yaygın yolu veri depoları olduğu için, bu eğitimde bir Discord botu oluşturmayı ve veri depoları için Açık Bulut API'sini kullanarak PII verilerini silmeyi otomatikleştiren bir örnek verilmektedir.
İş Akışı
Bu eğitimi tamamladığınızda, kullanıcıların silme hakkı taleplerini otomatik olarak işleyen, yerel olarak çalışan özelleştirilmiş bir program oluşturabilmelisiniz. Bu süreç için iş akışı şu şekildedir:
- Roblox Destek, bir kullanıcıdan silme hakkı talebi alır.
- Roblox webhook'u, yük içinde Kullanıcı Kimliği ve katıldıkları oyunların Başlangıç Yerleri Kimlikleri listesini içeren bir bildirim gönderir.
- Botunuz bu webhook bildirimlerini dinler, doğruluğunu kontrol eder ve veri depolarında depolanan PII verilerini silmek için Açık Bulut API'sini kullanır.
- Bot, Discord'daki webhook mesajına silme durumunu yanıtlar.

Üçüncü taraf entegrasyon ile bir webhook yapılandırın
Öncelikle bir bot oluşturmadan önce, üçüncü taraf mesajlaşma uygulamasında webhook entegrasyonu ile bir sunucu oluşturun. Ardından, Creator Dashboard'da bir webhook yapılandırmak için bu sunucuyu kullanın.
Bir sunucu oluşturun
Aşağıdaki adımlar, Discord kullanarak sunucuyu nasıl kuracağınızı göstermektedir.
Yeni bir Discord sunucusu oluşturun. Süreci bilmiyorsanız, lütfen Discord Destek sayfasına bakın.
Sunucu otomatik olarak varsayılan kanal olarak #genel kanalını oluşturur. #genel kanalının Kanalı Düzenle simgesine tıklayın.
İzinler altında, kanalı özel hale getirin.
Yeni sunucu ile bir webhook entegrasyonu oluşturun, adını GDPR Hook olarak belirleyin. Süreci bilmiyorsanız, Discord Destek sayfasına başvurun.
Webhook URL'sini kopyalayın ve güvenli bir yerde saklayın. URL'nin ifşa edilmesine yalnızca güvenilir ekip üyelerinin erişmesine izin verin, çünkü URL'nin sızdırılması kötü niyetli kişilerin sahte mesajlar göndermesine ve kullanıcı verilerinizi silmesine olanak tanıyabilir.
Roblox'da bir webhook yapılandırın
Üçüncü taraf sunucu URL'sini aldıktan sonra, bunu Creator Dashboard'da bir webhook yapılandırmak için kullanın, aşağıdaki ayarları kesinlikle gerçekleştirdiğinizden emin olun:
- Discord sunucu URL'sini Webhook URL'si olarak ekleyin.
- Özel bir Gizli Anahtar ekleyin. Gizli bir anahtar ayarları tamamlamak için isteğe bağlı olsa da, kötü niyetli kişilerden Roblox'u taklit etmelerini ve verilerinizi silmelerini önlemek için bir tane dahil etmelisiniz. Gizli anahtarın kullanımına dair daha fazla bilgi için Webhook güvenliğini doğrula sayfasına bakın.
- Tetikler altında Silme Hakkı Talebi seçeneğini belirleyin.
Webhook'unuzu test etmek için Test Yanıtı düğmesini kullanarak Roblox'dan sunucunuzun #genel kanalında bir bildirim alıp almadığınızı kontrol edebilirsiniz. Bildirimi almazsanız, yeniden deneyin veya hata ayıklamak için sunucu ayarlarınızı kontrol edin.
Bir bot yapılandırın
Webhook'u ekledikten sonra, botu şu adımlarla yapılandırmak için kullanın. Daha fazla bilgi için lütfen Discord belgeleri sayfasına bakın.
- Uygulamalar sayfasına gidin.
- Yeni bir uygulama oluşturun ve adını GDPR Bot koyun.
- Sistem sizi botun Genel Bilgiler ayarlarına yönlendirecektir. Uygulama ID'sini kopyalayın ve güvenli bir yerde saklayın.
- Ayarlar menüsünde OAuth2'yi seçin.
- OAuth2 ayarlarına gidin.
- bot kapsamını etkinleştirin; ek Bot İzinleri listesi görüntülenecektir.
- Yönetici iznini ekleyin. Değişikliklerinizi kaydedin.
- Oluşan URL'yi kaydedin.
- Oluşan URL'yi kopyalayın. Uygulama ayarlar sayfasını kapatmayın.
- Üzerinde oluşturulan URL'ye gidin. Hedef sunucuyu seçin. Devam butonuna tıklayın ve ardından Yetkilendir butonuna tıklayın.
- Uygulama ayarları sayfasına geri dönün ve Bot ayarlarına gidin.
- Yetkili Ağ Geçidi Niyetleri bölümünde Mesaj İçeriği Niyeti'ni etkinleştirin.
- Bot ayarları > Bot Oluştur bölümünde bot belirtecini güvenli bir yere kaydedin; sonraki adımlar için gerekecektir. Belirteci görmüyorsanız, yeni bir tane oluşturmak için Belirteci Sıfırla butonuna tıklayın.
Açık Bulut API anahtarı oluşturun
Üçüncü taraf botunuzun kullanıcıların PII verilerini depolamak için veri depolarınıza erişebilmesi için, oyunlarınıza erişebilecek bir Açık Bulut API anahtarı oluşturun ve veri silme işlemleri için Girişi Sil iznini ekleyin. PII depolamak için sıralı veri depoları kullanıyorsanız, sıralı veri depolarının Yazma iznini de eklemeniz gerekecektir. Tamamlandıktan sonra, API anahtarını güvenli bir yerde kopyalayın ve saklayın.
Oyunların ve yerlerin kimliklerini elde etme
Botun, kullanıcılar tarafından silinmesi talep edilen PII verilerini bulabilmesi için, botu kullanmayı düşündüğünüz tüm oyunların aşağıdaki kimliklerini elde edin:
- Evrensel Kimlik, oyununuzun benzersiz tanımlayıcısı.
- Başlangıç Yeri Kimliği, bir oyunun başlangıç yerinin benzersiz tanımlayıcısı.
Bu kimlikleri elde etmek için:
Creator Dashboard sayfasına gidin.
Bir oyunun küçük resminin üzerine gelin, ⋯ düğmesine tıklayın ve sırasıyla Evrensel Kimliği Kopyala ve Başlangıç Yeri Kimliği Kopyala seçeneğini belirleyin.

Betikleri ekleyin
Webhook'u, botu ve veri depoları için API anahtarını yapılandırdıktan sonra, botun otomasyon mantığını uygulayan betiklere ekleyin. Aşağıdaki örnek Python 3 kullanmaktadır.
Aşağıdaki komutları kullanarak Python kütüphanelerini kurun:
Kütüphaneleri Yüklepip3 install discordpip3 install requestspip3 install urllib3==1.26.6Aşağıdaki betikleri bot mantığının farklı parçalarını içerecek şekilde aynı dizine kopyalayın ve kaydedin:
bot_config.pyBOT_TOKEN = ""OPEN_CLOUD_API_KEY = ""ROBLOX_WEBHOOK_SECRET = ""# Başlangıç yeri Kimliğinden# (evrensel kimlik, (veri deposu adı, kapsam ve giriş anahtarı) listesi) için# Standart Veri Depoları# Bu girişlerde depolanan kullanıcı verileri silinecektirSTANDARD_DATA_STORE_ENTRIES = {# Başlangıç Yeri Kimliği111111111: (# Evrensel Kimlik222222222,[("StandardDataStore1", "Scope1", "Key1_{user_id}"),("StandardDataStore1", "Scope1", "Key2_{user_id}"),("StandardDataStore2", "Scope1", "Key3_{user_id}")]),33333333: (444444444,[("StandardDataStore3", "Scope1", "Key1_{user_id}")])}# Başlangıç yeri Kimliğinden# (evrensel kimlik, (veri deposu adı, kapsam ve giriş anahtarı) listesi) için# Sıralı Veri Depoları# Bu girişlerde depolanan kullanıcı verileri silinecektirORDERED_DATA_STORE_ENTRIES = {111111111: (222222222,[("OrderedDataStore1", "Scope2", "Key4_{user_id}")])}data_stores_api.pyimport requestsimport bot_configfrom collections import defaultdict"""Kullanıcı_id'si için tüm girişleri silmek üzere Veri Depoları Açık Bulut API'ini çağırır.Başarılı silmelerin ve silme hatalarının listesini döndürür."""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"""Sıralı Veri Depoları Açık Bulut API'sini, kullanıcı_id'si için tüm girişleri silmek üzere çağırır.Başarılı silmelerin ve silme hatalarının listesini döndürür."""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"""Gelen mesaj için Roblox imzasını ve zaman damgasını ayrıştırır; alt bilgi yalnızca gizli anahtar ayarladıysanız belirlenir."""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"""Geçerliliği kontrol etmek için ayarlanmış gizli anahtar ile Roblox imzasını doğrular."""def validate_signature(message, signature, timestamp):if not message or not signature or not timestamp:return False# 300 saniyelik bir pencerede yeniden oynamayı önlerrequest_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# İmza doğrulartimestamp_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# Geçerli imzareturn True"""Discord'da alınan bir webhook mesajını ayrıştırır. Kullanıcı kimliğini çıkartır, alınan zaman damgasına dayalı yeniden oynamayı önler ve ayarlanmış gizli anahtar ile Roblox imzasını doğrular."""def parse_message(message):# Alınan mesaj içinde kullanıcı kimliğini ve oyun kimliğini ayrıştırırif len(message.embeds) != 1 or \not message.embeds[0].description:return "", []description_match = re.match(r"Silme Hakkı için Kullanıcı Id'si: (.*) ile ilgili yeni bir bildirim aldınız " +r"oyun\(lar\) ile Id'ler: (.*)",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} Silme Hakkı mesajlarını dinliyor")"""Roblox'tan gelen webhook mesajları için işleyici"""@client.eventasync def on_message(message):# Mesajı ayrıştırır ve doğrularuser_id, start_place_ids = message_parser.parse_message(message)if not user_id or not start_place_ids:return# Standart veri depolarındaki kullanıcı verilerini siler[successes, failures] = data_stores_api.delete_standard_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Standart veri depolarındaki kullanıcı verileri silindi " +f"kullanıcı kimliği: {user_id}, veri: {dict(successes)}")if failures:await message.reply(f"Standart veri depolarındaki kullanıcı verileri silinemedi " +f"kullanıcı kimliği: {user_id}, veri: {dict(failures)}")# Sıralı veri depolarındaki kullanıcı verilerini siler[successes, failures] = data_stores_api.delete_ordered_data_stores(user_id, start_place_ids)if successes:await message.reply(f"Sıralı veri depolarındaki kullanıcı verileri silindi " +f"kullanıcı kimliği: {user_id}, veri: {dict(successes)}")if failures:await message.reply(f"Sıralı veri depolarındaki kullanıcı verileri silinemedi " +f"kullanıcı kimliği: {user_id}, veri: {dict(failures)}")client.run(bot_config.BOT_TOKEN)if __name__ == "__main__":run()bot_config.py dosyasında botun ana yapılandırması için:
- BOT_TOKEN'u botunuz tarafından üretilen belirteç ile ayarlayın.
- OPEN_CLOUD_API_KEY'i oluşturduğunuz API anahtarı olarak ayarlayın.
- ROBLOX_WEBHOOK_SECRET'i Creator Dashboard'da webhook'u yapılandırırken belirlediğiniz gizli anahtar olarak ayarlayın.
- STANDARD_DATA_STORE_ENTRIES ve ORDERED_DATA_STORE_ENTRIES sözlüklerinde silinecek kayıtların veri deposunu bulmak için:
- Kopyaladığınız Başlangıç Yeri Kimliklerini anahtar olarak ekleyin.
- Evrensel Kimlikleri, demetin ilk elemanı olarak ekleyin.
- Veri depolarınızın adını, kapsamını, giriş anahtarı adını ve ilişkili Kullanıcı Kimliğini içeren demetin ikinci elemanını değiştirin. Farklı bir veri şeması kullanıyorsanız, bunu kendi veri şemanıza uygun hale getirecek şekilde değiştirmelisiniz.
Botu çalıştırmak için şu komutu uygulayın:
Discord Botunu Çalıştırpython3 discord_bot.pyBot, silme hakkı talepleri için Roblox webhook'larını dinlemeye ve doğrulamaya başlar ve ilgili veri deposunun silinmesi için Açık Bulut uç noktasını çağırır.
Test
Özelleştirilmiş programınızın silme hakkı taleplerini düzgün bir şekilde ele alıp almadığını doğrulamak için bir test mesajı oluşturabilir ve çalıştırabilirsiniz:
Discord webhook sunucunuza aşağıdaki istek gövdesi ile bir HTTP POST isteği gönderin:
Örnek İstekcurl -X POST {serverUrl}-H 'Content-Type: application/json'-d '{"embeds":[{"title":"RightToErasureRequest","description":"Kullanıcı Id'si: {userIds} ile oyunlardaki Silme Hakkı için yeni bir bildirim aldınız","footer":{"icon_url":"https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png","text":"Roblox-Signature: {robloxSignature}, Zaman Damgası: {timestamp}"}}]}'Eğer bir webhook gizli anahtarınız varsa:
- Webhook gizli anahtarınıza HMAC-SHA256 kodlaması uygulayarak bir Roblox-Signature oluşturun.
- Geçerli zaman damgasını UTC zaman damgası olarak saniye cinsinden Timestamp olarak ayarlayın.
description'ı aşağıdaki formatta birleştirin:
Açıklama Alanı Formatı{Timestamp}. Kullanıcı Id'si: {userId} için oyunlardaki Silme Hakkı için yeni bir bildirim aldınız.Örneğin:
Örnek Açıklama Alanı1683927229. Kullanıcı Id'si: 2425654247 için oyunlardaki Silme Hakkı için yeni bir bildirim aldınız.
Programınız, mesajınızı gizli anahtarınızla kodlandığı için resmi Roblox kaynağından geldiğini tespit edebilmelidir. Böylece, talebinizle ilişkili PII verilerini silmelidir.
Örnek Gövde
{
"embeds": [
{
"title": "RightToErasureRequest",
"description": "Kullanıcı Id'si: 2425654247 için oyunlardaki Silme Hakkı için yeni bir bildirim aldınız.",
"footer": {
"icon_url": "https://create.roblox.com/dashboard/assets/webhooks/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Zaman Damgası: 1683927229"
}
}
]
}