로스트아크
플레이타임 | 결제수단 | 현질 금액 세부 확인


* 사용방법 *
1. 로스트아크 홈페이지에 로그인을 합니다. 
2. F12를 눌러 개발자도구를 엽니다.
3. console 탭을 선택 후 내용 붙여넣기를 합니다.
* 붙여넣기가 안되면 allow pasting 을 입력 해줍니다.

# 결제수단은 스토브 통합입니다.

https://lostark.game.onstove.com/Main


(async function analyzeLostArkFinal() {
    const getStoveCookie = (n) => {
        const v = `; ${document.cookie}`;
        const p = v.split(`; ${n}=`);
        return p.length === 2 ? p.pop().split(";").shift() : null;
    };
    const CONFIG = {
        YEARS: [2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026],
        CATEGORIES: {
            '강화/재료': ['T3', 'T4', '파편', '돌파', '실링', '융화', '강화', '수호', '파괴', '태양', '성장', '보석', '모험의 길', '올인원'],
            '패스/성장': ['패스', '베른', '로헨델', '욘', '페이튼', '파푸니카', '엘가시아', '쿠르잔', '슬롯', '확장'],
            '아바타/외형': ['아바타', '무기', '펫', '탈 것', '옷감', '염색', '머리', '상의', '하의', '벽지', '예복', '의상', '얼굴'],
            '유료서비스': ['베아트리스', '니나브', '축복', '아크 패스', '프리미엄'],
            '카드/확률형': ['카드', '복주머니', '팩', '랜덤', '럭키', '박스', '상자'],
            '화폐거래소': ['화폐거래소'],
            '기타/패키지': ['낙원', '크리스탈', '배틀', '회복', '기능성']
        }
    };
    let playTimeHTML = "";
    let chargeStats = {};
    const yearlyData = {};
    let grandTotal = 0;
    async function fetchPlayTime() {
        try {
            const t = getStoveCookie("SUAT");
            const r = await fetch("https://api.onstove.com/game/v2.0/member/logs", { headers: { Authorization: `Bearer ${t}` } });
            const d = await r.json();
            const l = d.value.find(g => g.game_name === "LOST ARK");
            if (l) {
                const pt = l.play_time, h = Math.floor(pt / 60), td = Math.floor(h / 24), ty = Math.floor(td / 365), rm = Math.floor((td % 365) / 30);
                playTimeHTML = `<div style="line-height:1.8;"><span style="color:#0984e3; font-weight:bold;">🕹️ 총 플레이시간:</span> <b>${h.toLocaleString()}시간</b><br><span style="color:#0984e3; font-weight:bold;">⏳ 시간 환산:</span> <b>${ty}년 ${rm}개월</b> (약 ${td.toLocaleString()}일)</div>`;
            }
        } catch (e) { playTimeHTML = "정보 로드 실패"; }
    }
    function structureItemName(n) {
        if (!n.includes(' - ')) return `<span style="font-size:14px; font-weight:500;">${n}</span>`;
        const p = n.split(' - '), t = p[0].trim(), c = p.slice(1).join(' - ').trim();
        const escT = t.replace(/[.*+?^${}()|[]]/g, '$&');
        const it = c.replace(new RegExp(escT, 'g'), '').split(/ - | -/).map(i => i.replace(/^[- ]+/, '').trim()).filter(i => i.length > 1);
        const u = [...new Set(it)];
        return `<strong style="color:#2d3436; display:block; margin-bottom:4px; font-size:14px;">${t}</strong><span style="color:#636e72; font-size:12px; line-height:1.5;">• ${u.join('<br>• ')}</span>`;
    }
    async function fetchData() {
        for (const y of CONFIG.YEARS) {
            const r = { StartDate: `${y}.01.01`, EndDate: `${y}.12.31` };
            yearlyData[y] = { total: 0, categories: {} };
            try {
                const cf = await $.ajax({ url: '/Cash/GetChargeList', data: { Page: 1, ...r } });
                const cl = parseInt($(cf).find('.pagination__last').attr('onClick')?.match(/d+/)?.[0] || 1);
                for (let i = 1; i <= cl; i++) {
                    const d = (i === 1) ? cf : await $.ajax({ url: '/Cash/GetChargeList', data: { Page: i, ...r } });
                    $(d).find('tbody tr').each((_, el) => {
                        const rw = $(el).find('td:nth-child(2)').text().trim(), pr = parseInt($(el).find('td:nth-child(3)').text().replace(/[^0-9]/g, '')) || 0;
                        if (rw && rw !== '-' && rw !== '충전수단') {
                            const cw = rw.split(/[0-9]|포인트/)[0].trim();
                            chargeStats[cw] = (chargeStats[cw] || 0) + pr;
                        }
                    });
                }
                const pf = await $.ajax({ url: '/Cash/GetPurchaseList', data: { Page: 1, ...r } });
                const pl = parseInt($(pf).find('.pagination__last').attr('onClick')?.match(/d+/)?.[0] || 1);
                for (let i = 1; i <= pl; i++) {
                    const d = (i === 1) ? pf : await $.ajax({ url: '/Cash/GetPurchaseList', data: { Page: i, ...r } });
                    $(d).find('tbody tr').each((_, el) => {
                        const rn = $(el).find('.list__buy-name').text().trim(), cs = parseInt($(el).find('.list__price').text().replace(/[^0-9]/g, '')) || 0;
                        if (rn) updateYearly(y, rn, cs);
                    });
                }
                const mf = await $.ajax({ url: '/Cash/GetMarketList', data: { Page: 1, ...r } });
                const ml = parseInt($(mf).find('.pagination__last').attr('onClick')?.match(/d+/)?.[0] || 1);
                for (let i = 1; i <= ml; i++) {
                    const d = (i === 1) ? mf : await $.ajax({ url: '/Cash/GetMarketList', data: { Page: i, ...r } });
                    $(d).find('tbody tr').each((_, el) => {
                        const cs = parseInt($(el).find('.list__exchange').text().replace(/[^0-9]/g, '')) || 0;
                        if (cs > 0) updateYearly(y, '화폐거래소', cs);
                    });
                }
            } catch (e) {}
        }
    }
    function updateYearly(y, rn, cs) {
        const yn = yearlyData[y]; yn.total += cs;
        let cat = '기타/패키지';
        for (const [k, v] of Object.entries(CONFIG.CATEGORIES)) { if (v.some(s => rn.includes(s))) { cat = k; break; } }
        if (!yn.categories[cat]) yn.categories[cat] = { total: 0, items: {} };
        yn.categories[cat].total += cs;
        const sn = structureItemName(rn);
        if (!yn.categories[cat].items[sn]) yn.categories[cat].items[sn] = { count: 0, price: 0 };
        yn.categories[cat].items[sn].count++; yn.categories[cat].items[sn].price += cs;
    }
    await fetchPlayTime();
    await fetchData();
    const ch = Object.entries(chargeStats).sort((a,b)=>b[1]-a[1]).map(([m, a]) => `<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #f0f0f0;"><span>${m}</span><b>${a.toLocaleString()}원</b></div>`).join('');
    const yh = Object.keys(yearlyData).sort((a,b)=>b-a).map(y => {
        const d = yearlyData[y]; if (d.total === 0) return "";
        grandTotal += d.total;
        const ct = Object.entries(d.categories).sort((a,b)=>b[1].total - a[1].total).map(([cn, cd]) => {
            const it = Object.entries(cd.items).sort((a,b)=>b[1].price - a[1].price).map(([n, i]) => `<div style="display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid #eee;"><div style="flex:1;padding-right:15px;">${n}</div><div style="text-align:right;min-width:100px;"><b>${i.price.toLocaleString()}원</b><br><small style="color:#6c5ce7;">${i.count}회</small></div></div>`).join('');
            return `<div style="margin-top:10px;border:1px solid #e1e8ed;border-radius:10px;"><button onclick="const n=this.nextElementSibling;n.style.display=n.style.display==='none'?'block':'none'" style="width:100%;padding:12px 15px;background:#f8f9fa;border:none;cursor:pointer;display:flex;justify-content:space-between;font-weight:bold;color:#0984e3;"><span>📦 ${cn}</span><span>${cd.total.toLocaleString()}원 ▾</span></button><div style="display:none;padding:5px 15px 10px;background:#fff;">${it}</div></div>`;
        }).join('');
        return `<div style="margin-bottom:15px;border:1px solid #ccc;border-radius:12px;overflow:hidden;"><button onclick="const n=this.nextElementSibling;n.style.display=n.style.display==='none'?'block':'none'" style="width:100%;padding:15px 25px;background:#2d3436;color:#fff;border:none;cursor:pointer;display:flex;justify-content:space-between;align-items:center;"><span style="font-size:18px;font-weight:bold;">📅 ${y}년</span><span style="font-size:18px;color:#fdcb6e;font-weight:bold;">${d.total.toLocaleString()}원 ▾</span></button><div style="display:none;padding:15px;background:#fff;">${ct}</div></div>`;
    }).join('');
    const l = `<div id="loa-final" style="position:fixed;top:0;left:0;width:100%;height:100%;background:#f1f2f6;z-index:999999;overflow-y:auto;padding:30px 15px;font-family:sans-serif;box-sizing:border-box;"><div style="max-width:700px;margin:0 auto;"><div style="background:#fff;padding:20px 25px;border-radius:15px;margin-bottom:15px;border:1px solid #d1d9e0;">${playTimeHTML}</div><div style="background:#fff;border-radius:15px;margin-bottom:15px;border:1px solid #d1d9e0;overflow:hidden;"><button onclick="const n=this.nextElementSibling;n.style.display=n.style.display==='none'?'block':'none'" style="width:100%;padding:18px 25px;background:#fff;border:none;cursor:pointer;display:flex;justify-content:space-between;font-weight:bold;font-size:16px;"><span>💳 결제 수단별 합계</span><span>보기 ▾</span></button><div style="display:none;padding:15px 25px;border-top:1px solid #eee;">${ch}</div></div><div style="background:#2d3436;color:#fff;padding:25px;border-radius:15px;text-align:center;margin-bottom:20px;"><div style="font-size:14px;opacity:0.8;margin-bottom:5px;">누적 총 결제액</div><div style="font-size:36px;font-weight:900;color:#00d2d3;">${grandTotal.toLocaleString()}원</div></div>${yh}<div style="text-align:center;margin:30px 0;"><button onclick="document.getElementById('loa-final').remove()" style="padding:15px 50px;background:#333;color:#fff;border:none;border-radius:10px;cursor:pointer;font-weight:bold;">닫기</button></div></div></div>`;
    document.body.insertAdjacentHTML('beforeend', l);
})();