Skip to content

Instantly share code, notes, and snippets.

@MahouSirin
Last active February 24, 2023 13:59
Show Gist options
  • Save MahouSirin/a5acb474e595e264712e7d49ceabad04 to your computer and use it in GitHub Desktop.
Save MahouSirin/a5acb474e595e264712e7d49ceabad04 to your computer and use it in GitHub Desktop.
arca.live @산호궁아이피
#!/usr/bin/env node
const axios = require('axios');
const crypto = require('crypto');
const path = require('path');
(async () => {
const cookie = 'REPLACE_WITH_COOKIE';
const encryptedCookie = Object.entries(parseCookie(cookie)).map(([key, value]) => {
// ID, 토큰 같은 정보만 비공개
if (['ltuid', 'ltoken', 'account_id', 'cookie_token'].includes(key)) {
value = '********';
}
return `${key}=${value}`;
}).join('; ');
console.log(`[DEBUG] Cookie set: ${encryptedCookie}`);
const dataMachine = axios.create({
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
'Cookie': cookie,
'Accept': 'application/json;charset=utf-8',
Referrer: 'https://webstatic-sea.mihoyo.com/',
'x-rpc-language': 'ko-kr',
'x-rpc-client_type': '4',
'x-rpc-app_version': '1.5.0',
DS: '',
}
});
dataMachine.interceptors.request.use((config) => {
config.headers.DS = generateDSToken();
return config;
});
dataMachine.interceptors.response.use((res) => {
console.log(`[DEBUG] DS Header: ${res.config.headers.DS}`);
return res;
});
const result = await dataMachine.get(`https://bbs-api-os.mihoyo.com/game_record/card/wapi/getGameRecordCard?uid=${parseCookie(cookie).ltuid}`).then(res => res.data?.data.list[0]);
const gameRoleID = result.game_role_id;
const server = result.region;
// 인증 여부 확인
const isAuthenticated = await dataMachine.get('https://sg-public-api.mihoyo.com/event/calculateos/avatar/auth/info?lang=ko-kr').then(res => !!res.data.data?.avatar_auth);
// 인증되지 않았을 경우 자동 인증 처리
if (!isAuthenticated) await dataMachine.post('https://sg-public-api.mihoyo.com/event/calculateos/avatar/auth', { avatar_auth: 1, lang: 'ko-kr' });
// 보유한 캐릭터 목록 조회
const syncAvatars = await dataMachine.post('https://sg-public-api.mihoyo.com/event/calculateos/sync/avatar/list', {
element_attr_ids: [],
lang: 'ko-kr',
page: 1,
region: server,
size: 523,
uid: gameRoleID,
weapon_cat_ids: [],
}).then(res => res.data.data);
// 특정 캐릭터 스탯 조회
const targetAvatar = syncAvatars.list.find((avatar) => avatar.id === 10000052); // 라이덴
const avatarID = targetAvatar.id;
const raidenStat = await dataMachine.get(`https://sg-public-api.mihoyo.com/event/calculateos/sync/avatar/detail?avatar_id=${avatarID}&uid=${gameRoleID}&region=${server}&lang=ko-kr`).then(res => res.data.data);
const raidenComputed = await dataMachine.post('https://sg-public-api.mihoyo.com/event/calculateos/compute', {
avatar_id: avatarID,
avatar_level_current: targetAvatar.level_current,
avatar_level_target: targetAvatar.max_level,
element_attr_id: targetAvatar.element_attr_id,
skill_list: raidenStat.skill_list.map((skill) => ({
id: skill.group_id,
level_current: skill.level_current === 0 ? 1 : skill.level_current,
level_target: skill.max_level,
})),
weapon: {
id: raidenStat.weapon.id,
level_current: raidenStat.weapon.level_current,
level_target: raidenStat.weapon.max_level,
},
reliquary_list: raidenStat.reliquary_list.map((relic) => ({
id: relic.id,
level_current: relic.level_current,
level_target: relic.max_level,
})),
lang: 'ko-kr',
}).then(res => res.data.data);
console.log(raidenComputed);
// Unused code snippets
/* let notePrimoRewards = [];
let i = 1;
while (true) {
console.log(`${i}번째 페이지`);
const detailNote = await dataMachine.get(`https://hk4e-api-os.mihoyo.com/event/ysledgeros/month_detail?month=11&current_page=${i}&type=1&region=${server}&uid=${gameRoleID}&lang=ko-kr`).then(res => res.data.data?.list);
if (!detailNote.length) break;
notePrimoRewards.push(detailNote);
i++;
};
notePrimoRewards = notePrimoRewards.flat();
console.log(`보상 ${notePrimoRewards.length}번 받았음`);
const profile = await dataMachine.get(`https://bbs-api-os.mihoyo.com/game_record/genshin/api/index?server=${server}&role_id=${gameRoleID}`).then(res => res.data.data);
const dailyNote = await dataMachine.get(`https://bbs-api-os.mihoyo.com/game_record/genshin/api/dailyNote?server=${server}&role_id=${gameRoleID}`).then(res => res.data.data);
console.log(dailyNote);
const character = await dataMachine.post('https://bbs-api-os.mihoyo.com/game_record/genshin/api/character', {
character_ids: profile.avatars.map(c => c.id),
role_id: gameRoleID,
server,
}).then(res => res.data.data);
console.log(character.avatars.find(c => c.name === '바바라')); */
})();
function generateDSToken() {
const time = Math.floor(Date.now() / 1000);
const DS_SALT = '6cqshh5dhw73bzxn20oexa9k516chk7s';
const randomChar = randomString(6);
const data = `salt=${DS_SALT}&t=${time}&r=${randomChar}`;
const hash = crypto.createHash('md5').update(data).digest('hex');
return `${time},${randomChar},${hash}`;
};
function randomString(len = 6, an) {
an = an && an.toLowerCase();
let str = '';
let i = 0;
let min = an === 'a' ? 10 : 0;
let max = an === 'n' ? 10 : 62;
for (;i++ < len;) {
let r = Math.random() * (max - min) + min << 0;
str += String.fromCharCode(r += r > 9 ? r < 36 ? 55 : 61 : 48);
}
return str;
};
function parseCookie(cookies) {
const output = {};
try {
cookies.split(/\s*;\s*/).forEach((pair) => {
pair = pair.split(/\s*=\s*/);
output[pair[0]] = pair.splice(1).join('=');
});
return output;
} catch {
return undefined;
}
};
function randomInt(min, max) {
return Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1)) + Math.ceil(min);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment