// <nowiki>
// @ts-check
// Extended info on IPs - gives a popup with their range, ASN, and ISP
// Parts cribbed from [[User:Krinkle/Scripts/CVNSimpleOverlay_wiki.js]] and [[MediaWiki:Gadget-markblocked.js]]
/* global mw, $ */
const ipExtIcon = 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Gnome-fs-web.svg/20px-Gnome-fs-web.svg.png'
/**
* Get all userlinks on the page
*
* @param {JQuery} $content page contents
* @return {Map} list of unique users on the page and their corresponding links
*/
function ipExtGetIPs ($content) {
const userLinks = new Map()
// Get all aliases for user: (taken from markblocked)
const userNS = []
for (const ns in mw.config.get('wgNamespaceIds')) {
if (mw.config.get('wgNamespaceIds')[ns] === 2) {
userNS.push(mw.util.escapeRegExp(ns.replace(/_/g, ' ')) ':')
}
}
// RegExp for all titles that are User:
const userTitleRX = new RegExp('^(' userNS.join('|') 'Special:Block/|Special:Contribs/|Special:Contributions/) ([^\\/#] )$', 'i')
const articleRX = new RegExp(mw.config.get('wgArticlePath').replace('$1', '') '([^#] )')
$('a', $content).each(function () {
if (!$(this).attr('href')) {
// Ignore if the <a> doesn't have a href
return
}
const articleTitleReMatch = articleRX.exec($(this).attr('href').toString())
if (!articleTitleReMatch) {
return
}
let pgTitle
try {
pgTitle = decodeURIComponent(articleTitleReMatch[1]).replace(/_/g, ' ')
} catch (error) {
return
}
const userTitleReMatch = userTitleRX.exec(pgTitle)
if (!userTitleReMatch) {
return
}
const username = userTitleReMatch[2]
if (mw.util.isIPAddress(username, true)) {
if (!userLinks.get(username)) {
userLinks.set(username, [])
}
userLinks.get(username).push($(this))
}
})
return userLinks
}
/**
* Get the WHOIS summary for an IP
*
* @param {string} ip IP address to check
*
* @return {Promise<string>} Summary of interesting parts of the IP's WHOIS
*/
async function ipExtWHOISInline (ip) {
const whoisResult = await fetch(`https://whois.toolforge.org/w/${ip}/lookup/json`)
const whoisJson = await whoisResult.json() // Why is json() async?
let providers = ''
whoisJson.nets.forEach((net) => {
providers = net.name ' '
})
return `${ip}:\n ASN: ${whoisJson.asn}\n ASN range: ${whoisJson.asn_cidr}\n ASN country: ${whoisJson.asn_country_code}\n ISP: ${providers}`
}
async function annotate (ipsOnPage) {
// Swiped from [[User:Suffusion_of_Yellow/batchtest-plus.js]]
const pending = []
const MAX_CONCURRENT_REQUESTS = 5
for (const [ip, val] of ipsOnPage) {
const idx = pending.length < MAX_CONCURRENT_REQUESTS ? pending.length : await Promise.race(pending)
if (idx === undefined) {
break // Something went wrong with the last request
}
pending[idx] = ipExtWHOISInline(ip).then((whoisText) => {
val.forEach(($link) => {
$link.after($('<a>').attr('href', `https://bullseye.toolforge.org/ip/${ip}`).attr('target', 'blank').attr('rel', 'noopener noreferrer')
.append($('<img>').attr('src', ipExtIcon).attr('title', mw.html.escape(whoisText))))
})
return idx
})
}
await Promise.all(pending)
}
// On window load, get all the IPs on the page and WHOIS them asynchronously
$.when($.ready, mw.loader.using(['mediawiki.util'])).then(function () {
mw.hook('wikipage.content').add(function ($content) {
const ipsOnPage = ipExtGetIPs($content)
annotate(ipsOnPage)
})
})