User:Username/BlockAbuser.js: Difference between revisions
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: | ||
} |
} |
||
// |
// Find all <li> entries inside mw-content-text (the AbuseLog entries) |
||
$content.find(' |
$content.find('li').each(function () { |
||
const $ |
const $li = $(this); |
||
| ⚫ | |||
| ⚫ | |||
// Find user link inside this <li> |
|||
const $userLink = $li.find('a').filter(function () { |
|||
| ⚫ | |||
| ⚫ | |||
| ⚫ | |||
| ⚫ | |||
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 |
||
// |
// 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(); |
|||
| ⚫ | |||
| ⚫ | |||
// Find 'details' or 'examine' link with abuse log id |
|||
let logId = null; |
let logId = null; |
||
$ |
$li.find('a').each(function () { |
||
const |
const href = $(this).attr('href') || ''; |
||
const |
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; |
if (!logId) return; |
||
// |
// Track user data, pick most recent log id |
||
if (!userData[username]) { |
if (!userData[username]) { |
||
userData[username] = { latestLogId: logId, extraHits: 0, $ |
userData[username] = { latestLogId: logId, extraHits: 0, $li: $li, $userLink: $userLink }; |
||
} else { |
} else { |
||
// Update latestLogId if |
// 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].$ |
userData[username].$li = $li; |
||
userData[username].$userLink = $userLink; |
|||
} |
} |
||
// Increment extra hits count |
|||
userData[username].extraHits++; |
userData[username].extraHits++; |
||
} |
} |
||
}); |
}); |
||
// |
// Add checkboxes inside <li>, before username link (only once per user) |
||
Object.entries(userData).forEach(([username, data]) => { |
Object.entries(userData).forEach(([username, data]) => { |
||
if ( |
if (data.$userLink.prev('.blockabuser-checkbox').length) { |
||
return; // already has |
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.$ |
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 |
// 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 |
// 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);
});
});