Last active
March 9, 2026 23:33
-
-
Save magnade/8711a64f415a908fca882682c68d0582 to your computer and use it in GitHub Desktop.
python script to automate skport endfield daily login claim
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Endfield Daily Check-In Script | |
| original: https://gist.github.com/cptmacp/70f66f2a4fb9d5fa708d33fbcc8e265a | |
| this was converted from original by deepseek | |
| Run locally with Python 3.6+ using only the standard library. | |
| external json support and exit codes on error provided by StoneToad | |
| """ | |
| import json | |
| import time | |
| import hmac | |
| import hashlib | |
| import urllib.request | |
| import urllib.error | |
| from binascii import hexlify | |
| import sys | |
| # if set, load profiles from this file | |
| #profiles_secrets_file = "endfield_secrets.json" | |
| # ------------------- CONFIGURATION (edit these) ------------------- | |
| profiles = [ | |
| { | |
| "cred": "xxxxxxxxxxxxxxxxxxxxxx", # Replace with your Endfield cred cookie | |
| "skGameRole": "xxxxxxxxxxxx", # Replace with your Endfield skGameRole cookie | |
| "platform": "3", | |
| "vName": "1.0.0", | |
| "accountName": "acc_name" # A simple identifier for this account | |
| } | |
| # Add more profiles if needed | |
| ] | |
| telegram_notify = False | |
| myTelegramID = "xxxxx" # Replace with your Telegram ID | |
| telegramBotToken = "xxxxxx:xxxxxxxx" # Replace with your bot token | |
| # ------------------------------------------------------------------- | |
| ATTENDANCE_URL = "https://zonai.skport.com/web/v1/game/endfield/attendance" | |
| REFRESH_URL = "https://zonai.skport.com/web/v1/auth/refresh" | |
| def bytes_to_hex(byte_data): | |
| """Convert bytes to a hexadecimal string.""" | |
| return hexlify(byte_data).decode('ascii') | |
| def generate_sign(path, body, timestamp, token, platform, vName): | |
| """ | |
| Generate the required sign header. | |
| Steps: | |
| 1. Concatenate: path + body + timestamp | |
| 2. Append header JSON: {"platform":p,"timestamp":t,"dId":"","vName":v} | |
| 3. HMAC‑SHA256 (key = token, message = the whole string) | |
| 4. Convert HMAC bytes to hex | |
| 5. MD5 of that hex string → final hex sign | |
| """ | |
| # Build the header JSON part | |
| header_json = json.dumps({ | |
| "platform": platform, | |
| "timestamp": timestamp, | |
| "dId": "", | |
| "vName": vName | |
| }, separators=(',', ':')) # compact, no extra spaces | |
| # The full string to be HMACed | |
| data_to_sign = path + body + timestamp + header_json | |
| # HMAC‑SHA256 using token (or empty string if token is None/empty) | |
| key = token.encode('utf-8') if token else b'' | |
| hmac_bytes = hmac.new(key, data_to_sign.encode('utf-8'), hashlib.sha256).digest() | |
| hmac_hex = bytes_to_hex(hmac_bytes) | |
| # MD5 of the hex string (the string itself, not the raw bytes) | |
| md5_hash = hashlib.md5(hmac_hex.encode('utf-8')).hexdigest() | |
| return md5_hash | |
| def refresh_token(cred, platform, vName): | |
| """ | |
| Call the refresh endpoint to obtain a fresh token. | |
| Returns the token string, or raises an exception on failure. | |
| """ | |
| headers = { | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", | |
| "Accept": "application/json, text/plain, */*", | |
| "cred": cred, | |
| "platform": platform, | |
| "vName": vName, | |
| "Origin": "https://game.skport.com", | |
| "Referer": "https://game.skport.com/" | |
| } | |
| req = urllib.request.Request(REFRESH_URL, headers=headers, method="GET") | |
| try: | |
| with urllib.request.urlopen(req, timeout=10) as resp: | |
| data = json.loads(resp.read().decode('utf-8')) | |
| if data.get("code") == 0 and data.get("data") and data["data"].get("token"): | |
| return data["data"]["token"] | |
| else: | |
| raise Exception(f"Refresh failed: {data.get('message', 'unknown error')}") | |
| except Exception as e: | |
| raise Exception(f"Refresh request error: {e}") | |
| def auto_claim(profile): | |
| """Perform the check‑in for one profile and return a result message.""" | |
| cred = profile["cred"] | |
| skGameRole = profile["skGameRole"] | |
| platform = profile["platform"] | |
| vName = profile["vName"] | |
| account_name = profile["accountName"] | |
| print(f"[{account_name}] Starting check‑in...") | |
| timestamp = str(int(time.time())) | |
| # Try to refresh the token – if it fails we continue with an empty token | |
| token = "" | |
| try: | |
| token = refresh_token(cred, platform, vName) | |
| print(f"[{account_name}] Token refreshed successfully.") | |
| except Exception as e: | |
| print(f"[{account_name}] Token refresh failed: {e} (continuing with empty token)") | |
| # Generate the sign | |
| sign = generate_sign("/web/v1/game/endfield/attendance", "", timestamp, token, platform, vName) | |
| # Prepare headers | |
| headers = { | |
| "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0", | |
| "Accept": "*/*", | |
| "Accept-Language": "en-US,en;q=0.9", | |
| "Accept-Encoding": "gzip, deflate, br, zstd", # urllib will handle decompression automatically | |
| "Referer": "https://game.skport.com/", | |
| "Content-Type": "application/json", | |
| "sk-language": "en", | |
| "sk-game-role": skGameRole, | |
| "cred": cred, | |
| "platform": platform, | |
| "vName": vName, | |
| "timestamp": timestamp, | |
| "sign": sign, | |
| "Origin": "https://game.skport.com", | |
| "Connection": "keep-alive", | |
| "Sec-Fetch-Dest": "empty", | |
| "Sec-Fetch-Mode": "cors", | |
| "Sec-Fetch-Site": "same-site" | |
| } | |
| req = urllib.request.Request(ATTENDANCE_URL, headers=headers, method="POST", data=None) | |
| ok = True | |
| message = f"Daily reward claim for {account_name}" | |
| try: | |
| try: | |
| with urllib.request.urlopen(req, timeout=10) as resp: | |
| response_status = resp.status | |
| response_data = json.loads(resp.read().decode('utf-8')) | |
| except urllib.error.HTTPError as e: | |
| response_status = e.code | |
| response_data = json.loads(e.read().decode('utf-8')) | |
| code = response_data.get("code") | |
| print(f"[{account_name}] API Response Code: {code}") | |
| if code == 0: | |
| message += "\nClaim successful!" | |
| awards = [] | |
| for award in response_data.get("data", {}).get("awardIds", []): | |
| resource = response_data["data"]["resourceInfoMap"].get(str(award["id"]), {}) | |
| awards.append(f"{resource.get('name', 'Unknown')}: {resource.get('count', '?')}") | |
| if awards: | |
| message += f"\nAwards: {', '.join(awards)}" | |
| elif code == 10001: | |
| message += "\nAlready claimed today." | |
| else: | |
| msg = response_data.get("message", "Unknown error") | |
| message += f"\nError: {msg}" | |
| ok = False | |
| except Exception as e: | |
| print(f"[{account_name}] Exception: {e}") | |
| message += f"\nFailed to claim: {e}" | |
| ok = False | |
| return message, ok | |
| def post_webhook(text): | |
| """Send a message via Telegram bot.""" | |
| print("Posting to Telegram...") | |
| url = f"https://api.telegram.org/bot{telegramBotToken}/sendMessage" | |
| payload = json.dumps({ | |
| "chat_id": myTelegramID, | |
| "text": text, | |
| "parse_mode": "HTML" | |
| }).encode('utf-8') | |
| headers = { | |
| "Content-Type": "application/json", | |
| "Content-Length": len(payload) | |
| } | |
| req = urllib.request.Request(url, data=payload, headers=headers, method="POST") | |
| try: | |
| with urllib.request.urlopen(req, timeout=10) as resp: | |
| # Just consume the response, no need to parse | |
| resp.read() | |
| except Exception as e: | |
| print(f"Telegram notification failed: {e}") | |
| def main(): | |
| global profiles_secrets_file, profiles | |
| try: | |
| if profiles_secrets_file: | |
| with open(profiles_secrets_file, 'r') as file: | |
| profiles = json.load(file) | |
| except NameError: | |
| print("profiles_secrets_file not defined using inline variable") | |
| """Run check‑in for all profiles and send a Telegram summary.""" | |
| messages = [] | |
| all_ok = True | |
| for profile in profiles: | |
| msg, this_ok = auto_claim(profile) | |
| messages.append(msg) | |
| all_ok = this_ok and all_ok | |
| combined = "\n\n".join(messages) | |
| if telegram_notify and telegramBotToken and myTelegramID: | |
| post_webhook(combined) | |
| # Also print to console | |
| print("\n" + "="*40) | |
| print(combined) | |
| print("="*40) | |
| if not all_ok: | |
| sys.exit(1) # let systemd figure out that a failure happened | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment