오른쪽 삭제 요청 자동화

*이 콘텐츠는 AI(베타)를 사용해 번역되었으며, 오류가 있을 수 있습니다. 이 페이지를 영어로 보려면 여기를 클릭하세요.

일반 데이터 보호 규정(GDPR)은 유럽의 데이터 보호 및 개인 개인정보규정입니다. 개인에게는 개인 데이터의 삭제를 요청할 수 있는 권리(즉, 삭제 권리)가 부여됩니다. 사용자의 개인 데이터를 저장하면 사용자의 사

수동으로 요청을 처리하는 대신, 웹 후크를 설정하고 웹 메시징 애플리케이션 내에서 봇을 사용하여 프로세스를 자동화할 수 있습니다. As 데이터 저장소를 가장 일반적인 방법으로 저장하는 데 데이터 스토어를

워크플로

이 튜토리얼을 완료하면 사용자에게 제거 요청을 처리하는 데 사용되는 지역 실행 사용자 지정 프로그램을 만들 수 있어야 합니다. 이 프로세스의 작업 흐름은 다음과 같습니다.

  1. Roblox 지원은 사용자가 요청한 때에 삭제 요청을 받습니다.
  2. Roblox 웹 훅이 트리거되어 사용자 ID와 경험에 참여한 장소 ID 목록이 포함된 경험 별 목록이 전송됩니다.
  3. 이 웹 후크 알림을 듣고, 확인하고, 데이터 스토어에 저장된 PII 데이터를 삭제하기 위해 클라우드 API를 사용합니다.
  4. 봇은 Discord 또는 Guilded에서 삭제 상태로 웹 후크 메시지에 응답합니다.

제3자 통합 웹훅 구성

봇을 생성하기 전에 웹 훅 통합이 있는 제3자 메시징 응용 프로그램에서 서버를 설정하십시오. 그런 다음 서버를 사용하여 크리에이터 대시보드에서 웹 훅을 구성하십시오.

서버 설정

다음 단계에서는 Guilded 또는 Discord를 사용하여 서버를 설정하는 방법을 보여줍니다.

  1. 새로운 길드 서버를 생성합니다. 프로세스에 익숙하지 않은 경우 길드 지원 을 참조하십시오.
  2. 개인 정보 설정에 따라 서버를 비공개설정합니다. 서버는 기본 채널로 자동으로 개인 채널 # general을 생성합니다.
  3. 새로운 서버와 웹 후크 통합을 만들고 쉽게 이해할 수 있는 이름을 지정하십시오, 예를 들어 GDPR Hook. 프로세스에 익숙하지 않은 경우, 자산 보기를 참조하십시오.
  4. 웹 후크 URL을 복사하고 안전한 플레이스저장하십시오. 웹 후크 URL을 유출하면 신뢰할 수 없는 팀 멤버가 액세스할 수 있으므로 사용자 데이터를 손상시킬 수 있습니다.

Roblox에서 웹훅 구성

제3자 서버 URL을 받은 후 크리에이터 대시보드에서 웹훅 구성을 구성하세요 make sure you perform the following settings:

  • Guilded 또는 Discord 서버 URL을 Webhook URL 로 추가합니다.
  • 사용자 지정 된 비밀 Secret 를 포함하십시오. 구성을 완료하는 것이 선택적이지만, 사기꾼이 Roblox을 사기 및 데이터를 삭제하는 것을 방지하기 위해 한 개를 포함해야 합니다. 사이버 보안 사용에 대한 자세한 내용은 Roblox 사이버 보안 검색를 참조하십시오.
  • Select Right to Erasure Request under Triggers .

테스트 응답 버튼을 사용하여 서버의 # general 채널에서 Roblox로부터 알림을 받는지 여부를 테스트하여 알림을 받지 못하는 경우 서버 설정을 다시 확인하거나 오류를 해결하십시오.

Example notification on Guilded

봇 구성

