User:Daniel Quinlan/Scripts/RangeHelper.js: Difference between revisions

Content deleted Content added
fix queries for IPs lacking a mask
reorder some functions, other minor changes
Line 129:
let formatTimeAndDate = null;
 
// special page handling
// activate on relevant special pages
const pageName = mw.config.get('wgPageName');
const specialPage = mw.config.get('wgCanonicalSpecialPageName');
Line 211:
}
 
// find range blocks
// generate styled div for IP range calculation results
async function createRangeDisplaydisplayRangeBlocks(ip) {
api = new mw.Api();
const display = document.createElement('div');
formatTimeAndDate = mw.loader.require('mediawiki.DateFormatter').formatTimeAndDate;
display.id = 'range-display';
document.title = `Range blocks for ${ip}`;
display.style.fontWeight = 'bold';
const heading = document.querySelector('#firstHeading');
display.style.border = '1px solid var(--border-color-base, #a2a9b1)';
if (heading) {
display.style.borderRadius = '2px';
heading.innerHTML = `Range blocks for ${ip}`;
display.style.padding = '16px';
display.style.fontSize = '1rem';
display.style.margin = '1em 0';
return display;
}
 
// compute common IP range for IP list
function computeCommonRange(ips) {
if (!ips.length) {
return '<span style="color:red;">No valid IPs found.</span>';
}
const contentContainer = document.querySelector('#mw-content-text');
const firstVersion = ips[0].version;
if (!contentContainer) return;
if (!ips.every(ip => ip.version === firstVersion)) {
contentContainer.innerHTML = '';
return '<span style="color:red;">Mixed IPv4 and IPv6 addresses are not supported.</span>';
const statusMessage = document.createElement('p');
statusMessage.innerHTML = `Querying logs for IP blocks affecting <a href="/wiki/Special:Contributions/${ip}">${ip}</a>...`;
contentContainer.appendChild(statusMessage);
const resultsList = document.createElement('ul');
contentContainer.appendChild(resultsList);
const masks = ip.version === 4 ? sequence(16, 31) : sequence(19, 64);
const ranges = masks.map(mask => ip.masked(mask).toString(false, true));
if (!masks.includes(ip.mask)) {
ranges.push(ip.toString(false, true));
}
const blocks = [];
const masks = firstVersion === 4 ? sequence(16, 32) : sequence(19, 64);
const bestMaskblockPromises = masksranges.findLastmap(mrange => {
return getBlockLogs(api, range).then(async (blockLogs) => {
const base = ips[0].masked(m);
for (const block of blockLogs) {
return ips.every(ip => ip.masked(m).equals(base));
const formattedBlock = await formatBlockEntry(block);
blocks.push({ logid: block.logid, formattedBlock });
}
}).catch(error => {
console.error(`Error fetching block logs for range ${range}:`, error);
});
});
await Promise.all(blockPromises);
if (!bestMask) {
blocks.sort((a, b) => b.logid - a.logid);
return '<span style="color:red;">No common range found.</span>';
blocks.forEach(({ formattedBlock }) => {
const li = document.createElement('li');
li.innerHTML = formattedBlock;
resultsList.appendChild(li);
});
if (!blocks.length) {
statusMessage.innerHTML = '<span style="color:red;">No blocks found.</span>';
} else {
statusMessage.innerHTML = `Range blocks for <a href="/wiki/Special:Contributions/${ip}">${ip}</a>`;
}
mw.hook('wikipage.content').fire($(contentContainer));
const resultRange = ips[0].masked(bestMask).toString(false, true);
const contribsLink = `<a href="/wiki/Special:Contributions/${resultRange}" target="_blank">${resultRange}</a>`;
const blockLink = `<a href="/wiki/Special:Block/${resultRange}" target="_blank">block</a>`;
return `<span>${ips.length} unique IP${ips.length === 1 ? '' : 's'}: ${contribsLink} (${blockLink})</span>`;
}
 
Line 320 ⟶ 332:
}
 
// generate styled div for IP range calculation results
// find range blocks
async function displayRangeBlockscreateRangeDisplay(ip) {
const display = document.createElement('div');
api = new mw.Api();
display.id = 'range-display';
formatTimeAndDate = mw.loader.require('mediawiki.DateFormatter').formatTimeAndDate;
display.style.fontWeight = 'bold';
document.title = `Range blocks for ${ip}`;
display.style.border = '1px solid var(--border-color-base, #a2a9b1)';
const heading = document.querySelector('#firstHeading');
display.style.borderRadius = '2px';
if (heading) {
display.style.padding = '16px';
heading.innerHTML = `Range blocks for ${ip}`;
display.style.fontSize = '1rem';
display.style.margin = '1em 0';
return display;
}
 
// compute common IP range for IP list
function computeCommonRange(ips) {
if (!ips.length) {
return '<span style="color:red;">No valid IPs found.</span>';
}
const firstVersion = ips[0].version;
const contentContainer = document.querySelector('#mw-content-text');
if (!ips.every(ip => ip.version === firstVersion)) {
if (!contentContainer) return;
return '<span style="color:red;">Mixed IPv4 and IPv6 addresses are not supported.</span>';
contentContainer.innerHTML = '';
const statusMessage = document.createElement('p');
statusMessage.innerHTML = `Querying logs for IP blocks affecting <a href="/wiki/Special:Contributions/${ip}">${ip}</a>...`;
contentContainer.appendChild(statusMessage);
const resultsList = document.createElement('ul');
contentContainer.appendChild(resultsList);
const masks = ip.version === 4 ? sequence(16, 31) : sequence(19, 64);
const ranges = masks.map(mask => ip.masked(mask).toString(false, true));
if (!masks.includes(ip.mask)) {
ranges.push(ip.toString(false, true));
}
const masks = firstVersion === 4 ? sequence(16, 32) : sequence(19, 64);
const blocks = [];
const blockPromisesbestMask = rangesmasks.mapfindLast(rangem => {
const base = ips[0].masked(m);
return getBlockLogs(api, range).then(async (blockLogs) => {
return ips.every(ip => ip.masked(m).equals(base));
for (const block of blockLogs) {
const formattedBlock = await formatBlockEntry(block);
blocks.push({ logid: block.logid, formattedBlock });
}
}).catch(error => {
console.error(`Error fetching block logs for range ${range}:`, error);
});
});
if (!bestMask) {
await Promise.all(blockPromises);
return '<span style="color:red;">No common range found.</span>';
blocks.sort((a, b) => b.logid - a.logid);
blocks.forEach(({ formattedBlock }) => {
const li = document.createElement('li');
li.innerHTML = formattedBlock;
resultsList.appendChild(li);
});
if (!blocks.length) {
statusMessage.innerHTML = '<span style="color:red;">No blocks found.</span>';
} else {
statusMessage.innerHTML = `Range blocks for <a href="/wiki/Special:Contributions/${ip}">${ip}</a>`;
}
const resultRange = ips[0].masked(bestMask).toString(false, true);
mw.hook('wikipage.content').fire($(contentContainer));
const contribsLink = `<a href="/wiki/Special:Contributions/${resultRange}" target="_blank">${resultRange}</a>`;
const blockLink = `<a href="/wiki/Special:Block/${resultRange}" target="_blank">block</a>`;
return `<span>${ips.length} unique IP${ips.length === 1 ? '' : 's'}: ${contribsLink} (${blockLink})</span>`;
}
 
Line 387:
range: range
}));
}
 
// generate sequence of numbers
function sequence(n, m, step = 1) {
for (var i = n, r = []; i <= m; i += step) r.push(i);
return r;
}
 
// add IP to array
function ipListAdd(ipList, ip) {
if (!ipList.some(i => i.equals(ip))) ipList.push(ip);
}
 
// remove IP from array
function ipListRemove(ipList, ip) {
const index = ipList.findIndex(i => i.equals(ip));
if (index !== -1) ipList.splice(index, 1);
}
 
Line 466 ⟶ 449:
}
 
// convert wikitext to HTML
async function wikitextToHTML(wikitext) {
if (wikitextCache.has(wikitext)) {
Line 494 ⟶ 476:
}
return wikitext;
}
 
function sequence(n, m, step = 1) {
let r = [];
for (let i = n; i <= m; i += step) r.push(i);
return r;
}
 
function ipListAdd(ipList, ip) {
if (!ipList.some(i => i.equals(ip))) ipList.push(ip);
}
 
function ipListRemove(ipList, ip) {
const index = ipList.findIndex(i => i.equals(ip));
if (index !== -1) ipList.splice(index, 1);
}
});