Browse Source

expanded interwiki list

Markus-Rost 5 năm trước cách đây
mục cha
commit
141f8f0017

+ 4 - 4
bot.js

@@ -99,13 +99,13 @@ Discord.Message.prototype.uploadFiles = function() {
 	return this.channel.type !== 'text' || this.channel.permissionsFor(client.user).has('ATTACH_FILES');
 };
 
-String.prototype.toLink = function(title = '', querystring = '', fragment = '', path, isMarkdown = false) {
+String.prototype.toLink = function(title = '', querystring = '', fragment = '', {server: serverURL, articlepath: articlePath}, isMarkdown = false) {
 	var linksuffix = ( querystring ? '?' + querystring : '' ) + ( fragment ? '#' + fragment.toSection() : '' );
-	if ( path ) return ( path.server.startsWith( '//' ) ? 'https:' : '' ) + path.server + path.articlepath.replaceSave( '$1', title.toTitle(isMarkdown, path.articlepath.includes( '?' )) ) + ( path.articlepath.includes( '?' ) && linksuffix.startsWith( '?' ) ? '&' + linksuffix.substring(1) : linksuffix );
+	if ( serverURL && articlePath ) return serverURL.replace( /^(?:https?:)?\/\//, 'https://' ) + articlePath.replaceSave( '$1', title.toTitle(isMarkdown, articlePath.includes( '?' )) ) + ( articlePath.includes( '?' ) && linksuffix.startsWith( '?' ) ? '&' + linksuffix.substring(1) : linksuffix );
 	if ( this.endsWith( '.gamepedia.com/' ) ) return this + title.toTitle(isMarkdown) + linksuffix;
 	if ( this.isFandom() ) return this + 'wiki/' + title.toTitle(isMarkdown) + linksuffix;
-	if ( wikiProjects.some( project => this.includes( project.name ) ) ) {
-		let project = wikiProjects.find( project => this.includes( project.name ) );
+	let project = wikiProjects.find( project => this.split('/')[2].endsWith( project.name ) );
+	if ( project ) {
 		let regex = this.match( new RegExp( project.regex ) );
 		if ( regex ) return 'https://' + regex[1] + project.articlePath + title.toTitle(isMarkdown, project.articlePath.includes( '?' )) + ( project.articlePath.includes( '?' ) && linksuffix.startsWith( '?' ) ? '&' + linksuffix.substring(1) : linksuffix );
 	}

+ 8 - 8
cmds/settings.js

@@ -78,15 +78,15 @@ function cmd_settings(lang, msg, args, line, wiki) {
 			}
 			else if ( /^<?(?:https?:)?\/\//.test(args[1]) ) {
 				args[1] = args[1].replace( /^<?(?:https?:)?\/\//, 'https://' );
-				var value = args[1].split(/>? /);
-				if ( value.length === 2 && value[1] === '--force' ) isForced = true;
-				if ( wikiProjects.some( project => value[0].includes( project.name ) ) ) {
-					let project = wikiProjects.find( project => value[0].includes( project.name ) );
-					let regex = value[0].match( new RegExp( project.regex ) );
-					if ( regex ) value[0] = 'https://' + regex[1] + project.scriptPath;
+				[wikinew, ...value] = args[1].split(/>? /);
+				if ( value.join(' ') === '--force' ) isForced = true;
+				let project = wikiProjects.find( project => wikinew.split('/')[2].endsWith( project.name ) );
+				if ( project ) {
+					let regex = wikinew.match( new RegExp( project.regex ) );
+					if ( regex ) wikinew = 'https://' + regex[1] + project.scriptPath;
 				}
-				value[0] = value[0].replace( /\/(?:api|index)\.php(?:|\?.*)$/, '/' );
-				wikinew = value[0] + ( value[0].endsWith( '/' ) ? '' : '/' );
+				else wikinew = wikinew.replace( /\/(?:api|index)\.php(?:|\?.*)$/, '/' );
+				if ( !wikinew.endsWith( '/' ) ) wikinew += '/';
 			}
 			else {
 				var text = lang.get('settings.wikiinvalid') + wikihelp;

+ 3 - 3
cmds/wiki/fandom.js

@@ -389,7 +389,7 @@ function fandom_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '',
 					}
 					interwiki = body.query.interwiki[0].url;
 					var maxselfcall = ( msg.channel.type === 'text' && msg.guild.id in patreons ? 10 : 5 );
-					if ( selfcall < maxselfcall ) {
+					if ( selfcall < maxselfcall && /^(?:https?:)?\/\//.test(interwiki) ) {
 						selfcall++;
 						var regex = interwiki.match( /^(?:https?:)?\/\/(([a-z\d-]{1,50})\.(?:fandom\.com|wikia\.org)(?:(?!\/wiki\/)\/([a-z-]{2,12}))?)(?:\/wiki\/|\/?$)/ );
 						if ( regex ) {
@@ -403,8 +403,8 @@ function fandom_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '',
 							this.gamepedia(lang, msg, iwtitle, 'https://' + regex[1] + '.gamepedia.com/', '!' + regex[1] + ' ', reaction, spoiler, querystring, fragment, interwiki, selfcall);
 							return;
 						}
-						if ( wikiProjects.some( project => interwiki.includes( project.name ) ) ) {
-							let project = wikiProjects.find( project => interwiki.includes( project.name ) );
+						let project = wikiProjects.find( project => interwiki.split('/')[2].endsWith( project.name ) );
+						if ( project ) {
 							regex = interwiki.match( new RegExp( '^(?:https?:)?\\/\\/' + project.regex ) );
 							if ( regex ) {
 								let iwtitle = decodeURIComponent( interwiki.replace( regex[0], '' ) ).replace( /\_/g, ' ' );

+ 14 - 8
cmds/wiki/gamepedia.js

@@ -176,7 +176,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 											if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.pageimage.toLowerCase()) ) embed.setImage( pageimage );
 											else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.pageimage}] );
 										} else embed.setThumbnail( pageimage );
-									} else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+									} else embed.setThumbnail( logoToURL(body.query.general) );
 									
 									var prefix = ( msg.channel.type === 'text' && patreons[msg.guild.id] || process.env.prefix );
 									var linksuffix = ( querystring ? '?' + querystring : '' ) + ( fragment ? '#' + fragment : '' );
@@ -220,7 +220,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 					}
 					else if ( querypage.ns === -1 ) {
 						var pagelink = wiki.toLink(querypage.title, querystring.toTitle(), fragment, body.query.general);
-						var embed =  new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+						var embed =  new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( querypage.title.escapeFormatting() ).setURL( pagelink ).setThumbnail( logoToURL(body.query.general) );
 						var specialpage = body.query.specialpagealiases.find( sp => body.query.namespaces['-1']['*'] + ':' + sp.aliases[0].replace( /\_/g, ' ' ) === querypage.title.split('/')[0] );
 						specialpage = ( specialpage ? specialpage.realname : querypage.title.replace( body.query.namespaces['-1']['*'] + ':', '' ).split('/')[0] ).toLowerCase();
 						fn.special_page(lang, msg, querypage.title, specialpage, embed, wiki, reaction, spoiler);
@@ -250,7 +250,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 								if ( msg.showEmbed() && /\.(?:png|jpg|jpeg|gif)$/.test(querypage.pageimage.toLowerCase()) ) embed.setImage( pageimage );
 								else if ( msg.uploadFiles() ) embed.attachFiles( [{attachment:pageimage,name:( spoiler ? 'SPOILER ' : '' ) + querypage.pageimage}] );
 							} else embed.setThumbnail( pageimage );
-						} else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+						} else embed.setThumbnail( logoToURL(body.query.general) );
 						if ( querypage.categoryinfo ) {
 							var category = [lang.get('search.category.content')];
 							if ( querypage.categoryinfo.size === 0 ) {
@@ -282,7 +282,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 					}
 					interwiki = body.query.interwiki[0].url;
 					var maxselfcall = ( msg.channel.type === 'text' && msg.guild.id in patreons ? 10 : 5 );
-					if ( selfcall < maxselfcall ) {
+					if ( selfcall < maxselfcall && /^(?:https?:)?\/\//.test(interwiki) ) {
 						selfcall++;
 						var regex = interwiki.match( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.gamepedia\.com(?:\/|$)/ );
 						if ( regex ) {
@@ -296,8 +296,8 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 							this.fandom(lang, msg, iwtitle, 'https://' + regex[1] + '/', ( regex[1].includes( '.wikia.org' ) ? '??' : '?' ) + ( regex[3] ? regex[3] + '.' : '' ) + regex[2] + ' ', reaction, spoiler, querystring, fragment, interwiki, selfcall);
 							return;
 						}
-						if ( wikiProjects.some( project => interwiki.includes( project.name ) ) ) {
-							let project = wikiProjects.find( project => interwiki.includes( project.name ) );
+						let project = wikiProjects.find( project => interwiki.split('/')[2].endsWith( project.name ) );
+						if ( project ) {
 							regex = interwiki.match( new RegExp( '^(?:https?:)?\\/\\/' + project.regex ) );
 							if ( regex ) {
 								let iwtitle = decodeURIComponent( interwiki.replace( regex[0], '' ) ).replace( /\_/g, ' ' );
@@ -319,7 +319,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				}
 				else if ( body.query.redirects ) {
 					var pagelink = wiki.toLink(body.query.redirects[0].to, querystring.toTitle(), ( fragment || body.query.redirects[0].tofragment || '' ), body.query.general);
-					var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.redirects[0].to.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+					var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.redirects[0].to.escapeFormatting() ).setURL( pagelink ).setThumbnail( logoToURL(body.query.general) );
 					
 					msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, {embed} );
 					
@@ -327,7 +327,7 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 				}
 				else {
 					var pagelink = wiki.toLink(body.query.general.mainpage, querystring.toTitle(), fragment, body.query.general);
-					var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.general.mainpage.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+					var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( body.query.general.mainpage.escapeFormatting() ).setURL( pagelink ).setThumbnail( logoToURL(body.query.general) );
 					got.get( wiki + 'api.php?action=query' + ( noRedirect ? '' : '&redirects=true' ) + '&prop=pageprops|extracts&ppprop=description|displaytitle&explaintext=true&exsectionformat=raw&exlimit=1&titles=' + encodeURIComponent( body.query.general.mainpage ) + '&format=json', {
 						responseType: 'json'
 					} ).then( mpresponse => {
@@ -378,6 +378,12 @@ function gamepedia_check_wiki(lang, msg, title, wiki, cmd, reaction, spoiler = '
 	}
 }
 
+function logoToURL({logo, server: serverURL}) {
+	if ( /^(?:https?:)?\/\//.test(logo) ) logo = logo.replace( /^(?:https?:)?\/\//, 'https://' );
+	else logo = serverURL + ( logo.startsWith( '/' ) ? '' : '/' ) + logo;
+	return logo;
+}
+
 function htmlToPlain(html) {
 	var text = '';
 	var parser = new htmlparser.Parser( {

+ 1 - 1
cmds/wiki/gamepedia/overview.js

@@ -53,7 +53,7 @@ function gamepedia_overview(lang, msg, wiki, reaction, spoiler) {
 			
 			if ( msg.showEmbed() ) {
 				var text = '<' + pagelink + '>';
-				var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( title.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+				var embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( title.escapeFormatting() ).setURL( pagelink ).setThumbnail( ( /^(?:https?:)?\/\//.test(body.query.general.logo) ? body.query.general.logo.replace( /^(?:https?:)?\/\//, 'https://' ) : body.query.general.server + ( body.query.general.logo.startsWith( '/' ) ? '' : '/' ) + body.query.general.logo ) );
 			}
 			else {
 				var embed = {};

+ 1 - 1
cmds/wiki/gamepedia/random.js

@@ -39,7 +39,7 @@ function gamepedia_random(lang, msg, wiki, reaction, spoiler) {
 			if ( querypage.pageimage && querypage.original && querypage.title !== body.query.general.mainpage ) {
 				embed.setThumbnail( querypage.original.source );
 			}
-			else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+			else embed.setThumbnail( ( /^(?:https?:)?\/\//.test(body.query.general.logo) ? body.query.general.logo.replace( /^(?:https?:)?\/\//, 'https://' ) : body.query.general.server + ( body.query.general.logo.startsWith( '/' ) ? '' : '/' ) + body.query.general.logo ) );
 			
 			msg.sendChannel( '🎲 ' + spoiler + '<' + pagelink + '>' + spoiler, {embed} );
 		}

+ 1 - 1
cmds/wiki/gamepedia/user.js

@@ -194,7 +194,7 @@ function gamepedia_user(lang, msg, namespace, username, wiki, querystring, fragm
 						if ( querypage.pageimage && querypage.original ) {
 							var pageimage = querypage.original.source;
 							embed.setThumbnail( pageimage );
-						} else embed.setThumbnail( ( body.query.general.logo.startsWith( '//' ) ? 'https:' : '' ) + body.query.general.logo );
+						} else embed.setThumbnail( ( /^(?:https?:)?\/\//.test(body.query.general.logo) ? body.query.general.logo.replace( /^(?:https?:)?\/\//, 'https://' ) : body.query.general.server + ( body.query.general.logo.startsWith( '/' ) ? '' : '/' ) + body.query.general.logo ) );
 						
 						msg.sendChannel( spoiler + '<' + pagelink + '>' + spoiler, {embed} );
 					}

+ 1 - 1
i18n/en.json

@@ -359,7 +359,7 @@
 			{ "cmd": "verification <id> editcount <new edit count>", "desc": "I will change the minimal edit count for the wiki verification.", "hide": true, "admin": true },
 			{ "cmd": "verification <id> usergroup <new user group>", "desc": "I will change the user group for the wiki verification. Accepts a `|` separated list.\n\t• Provide `AND` as the first list entry to make all provided user groups required.", "hide": true, "admin": true },
 			{ "cmd": "verification <id> accountage <new account age>", "desc": "I will change the minimal account age (in days) for the wiki verification.", "hide": true, "admin": true },
-			{ "cmd": "verification <id> rename", "desc": "I will change if the users Discord nickname should be changed to their wiki username for the wiki verification.", "hide": true, "admin": true },
+			{ "cmd": "verification <id> rename", "desc": "I will change if the user's Discord nickname should be changed to their wiki username for the wiki verification.", "hide": true, "admin": true },
 			{ "cmd": "verification <id> delete", "desc": "I will delete the wiki verification.", "hide": true, "admin": true },
 			{ "cmd": "voice", "desc": "I try to give everyone in a voice channel a specific role.", "admin": true, "pause": true },
 			{ "cmd": "pause @mention", "desc": "I will ignore all commands on this server, except a few admin commands.", "admin": true },

+ 24 - 0
util/default.json

@@ -130,6 +130,30 @@
 			"regex": "((?:[a-z\\d-]{1,50}\\.)?wikivoyage\\.org)(?:\\/wiki\\/|\\/?$)",
 			"articlePath": "/wiki/",
 			"scriptPath": "/w/"
+		},
+		{
+			"name": "miraheze.org",
+			"regex": "((?:[a-z\\d-]{1,50}\\.)miraheze\\.org)(?:\\/wiki\\/|\\/?$)",
+			"articlePath": "/wiki/",
+			"scriptPath": "/w/"
+		},
+		{
+			"name": "shoutwiki.com",
+			"regex": "DOES NOT SUPPORT HTTPS – ((?:[a-z\\d-]{1,50}\\.)?shoutwiki\\.com)(?:\\/wiki\\/|\\/?$)",
+			"articlePath": "/wiki/",
+			"scriptPath": "/w/"
+		},
+		{
+			"name": "paradoxwikis.com",
+			"regex": "((?:[a-z\\d-]{1,50}\\.)paradoxwikis\\.com)(?:\\/|$)",
+			"articlePath": "/",
+			"scriptPath": "/"
+		},
+		{
+			"name": "runescape.wiki",
+			"regex": "((?:(?:classic|oldschool|pt|www)\\.)?runescape\\.wiki)(?:\\/w\\/|\\/?$)",
+			"articlePath": "/w/",
+			"scriptPath": "/"
 		}
 	]
 }

+ 43 - 33
util/newMessage.js

@@ -1,5 +1,5 @@
 const {Util} = require('discord.js');
-const {defaultSettings} = require('./default.json');
+const {defaultSettings, wikiProjects} = require('./default.json');
 const check_wiki = {
 	fandom: require('../cmds/wiki/fandom.js'),
 	gamepedia: require('../cmds/wiki/gamepedia.js')
@@ -25,15 +25,16 @@ function newMessage(msg, lang, wiki = defaultSettings.wiki, prefix = process.env
 	var cleanCont = ( content && Util.cleanContent(content, msg) || msg.cleanContent );
 	var author = msg.author;
 	var channel = msg.channel;
-	var invoke = cont.substring(prefix.length).split(' ')[0].split('\n')[0].toLowerCase();
-	var aliasInvoke = ( lang.get('aliases')[invoke] || invoke );
-	var ownercmd = ( msg.isOwner() && aliasInvoke in ownercmdmap );
-	if ( cont.hasPrefix(prefix) && ownercmd ) {
-		cont = cont.substring(prefix.length);
-		var args = cont.split(' ').slice(1);
-		if ( cont.split(' ')[0].split('\n')[1] ) args.unshift( '', cont.split(' ')[0].split('\n')[1] );
-		console.log( ( channel.type === 'text' ? msg.guild.id : '@' + author.id ) + ': ' + prefix + cont );
-		return ownercmdmap[aliasInvoke](lang, msg, args, cont, wiki);
+	if ( msg.isOwner() && cont.hasPrefix(prefix) ) {
+		let invoke = cont.substring(prefix.length).split(' ')[0].split('\n')[0].toLowerCase();
+		let aliasInvoke = ( lang.get('aliases')[invoke] || invoke );
+		if ( aliasInvoke in ownercmdmap ) {
+			cont = cont.substring(prefix.length);
+			let args = cont.split(' ').slice(1);
+			if ( cont.split(' ')[0].split('\n')[1] ) args.unshift( '', cont.split(' ')[0].split('\n')[1] );
+			console.log( ( channel.type === 'text' ? msg.guild.id : '@' + author.id ) + ': ' + prefix + cont );
+			return ownercmdmap[aliasInvoke](lang, msg, args, cont, wiki);
+		}
 	}
 	var count = 0;
 	var maxcount = ( channel.type === 'text' && msg.guild.id in patreons ? 15 : 10 );
@@ -47,32 +48,41 @@ function newMessage(msg, lang, wiki = defaultSettings.wiki, prefix = process.env
 			return;
 		}
 		line = line.substring(prefix.length);
-		invoke = line.split(' ')[0].toLowerCase();
+		var invoke = line.split(' ')[0].toLowerCase();
 		var args = line.split(' ').slice(1);
-		aliasInvoke = ( lang.get('aliases')[invoke] || invoke );
-		ownercmd = ( msg.isOwner() && aliasInvoke in ownercmdmap );
-		if ( channel.type === 'text' && pause[msg.guild.id] && !( ( msg.isAdmin() && aliasInvoke in pausecmdmap ) || ownercmd ) ) console.log( msg.guild.id + ': Paused' );
-		else console.log( ( channel.type === 'text' ? msg.guild.id : '@' + author.id ) + ': ' + prefix + line );
-		if ( ownercmd ) ownercmdmap[aliasInvoke](lang, msg, args, line, wiki);
-		else if ( channel.type !== 'text' || !pause[msg.guild.id] || ( msg.isAdmin() && aliasInvoke in pausecmdmap ) ) {
-			if ( aliasInvoke in cmdmap ) cmdmap[aliasInvoke](lang, msg, args, line, wiki);
-			else if ( /^![a-z\d-]{1,50}$/.test(invoke) ) {
-				cmdmap.LINK(lang, msg, args.join(' '), 'https://' + invoke.substring(1) + '.gamepedia.com/', invoke + ' ');
-			}
-			else if ( /^\?(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
-				var invokeWiki = wiki;
-				if ( invoke.includes( '.' ) ) invokeWiki = 'https://' + invoke.split('.')[1] + '.fandom.com/' + invoke.substring(1).split('.')[0] + '/';
-				else invokeWiki = 'https://' + invoke.substring(1) + '.fandom.com/';
-				cmdmap.LINK(lang, msg, args.join(' '), invokeWiki, invoke + ' ');
-			}
-			else if ( /^\?\?(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
-				var invokeWiki = wiki;
-				if ( invoke.includes( '.' ) ) invokeWiki = 'https://' + invoke.split('.')[1] + '.wikia.org/' + invoke.substring(2).split('.')[0] + '/';
-				else invokeWiki = 'https://' + invoke.substring(2) + '.wikia.org/';
-				cmdmap.LINK(lang, msg, args.join(' '), invokeWiki, invoke + ' ');
+		var aliasInvoke = ( lang.get('aliases')[invoke] || invoke );
+		var ownercmd = ( msg.isOwner() && aliasInvoke in ownercmdmap );
+		var pausecmd = ( msg.isAdmin() && pause[msg.guild.id] && aliasInvoke in pausecmdmap );
+		if ( channel.type === 'text' && pause[msg.guild.id] && !( pausecmd || ownercmd ) ) {
+			return console.log( msg.guild.id + ': Paused' );
+		}
+		console.log( ( channel.type === 'text' ? msg.guild.id : '@' + author.id ) + ': ' + prefix + line );
+		if ( ownercmd ) return ownercmdmap[aliasInvoke](lang, msg, args, line, wiki);
+		if ( pausecmd ) return pausecmdmap[aliasInvoke](lang, msg, args, line, wiki);
+		if ( aliasInvoke in cmdmap ) return cmdmap[aliasInvoke](lang, msg, args, line, wiki);
+		if ( /^![a-z\d-]{1,50}$/.test(invoke) ) {
+			return cmdmap.LINK(lang, msg, args.join(' '), 'https://' + invoke.substring(1) + '.gamepedia.com/', invoke + ' ');
+		}
+		if ( /^\?(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
+			let invokeWiki = wiki;
+			if ( invoke.includes( '.' ) ) invokeWiki = 'https://' + invoke.split('.')[1] + '.fandom.com/' + invoke.substring(1).split('.')[0] + '/';
+			else invokeWiki = 'https://' + invoke.substring(1) + '.fandom.com/';
+			return cmdmap.LINK(lang, msg, args.join(' '), invokeWiki, invoke + ' ');
+		}
+		if ( /^\?\?(?:[a-z-]{2,12}\.)?[a-z\d-]{1,50}$/.test(invoke) ) {
+			let invokeWiki = wiki;
+			if ( invoke.includes( '.' ) ) invokeWiki = 'https://' + invoke.split('.')[1] + '.wikia.org/' + invoke.substring(2).split('.')[0] + '/';
+			else invokeWiki = 'https://' + invoke.substring(2) + '.wikia.org/';
+			return cmdmap.LINK(lang, msg, args.join(' '), invokeWiki, invoke + ' ');
+		}
+		if ( /^!!(?:[a-z\d-]{1,50}\.)?[a-z\d-]{1,50}\.[a-z\d-]{1,10}(?:\/|$)/.test(invoke) ) {
+			let project = wikiProjects.find( project => invoke.split('/')[0].endsWith( project.name ) );
+			if ( project ) {
+				let regex = invoke.match( new RegExp( project.regex ) );
+				if ( regex && invoke === '!!' + regex[1] ) return cmdmap.LINK(lang, msg, args.join(' '), 'https://' + regex[1] + project.scriptPath, invoke + ' ');
 			}
-			else cmdmap.LINK(lang, msg, line, wiki);
 		}
+		return cmdmap.LINK(lang, msg, line, wiki);
 	} );
 	
 		if ( ( channel.type !== 'text' || !pause[msg.guild.id] ) && !noInline && ( cont.includes( '[[' ) || cont.includes( '{{' ) ) ) {