跳转到内容

User:LuciferianThomas/js/mark-locked.js

维基百科,自由的百科全书
注意:保存之后,你必须清除浏览器缓存才能看到做出的更改。Google ChromeFirefoxMicrosoft EdgeSafari:按住⇧ Shift键并单击工具栏的“刷新”按钮。参阅Help:绕过浏览器缓存以获取更多帮助。
// <nowiki>
// @ts-check
// Companion to markblocked - asynchronously marks locked users
// Chunks borrowed from [[User:Krinkle/Scripts/CVNSimpleOverlay_wiki.js]],
// [[User:GeneralNotability/ip-ext-info.js]], and [[MediaWiki:Gadget-markblocked.js]]

/**R
 * 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 lockedUsers_getUsers($content) {
	const userLinks = new Map();

	// Get all aliases for user: & user_talk: (taken from markblocked)
	const userNS = [];
	for (const ns in mw.config.get( 'wgNamespaceIds' ) ) {
		if (mw.config.get('wgNamespaceIds')[ns] === 2 || mw.config.get('wgNamespaceIds')[ns] === 3) {
			userNS.push(mw.util.escapeRegExp(ns.replace(/_/g, ' ')) + ':');
		}
	}

	// var wikis that are importing this gadget specify the local alias of Special:Contributions
	const markblocked_contributions = window.markblocked_contributions || 'Special:(?:Contribs|Contributions|用户贡献|用戶貢獻|使用者贡献|使用者貢獻)';
	// RegExp for all titles that are  User:| User_talk: | Special:Contributions/ (for userscripts)
	const userTitleRX = new RegExp('^((' + userNS.join('|') + ')([^\\/#]+)|' + markblocked_contributions + '\\/([^#]+))+$', 'i');
	const articleRX = new RegExp(mw.config.get('wgArticlePath').replace('$1', '') + '([^#]+)');
	const redlinkRX = new RegExp(mw.config.get('wgScript') + '\\?title=([^#&]+)');
	$('a', $content).each(function () {
		if (!$(this).attr('href')) {
			// Ignore if the <a> doesn't have a href
			return;
		}
		let articleTitleReMatch = articleRX.exec($(this).attr('href').toString());
		if (!articleTitleReMatch) {
			// Try the redlink check
			articleTitleReMatch = redlinkRX.exec($(this).attr('href').toString());
			if (!articleTitleReMatch) {
				return;
			}
		}
		let pgTitle;
		try {
			pgTitle = decodeURIComponent(articleTitleReMatch[1]).replace(/_/g, ' ');
		} catch (error) {
			// Happens sometimes on non-username paths, like if there's a slash in the path
			return;
		}
		const userTitleReMatch = userTitleRX.exec(pgTitle);
		// console.log(pgTitle, userTitleReMatch);
		if (!userTitleReMatch) {
			return;
		}
		const username = userTitleReMatch[3] || userTitleReMatch[4] ;
		// if (!mw.util.isIPAddress(username, true)) {
			if (!userLinks.get(username)) {
				userLinks.set(username, []);
			}
			userLinks.get(username).push($(this));
		// }
	});
	// console.log( userLinks )
	return userLinks;
}

/**
 * Check whether a user is locked and if they are, return details about it
 *
 * @param {string} user Username to check
 *
 * @return {Promise<object>} Whether the user in question is locked and a formatted tooltip about the lock
 */
