[에러 해결] 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_chk 및 generate_upload_seed 함수의 실제 구현 코드를 스크립트 내에 정의해야 합니다. Geometry Dash API와 관련된 커뮤니티나 다른 오픈소스 프로젝트를 통해 해당 함수들의 구현을 찾거나, API 문서의 내용을 바탕으로 직접 구현해야 할 수도 있습니다.
아래는 Geometry Dash API의 seed2 생성을 위한 generate_chk 및 generate_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_seed와 generate_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와의 인터랙션에서 중요한 데이터를 생성하는 함수에 유용합니다.
답글 남기기