"""GAMEBEAST JSON signing. Kept independent for the downloadable Python example.""" import hashlib import json import math def number_string(value): # JSON numbers are IEEE-754 Numbers in the existing JavaScript caller. n=float(value) if math.isnan(n):return 'NaN' if math.isinf(n):return 'Infinity' if n>0 else '-Infinity' if not n:return '0' sign='-' if n<0 else '';n=abs(n) s=repr(n).lower() mantissa,_,exponent=s.partition('e') whole,_,fraction=mantissa.partition('.') digits=(whole+fraction).rstrip('0') point=len(whole)+int(exponent or 0) if 1e-6<=n<1e21: if point<=0:return sign+'0.'+'0'*(-point)+digits if point>=len(digits):return sign+digits+'0'*(point-len(digits)) return sign+digits[:point]+'.'+digits[point:] power=point-1 return sign+digits[0]+('.'+digits[1:] if len(digits)>1 else '')+'e'+('+' if power>=0 else '-')+str(abs(power)) def quote(value): # Well-formed JSON.stringify escapes lone surrogates, but keeps non-ASCII. raw=json.dumps(value,ensure_ascii=False,separators=(',',':')) return ''.join('\\u%04x'%ord(ch) if 0xd800<=ord(ch)<=0xdfff else ch for ch in raw) def json_stringify(value): if value is None:return 'null' if value is True:return 'true' if value is False:return 'false' if isinstance(value,str):return quote(value) if isinstance(value,(int,float)): return number_string(value) if math.isfinite(float(value)) else 'null' if isinstance(value,list):return '['+','.join(json_stringify(x) for x in value)+']' if isinstance(value,dict): # JS enumeration puts array-index keys first, then insertion-order keys. indices=sorted((k for k in value if k.isascii() and k.isdigit() and str(int(k))==k and int(k)<4294967295),key=int) keys=indices+[k for k in value if k not in indices] return '{'+','.join(quote(k)+':'+json_stringify(value[k]) for k in keys)+'}' raise TypeError('JSON value required') def scalar(value): if isinstance(value,(dict,list)):return json_stringify(value) if isinstance(value,str):return value return json_stringify(value) if not isinstance(value,(int,float)) or isinstance(value,bool) else number_string(value) def create_sign(secret,params): keys=sorted(params,key=lambda k:k.encode('utf-16-be','surrogatepass')) body='&'.join(k+'='+scalar(params[k]) for k in keys if k not in ('sign','cards') and params[k] is not None and params[k]!='') text=(secret+body+secret).lower() # Buffer.from(text, 'utf8') replaces unpaired UTF-16 code units with U+FFFD. text=text.encode('utf-16-le','surrogatepass').decode('utf-16-le','replace') return hashlib.md5(text.encode('utf-8')).hexdigest()