User:Username/BlockAbuser.js: Difference between revisions
From Test Wiki
Content deleted Content added
No edit summary |
... |
||
| Line 1: | Line 1: | ||
mw.loader.using( |
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'jquery'], function () { |
||
if (mw.config.get('wgCanonicalSpecialPageName') !== 'AbuseLog') { |
|||
// Only run on Special:AbuseFilter |
|||
if ( mw.config.get( 'wgCanonicalSpecialPageName' ) !== 'AbuseFilter' ) { |
|||
return; |
return; |
||
} |
} |
||
const $content = $('#mw-content-text'); |
|||
const seenUsers = new Set(); |
const seenUsers = new Set(); |
||
const userData = {}; // username => { latestLogId, extraHits } |
|||
// Very strict IPv4 / IPv6 detection |
|||
const ipRegex = /^(?:\d{1,3}\.){3}\d{1,3}$|:/; |
const ipRegex = /^(?:\d{1,3}\.){3}\d{1,3}$|:/; |
||
// Helper: decode username from URL |
|||
// Find all links inside the abuse log table/content |
|||
function getUsernameFromHref(href) { |
|||
const |
const parts = href.split('/wiki/User:'); |
||
if (parts.length < 2) return null; |
|||
return decodeURIComponent(parts[1]).replace(/_/g, ' ').trim(); |
|||
} |
|||
// 1. Scan all user links, build data for each user (latest log ID, counts) |
|||
if ( !href || !text ) { |
|||
$content.find('a').each(function () { |
|||
return; |
|||
const $link = $(this); |
|||
const href = $link.attr('href'); |
|||
if (!href || !href.includes('/wiki/User:')) return; |
|||
const username = getUsernameFromHref(href); |
|||
if (!username) return; |
|||
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 |
|||
// 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; |
|||
if ( !href.includes( '/wiki/User:' ) ) { |
|||
// Find 'details' or 'examine' link with abuse log id |
|||
let logId = null; |
|||
$row.find('a').each(function () { |
|||
const $a = $(this); |
|||
const ahref = $a.attr('href') || ''; |
|||
const match = ahref.match(/Special:AbuseLog\/(\d+)/); |
|||
if (match) { |
|||
logId = match[1]; |
|||
return false; // break |
|||
} |
|||
}); |
|||
if (!logId) return; // no log id found, skip |
|||
// Store only the most recent log id (higher number assumed more recent) |
|||
if (!userData[username]) { |
|||
userData[username] = { latestLogId: logId, extraHits: 0, $link: $link }; |
|||
} else { |
|||
// Update latestLogId if this is newer (numerical compare) |
|||
if (parseInt(logId) > parseInt(userData[username].latestLogId)) { |
|||
userData[username].latestLogId = logId; |
|||
userData[username].$link = $link; // store link for checkbox placement |
|||
} |
|||
// Increment extra hits count |
|||
userData[username].extraHits++; |
|||
} |
|||
}); |
|||
// 2. Add checkboxes ONLY next to the most recent log entry user link |
|||
Object.entries(userData).forEach(([username, data]) => { |
|||
if (!data.$link || data.$link.prev('.blockabuser-checkbox').length) { |
|||
return; // already has a 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.$link.before($checkbox); |
|||
}); |
|||
// 3. Add control button above abuse log to process checked users |
|||
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; |
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 user filtered AbuseLog in new tab |
|||
const abuseLogUrl = mw.util.getUrl('Special:AbuseLog', { |
|||
wpSearchUser: username |
|||
}); |
|||
window.open(abuseLogUrl, '_blank'); |
|||
// Compose summary line for this user |
|||
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'); |
|||
// Show summary in a popup for easy copy-paste |
|||
alert(summaryText); |
|||
}); |
|||
}); |
|||