웹 후크를 추가한 후 다음 단계를 사용하여 봇을 구성하세요.

  1. 아이콘을 클릭하거나 바로가기를 사용하여 모든 서버 목록을 열거하십시오.

    • CtrlS 에서 Windows에서.
    • S 맥에서.
  2. 오른쪽 사서함 알림을 받으려는 서버를 선택하십시오.

  3. 서버 홈 아래에 있는 목록을 확장하고 봇 관리 를 선택합니다.

  4. 서버는 기본 채널로 자동으로 비공개 채널 # general을 생성합니다.

  5. 봇 생성 버튼을 클릭하고 봇 이름을 입력하십시오. 길드는 봇 구성 페이지로 이동합니다.

  6. 봇 구성 페이지에서 API 섹션을 선택합니다.

  7. Under the 토큰 section, click the 토큰 생성 button.

  8. 생성된 토큰을 안전한 플레이스저장하십시오.

Open Cloud API 키 생성

사용자의 개인 식별 정보데이터를 저장하기 위해 제3자 봇이 데이터 저장소에 액세스할 수 있도록 하려면, Open Cloud API 키를 생성 하여 경험에 액세스하고 데이터 저장소의 데이터 삭제 권한을 추가할 수 있습니다.

경험과 장소의 식별자 얻기

사용자가 삭제를 요청한 PII 데이터를 찾을 수 있도록 다음 식별자를 모든 사용자가 사용하려는 모든 경험에 대해 얻으십시오.

  • 경험의 고유 식별자인 유니버스 ID입니다.
  • 경험의 시작 장소의 고유 식별자인 시작 장소 ID 입니다.

이 식별자를 얻으려면 크리에이터 대시보드의 생성 페이지를 엽니다. 그런 다음 경험을 선택하고 우주 ID 및 2>시작 장소 ID2>를 복사합니다.

Copy Universe ID and Copy Start Place ID options from Creator Dashboard

스크립트 추가

