กฎการคุ้มครองข้อมูลทั่วไป (GDPR) เป็นกฎระเบียบของยุโรปเกี่ยวกับการคุ้มครองข้อมูลและความเป็นส่วนตัว มันมอบสิทธิให้บุคคลในการขอให้ลบข้อมูลส่วนบุคคลของตน ซึ่งเรียกว่าคำว่า สิทธิ์การลบ หากคุณเก็บข้อมูล ข้อมูลที่สามารถระบุตัวตนได้ (PII) ของผู้ใช้ เช่น รหัสผู้ใช้ของพวกเขา คุณต้องปฏิบัติตามข้อกำหนดของ GDPR โดยการลบข้อมูลนี้เมื่อได้รับคำขอจากผู้ใช้
แทนที่จะจัดการคำขอด้วยตนเอง คุณสามารถ ตั้งเว็บฮุค และใช้บอทภายในแอปพลิเคชันการสื่อสารของบุคคลที่สามเพื่อทำให้กระบวนการเป็นอัตโนมัติ โดย ที่เก็บข้อมูล เป็นวิธีที่พบบ่อยที่สุดในการเก็บข้อมูล PII บทแนะนำนี้ให้ตัวอย่างว่าคุณจะสร้างบอทภายใน Discord ที่ใช้ Open Cloud API สำหรับที่เก็บข้อมูล เพื่อลบข้อมูล PII เป็นโซลูชันอัตโนมัติได้อย่างไร
การทำงาน
เมื่อคุณทำตามบทแนะนำนี้เรียบร้อยแล้ว คุณควรจะสามารถสร้างโปรแกรมที่ทำงานในเครื่องที่กำหนดเองซึ่งอัตโนมัติการจัดการคำขอสิทธิ์การลบจากผู้ใช้ ขั้นตอนการทำงานสำหรับกระบวนการนี้มีดังนี้:
- การสนับสนุนของ Roblox รับคำขอสิทธิ์การลบจากผู้ใช้
- เว็บฮุคของ Roblox ถูกกระตุ้น โดยมีรหัสผู้ใช้และรายการรหัสที่เริ่มเกมที่ผู้ใช้เข้าร่วมใน Payload
- บอทของคุณฟังการแจ้งเตือนเว็บฮุคเหล่านี้ ยืนยันความถูกต้องของพวกเขา และใช้ Open Cloud API สำหรับที่เก็บข้อมูล เพื่อลบข้อมูล PII ที่จัดเก็บอยู่ในที่เก็บข้อมูล
- บอทตอบกลับข้อความเว็บฮุคใน Discord ด้วยสถานะการลบ