async function lockedUsers_getLock(user) {
	let locked = false;
	let tooltip = '';

	user = user.charAt(0).toUpperCase() + user.slice(1);
	const api = new mw.ForeignApi('https://meta.wikimedia.org/w/api.php');
	// Pre-check whether they're locked at all - if no, return early
	try {
		const response = await api.get({
			action: 'query',
			list: 'globalallusers',
			agulimit: '1',
			agufrom: user,
			aguto: user,
			aguprop: 'lockinfo'
		});
		if (response.query.globalallusers.length === 0) {
			// If the length is 0, then we couldn't find the global user
			return { locked, tooltip };
		}
		// If the 'locked' field is present, then the user is locked
		if (!('locked' in response.query.globalallusers[0])) {
			return { locked, tooltip };
		}
	} catch (error) {
		return { locked, tooltip };
	}

	try {
		const response = await api.get({
			action: 'query',
			list: 'logevents',
			leprop: 'user|timestamp|comment|details',
			leaction: 'globalauth/setstatus',
			letitle: `User:${user}@global`
		});

		// If the length is 0, then we couldn't find the log event
		for (let logEvent of response.query.logevents) {
			let isLockEvent = false;
			// only works for more recent log entries, but most accurate
			try {
				isLockEvent = logEvent.params.added.includes('locked');
			} catch (error) {}
			// Older style log entries
			if (!isLockEvent) {
				try {
					isLockEvent = logEvent.params[0] == 'locked';
				} catch (error) {}
			}

			if (isLockEvent) {
				const timestamp = new Date(logEvent.timestamp);
				const prettyTimestamp = lockedUsers_formatTimeSince(timestamp);
				tooltip = `Locked by ${logEvent.user}: ${logEvent.comment} (${prettyTimestamp} ago)`;
				locked = true;
				// Intentionally not breaking - cycle through to find the most recent lock in case there are multiple
			}
		}
	} catch (error) {}

	return { locked, tooltip };
}


/**
 * Check whether a user is locked and if they are, return details about it
 *
 * @param {string} user Username to check
 *
 * @return {Promise<object>} Whether the user in question is locked and a formatted tooltip about the lock
 */
async function blockedIPs_getBlock(user) {
	let blocked = false;
	let tooltip = '';

	if (mw.util.isIPv6Address(user)) user = user.toUpperCase();
	const api = new mw.ForeignApi('https://meta.wikimedia.org/w/api.php');
	// Pre-check whether they're locked at all - if no, return early
	try {
		const response = await api.get({
			action: 'query',
			list: 'globalblocks',
			bgip: user,
			bgprop: 'id|address|by|timestamp|expiry|reason|range'
		});
		// console.log( user, response )
		if (response.query.globalblocks.length === 0) {
			// If the length is 0, then the IP is not globally blocked
			return { blocked, tooltip };
		}
		const item = response.query.globalblocks[ 0 ];
		// console.log( user, item )
		const ts = new Date(item.timestamp);
		const exp = new Date(item.expiry);
		blocked = true;
		tooltip = `Globally blocked by ${ item.by }: ${ item.reason } (${ lockedUsers_formatTimeSince( ts ) } ago, expiring in ${ lockedUsers_formatTimeSince( exp ) })`;
	} catch (error) {
		console.log( error )
		return { blocked, tooltip };
	}

	return { blocked, tooltip };
}

/**
 * Formats time since a date. Taken from mark-blocked.js
 *
 * @param {targetDate} Date to check the time since for
 *
 * @return {string} A prettified string regarding time since the lock occured
 */
function lockedUsers_formatTimeSince(targetDate) {
	const lockedUsers_padNumber = (number) => number <= 9 ? '0' + number : number;

	const msSince = Math.abs( new Date() - targetDate );

	let minutes = Math.floor(msSince / 60000);
	if (!minutes) {
		return Math.floor(msSince / 1000) + 's';
	}

	let hours = Math.floor(minutes / 60);
	minutes %= 60;

	let days = Math.floor(hours / 24);
	hours %= 24;
	if (days) {
		return `${days}${(days < 10 ? '.' + lockedUsers_padNumber(hours) : '' )}d`;
	}
	return `${hours}:${lockedUsers_padNumber(minutes)}`;
}

// On window load, get all the users on the page and check if they're blocked
$.when( $.ready, mw.loader.using( 'mediawiki.util' ) ).then( function () {
	mw.hook('wikipage.content').add(function ($content) {
		const usersOnPage = lockedUsers_getUsers( $content );
		usersOnPage.forEach(async (val, key, _) => {
			function markLock( tooltip ) {
				val.forEach(($link) => {
					$link.css({ opacity: 0.4, 'border-bottom-size': 'thick', 'border-bottom-style': 'dashed', 'border-bottom-color': 'red' });
					$link.attr('title', $link.attr( 'title' ) + '; ' + tooltip);
				});
			}
			if ( !mw.util.isIPAddress( key, true ) )
				lockedUsers_getLock( key ).then( function ( { locked, tooltip } ) {
					if ( locked ) markLock( tooltip );
				} )
			else
				blockedIPs_getBlock( key ).then( function ( { blocked, tooltip } ) {
					if ( blocked ) markLock( tooltip );
				} )
		});
	});
});
// </nowiki>