Nojima Cookie Sender

Gửi cookies về server local khi vào trang mypage hoặc cart

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Nojima Cookie Sender
// @namespace    http://tampermonkey.net/
// @version      1.5
// @description  Gửi cookies về server local khi vào trang mypage hoặc cart
// @author       You
// @match        https://online.nojima.co.jp/app/mypage/mypage/*
// @match        https://online.nojima.co.jp/app/mypage/mypage
// @match        https://online.nojima.co.jp/sp/app/mypage/mypage/*
// @match        https://online.nojima.co.jp/sp/app/mypage/mypage
// @match        https://online.nojima.co.jp/sp/app/cart/cart/*
// @match        https://online.nojima.co.jp/sp/app/cart/cart
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @connect      localhost
// @connect      127.0.0.1
// @connect      192.168.1.13
// @run-at       document-idle
// ==/UserScript==

(function() {
    'use strict';

    // ====== CẤU HÌNH ======
    const SERVER_URL = 'http://192.168.1.13:3000/receive-cookies';
    const CHECK_INTERVAL = 2000; // Kiểm tra mỗi 2 giây
    const MAX_WAIT_TIME = 30000; // Đợi tối đa 30 giây

    // ====== HÀM UTILITY ======

    // Lưu trữ Set-Cookie headers
    let capturedSetCookies = [];
    let lastResponseHeaders = null;

    // Intercept fetch để lấy Set-Cookie headers
    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const response = await originalFetch(...args);
        
        // Clone response để đọc headers
        const clonedResponse = response.clone();
        
        // Lấy Set-Cookie từ headers (nếu có)
        const setCookie = clonedResponse.headers.get('set-cookie');
        if (setCookie) {
            console.log('[Nojima] Phát hiện Set-Cookie header:', setCookie);
            capturedSetCookies.push({
                url: args[0],
                timestamp: new Date().toISOString(),
                setCookie: setCookie
            });
        }
        
        // Lưu headers
        lastResponseHeaders = Array.from(clonedResponse.headers.entries());
        
        return response;
    };

    // Intercept XMLHttpRequest để lấy Set-Cookie
    const originalXHROpen = XMLHttpRequest.prototype.open;
    const originalXHRSend = XMLHttpRequest.prototype.send;
    
    XMLHttpRequest.prototype.open = function(...args) {
        this._url = args[1];
        return originalXHROpen.apply(this, args);
    };
    
    XMLHttpRequest.prototype.send = function(...args) {
        this.addEventListener('load', function() {
            const setCookie = this.getResponseHeader('set-cookie');
            if (setCookie) {
                console.log('[Nojima] XHR Set-Cookie:', setCookie);
                capturedSetCookies.push({
                    url: this._url,
                    timestamp: new Date().toISOString(),
                    setCookie: setCookie
                });
            }
        });
        return originalXHRSend.apply(this, args);
    };

    // Lấy tất cả cookies hiện tại
    function getAllCookies() {
        const cookies = document.cookie.split(';').map(cookie => {
            const [name, ...rest] = cookie.trim().split('=');
            return {
                name: name,
                value: rest.join('=')
            };
        });
        return cookies;
    }

    // Lấy cookie cụ thể
    function getCookie(name) {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
        return null;
    }

    // Parse Set-Cookie header thành array
    function parseSetCookieHeader(setCookieString) {
        if (!setCookieString) return [];
        
        // Split by comma nhưng chú ý không split trong expires date
        const cookies = [];
        let current = '';
        let inExpires = false;
        
        for (let i = 0; i < setCookieString.length; i++) {
            const char = setCookieString[i];
            
            if (char === ',' && !inExpires) {
                if (current.trim()) {
                    cookies.push(current.trim());
                }
                current = '';
            } else {
                current += char;
                if (setCookieString.substr(i, 8).toLowerCase() === 'expires=') {
                    inExpires = true;
                }
                if (char === ';') {
                    inExpires = false;
                }
            }
        }
        
        if (current.trim()) {
            cookies.push(current.trim());
        }
        
        return cookies;
    }

    // Kiểm tra có phải trang hợp lệ không (không bị redirect)
    function isValidPage() {
        const currentUrl = window.location.href;
        const isMypage = currentUrl.includes('/mypage/mypage'); // Bỏ /app/ để match cả /sp/app/mypage
        const isCart = currentUrl.includes('/sp/app/cart/cart');

        // Kiểm tra xem có bị redirect về login không
        if (currentUrl.includes('/login')) {
            console.log('[Nojima] Bị redirect về login - cookies không hợp lệ');
            return false;
        }

        // Kiểm tra xem có bị redirect về error không
        if (currentUrl.includes('/error')) {
            console.log('[Nojima] Bị redirect về error');
            return false;
        }

        // Nếu là mypage (cả desktop /app/ và mobile /sp/app/)
        if (isMypage) {
            const pageType = currentUrl.includes('/sp/') ? 'mypage (mobile)' : 'mypage (desktop)';
            console.log(`[Nojima] Đang ở trang ${pageType}`);
            
            // Kiểm tra có element đặc trưng
            const mypageElements = document.querySelector('[class*="mypage"], [id*="mypage"], .member-info, .user-info, body');
            
            // Hoặc kiểm tra title
            const titleCheck = document.title.includes('マイページ') || 
                              document.title.includes('mypage') || 
                              document.title.toLowerCase().includes('my page');
            
            return mypageElements !== null || titleCheck;
        }

        // Nếu là cart
        if (isCart) {
            console.log('[Nojima] Đang ở trang cart');
            const cartElements = document.querySelector('.cart-list, .cart-item, [class*="cart"], body');
            return cartElements !== null;
        }

        return false;
    }

    // Gửi cookies về server
    function sendCookiesToServer() {
        const cookies = getAllCookies();

        // Lấy các cookies quan trọng
        const importantCookies = {
            '_abck': getCookie('_abck'),
            'JSESSIONID': getCookie('JSESSIONID'),
            'bm_sz': getCookie('bm_sz'),
            'bm_sv': getCookie('bm_sv'),
            'bm_mi': getCookie('bm_mi')
        };

        // Parse Set-Cookie headers đã capture
        const parsedSetCookies = [];
        capturedSetCookies.forEach(item => {
            const parsed = parseSetCookieHeader(item.setCookie);
            parsed.forEach(cookie => {
                parsedSetCookies.push({
                    url: item.url,
                    timestamp: item.timestamp,
                    rawCookie: cookie,
                    // Parse name=value
                    name: cookie.split('=')[0],
                    value: cookie.split('=').slice(1).join('=').split(';')[0]
                });
            });
        });

        const data = {
            timestamp: new Date().toISOString(),
            url: window.location.href,
            cookies: cookies,
            importantCookies: importantCookies,
            userAgent: navigator.userAgent,
            cookieString: document.cookie,
            // Thêm Set-Cookie headers
            setCookieHeaders: capturedSetCookies,
            parsedSetCookies: parsedSetCookies,
            lastResponseHeaders: lastResponseHeaders
        };

        console.log('[Nojima] Đang gửi cookies về server (có Set-Cookie headers)...', data);

        // Gửi bằng GM_xmlhttpRequest (bypass CORS)
        GM_xmlhttpRequest({
            method: 'POST',
            url: SERVER_URL,
            headers: {
                'Content-Type': 'application/json'
            },
            data: JSON.stringify(data),
            onload: function(response) {
                if (response.status === 200) {
                    console.log('[Nojima] ✅ Đã gửi cookies thành công!');
                    console.log('[Nojima] Response:', response.responseText);

                    // Hiển thị thông báo trên trang
                    showNotification('✅ Cookies đã được gửi về server!', 'success');

                    // Lưu timestamp để tránh gửi lại quá nhanh
                    GM_setValue('lastSentTime', Date.now());
                } else {
                    console.error('[Nojima] ❌ Lỗi khi gửi cookies:', response.status, response.statusText);
                    showNotification('❌ Lỗi khi gửi cookies: ' + response.status, 'error');
                }
            },
            onerror: function(error) {
                console.error('[Nojima] ❌ Không thể kết nối đến server:', error);
                showNotification('❌ Không thể kết nối đến server local!', 'error');
            }
        });
    }

    // Hiển thị thông báo trên trang
    function showNotification(message, type = 'info') {
        // Tạo element notification
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 15px 20px;
            background-color: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#f44336' : '#2196F3'};
            color: white;
            border-radius: 5px;
            box-shadow: 0 4px 6px rgba(0,0,0,0.1);
            z-index: 999999;
            font-family: Arial, sans-serif;
            font-size: 14px;
            max-width: 300px;
            animation: slideIn 0.3s ease-out;
        `;
        notification.textContent = message;

        // Thêm animation
        const style = document.createElement('style');
        style.textContent = `
            @keyframes slideIn {
                from {
                    transform: translateX(400px);
                    opacity: 0;
                }
                to {
                    transform: translateX(0);
                    opacity: 1;
                }
            }
        `;
        document.head.appendChild(style);

        document.body.appendChild(notification);

        // Tự động xóa sau 5 giây
        setTimeout(() => {
            notification.style.animation = 'slideOut 0.3s ease-out';
            setTimeout(() => notification.remove(), 300);
        }, 5000);
    }

    // ====== MAIN LOGIC ======

    function main() {
        console.log('[Nojima] Script đã load!');
        console.log('[Nojima] URL hiện tại:', window.location.href);

        // Kiểm tra xem đã gửi gần đây chưa (tránh spam)
        const lastSentTime = GM_getValue('lastSentTime', 0);
        const timeSinceLastSent = Date.now() - lastSentTime;

        if (timeSinceLastSent < 10000) { // 10 giây
            console.log('[Nojima] Vừa mới gửi cookies, bỏ qua lần này');
            showNotification('ℹ️ Vừa mới gửi cookies rồi', 'info');
            return;
        }

        let attempts = 0;
        const maxAttempts = MAX_WAIT_TIME / CHECK_INTERVAL;

        showNotification('⏳ Đang kiểm tra trang...', 'info');

        // Đợi và kiểm tra trang có load đầy đủ không
        const checkInterval = setInterval(() => {
            attempts++;

            console.log(`[Nojima] Kiểm tra lần ${attempts}/${maxAttempts}...`);

            if (isValidPage()) {
                console.log('[Nojima] ✅ Trang đã load thành công!');
                clearInterval(checkInterval);

                // Đợi thêm 2 giây để chắc chắn
                setTimeout(() => {
                    sendCookiesToServer();
                }, 2000);

            } else if (attempts >= maxAttempts) {
                console.log('[Nojima] ⏱️ Timeout - trang không hợp lệ hoặc bị redirect');
                clearInterval(checkInterval);
                showNotification('⚠️ Không thể xác nhận trang', 'error');
            }
        }, CHECK_INTERVAL);
    }

    // Chạy khi DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', main);
    } else {
        main();
    }

})();