ตั้งค่าเว็บฮุคกับการผสานงานของบุคคลที่สาม
ก่อนที่คุณจะสร้างบอท ให้ตั้งค่าเซิร์ฟเวอร์ด้วยการรวม webhook บนแอปพลิเคชันการสื่อสารของบุคคลที่สาม จากนั้นใช้เซิร์ฟเวอร์เพื่อกำหนดค่าเว็บฮุคบน Creator Dashboard
ตั้งค่าเซิร์ฟเวอร์
ขั้นตอนต่อไปนี้แสดงวิธีตั้งค่าเซิร์ฟเวอร์โดยใช้ Discord
สร้างเซิร์ฟเวอร์ Discord ใหม่ หากคุณไม่คุ้นเคยกับกระบวนการนี้ โปรดดูที่ Discord Support.
เซิร์ฟเวอร์จะสร้างช่อง #general โดยอัตโนมัติเป็นช่องเริ่มต้นของคุณ คลิกที่ไอคอน แก้ไขช่อง ของช่อง #general
ภายใต้ สิทธิ์ ให้ตั้งช่องให้เป็นส่วนตัว
สร้างการรวมเว็บฮุคกับเซิร์ฟเวอร์ใหม่ ตั้งชื่อให้เป็น GDPR Hook หากคุณไม่คุ้นเคยกับกระบวนการนี้ โปรดดูที่ Discord Support.
คัดลอก URL ของเว็บฮุคและเก็บไว้ในที่ปลอดภัย อนุญาตให้เฉพาะสมาชิกทีมที่เชื่อถือได้เข้าถึงเท่านั้น เพราะการเปิดเผย URL อาจอนุญาตให้ผู้ที่ไม่ดีส่งข้อความปลอมและอาจลบข้อมูลของผู้ใช้ของคุณ
กำหนดค่าเว็บฮุคบน Roblox
หลังจากที่คุณได้รับ URL เซิร์ฟเวอร์ของบุคคลที่สาม ใช้มันเพื่อ กำหนดค่าเว็บฮุค บน Creator Dashboard โดยให้แน่ใจว่าคุณทำการตั้งค่าดังต่อไปนี้:
- เพิ่ม URL เซิร์ฟเวอร์ Discord เป็น Webhook URL
- รวม Secret ที่กำหนดเอง แม้ว่า secret จะเป็นตัวเลือกในการทำให้การกำหนดค่าการตั้งค่าเสร็จสมบูรณ์ แต่คุณควรรวมหนึ่งเพื่อป้องกันไม่ให้ผู้ไม่ดีเลียนแบบ Roblox และลบข้อมูลของคุณ สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการใช้ secret โปรดดูที่ ตรวจสอบความปลอดภัยของเว็บฮุค.
- เลือก Right to Erasure Request ภายใต้ Triggers.
คุณสามารถทดสอบเว็บฮุคโดยใช้ปุ่ม Test Response เพื่อดูว่าคุณได้รับการแจ้งเตือนในช่อง #general ของเซิร์ฟเวอร์จาก Roblox หากคุณไม่ได้รับการแจ้งเตือน ลองอีกครั้งหรือเช็คการตั้งค่าเซิร์ฟเวอร์ของคุณเพื่อหาข้อผิดพลาด
กำหนดค่าบอท
หลังจากที่คุณเพิ่มเว็บฮุค ใช้มันเพื่อกำหนดค่าบอทด้วยขั้นตอนต่อไปนี้ สำหรับข้อมูลเพิ่มเติม โปรดดูที่ เอกสาร Discord.
- ไปที่ หน้า Applications.
- สร้างแอปพลิเคชันใหม่และตั้งชื่อเป็น GDPR Bot.
- ระบบจะเปลี่ยนเส้นทางไปยังการตั้งค่า ข้อมูลทั่วไป ของบอท คัดลอกและบันทึก Application ID ในที่ปลอดภัย
- ภายใต้เมนูการตั้งค่า เลือก OAuth2
- ไปที่การตั้งค่า OAuth2
- เปิดใช้งานสโคป บอท จะมีรายการสิทธิ์ของบอทเพิ่มเติมแสดงขึ้น
- เพิ่มสิทธิ์ ผู้ดูแลระบบ บันทึกการเปลี่ยนแปลงของคุณ
- บันทึก URL ที่สร้างขึ้น
- คัดลอก URL ที่สร้างขึ้น ทิ้งหน้าการตั้งค่าแอปพลิเคชันไว้ในสถานะเปิด
- ไปที่ URL ที่สร้างขึ้น เลือกเซิร์ฟเวอร์เป้าหมาย คลิกที่ปุ่ม ดำเนินการต่อ แล้วคลิกปุ่ม อนุญาต
- กลับไปที่หน้าการตั้งค่าของแอปพลิเคชันและไปที่การตั้งค่าของบอท
- ภายใต้ส่วน Privileged Gateway Intents เปิดใช้งาน Message Content Intent
- ในการตั้งค่าบอท > ส่วน Build-A-Bot บันทึกโทเค็นบอทในที่ปลอดภัยสำหรับขั้นตอนในภายหลัง ถ้าคุณไม่เห็นโทเค็นให้คลิกที่ปุ่ม Reset Token เพื่อสร้างโทเค็นใหม่
สร้างคีย์ Open Cloud API
เพื่อให้บอทของคุณเข้าถึงที่เก็บข้อมูลของคุณสำหรับการเก็บข้อมูล PII ของผู้ใช้ สร้างคีย์ Open Cloud API ที่สามารถเข้าถึงเกมของคุณและเพิ่มสิทธิ์ Delete Entry ของที่เก็บข้อมูลสำหรับการลบข้อมูล หากคุณใช้ที่เก็บข้อมูลที่เรียงลำดับสำหรับการเก็บข้อมูล PII คุณยังต้องเพิ่มสิทธิ์ Write ของที่เก็บข้อมูลที่เรียงลำดับ หลังจากเสร็จสิ้น ให้นำคีย์ API ไปเก็บในสถานที่ปลอดภัยเพื่อนำไปใช้ในขั้นตอนถัดไป
รับตัวระบุของเกมและที่ตั้ง
เพื่อให้บอทสามารถค้นหาข้อมูล PII ที่ร้องขอโดยผู้ใช้ในการลบ ให้รับตัวระบุของทุกเกมที่คุณตั้งใจจะใช้บอทดังต่อไปนี้:
- Universe ID ซึ่งเป็นตัวระบุเฉพาะของเกมของคุณ
- Start Place ID ซึ่งเป็นตัวระบุเฉพาะของที่ตั้งเริ่มต้นของเกม
เพื่อรับตัวระบุเหล่านี้:
ไปที่ Creator Dashboard
เลื่อนเมาส์ไปที่ภาพขนาดย่อของเกม คลิกที่ปุ่ม ⋯ แล้วเลือก คัดลอกรหัส Universe และ คัดลอกรหัส Start Place ตามลำดับ

