Endfield Login
/** Config Starts. **/
const profiles = [
{
cred: "change", // Replace with your Endfield cred cookie value ( get from cookie )
skGameRole: "change", // Replace with your Endfield skGameRole cookie value ( get from cookie )
platform: "3",
vName: "1.0.0",
accountName: "change" // Replace with a name to identify this account( a simple identifier )
}
// Add more profiles if needed
];
const discord_notify = true;
const myDiscordID = "change"; // Replace with your Discord ID (optional, for pinging)
const discordWebhook = "change"; // Replace with your Discord webhook URL
/** Config ends. **/
const attendanceUrl = 'https://zonai.skport.com/web/v1/game/endfield/attendance';
async function main() {
const results = await Promise.all(profiles.map(autoClaimFunction));
if (discord_notify && discordWebhook) {
postWebhook(results);
}
}
function autoClaimFunction({ cred, skGameRole, platform, vName, accountName }) {
console.log(`[${accountName}] Checking credentials and performing check-in...`);
const timestamp = Math.floor(Date.now() / 1000).toString();
// Attempt to refresh token used for signing. If refresh fails, token will be empty.
let token = "";
try {
token = refreshToken(cred, platform, vName);
console.log(`[${accountName}] Token refreshed successfully.`);
} catch (e) {
console.error(`[${accountName}] Token refresh failed: ${e.message}`);
// proceed with empty token; API may reject 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 result = {
name: accountName,
success: false,
status: "",
rewards: ""
};
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) {
result.success = true;
result.status = "Thank you for checking in today.";
if (responseJson.data && responseJson.data.awardIds) {
const awards = responseJson.data.awardIds.map(award => {
const resource = responseJson.data.resourceInfoMap ? responseJson.data.resourceInfoMap[award.id] : null;
return resource ? `${resource.name} x${resource.count}` : (award.id || "Unknown Item");
}).join('\n');
result.rewards = awards;
} else {
result.rewards = "No detailed reward info.";
}
} else if (responseJson.code === 10001) {
result.success = true;
result.status = "You have already checked in today.";
result.rewards = "Nothing to claim";
} else {
result.success = false;
result.status = `❌ Error (Code: ${responseJson.code})`;
result.rewards = responseJson.message || "Unknown Error";
}
} catch (error) {
result.success = false;
result.status = "💥 Exception";
result.rewards = error.message;
console.error(`[${accountName}] Exception: ${error.message}`);
}
return result;
}
function postWebhook(results) {
console.log('Posting to Discord webhook...');
const messages = results.map(r => {
const mention = myDiscordID ? `<@${myDiscordID}>` : '';
const showAwards = r.success && r.status === "Thank you for checking in today.";
const rewardsLine = showAwards
? `Rewards: ${r.rewards.replace(/\n/g, ', ')}`
: '';
return (
`Hello Endministrator ${mention}.\n` +
`${r.status}\n` +
(rewardsLine ? `\n${rewardsLine}` : '')
);
});
const payload = {
username: "Supervisor Perlica",
avatar_url: "https://i.imgur.com/xboRe5Q.png",
content: messages.join('\n\n')
};
const options = {
method: 'POST',
contentType: 'application/json',
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
try {
UrlFetchApp.fetch(discordWebhook, options);
} catch (e) {
console.error("Failed to send Discord webhook: " + e.message);
}
}
/** 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 then 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('');
}
Public Last updated: 2026-02-07 08:33:21 PM