-
-
Save cptmacp/70f66f2a4fb9d5fa708d33fbcc8e265a to your computer and use it in GitHub Desktop.
| /** Config Starts. **/ | |
| const profiles = [ | |
| { | |
| cred: "xxxxxxxxxxxxxxxxxxxxxx", // Replace with your Endfield cred cookie value ( get from cookie ) | |
| skGameRole: "xxxxxxxxxxxx", // Replace with your Endfield skGameRole cookie value ( get from cookie ) | |
| platform: "3", | |
| vName: "1.0.0", | |
| accountName: "acc_name" // Replace with a name to identify this account( a simple identifier ) | |
| } | |
| // Add more profiles if needed | |
| ]; | |
| const telegram_notify = true; | |
| const myTelegramID = "xxxxx"; // Replace with your Telegram ID | |
| const telegramBotToken = "xxxxxx:xxxxxxxx"; // Replace with your bot token | |
| /** Config ends. **/ | |
| const attendanceUrl = 'https://zonai.skport.com/web/v1/game/endfield/attendance'; | |
| async function main() { | |
| const messages = await Promise.all(profiles.map(autoClaimFunction)); | |
| const endfieldResp = `${messages.join('\n\n')}`; | |
| if (telegram_notify && telegramBotToken && myTelegramID) { | |
| postWebhook(endfieldResp); | |
| } | |
| } | |
| function autoClaimFunction({ cred, skGameRole, platform, vName, accountName }) { | |
| console.log(`[${accountName}] Checking credentials and performing check-in...`); | |
| const timestamp = Math.floor(Date.now() / 1000).toString(); | |
| // Try to refresh token for signing (best-effort) | |
| let token = ""; | |
| try { | |
| token = refreshToken(cred, platform, vName); | |
| console.log(`[${accountName}] Token refreshed successfully.`); | |
| } catch (e) { | |
| console.error(`[${accountName}] Token refresh failed: ${e.message}`); | |
| // continue with empty token; request may fail if token required | |
| } | |
| const sign = generateSign('/web/v1/game/endfield/attendance', '', timestamp, token, platform, vName); | |
| const header = { | |
| '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', | |
| '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' | |
| }; | |
| const options = { | |
| method: 'POST', | |
| headers: header, | |
| muteHttpExceptions: true, | |
| }; | |
| let response = `Daily reward claim for ${accountName}`; | |
| try { | |
| const endfieldResponse = UrlFetchApp.fetch(attendanceUrl, options); | |
| const responseJson = JSON.parse(endfieldResponse.getContentText()); | |
| console.log(`[${accountName}] API Response Code: ${responseJson.code}`); | |
| if (responseJson.code === 0) { | |
| response += '\nClaim successful!'; | |
| const awards = responseJson.data.awardIds.map(award => { | |
| const resource = responseJson.data.resourceInfoMap[award.id]; | |
| return `${resource.name}: ${resource.count}`; | |
| }).join(', '); | |
| response += `\nAwards: ${awards}`; | |
| } else if (responseJson.code === 10001) { | |
| response += '\nAlready claimed today.'; | |
| } else { | |
| response += `\nError: ${responseJson.message}`; | |
| } | |
| } catch (error) { | |
| console.error(`[${accountName}] Exception: ${error.message}`); | |
| response += `\nFailed to claim: ${error.message}`; | |
| } | |
| return response; | |
| } | |
| function postWebhook(data) { | |
| console.log('Posting to Telegram...'); | |
| let payload = JSON.stringify({ | |
| 'chat_id': myTelegramID, | |
| 'text': data, | |
| 'parse_mode': 'HTML' | |
| }); | |
| const options = { | |
| method: 'POST', | |
| contentType: 'application/json', | |
| payload: payload, | |
| muteHttpExceptions: true | |
| }; | |
| UrlFetchApp.fetch('https://api.telegram.org/bot' + telegramBotToken + '/sendMessage', options); | |
| } | |
| /** Helper: Refresh token used for signing **/ | |
| function refreshToken(cred, platform, vName) { | |
| const refreshUrl = 'https://zonai.skport.com/web/v1/auth/refresh'; | |
| const header = { | |
| '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/' | |
| }; | |
| const options = { | |
| method: 'GET', | |
| headers: header, | |
| muteHttpExceptions: true | |
| }; | |
| const response = UrlFetchApp.fetch(refreshUrl, options); | |
| const json = JSON.parse(response.getContentText()); | |
| if (json.code === 0 && json.data && json.data.token) { | |
| return json.data.token; | |
| } else { | |
| throw new Error(`Refresh Failed (Code: ${json.code}, Msg: ${json.message})`); | |
| } | |
| } | |
| /** Signature generation (HMAC-SHA256 -> MD5) **/ | |
| function generateSign(path, body, timestamp, token, platform, vName) { | |
| let str = path + body + timestamp; | |
| const headerJson = `{"platform":"${platform}","timestamp":"${timestamp}","dId":"","vName":"${vName}"}`; | |
| str += headerJson; | |
| const hmacBytes = Utilities.computeHmacSha256Signature(str, token || ''); | |
| const hmacHex = bytesToHex(hmacBytes); | |
| const md5Bytes = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, hmacHex); | |
| return bytesToHex(md5Bytes); | |
| } | |
| function bytesToHex(bytes) { | |
| return bytes.map(function(byte) { | |
| return ('0' + (byte & 0xFF).toString(16)).slice(-2); | |
| }).join(''); | |
| } |
Thanks for sharing! That's very useful.
Thank you very much for the code!
May I ask you to comment skGameRole that it's content needs to be changed to person's own account ID?
I was having "Already claimed today." message despite not actually claiming until I figured to change this var.😅
Thank you very much for the code! May I ask you to comment skGameRole that it's content needs to be changed to person's own account ID? I was having "Already claimed today." message despite not actually claiming until I figured to change this var.😅
May I ask how you fix this? I changed every ID I could find but still can’t figure out how it works.
Thank you very much for the code! May I ask you to comment skGameRole that it's content needs to be changed to person's own account ID? I was having "Already claimed today." message despite not actually claiming until I figured to change this var.😅
May I ask how you fix this? I changed every ID I could find but still can’t figure out how it works.
F12- netowrk - record page- update - paste sk-game-role in find field - find YOURS sk-game-role numbers - paste in code (full numbers) - fixed for me