เพิ่มสคริปต์
หลังจากที่คุณตั้งค่าเว็บฮุค บอท และคีย์ API สำหรับที่เก็บข้อมูลเสร็จแล้ว ให้เพิ่มให้กับสคริปต์ที่ implement logic อัตโนมัติของบอท ตัวอย่างด้านล่างนี้ใช้ Python 3
ติดตั้งไลบรารี Python โดยใช้คำสั่งต่อไปนี้:
ติดตั้ง Librariespip3 install discordpip3 install requestspip3 install urllib3==1.26.6คัดลอกและบันทึกสคริปต์ต่อไปนี้ที่สอดคล้องกับส่วนต่าง ๆ ของ logic บอทในไดเรกทอรีเดียวกัน:
bot_config.pyBOT_TOKEN = ""OPEN_CLOUD_API_KEY = ""ROBLOX_WEBHOOK_SECRET = ""# พจนานุกรมของ Start Place ID ไปยัง# (Universe ID, รายการของ (ชื่อที่เก็บข้อมูล, สโคป, และคีย์เอนทรี)) สำหรับ# Standard Data Stores# ข้อมูลผู้ใช้ที่จัดเก็บภายใต้เอนทรีเหล่านี้จะถูกลบSTANDARD_DATA_STORE_ENTRIES = {# Start Place ID111111111: (# Universe ID222222222,[("StandardDataStore1", "Scope1", "Key1_{user_id}"),("StandardDataStore1", "Scope1", "Key2_{user_id}"),("StandardDataStore2", "Scope1", "Key3_{user_id}")]),33333333: (444444444,[("StandardDataStore3", "Scope1", "Key1_{user_id}")])}# พจนานุกรมของ Start Place ID ไปยัง# (Universe ID, รายการของ (ชื่อที่เก็บข้อมูล, สโคป, และคีย์เอนทรี)) สำหรับ# Ordered Data Stores# ข้อมูลผู้ใช้ที่จัดเก็บภายใต้เอนทรีเหล่านี้จะถูกลบORDERED_DATA_STORE_ENTRIES = {111111111: (222222222,[("OrderedDataStore1", "Scope2", "Key4_{user_id}")])}data_stores_api.pyimport requestsimport bot_configfrom collections import defaultdict"""เรียกใช้ Data Stores Open Cloud API เพื่อลบเอนทรีทั้งหมดสำหรับ user_id ที่กำหนดในSTANDARD_DATA_STORE_ENTRIES คืนค่ารายการของการลบที่สำเร็จและความล้มเหลวในการลบ"""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"""เรียกใช้ Ordered Data Stores Open Cloud API เพื่อลบเอนทรีทั้งหมดสำหรับ user_id ที่กำหนดในORDERED_DATA_STORE_ENTRIES คืนค่ารายการของการลบที่สำเร็จและความล้มเหลวในการลบ"""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"""วิเคราะห์ข้อความที่ได้รับสำหรับลายเซ็นและเวลา ในฟุตเตอร์จะตั้งค่าเฉพาะถ้าคุณกำหนดเว็บฮุค 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"""ยืนยันลายเซ็น Roblox ด้วย secret ที่กำหนดเพื่อตรวจสอบความถูกต้อง"""def validate_signature(message, signature, timestamp):if not message or not signature or not timestamp:return False# ป้องกันการโจมตีแบบ replay ภายใน 300 วินาทีrequest_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# ยืนยันลายเซ็นtimestamp_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# ลายเซ็นถูกต้องreturn True"""วิเคราะห์ข้อความเว็บฮุคที่ได้รับจาก Discord ดึงข้อมูลรหัสผู้ใช้ ป้องกันการโจมตีแบบ replayตามเวลาได้รับ และยืนยันลายเซ็น Roblox ด้วย secret ที่กำหนดเพื่อตรวจสอบความถูกต้อง"""def parse_message(message):# วิเคราะห์ข้อความที่ได้รับสำหรับรหัสผู้ใช้และรหัสเกมif 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 "", []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} is listening to Right to Erasure messages")"""ตัวจัดการข้อความเว็บฮุคจาก Roblox"""@client.eventasync def on_message(message):# วิเคราะห์และยืนยันข้อความuser_id, start_place_ids = message_parser.parse_message(message)if not user_id or not start_place_ids:return# ลบข้อมูลผู้ใช้จากที่เก็บข้อมูลแบบมาตรฐาน[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)}")# ลบข้อมูลผู้ใช้จากที่เก็บข้อมูลแบบจัดลำดับ[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.BOT_TOKEN)if __name__ == "__main__":run()ในไฟล์ bot_config.py สำหรับการกำหนดค่าหลักของบอท:
- ตั้งค่า BOT_TOKEN เป็นโทเค็นที่สร้างโดยบอทของคุณ.
- ตั้งค่า OPEN_CLOUD_API_KEY เป็นคีย์ API ที่คุณสร้าง
- ตั้งค่า ROBLOX_WEBHOOK_SECRET เป็น secret ที่คุณตั้งค่าเมื่อกำหนดค่าเว็บฮุคใน Creator Dashboard
- ในพจนานุกรม STANDARD_DATA_STORE_ENTRIES และ ORDERED_DATA_STORE_ENTRIES เพื่อค้นหาที่เก็บข้อมูลของแต่ละระเบียนที่จะลบ:
- เพิ่ม Start Place ID ที่คุณคัดลอกเป็นกุญแจ
- เพิ่ม Universe ID เป็นองค์ประกอบแรกของค่า
- เปลี่ยนองค์ประกอบที่สองของทูเพิลเป็นชื่อ สโคป ชื่อคีย์เอนทรี และรหัสผู้ใช้ที่เกี่ยวข้องของที่เก็บข้อมูล หากคุณใช้สคีมข้อมูลที่แตกต่างกัน ให้ปรับเปลี่ยนให้ตรงกับสคีมข้อมูลของคุณเอง
ประมวลผลคำสั่งต่อไปนี้เพื่อรันบอท:
รัน Discord Botpython3 discord_bot.pyบอทจะเริ่มฟังและตรวจสอบเว็บฮุคของ Roblox สำหรับคำขอสิทธิ์การลบและเรียกใช้ Open Cloud endpoint สำหรับการลบที่เก็บข้อมูลที่เกี่ยวข้อง
ทดสอบ
คุณสามารถสร้างและรันข้อความทดสอบเพื่อตรวจสอบว่าโปรแกรมกำหนดเองของคุณสามารถจัดการคำขอสิทธิ์การลบและลบข้อมูล PII ได้อย่างถูกต้อง:
ส่งคำขอ HTTP POST ไปยังเซิร์ฟเวอร์เว็บฮุคของ Discord ของคุณด้วยเนื้อหาคำขอที่ตามนี้:
ตัวอย่างคำขอcurl -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}"}}]}'หากคุณมีเว็บฮุค secret:
- สร้าง Roblox-Signature โดยใช้การเข้ารหัส HMAC-SHA256 กับคีย์เว็บฮุค secret ของคุณ
- ตั้งเวลาปัจจุบันเป็น Timestamp ในวินาทีตาม UTC
รวบรวม description ในรูปแบบดังต่อไปนี้:
รูปแบบฟิลด์คำอธิบาย{Timestamp}. You have received a new notification for Right to Erasure for the User Id: {userId} in the game(s) with Ids: {gameIds}`.ตัวอย่าง:
ตัวอย่างฟิลด์คำอธิบาย1683927229. You have received a new notification for Right to Erasure for the User Id: 2425654247 in the game(s) with Ids: 10539205763, 13260950955
โปรแกรมของคุณควรสามารถระบุได้ว่าข้อความของคุณมาจากแหล่งที่มีอำนาจจาก Roblox เพราะคุณได้เข้ารหัสข้อความด้วย secret ของคุณ จึงควรลบข้อมูล PII ที่เกี่ยวข้องกับคำขอของคุณ
ตัวอย่างเนื้อหา
{
"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/dashboard/assets/webhooks/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}