Comment Scraper

Draggable and easy to use comment scraper. Made for personal use with the goal to look for malware advertisements.

目前为 2025-01-20 提交的版本。查看 最新版本

// ==UserScript==
// @name         Comment Scraper
// @namespace    discord.gg/@simonvhs
// @version      1.6
// @description  Draggable and easy to use comment scraper. Made for personal use with the goal to look for malware advertisements.
// @author       Simon (dork)
// @match        https://www.kogama.com/games/play/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const MAX_FILE_SIZE = 8 * 1024 * 1024; // Newest Webhook file size limit
    let processed_requests = 0;
    let total_pages = 0;

    const extractGameIdFromUrl = () => {
        const match = window.location.pathname.match(/\/games\/play\/([^/]+)\//);
        return match ? match[1] : null;
    };

    const createMenu = () => {
        const style = document.createElement('style');
        style.innerHTML = `
            #ayaka-menu {
                position: fixed;
                top: 20px;
                left: 20px;
                width: 300px;
                background: #121212;
                color: white;
                border-radius: 10px;
                padding: 20px;
                box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
                font-family: Arial, sans-serif;
                z-index: 9999;
            }
            #ayaka-menu h1 {
                font-size: 18px;
                margin-bottom: 10px;
                text-align: center;
                color: #72bcd4;
            }
            #ayaka-menu input, #ayaka-menu button {
                width: 100%;
                margin: 10px 0;
                padding: 10px;
                border-radius: 5px;
                border: none;
            }
            #ayaka-menu input {
                background: #1e1e1e;
                color: white;
            }
            #ayaka-menu button {
                background: linear-gradient(90deg, #6dd5fa, #1e90ff);
                color: white;
                cursor: pointer;
                transition: transform 0.2s;
            }
            #ayaka-menu button:hover {
                transform: scale(1.05);
            }
            #ayaka-progress {
                font-size: 14px;
                text-align: center;
                margin-top: 10px;
            }
        `;
        document.head.appendChild(style);

        const menu = document.createElement('div');
        menu.id = 'ayaka-menu';
        menu.innerHTML = `
            <h1>Comment Scraper</h1>
            <input id="webhook-url" type="text" placeholder="Webhook URL">
            <input id="total-pages" type="number" placeholder="Total Pages">
            <button id="send-button">Send</button>
            <div id="ayaka-progress">Progress: 0 / 0</div>
        `;
        document.body.appendChild(menu);

        makeDraggable(menu);

        document.getElementById('total-pages').addEventListener('input', (e) => {
            total_pages = parseInt(e.target.value, 10);
            updateProgress();
        });
    };

    const makeDraggable = (element) => {
        let isDragging = false, startX, startY, initialX, initialY;
        element.addEventListener('mousedown', (e) => {
            isDragging = true;
            startX = e.clientX;
            startY = e.clientY;
            initialX = element.offsetLeft;
            initialY = element.offsetTop;
            document.addEventListener('mousemove', onDrag);
            document.addEventListener('mouseup', onStopDrag);
        });

        const onDrag = (e) => {
            if (!isDragging) return;
            const dx = e.clientX - startX;
            const dy = e.clientY - startY;
            element.style.left = `${initialX + dx}px`;
            element.style.top = `${initialY + dy}px`;
        };

        const onStopDrag = () => {
            isDragging = false;
            document.removeEventListener('mousemove', onDrag);
            document.removeEventListener('mouseup', onStopDrag);
        };
    };

    const fetchPage = async (url) => {
        const response = await fetch(url);
        if (response.ok) {
            processed_requests++;
            updateProgress();
            return await response.json();
        } else {
            throw new Error(`Failed to fetch ${url}`);
        }
    };

    const sendFileToWebhook = async (webhookUrl, fileData, fileName) => {
        const blob = new Blob([fileData], { type: 'text/plain' });
        const formData = new FormData();
        formData.append('file', blob, fileName);

        const response = await fetch(webhookUrl, {
            method: 'POST',
            body: formData,
        });

        if (!response.ok) {
            throw new Error('Failed to send file');
        }

        console.log('File sent successfully');
    };

    const formatCommentData = (comment) => {
        const content = JSON.parse(comment._data).data || 'No Content';
        const createdAt = new Date(comment.created).toLocaleString();
        return `[${createdAt}] ${comment.profile_username} (${comment.profile_id}): ${content}`;
    };

    const generateMetadata = () => {
        const date = new Date().toLocaleString();
        const gameId = extractGameIdFromUrl();
        return `Date: ${date}\nGame ID: ${gameId}\nTotal Pages: ${total_pages}\n\n▃▃▃▃▃▃▃▃▃▃▃▃▃▃▃▃▃\n\n `;
    };

    const updateProgress = () => {
        const progressText = `${processed_requests} / ${total_pages}`;
        document.getElementById('ayaka-progress').textContent = `Progress: ${progressText}`;
    };

    const processAllComments = async (webhookUrl) => {
        const fetchPromises = [];
        let currentPage = 1;

        while (currentPage <= total_pages) {
            const pageUrl = `https://www.kogama.com/game/${extractGameIdFromUrl()}/comment/?page=${currentPage}&count=400`;
            fetchPromises.push(fetchPage(pageUrl));
            currentPage++;
        }

        try {
            const allPageResults = await Promise.all(fetchPromises);
            let allComments = [];
            allPageResults.forEach((result) => {
                if (result.data) {
                    allComments = allComments.concat(result.data);
                }
            });

            allComments.sort((a, b) => new Date(b.created) - new Date(a.created));

            const formattedData = allComments.map(formatCommentData).join('\n');
            const totalComments = formattedData.split('\n').length;

            let currentFileData = generateMetadata();
            let currentFileSize = 0;
            let fileCount = 1;

            let fileDataBuffer = currentFileData + formattedData;

            if (new Blob([fileDataBuffer]).size > MAX_FILE_SIZE) {
                await sendFileToWebhook(webhookUrl, fileDataBuffer, `comments_${fileCount}.txt`);
                fileCount++;
                fileDataBuffer = formattedData;
            }

            if (fileDataBuffer) {
                await sendFileToWebhook(webhookUrl, fileDataBuffer, `comments_${fileCount}.txt`);
            }

            console.log('All comments processed and files sent!');
        } catch (err) {
            console.error('Error processing comments:', err);
        }
    };

    const startProcess = async () => {
        const webhookUrl = document.getElementById('webhook-url').value;

        if (!webhookUrl || isNaN(total_pages) || total_pages <= 0) {
            alert('Please fill all fields correctly.');
            return;
        }

        processed_requests = 0;
        updateProgress();

        await processAllComments(webhookUrl);
    };

    createMenu();
    document.getElementById('send-button').addEventListener('click', startProcess);
})();