User:Username/BlockAbuser.js: Difference between revisions

From Test Wiki
Jump to navigation Jump to search
Content deleted Content added
...
No edit summary
Line 6: Line 6:
const $content = $('#mw-content-text');
const $content = $('#mw-content-text');
const seenUsers = new Set();
const seenUsers = new Set();
const userData = {}; // username => { latestLogId, extraHits }
const userData = {}; // username => { latestLogId, extraHits, $li }


const ipRegex = /^(?:\d{1,3}\.){3}\d{1,3}$|:/;
const ipRegex = /^(?:\d{1,3}\.){3}\d{1,3}$|:/;
Line 17: Line 17:
}
}


// 1. Scan all user links, build data for each user (latest log ID, counts)
// Find all <li> entries inside mw-content-text (the AbuseLog entries)
$content.find('a').each(function () {
$content.find('li').each(function () {
const $link = $(this);
const $li = $(this);
const href = $link.attr('href');
if (!href || !href.includes('/wiki/User:')) return;


const username = getUsernameFromHref(href);
// Find user link inside this <li>
const $userLink = $li.find('a').filter(function () {
const href = $(this).attr('href') || '';
return href.includes('/wiki/User:');
}).first();

if ($userLink.length === 0) return; // no user link, skip

const username = getUsernameFromHref($userLink.attr('href'));
if (!username) return;
if (!username) return;
if (ipRegex.test(username)) return; // skip IPs
if (ipRegex.test(username)) return; // skip IPs


// The abuse log entry ID is usually part of the URL in a sibling 'details' link or 'examine' link
// Find AbuseLog id from details/examine links inside this <li>
// but since your example shows a pattern, we'll grab the closest abuse log id from a nearby 'details' link

// Try to find closest "details" link in this log row (it's a link with URL containing 'Special:AbuseLog/')
let $row = $link.closest('li, tr, div'); // abuse log entries are typically in <li> or <tr> or <div>
if ($row.length === 0) {
// fallback: parent of parent
$row = $link.parent().parent();
}

if ($row.length === 0) return;

// Find 'details' or 'examine' link with abuse log id
let logId = null;
let logId = null;
$row.find('a').each(function () {
$li.find('a').each(function () {
const $a = $(this);
const href = $(this).attr('href') || '';
const ahref = $a.attr('href') || '';
const match = href.match(/Special:AbuseLog\/(\d+)/);
const match = ahref.match(/Special:AbuseLog\/(\d+)/);
if (match) {
if (match) {
logId = match[1];
logId = match[1];
Line 51: Line 44:
});
});


if (!logId) return; // no log id found, skip
if (!logId) return;


// Store only the most recent log id (higher number assumed more recent)
// Track user data, pick most recent log id
if (!userData[username]) {
if (!userData[username]) {
userData[username] = { latestLogId: logId, extraHits: 0, $link: $link };
userData[username] = { latestLogId: logId, extraHits: 0, $li: $li, $userLink: $userLink };
} else {
} else {
// Update latestLogId if this is newer (numerical compare)
// Update latestLogId if newer
if (parseInt(logId) > parseInt(userData[username].latestLogId)) {
if (parseInt(logId) > parseInt(userData[username].latestLogId)) {
userData[username].latestLogId = logId;
userData[username].latestLogId = logId;
userData[username].$link = $link; // store link for checkbox placement
userData[username].$li = $li;
userData[username].$userLink = $userLink;
}
}
// Increment extra hits count
userData[username].extraHits++;
userData[username].extraHits++;
}
}
});
});


// 2. Add checkboxes ONLY next to the most recent log entry user link
// Add checkboxes inside <li>, before username link (only once per user)
Object.entries(userData).forEach(([username, data]) => {
Object.entries(userData).forEach(([username, data]) => {
if (!data.$link || data.$link.prev('.blockabuser-checkbox').length) {
if (data.$userLink.prev('.blockabuser-checkbox').length) {
return; // already has a checkbox
return; // already has checkbox
}
}
const $checkbox = $('<input>', {
const $checkbox = $('<input>', {
Line 84: Line 77:
}).attr('title', 'Select this user for block review');
}).attr('title', 'Select this user for block review');


data.$link.before($checkbox);
data.$userLink.before($checkbox);
});
});


// Button at top (same as before)
// 3. Add control button above abuse log to process checked users
const $btn = $('<button>')
const $btn = $('<button>')
.text('Open selected user AbuseLogs and generate summary')
.text('Open selected user AbuseLogs and generate summary')
Line 112: Line 105:
const extraHits = parseInt($cb.data('extrahits'), 10);
const extraHits = parseInt($cb.data('extrahits'), 10);


// Open user filtered AbuseLog in new tab
// Open AbuseLog filtered by user
const abuseLogUrl = mw.util.getUrl('Special:AbuseLog', {
const abuseLogUrl = mw.util.getUrl('Special:AbuseLog', {
wpSearchUser: username
wpSearchUser: username
Line 118: Line 111:
window.open(abuseLogUrl, '_blank');
window.open(abuseLogUrl, '_blank');


// Compose summary line for this user
// Compose summary line
let line = `[[Special:AbuseLog/${latestLogId}]]`;
let line = `[[Special:AbuseLog/${latestLogId}]]`;
if (extraHits > 0) {
if (extraHits > 0) {
Line 130: Line 123:
summaryLines.join('\n');
summaryLines.join('\n');


// Show summary in a popup for easy copy-paste
alert(summaryText);
alert(summaryText);
});
});

Revision as of 02:21, 21 January 2026

mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery'], function () {
    if (mw.config.get('wgCanonicalSpecialPageName') !== 'AbuseLog') {
        return;
    }

    const $content = $('#mw-content-text');
    const seenUsers = new Set();
    const userData = {}; // username => { latestLogId, extraHits, $li }

    const ipRegex = /^(?:\d{1,3}\.){3}\d{1,3}$|:/;

    // Helper: decode username from URL
    function getUsernameFromHref(href) {
        const parts = href.split('/wiki/User:');
        if (parts.length < 2) return null;
        return decodeURIComponent(parts[1]).replace(/_/g, ' ').trim();
    }

    // Find all <li> entries inside mw-content-text (the AbuseLog entries)
    $content.find('li').each(function () {
        const $li = $(this);

        // Find user link inside this <li>
        const $userLink = $li.find('a').filter(function () {
            const href = $(this).attr('href') || '';
            return href.includes('/wiki/User:');
        }).first();

        if ($userLink.length === 0) return; // no user link, skip

        const username = getUsernameFromHref($userLink.attr('href'));
        if (!username) return;
        if (ipRegex.test(username)) return; // skip IPs

        // Find AbuseLog id from details/examine links inside this <li>
        let logId = null;
        $li.find('a').each(function () {
            const href = $(this).attr('href') || '';
            const match = href.match(/Special:AbuseLog\/(\d+)/);
            if (match) {
                logId = match[1];
                return false; // break
            }
        });

        if (!logId) return;

        // Track user data, pick most recent log id
        if (!userData[username]) {
            userData[username] = { latestLogId: logId, extraHits: 0, $li: $li, $userLink: $userLink };
        } else {
            // Update latestLogId if newer
            if (parseInt(logId) > parseInt(userData[username].latestLogId)) {
                userData[username].latestLogId = logId;
                userData[username].$li = $li;
                userData[username].$userLink = $userLink;
            }
            userData[username].extraHits++;
        }
    });

    // Add checkboxes inside <li>, before username link (only once per user)
    Object.entries(userData).forEach(([username, data]) => {
        if (data.$userLink.prev('.blockabuser-checkbox').length) {
            return; // already has checkbox
        }
        const $checkbox = $('<input>', {
            type: 'checkbox',
            class: 'blockabuser-checkbox',
            'data-username': username,
            'data-latestlogid': data.latestLogId,
            'data-extrahits': data.extraHits
        }).css({
            marginRight: '6px',
            verticalAlign: 'middle',
            cursor: 'pointer'
        }).attr('title', 'Select this user for block review');

        data.$userLink.before($checkbox);
    });

    // Button at top (same as before)
    const $btn = $('<button>')
        .text('Open selected user AbuseLogs and generate summary')
        .css({
            margin: '1em 0',
            padding: '6px 12px',
            cursor: 'pointer'
        });

    $content.prepend($btn);

    $btn.on('click', function () {
        const checked = $('.blockabuser-checkbox:checked');
        if (!checked.length) {
            alert('Please select at least one user.');
            return;
        }

        let summaryLines = [];
        checked.each(function () {
            const $cb = $(this);
            const username = $cb.data('username');
            const latestLogId = $cb.data('latestlogid');
            const extraHits = parseInt($cb.data('extrahits'), 10);

            // Open AbuseLog filtered by user
            const abuseLogUrl = mw.util.getUrl('Special:AbuseLog', {
                wpSearchUser: username
            });
            window.open(abuseLogUrl, '_blank');

            // Compose summary line
            let line = `[[Special:AbuseLog/${latestLogId}]]`;
            if (extraHits > 0) {
                line += ` (+[[Special:AbuseLog/${username}|${extraHits}]])`;
            }
            summaryLines.push(line);
        });

        const summaryText =
            'Spambot or spam-only accounts detected. Details:\n' +
            summaryLines.join('\n');

        alert(summaryText);
    });
});