Thank you very much for the code! May I ask you to comment skGameRole that it's content needs to be changed to person's own account ID? I was having "Already claimed today." message despite not actually claiming until I figured to change this var.😅
May I ask how you fix this? I changed every ID I could find but still can’t figure out how it works.
Or you can simply use the console to get that data (f12 -> console -> allow paste -> paste these code). it also work for me.
(async()=>{
const h=await(await fetch(location.href)).text();
const f=document.createElement('iframe');f.style.display='none';
window.addEventListener('message',e=>{if(e.data?.t==='G'){console.clear();console.log(JSON.stringify(e.data.d,null,4));f.remove()}});
document.body.appendChild(f);
f.contentDocument.write(h.replace(/<head[^>]*>/i,m=>m+`<script>
const _f=window.fetch;window.fetch=async(...a)=>{
const[u,c]=a;
if(c?.headers){
let k={};
(c.headers instanceof Headers?c.headers:Object.entries(c.headers)).forEach(x=>{const[n,v]=Array.isArray(x)?x:[x,c.headers[x]];k[n.toLowerCase()]=v});
if(k['sk-game-role'])window.parent.postMessage({t:'G',d:{cred:k.cred,skGameRole:k['sk-game-role'],timestamp:k.timestamp,sign:k.sign}},'*');
}return _f(u,c)}
<\/script><base href="${location.href}">`));
f.contentDocument.close();
})();
Thank you very much for the code! May I ask you to comment skGameRole that it's content needs to be changed to person's own account ID? I was having "Already claimed today." message despite not actually claiming until I figured to change this var.😅
May I ask how you fix this? I changed every ID I could find but still can’t figure out how it works.
Or you can simply use the console to get that data (f12 -> console -> allow paste -> paste these code). it also work for me.
(async()=>{ const h=await(await fetch(location.href)).text(); const f=document.createElement('iframe');f.style.display='none'; window.addEventListener('message',e=>{if(e.data?.t==='G'){console.clear();console.log(JSON.stringify(e.data.d,null,4));f.remove()}}); document.body.appendChild(f); f.contentDocument.write(h.replace(/<head[^>]*>/i,m=>m+`<script> const _f=window.fetch;window.fetch=async(...a)=>{ const[u,c]=a; if(c?.headers){ let k={}; (c.headers instanceof Headers?c.headers:Object.entries(c.headers)).forEach(x=>{const[n,v]=Array.isArray(x)?x:[x,c.headers[x]];k[n.toLowerCase()]=v}); if(k['sk-game-role'])window.parent.postMessage({t:'G',d:{cred:k.cred,skGameRole:k['sk-game-role'],timestamp:k.timestamp,sign:k.sign}},'*'); }return _f(u,c)} <\/script><base href="${location.href}">`)); f.contentDocument.close(); })();
Thank you, very appreciative.
Hi Thanks. Seems like skGameRole is tied to accounts.
I tried the same script on two different accounts, but I noticed they were the same at that time ( could be due to cookies LOL )
Anyways, I updated the info and script, and a way to get cred and skGameRole
Also, there is another script for Discord if anyone is interested.
https://gist.github.com/cptmacp/1e9a9f20f69c113a0828fea8d13cb34c
hi when we logout and sign in cred was change and Error: User is not logged in
hi when we logout and sign in cred was change and Error: User is not logged in
Yes, the cred would get regenerated and maybe skGameRole also.
If you want to do it for multiple accounts, you need to use another browser or incognito mode.
Thanks for sharing, was always using the Hoyolab auto sign-in via Github action, this is also useful!
My daily sign in is completely broken I get a 10000 Request exception response. I also cannot manually do it on the website.
Receiving the same error as @Renari

Hi please refer to my comment here
Update 03 Feb 2025
The script are fixed now
Working great here, thank you for your work.
A simple JS script to automate the endfield daily claim login from
https://game.skport.com/endfield/sign-in?header=0&hg_media=skport&hg_link_campaign=tools
Replace
myTelegramIDandtelegramBotTokenwith the telegram bot token// get
credandskGameRolefrom hereInstall tampermonkey for chrome ( Also available for other browsers )
make sure to enable "Allow User Scripts" from extension settings for tampermonkey
download and import script to tampermonkey via dashboard here
after that, open "https://game.skport.com/endfield/sign-in?header=0&hg_media=skport&hg_link_campaign=tools" and wait for popup
You will see
credandskGameRoleI am still not sure whatsignis, but in the HAR log, I saw it getting appended in a POST request.You can either randomize it or leave it as it is.
signis basically ( as per @HHim8826 )Use https://script.google.com/home/ to automate and create a trigger to auto-run the script
Script inspired by: https://github.com/canaria3406/hoyolab-auto-sign