[에러 해결] Python NameError: name ‘generate_chk’ is not defined 원인과 해결 방법

[에러 해결] Python NameError: name ‘generate_chk’ is not defined 원인과 해결 방법

에러 상황 소개

Python 스크립트 실행 중 NameError: name 'generate_chk' is not defined 오류가 발생했습니다. 이 오류는 Geometry Dash 레벨 업로드 프로그램을 파이썬으로 개발하는 과정에서 seed2 값을 생성하기 위해 generate_chk 함수를 호출했으나, 해당 함수가 정의되지 않아 발생하는 전형적인 문제였습니다.

문제의 스크립트에서 발생한 에러 메시지는 다음과 같습니다:

[***@archlinux usernm]$ python upload
What is your username?levelnotinauto
What is your Account ID?38627839
What is your password?********
What is the Level name?Check Desc
How many coins are there?4848394
How many stars do you want?392084092849
Traceback (most recent call last):
  File "/home/***/scripts/usernm/upload", line 43, in <module>
    "seed2": generate_chk(key="41274", values=[generate_upload_seed(levelString)], salt="xI25fpAapCQg"), # This is talked about in the CHK encryption,
             ^^^^^^^^^^^^
NameError: name 'generate_chk' is not defined. Did you mean: 'generate_gjp2'?

발생 원인

NameError: name 'generate_chk' is not defined 오류는 파이썬 인터프리터가 generate_chk라는 이름의 함수를 찾을 수 없을 때 발생합니다. 문제의 스크립트에서는 requests, base64, hashlib 모듈을 임포트하고 있었지만, generate_chk 함수는 이들 표준 라이브러리에 포함되어 있지 않습니다. 또한, 사용자 본인이 해당 함수를 직접 정의하지도 않았습니다.

사용자는 특정 Geometry Dash API 문서(현재는 아카이브됨)에서 generate_chk(key="41274", values=[generate_upload_seed(levelString)], salt="xI25fpAapCQg")와 같은 함수 호출 방식을 확인했으나, 실제 함수의 구현 코드를 누락한 채 사용했습니다. 이는 마치 특정 요리법에 "특별 소스 추가"라고만 적혀있고, 그 특별 소스를 만드는 방법은 알려주지 않은 상황과 같습니다. 즉, 함수의 호출 방식만 알고 실제 구현이 없는 상태에서 호출했기 때문에 발생한 오류입니다.

특히 generate_chk 함수가 generate_upload_seed 함수의 반환 값을 인자로 받기 때문에, generate_upload_seed 함수 또한 정의되지 않았을 가능성이 높습니다.

추가적으로, 원본 코드에서 generate_gjp2 함수는 정의되었으나, 그 반환 값인 hash를 전역 변수로 할당하지 않아 data 딕셔너리에서 "gjp": hash 부분도 NameError를 일으킬 수 있는 잠재적인 문제가 있었습니다.

해결 방법 코드 예시

이 문제를 해결하려면 generate_chkgenerate_upload_seed 함수의 실제 구현 코드를 스크립트 내에 정의해야 합니다. Geometry Dash API와 관련된 커뮤니티나 다른 오픈소스 프로젝트를 통해 해당 함수들의 구현을 찾거나, API 문서의 내용을 바탕으로 직접 구현해야 할 수도 있습니다.

아래는 Geometry Dash API의 seed2 생성을 위한 generate_chkgenerate_upload_seed 함수의 예시 구현입니다. 실제 Geometry Dash API의 동작 방식에 따라 내부 로직이 달라질 수 있으므로, 참고하여 실제 API의 요구사항에 맞게 수정해야 합니다. (특히 XOR 연산 방식과 사용되는 상수 값들을 주의 깊게 확인해야 합니다.)

에러 코드 (Before)

# ... (생략) ...

data = {
    # ...
    "gjp": hash, # 'hash'가 정의되지 않았을 수 있음
    # ...
    "seed2": generate_chk(key="41274", values=[generate_upload_seed(levelString)], salt="xI25fpAapCQg"), # NameError 발생 지점
    "secret": "Wmfd2893gb7"
}

# ... (생략) ...

수정된 코드 예시 (After)

generate_upload_seedgenerate_chk 함수 정의를 추가하고, generate_gjp2 함수의 호출을 명확히 합니다.

import requests
import base64
import hashlib  # sha1() lives there

user = input("What is your username?")
accid = input("What is your Account ID?")
passwd = input("What is your password?")
lname = input ("What is the Level name?")
coin = input("How many coins are there?")
stars = input("How many stars do you want?")

# --- Geometry Dash API 관련 유틸리티 함수 정의 시작 ---
# 이 함수들은 Geometry Dash API의 특정 암호화 로직을 따르므로,
# 공식 문서 또는 다른 구현을 참고하여 정확성을 확인해야 합니다.

def generate_gjp2(password: str, salt: str = "mI29fmAnxgTs") -> str:
    password_with_salt = password + salt
    return hashlib.sha1(password_with_salt.encode()).hexdigest()

def generate_upload_seed(level_string: str) -> str:
    """
    Geometry Dash API의 레벨 업로드 시 사용되는 seed 값을 생성하는 예시 함수.
    실제 구현은 levelString을 특정 방식으로 처리(예: Base64 인코딩, XOR 연산 등)할 수 있습니다.
    여기서는 예시를 위해 levelString의 길이를 반환합니다.
    실제 GD API는 더 복잡한 로직을 가질 수 있으니, 필요시 정확한 구현을 찾아야 합니다.
    """
    return str(len(level_string)) # placeholder: 실제 로직으로 교체 필요

def generate_chk(key: str, values: list, salt: str) -> str:
    """
    Geometry Dash API의 "seed2" 값을 생성하는 예시 함수.
    일반적으로 key, values (generate_upload_seed의 결과), salt를 조합하여
    XOR 연산 및 SHA1 해싱을 통해 체크섬을 생성합니다.
    
    이 구현은 일반적인 Geometry Dash API 체크섬 패턴을 기반으로 한 예시입니다.
    정확한 알고리즘은 API 버전이나 엔드포인트에 따라 다를 수 있으므로,
    공식 문서 또는 역설계된 구현과 대조하여 검증하는 것이 필수적입니다.
    """
    if not values:
        raise ValueError("Values list cannot be empty for generate_chk.")
        
    main_value = str(values[0]) # values는 보통 단일 요소 리스트일 것
    
    # GD API 체크섬 로직의 일반적인 패턴 (XOR 연산 및 SHA1 해싱)
    
    # 1. key와 main_value를 조합
    # 예시 조합: seed + key
    combined_string_for_xor = main_value + key
    
    # 2. XOR 연산 적용 (GD API의 특정 방식)
    # 원본 문제의 seed2 salt 값을 XOR 키로 활용하는 경우가 많음
    xor_secret_key = "xI25fpAapCQg" # 원본 호출의 salt 값 재사용, 실제 GD API의 특정 값을 사용해야 함
    
    xor_result_chars = []
    for i, char_code in enumerate(combined_string_for_xor):
        xor_result_chars.append(chr(ord(char_code) ^ ord(xor_secret_key[i % len(xor_secret_key)])))
    xor_string = "".join(xor_result_chars)
        
    # 3. salt 추가 및 SHA1 해싱
    final_string_to_hash = xor_string + salt
    hashed_result = hashlib.sha1(final_string_to_hash.encode()).hexdigest()
    
    return hashed_result
# --- Geometry Dash API 관련 유틸리티 함수 정의 끝 ---


# gjp 해시 생성: 함수 호출 결과를 'hash' 변수에 할당
hash = generate_gjp2(password=passwd)

levelString = "H4sIAAAAAAAAC6WQwQ3DIAxFF3IlfxsIUU6ZIQP8AbJChy_GPSZqpF7-A4yfDOfhXcCiNMIqnVYrgYQl8rDwBTZCVbkQRI3oVHbiDU6F2jMF_lesl4q4kw2PJMbovxLBQxTpM3-I6q0oHmXjzx7N0240cu5w0UBNtESRkble8uSLHjh8nTubmYJZ2MvMrEITEN0gEJMxlLiMZ28frmj"

data = {
    "gameVersion": 22,
    "accountID": accid,
    "gjp": hash, # 이제 'hash' 변수가 정확히 정의되어 있습니다.
    "userName": user,
    "levelID": 0,
    "levelName": lname,
    "levelDesc": "Q3Vyc2VkIGxldmVsIG55ZWghaGVoIGhlaCBhbnl3YXkgaXRzIGEgdmVyeSBicm9rZW4gbGV2ZWwgaSBzZW50IGEgcmVxdWVzdCB0byByb2Igc2VydmVyIHRocm91Z2ggcHl0aG9uIGxvbA",
    "levelVersion": "-47577473775738573",
    "levelLength": 48483845738573486574873869465947694,
    "audioTrack": 0,
    "auto": 0,
    "password": 314159,
    "original": 55610687,
    "twoPlayer": 0,
    "songID": 839583504693858468444987694,
    "objects": 1,
    "coins": coin,
    "requestedStars": stars,
    "unlisted": 0,
    "ldm": 0,
    "levelString": levelString,
    "seed2": generate_chk(key="41274", values=[generate_upload_seed(levelString)], salt="xI25fpAapCQg"), # 정의된 함수 사용
    "secret": "Wmfd2893gb7"
}

headers = {
    "User-Agent": ""
}

url = "http://www.boomlings.com/database/uploadGJLevel21.php"

req = requests.post(url=url, data=data, headers=headers)
print(req.text)

향후 예방을 위한 팁

  • 외부 코드 사용 시 주의: API 문서나 예제 코드를 참조할 때는 호출 방식뿐만 아니라, 해당 함수나 클래스의 정의(구현)가 함께 제공되는지 반드시 확인해야 합니다. 누락된 부분이 있다면 직접 구현하거나 다른 자료를 찾아야 합니다.
  • 종속성 명확히 파악: 프로젝트에 사용되는 모든 함수, 클래스, 변수가 어디에서 오는지 (내부 정의, 특정 모듈 임포트 등) 명확히 이해하고 있어야 합니다. 파이썬의 NameError는 이러한 종속성이 제대로 충족되지 않았을 때 가장 흔히 발생합니다.
  • 아카이브된 문서 활용 시 주의: 아카이브되거나 오래된 문서는 정보가 불완전하거나 최신 API와 호환되지 않을 수 있습니다. 가능한 한 최신 공식 문서를 참조하고, 필요한 경우 관련 커뮤니티의 도움을 받는 것이 좋습니다.
  • 재사용 가능한 모듈화: 만약 generate_chk와 같은 유틸리티 함수를 직접 구현한다면, 이를 별도의 파일(예: gd_utils.py)로 분리하고 필요할 때 임포트하여 사용하는 것이 코드의 가독성과 재사용성을 높이는 좋은 방법입니다.
  • 단위 테스트: 핵심 로직을 담당하는 함수(예: generate_chk)에 대해서는 단위 테스트를 작성하여, 함수의 동작이 예상대로 이루어지는지 검증하는 습관을 들이면 좋습니다. 이는 특히 외부 API와의 인터랙션에서 중요한 데이터를 생성하는 함수에 유용합니다.

댓글 남기기