데이터 저장소에 대한 웹 호크, 봇, API 키를 설정한 후 스크립트에 봇의 자동 로직을 구현하는 코드를 추가합니다. 다음 예시에서는 Python 3를 사용합니다.

  1. 다음 명령을 사용하여 파이톤 라이브러리를 설치하세요:

    라이브러리 설치

    pip3 install discord
    pip3 install guilded.py==1.8.0
    pip3 install requests
    pip3 install urllib3==1.26.6
  2. 다른 봇 로직 부품에 해당하는 스크립트를 복사하고 동일한 디렉터리에 저장하십시오.

    bot_config.py

    DISCORD_BOT_TOKEN = ""
    GUILDED_BOT_TOKEN = ""
    OPEN_CLOUD_API_KEY = ""
    ROBLOX_WEBHOOK_SECRET = ""
    # 시작 장소 ID의 사전
    # (宇宙 ID, 데이터 저장소 이름, 범위 및 키 입력 키)) for
    # 일반적인 데이터 저장소
    # 이 항목 아래에 저장된 사용자 데이터는 삭제됩니다.
    STANDARD_DATA_STORE_ENTRIES = {
    # 시작 장소 ID
    111111111: (
    # 유니버스 ID
    222222222,
    [
    ("StandardDataStore1", "Scope1", "Key1_{user_id}"),
    ("StandardDataStore1", "Scope1", "Key2_{user_id}"),
    ("StandardDataStore2", "Scope1", "Key3_{user_id}")
    ]
    ),
    33333333: (
    444444444,
    [
    ("StandardDataStore3", "Scope1", "Key1_{user_id}")
    ]
    )
    }
    # 시작 장소 ID의 사전
    # (宇宙 ID, 데이터 저장소 이름, 범위 및 키 입력 키)) for
    # 주문형 데이터 저장소
    # 이 항목 아래에 저장된 사용자 데이터는 삭제됩니다.
    ORDERED_DATA_STORE_ENTRIES = {
    111111111: (
    222222222,
    [
    ("OrderedDataStore1", "Scope2", "Key4_{user_id}")
    ]
    )
    }
    data_stores_api.py

    import requests
    import bot_config
    from collections import defaultdict
    """
    Calls Data Stores Open Cloud API to delete all entries for a user_id configured in
    STANDARD_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:
    continue
    universe_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 in
    ORDERED_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:
    continue
    universe_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, failures
    메시지_액터.py

    import time
    import hmac
    import hashlib
    import re
    import base64
    import bot_config
    """
    Parses received message for Roblox signature and timestamp, the footer is only set if you
    configured webhook secret
    """
    def parse_footer(message):
    if not message.embeds[0].footer or \
    not message.embeds[0].footer.text:
    return "", 0
    footer_match = re.match(
    r"Roblox-Signature: (.*), Timestamp: (.*)",
    message.embeds[0].footer.text
    )
    if not footer_match:
    return "", 0
    else:
    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
    # 300초 창 내의 리플레이 공격 방지
    request_timestamp_ms = timestamp * 1000
    window_time_ms = 300 * 1000
    oldest_timestamp_allowed = round(time.time() * 1000) - window_time_ms
    if 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
    """
    Parses a received webhook messaged on Discord or Guilded. Extracts user ID, prevents replay attack
    based on timestamp received, and verifies Roblox signature with configured secret to check for
    validity.
    """
    def parse_message(message):
    # 사용자 ID 및 게임 ID 대한 메시지 받기
    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_ids
    else:
    return "", []
    guilded_bot.py

    import guilded
    import json
    import bot_config
    import data_stores_api
    import message_parser
    def run():
    client = guilded.Client()
    @client.event
    async def on_ready():
    print(f"{client.user} is listening to Right to Erasure messages")
    """
    Handler for webhook messages from Roblox
    """
    @client.event
    async 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)}")
    # 주문된 데이터 저장소의 사용자 데이터를 삭제합니다.Deletes ordered data stores user data
    [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()
  3. 봇의 메인 구성에 대한 bot_config.py 파일:

    1. 봇에 생성된 토큰에 DISCORD_BOT_TOKEN 또는 GUILDED_BOT_TOKEN을 설정합니다.
    2. API 키로 OPEN_CLOUD_API_KEY를 설정합니다.
    3. 크리에이터 대시보드에서 웹 후크를 구성할 때 비밀로 ROBLOX_WEBHOOK_SECRET를 설정하세요.
    4. 각 레코드의 데이터 저장소를 삭제하려면 STANDARD_DATA_STORE_ENTRIESORDERED_DATA_STORE_ENTRIES 사전에서 찾아야 합니다.
      1. 복사한 시작 장소 ID를 열에 추가합니다.
      2. 첫 번째 요소로 유니버스 ID를 추가합니다.
      3. 테이블의 두 번째 요소를 이름, 범위, 키 이름 및 데이터 저장소의 연관된 사용자 ID로 대체합니다. 다른 데이터 스키마를 사용하는 경우 해당 데이터 스키마에 대해 수정합니다.
  4. 다음 명령을 실행하여 봇을 실행합니다.

    길드 봇 실행

    python3 guilded_bot.py
  5. 이제 봇이 로블록스 웹 호크를 듣고 제거 요청 및 호출에 대해 Roblox 웹 호크를 확인하고 해당 데이터 상점삭제하기 위해 Open Cloud 엔드포인트를 호출합니다.

테스트

사용자 정의 프로그램이 오른쪽 삭제 요청을 올바르게 처리하고 PII 데이터를 삭제하도록 테스트 메시지를 만들고 실행할 수 있습니다.You can create and run a test message to verify that your custom program can properly handle right to erasure requests and delete PII data:

  1. 다음 요청 신체가진 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}"
    }
    }]
    }'
  2. 웹 후크 비밀이 있으면:

    1. 웹 훅 비밀 키에 HMAC-SHA256 인코딩을 적용하여 Roblox-Signature 생성.
    2. 현재 시간을 UTC 타임스탬프를 사용하여 Timestamp 로 설정합니다.
  3. 다음 형식으로 description을 모으세요:

    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}`.

    예를 들어:

    Example Description Field

    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 본사에서 메시지를 식별할 수 있어야 합니다. 그런 다음 요청과 관련된 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/dash/ assets/webhook/roblox_logo_metal.png",
"text": "Roblox-Signature: UIe6GJ78MHCmU/zUKBYP3LV0lAqwWRFR6UEfPt1xBFw=, Timestamp: 1683927229"
}
}
]
}