Jump to content

User:GeneralNotability/spihelper.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by GeneralNotability (talk | contribs) at 02:08, 23 August 2021 (Deploying revision [https://github.com/GeneralNotability/ip-ext-info/commit/77b7c1d04e53da83412153415d05e3bd48518f5c 77b7c1d04e53da83412153415d05e3bd48518f5c]). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <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 = '/media/wikipedia/commons/thumb/3/35/Gnome-fs-web.svg/20px-Gnome-fs-web.svg.png'
const ipExtDetailPage = 'Special:BlankPage/ExtendedIPInfo'

/**
 * 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
    }
    const pgTitle = decodeURIComponent(articleTitleReMatch[1]).replace(/_/g, ' ')
    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-dev.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}`
}

function ipExtMakeWHOISAccordion(ip, whoisJson) {
  // build WHOIS accordion
  const $whoisAccordion = $('<div>').addClass('toccolours mw-collapsible').attr('id', 'whois-accordion')

  const $whoisAccordionContent = $('<div>')
  $whoisAccordionContent.append($('<p>').text(`ASN: ${whoisJson.asn}`))
  $whoisAccordionContent.append($('<p>').text(`ASN CIDR: ${whoisJson.asn_cidr}`))

  $whoisAccordion.append($whoisAccordionContent)
  $whoisAccordion.makeCollapsible({ collapsed: true })
  // Add the header
  $('.mw-collapsible-toggle', $whoisAccordion).after($('<h3>').append($('<a>').attr('href', `https://whois-dev.toolforge.org/w/${ip}/lookup/json`).text('WHOIS')))

  return $whoisAccordion
}

async function ipExtRenderDetailPage ($content) {
  $content.empty()
  const $header = $('.firstHeading', document)
  const ip = mw.util.getParamValue('address')
  if (!ip) {
    $content.text('Address parameter was not specified in the URL')
    return
  }
  $header.text(ip)
  mw.config.set('wgRelevantUserName', ip)
  const whoisResult = await fetch(`https://whois-dev.toolforge.org/w/${ip}/lookup/json`)
  const whoisJson = await whoisResult.json()
  const lastNet = whoisJson.nets[whoisJson.nets.length - 1]
  $content.append($('<p>').text(`Range: ${lastNet.cidr}`))
  $content.append($('<p>').text(`ISP: ${lastNet.description}`))
  $content.append($('<p>').text(`Country: ${lastNet.country}`))
  $content.append($('<p>').text(`City: ${lastNet.city}`))

  $content.append(ipExtMakeWHOISAccordion(ip, whoisJson))
}

// On window load, get all the IPs on the page and WHOIS them asynchronously
$.when($.ready, mw.loader.using(['mediawiki.util', 'jquery.makeCollapsible'])).then(function () {
  mw.hook('wikipage.content').add(function ($content) {
    if (mw.config.get('wgPageName') === ipExtDetailPage) {
      ipExtRenderDetailPage($content)
    }
    const ipsOnPage = ipExtGetIPs($content)
    ipsOnPage.forEach(async (val, ip, _) => {
      const whoisText = await ipExtWHOISInline(ip)
      val.forEach(($link) => {
        $link.after($('<a>').attr('href', mw.util.getUrl(ipExtDetailPage, { address: ip }))
          .append($('<img>').attr('src', ipExtIcon).attr('title', mw.html.escape(whoisText))))
      })
    })
  })
})