Last active
February 24, 2023 13:59
-
-
Save MahouSirin/a5acb474e595e264712e7d49ceabad04 to your computer and use it in GitHub Desktop.
arca.live @산호궁아이피
This file contains 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 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}®ion=${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¤t_page=${i}&type=1®ion=${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