Browse Source

Add custom verification notices

Markus-Rost 4 years ago
parent
commit
3d79f64785

+ 8 - 0
cmds/eval.js

@@ -269,6 +269,14 @@ function removePatreons(guild, msg) {
 				console.log( '- Error while updating the RcGcDw: ' + dberror );
 				messages.push('Error while updating the RcGcDw: ' + dberror);
 			} );
+		} ).then( () => {
+			return client.query( 'DELETE FROM verifynotice WHERE guild = $1', [guild] ).then( () => {
+				console.log( '- Verification notices successfully deleted.' );
+				messages.push('Verification notices successfully deleted.');
+			}, dberror => {
+				console.log( '- Error while deleting the verification notices: ' + dberror );
+				messages.push('Error while deleting the verification notices: ' + dberror);
+			} );
 		} ).then( () => {
 			if ( !messages.length ) messages.push('No settings found that had to be removed.');
 			return messages;

+ 12 - 0
cmds/patreon.js

@@ -142,6 +142,12 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 				}, dberror => {
 					console.log( '- Error while updating the RcGcDw: ' + dberror );
 				} );
+			} ).then( () => {
+				return client.query( 'DELETE FROM verifynotice WHERE guild = $1', [args[1]] ).then( () => {
+					console.log( '- Verification notices successfully deleted.' );
+				}, dberror => {
+					console.log( '- Error while deleting the verification notices: ' + dberror );
+				} );
 			} ).catch( error => {
 				if ( error ) console.log( '- Error while removing the patreon features: ' + error );
 			} ).finally( () => {
@@ -299,6 +305,12 @@ function cmd_patreon(lang, msg, args, line, wiki) {
 				}, dberror => {
 					console.log( '- Error while updating the RcGcDw: ' + dberror );
 				} );
+			} ).then( () => {
+				return client.query( 'DELETE FROM verifynotice WHERE guild IN (' + guilds.map( (guild, i) => '$' + ( i + 1 ) ).join(', ') + ')', guilds ).then( () => {
+					console.log( '- Verification notices successfully deleted.' );
+				}, dberror => {
+					console.log( '- Error while deleting the verification notices: ' + dberror );
+				} );
 			} ).catch( error => {
 				if ( error ) console.log( '- Error while removing the patreon features: ' + error );
 			} ).finally( () => {

+ 11 - 1
cmds/verify.js

@@ -40,7 +40,17 @@ function cmd_verify(lang, msg, args, line, wiki) {
 		msg.reactEmoji('⏳').then( reaction => {
 			verify(lang, msg.channel, msg.member, username, wiki, rows).then( result => {
 				if ( result.reaction ) msg.reactEmoji(result.reaction);
-				else msg.replyMsg( result.content, {embed: result.embed}, false, false );
+				else msg.replyMsg( result.content, {embed: result.embed}, false, false ).then( message => {
+					if ( !result.logging.channel || !msg.guild.channels.cache.has(result.logging.channel) ) return;
+					if ( message ) {
+						if ( result.logging.embed ) result.logging.embed.addField(message.url, '<#' + msg.channel.id + '>');
+						else result.logging.content += '\n<#' + msg.channel.id + '> – <' + message.url + '>';
+					}
+					msg.guild.channels.cache.get(result.logging.channel).send(result.logging.content, {
+						embed: result.logging.embed,
+						allowedMentions: {parse: []}
+					}).catch(log_error);
+				} );
 				if ( reaction ) reaction.removeEmoji();
 			}, error => {
 				console.log( '- Error during the verifications: ' + error );

+ 1 - 2
dashboard/guilds.js

@@ -24,9 +24,8 @@ const file = require('fs').readFileSync('./dashboard/index.html');
  * @param {String[]} [actionArgs] - The arguments for the action
  */
 function dashboard_guilds(res, dashboardLang, theme, userSession, reqURL, action, actionArgs) {
-	reqURL.pathname = reqURL.pathname.replace( /^(\/(?:guild\/\d+(?:\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?)?)?)(?:\/.*)?$/, '$1' );
+	reqURL.pathname = reqURL.pathname.replace( /^(\/(?:guild\/\d+(?:\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?)?)?)(?:\/.*)?$/, '$1' );
 	var args = reqURL.pathname.split('/');
-	args = reqURL.pathname.split('/');
 	var settings = settingsData.get(userSession.user_id);
 	if ( reqURL.searchParams.get('owner') && process.env.owner.split('|').includes(userSession.user_id) ) {
 		args[0] = 'owner';

+ 8 - 1
dashboard/i18n/en.json

@@ -215,8 +215,12 @@
             "confirm": "Do you really want to delete the verification?",
             "editcount": "Minimal edit count:",
             "entry": "Verification #$1",
+            "logging": "Logging channel:",
+            "match": "Missing requirements notice:",
+            "match_placeholder": "Markdown text on matching Discord tags but not fulfilling any requirements for roles.",
             "more": "Add more",
             "new": "New Verification",
+            "notice": "Verification Notices",
             "postcount": "Minimal post count:",
             "postcount_and": "Require both edit and post count.",
             "postcount_both": "Require combined edit and post count.",
@@ -226,9 +230,12 @@
             "role": "Role:",
             "select_channel": "-- Select a Channel --",
             "select_role": "-- Select a Role --",
+            "success": "Success notice:",
+            "success_placeholder": "Markdown text on successful verification.",
             "usergroup": "Wiki user group:",
             "usergroup_and": "Require all user groups:"
         },
-        "new": "New verification"
+        "new": "New verification",
+        "notice": "Verification notices"
     }
 }

+ 6 - 6
dashboard/index.js

@@ -59,7 +59,7 @@ const server = http.createServer( (req, res) => {
 		} )?.map( cookie => cookie.replace( /^wikibot="([\da-f]+(?:-\d+)*)"$/, '$1' ) )?.join();
 
 		if ( args.length === 5 && ['settings', 'verification', 'rcscript', 'slash'].includes( args[3] )
-		&& /^(?:default|new|\d+)$/.test(args[4]) && sessionData.has(state) && settingsData.has(sessionData.get(state).user_id)
+		&& /^(?:default|new|notice|\d+)$/.test(args[4]) && sessionData.has(state) && settingsData.has(sessionData.get(state).user_id)
 		&& settingsData.get(sessionData.get(state).user_id).guilds.isMember.has(args[2]) ) {
 			if ( process.env.READONLY ) return save_response(`${req.url}?save=failed`);
 			let body = [];
@@ -155,8 +155,8 @@ const server = http.createServer( (req, res) => {
 	res.setHeader('Content-Language', [dashboardLang.lang]);
 
 	var lastGuild = req.headers?.cookie?.split('; ')?.filter( cookie => {
-		return cookie.split('=')[0] === 'guild' && /^"\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?"$/.test(( cookie.split('=')[1] || '' ));
-	} )?.map( cookie => cookie.replace( /^guild="(\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?)"$/, '$1' ) )?.join();
+		return cookie.split('=')[0] === 'guild' && /^"\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?"$/.test(( cookie.split('=')[1] || '' ));
+	} )?.map( cookie => cookie.replace( /^guild="(\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?)"$/, '$1' ) )?.join();
 	if ( lastGuild ) res.setHeader('Set-Cookie', ['guild=""; SameSite=Lax; Path=/; Max-Age=0']);
 
 	var state = req.headers.cookie?.split('; ')?.filter( cookie => {
@@ -181,7 +181,7 @@ const server = http.createServer( (req, res) => {
 	if ( !state ) {
 		if ( reqURL.pathname.startsWith( '/guild/' ) ) {
 			let pathGuild = reqURL.pathname.split('/').slice(2, 5).join('/');
-			if ( /^\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?$/.test(pathGuild) ) {
+			if ( /^\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?$/.test(pathGuild) ) {
 				res.setHeader('Set-Cookie', [`guild="${pathGuild}"; SameSite=Lax; Path=/`]);
 			}
 		}
@@ -195,7 +195,7 @@ const server = http.createServer( (req, res) => {
 	if ( !sessionData.has(state) || !settingsData.has(sessionData.get(state).user_id) ) {
 		if ( reqURL.pathname.startsWith( '/guild/' ) ) {
 			let pathGuild = reqURL.pathname.split('/').slice(2, 5).join('/');
-			if ( /^\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?$/.test(pathGuild) ) {
+			if ( /^\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?$/.test(pathGuild) ) {
 				res.setHeader('Set-Cookie', [`guild="${pathGuild}"; SameSite=Lax; Path=/`]);
 			}
 		}
@@ -204,7 +204,7 @@ const server = http.createServer( (req, res) => {
 
 	if ( reqURL.pathname === '/refresh' ) {
 		let returnLocation = reqURL.searchParams.get('return');
-		if ( !/^\/guild\/\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?$/.test(returnLocation) ) {
+		if ( !/^\/guild\/\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?$/.test(returnLocation) ) {
 			returnLocation = '/';
 		}
 		return pages.refresh(res, sessionData.get(state), returnLocation);

+ 1 - 1
dashboard/oauth.js

@@ -156,7 +156,7 @@ function dashboard_oauth(res, state, searchParams, lastGuild) {
 					lastGuild = searchParams.get('guild_id') + '/settings';
 				}
 				res.writeHead(302, {
-					Location: ( lastGuild && /^\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new))?$/.test(lastGuild) ? `/guild/${lastGuild}` : '/' ),
+					Location: ( lastGuild && /^\d+\/(?:settings|verification|rcscript|slash)(?:\/(?:\d+|new|notice))?$/.test(lastGuild) ? `/guild/${lastGuild}` : '/' ),
 					'Set-Cookie': [`wikibot="${userSession.state}"; HttpOnly; SameSite=Lax; Path=/; Max-Age=31536000`]
 				});
 				return res.end();

+ 1 - 1
dashboard/rcscript.js

@@ -277,7 +277,7 @@ function dashboard_rcscript(res, $, guild, args, dashboardLang) {
  * @param {String} [settings.delete_settings]
  */
 function update_rcscript(res, userSettings, guild, type, settings) {
-	if ( type === 'default' ) {
+	if ( type === 'default' || type === 'notice' ) {
 		return res(`/guild/${guild}/rcscript`, 'savefail');
 	}
 	if ( !settings.save_settings === !settings.delete_settings ) {

+ 19 - 4
dashboard/src/index.css

@@ -159,16 +159,16 @@ a[alt]:hover:after {
 	background-color: #7289da;
 }
 .svg-avatar {
-	color: #43b581;
+	color: #3ba55c;
 	background: #36393f;
 }
 .theme-light .svg-avatar {
-	color: #43b581;
+	color: #3ba55c;
 	background-color: #ffffff;
 }
 .guild:hover .svg-avatar {
 	color: #ffffff;
-	background-color: #43b581;
+	background-color: #3ba55c;
 }
 .separator {
 	height: 2px;
@@ -351,6 +351,11 @@ a:hover .description,
 .notice a:hover {
 	text-decoration: underline;
 }
+textarea,
+code,
+pre {
+	font-family: Consolas,Andale Mono WT,Andale Mono,Lucida Console,Lucida Sans Typewriter,DejaVu Sans Mono,Bitstream Vera Sans Mono,Liberation Mono,Nimbus Mono L,Monaco,Courier New,Courier,monospace;
+}
 code {
 	background: #2f3136;
 	padding: 1px 3px;
@@ -358,7 +363,6 @@ code {
 	border: 1px solid #202225;
 	border-radius: 3px;
 	white-space: pre-wrap;
-	font-size: 120%;
 }
 .theme-light code {
 	background-color: #f2f3f5;
@@ -514,6 +518,17 @@ fieldset input[type="url"] {
 fieldset input:invalid {
 	background: #FFAAAA;
 }
+fieldset textarea {
+	-moz-tab-size : 4;
+	-o-tab-size : 4;
+	tab-size : 4;
+	color: #dcddde;
+	background: #40444b;
+}
+.theme-light fieldset textarea {
+	color: #2e3338;
+	background-color: #ebedef;
+}
 .wb-settings-display:first-of-type,
 .wb-settings-permission:first-of-type {
 	display: inline-block;

+ 22 - 0
dashboard/src/index.js

@@ -363,6 +363,28 @@ if ( addRole && addRoleButton ) addRoleButton.onclick = function() {
 	}
 };
 
+var textAreas = document.getElementsByTagName('textarea');
+for ( var ta = 0; ta < textAreas.length; ta++ ) {
+	textAreas[ta].addEventListener('keydown', allowTabs);
+}
+
+/**
+ * @this HTMLTextAreaElement
+ * @param {KeyboardEvent} e
+ */
+function allowTabs(e) {
+	if ( e.key === 'Tab' ) {
+		if ( this.value.includes( '`ˋ`' ) ) this.value = this.value.replace( /`ˋ`/g, '```' );
+		var start = this.selectionStart;
+		var end = this.selectionEnd;
+		if ( this.value.substring(0, start).includes( '```' ) && this.value.substring(end).includes( '```' ) ) {
+			e.preventDefault();
+			this.value = this.value.substring(0, start) + '\t' + this.value.substring(end);
+			this.selectionStart = this.selectionEnd = start + 1;
+		}
+	}
+}
+
 /*
 var collapsible = document.getElementsByClassName('collapsible');
 for ( var i = 0; i < collapsible.length; i++ ) {

+ 196 - 1
dashboard/verification.js

@@ -268,7 +268,12 @@ function dashboard_verification(res, $, guild, args, dashboardLang) {
 			$('<a class="channel" id="channel-new">').append(
 				$('<img>').attr('src', '/src/channel.svg'),
 				$('<div>').text(dashboardLang.get('verification.new'))
-			).attr('href', `/guild/${guild.id}/verification/new${suffix}`) )
+			).attr('href', `/guild/${guild.id}/verification/new${suffix}`) ),
+			( !guild.patreon || !rows.length ? '' :
+			$('<a class="channel" id="channel-notice">').append(
+				$('<img>').attr('src', '/src/channel.svg'),
+				$('<div>').text(dashboardLang.get('verification.notice'))
+			).attr('href', `/guild/${guild.id}/verification/notice${suffix}`) )
 		);
 		if ( args[4] === 'new' && !( process.env.READONLY || rows.length >= verificationLimit[( guild.patreon ? 'patreon' : 'default' )] ) ) {
 			$('.channel#channel-new').addClass('selected');
@@ -283,6 +288,67 @@ function dashboard_verification(res, $, guild, args, dashboardLang) {
 			$(`.channel#channel-${row.configid}`).addClass('selected');
 			createForm($, dashboardLang.get('verification.form.entry', false, row.configid), dashboardLang, row, guild.channels, guild.roles).attr('action', `/guild/${guild.id}/verification/${row.configid}`).appendTo('#text');
 		}
+		else if ( args[4] === 'notice' && guild.patreon && rows.length ) {
+			$(`.channel#channel-notice`).addClass('selected');
+			return db.query( 'SELECT logchannel, onsuccess, onmatch FROM verifynotice WHERE guild = $1', [guild.id] ).then( ({rows:[row]}) => {
+				let curCat = null;
+				$('<form id="wb-settings" method="post" enctype="application/x-www-form-urlencoded">').append(
+					$('<h2>').text(dashboardLang.get('verification.form.notice')),
+					$('<fieldset>').append(
+						$('<div>').append(
+							$('<label for="wb-settings-channel">').text(dashboardLang.get('verification.form.logging')),
+							$('<select id="wb-settings-channel" name="channel">').append(
+								$('<option class="wb-settings-channel-default defaultSelect">').val('').text(dashboardLang.get('verification.form.select_channel')),
+								...guild.channels.filter( guildChannel => {
+									return ( ( hasPerm(guildChannel.botPermissions, 'VIEW_CHANNEL', 'SEND_MESSAGES') && hasPerm(guildChannel.userPermissions, 'VIEW_CHANNEL') ) || guildChannel.isCategory );
+								} ).map( guildChannel => {
+									if ( guildChannel.isCategory ) {
+										curCat = $('<optgroup>').attr('label', guildChannel.name);
+										return curCat;
+									}
+									var optionChannel = $(`<option class="wb-settings-channel-${guildChannel.id}">`).val(guildChannel.id).text(`${guildChannel.id} – #${guildChannel.name}`);
+									if ( guildChannel.id === row?.logchannel ) optionChannel.attr('selected', '');
+									if ( !curCat ) return optionChannel;
+									optionChannel.appendTo(curCat);
+								} ).filter( catChannel => {
+									if ( !catChannel ) return false;
+									if ( catChannel.is('optgroup') && !catChannel.children('option').length ) return false;
+									return true;
+								} )
+							)
+						),
+						$('<div>').append(
+							$('<label for="wb-settings-success">').text(dashboardLang.get('verification.form.success')),
+							$('<textarea id="wb-settings-success" name="success" spellcheck="true" maxlength="500" cols="65">').attr('rows', ( row?.onsuccess || '' ).split('\n').length).attr('placeholder', dashboardLang.get('verification.form.success_placeholder')).text(row?.onsuccess || '')
+						),
+						$('<div>').append(
+							$('<label for="wb-settings-match">').text(dashboardLang.get('verification.form.match')),
+							$('<textarea id="wb-settings-match" name="match" spellcheck="true" maxlength="500" cols="65">').attr('rows', ( row?.onmatch || '' ).split('\n').length).attr('placeholder', dashboardLang.get('verification.form.match_placeholder')).text(row?.onmatch || '')
+						),
+						$('<input type="submit" id="wb-settings-save" name="save_settings">').val(dashboardLang.get('general.save'))
+					)
+				).attr('action', `/guild/${guild.id}/verification/notice`).appendTo('#text');
+				if ( process.env.READONLY ) {
+					$('input, textarea').attr('readonly', '');
+					$('textarea, option, optgroup').attr('disabled', '');
+					$('input[type="submit"]').remove();
+				}
+				let body = $.html();
+				res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
+				res.write( body );
+				return res.end();
+			}, dberror => {
+				console.log( '- Dashboard: Error while getting the verification notices: ' + dberror );
+				createNotice($, 'error', dashboardLang);
+				$('#text .description').html(dashboardLang.get('verification.explanation'));
+				$('#text code.prefix').prepend(escapeText(process.env.prefix));
+				$('.channel#verification').addClass('selected');
+				let body = $.html();
+				res.writeHead(200, {'Content-Length': Buffer.byteLength(body)});
+				res.write( body );
+				return res.end();
+			} );
+		}
 		else {
 			$('.channel#verification').addClass('selected');
 			$('#text .description').html(dashboardLang.get('verification.explanation'));
@@ -328,6 +394,7 @@ function update_verification(res, userSettings, guild, type, settings) {
 	if ( type === 'default' ) {
 		return res(`/guild/${guild}/verification`, 'savefail');
 	}
+	if ( type === 'notice' ) return update_notices(res, userSettings, guild, type, settings);
 	if ( !settings.save_settings === !settings.delete_settings ) {
 		return res(`/guild/${guild}/verification/${type}`, 'savefail');
 	}
@@ -730,6 +797,134 @@ function update_verification(res, userSettings, guild, type, settings) {
 	} );
 }
 
+/**
+ * Change verification notices
+ * @param {Function} res - The server response
+ * @param {import('./util.js').Settings} userSettings - The settings of the user
+ * @param {String} guild - The id of the guild
+ * @param {String} type - The setting to change
+ * @param {Object} settings - The new settings
+ * @param {String} [settings.channel]
+ * @param {String} [settings.success]
+ * @param {String} [settings.match]
+ * @param {String} settings.save_settings
+ */
+function update_notices(res, userSettings, guild, type, settings) {
+	if ( !settings.save_settings ) {
+		return res(`/guild/${guild}/verification/${type}`, 'savefail');
+	}
+	if ( settings.channel && !/^\d+$/.test(settings.channel) ) {
+		return res(`/guild/${guild}/verification/${type}`, 'savefail');
+	}
+	if ( settings.success && settings.success.trim().length > 500 ) {
+		return res(`/guild/${guild}/verification/${type}`, 'savefail');
+	}
+	if ( settings.match && settings.match.trim().length > 500 ) {
+		return res(`/guild/${guild}/verification/${type}`, 'savefail');
+	}
+	settings.channel = ( settings.channel || null );
+	settings.success = ( settings.success?.trim().replace( /`ˋ`/g, '```' ) || null );
+	settings.match = ( settings.match?.trim().replace( /`ˋ`/g, '```' ) || null );
+	sendMsg( {
+		type: 'getMember',
+		member: userSettings.user.id,
+		guild: guild,
+		newchannel: settings.channel
+	} ).then( response => {
+		if ( !response ) {
+			userSettings.guilds.notMember.set(guild, userSettings.guilds.isMember.get(guild));
+			userSettings.guilds.isMember.delete(guild);
+			return res(`/guild/${guild}`, 'savefail');
+		}
+		if ( response === 'noMember' || !hasPerm(response.userPermissions, 'MANAGE_GUILD') ) {
+			userSettings.guilds.isMember.delete(guild);
+			return res('/', 'savefail');
+		}
+		if ( settings.channel && ( response.message === 'noChannel' || !( hasPerm(response.botPermissionsNew, 'VIEW_CHANNEL', 'SEND_MESSAGES') && hasPerm(response.userPermissions, 'VIEW_CHANNEL') ) ) ) {
+			return res(`/guild/${guild}/verification/${type}`, 'savefail');
+		}
+		return db.connect().then( client => {
+			return client.query( 'SELECT logchannel, onsuccess, onmatch FROM verifynotice WHERE guild = $1', [guild] ).then( ({rows:[row]}) => {
+				if ( !row ) {
+					if ( !( settings.channel || settings.success || settings.match ) ) {
+						return res(`/guild/${guild}/verification/${type}`, 'save');
+					}
+					return client.query( 'INSERT INTO verifynotice(guild, logchannel, onsuccess, onmatch) VALUES($1, $2, $3, $4)', [guild, settings.channel, settings.success, settings.match] ).then( () => {
+						console.log( `- Dashboard: Verification notices successfully added: ${guild}` );
+						res(`/guild/${guild}/verification/${type}`, 'save');
+						return client.query( 'SELECT lang FROM discord WHERE guild = $1 AND channel IS NULL', [guild] ).then( ({rows:[channel]}) => {
+							var lang = new Lang(channel?.lang);
+							var text = lang.get('verification.dashboard.added_notice', `<@${userSettings.user.id}>`) + '\n';
+							if ( settings.channel ) text += `${lang.get('verification.logging')} <#${settings.channel}>\n`;
+							if ( settings.success ) text += `${lang.get('verification.success')} \`\`\`md\n${settings.success.replace( /```/g, '`ˋ`' )}\n\`\`\``;
+							if ( settings.match ) text += `${lang.get('verification.match')} \`\`\`md\n${settings.match.replace( /```/g, '`ˋ`' )}\n\`\`\``;
+							text += `<${new URL(`/guild/${guild}/verification/${type}`, process.env.dashboard).href}>`;
+							if ( settings.success?.includes( '](' ) || settings.match?.includes( '](' ) ) {
+								text += '\n\n' + lang.get('verification.notice_embed');
+							}
+							sendMsg( {
+								type: 'notifyGuild', guild, text
+							} ).catch( error => {
+								console.log( '- Dashboard: Error while notifying the guild: ' + error );
+							} );
+						}, dberror => {
+							console.log( '- Dashboard: Error while notifying the guild: ' + dberror );
+						} );
+					}, dberror => {
+						console.log( '- Dashboard: Error while adding the verification notices: ' + dberror );
+						return res(`/guild/${guild}/verification/${type}`, 'savefail');
+					} );
+				}
+				if ( settings.channel === row.logchannel && settings.success === row.onsuccess && settings.match === row.onmatch ) {
+					return res(`/guild/${guild}/verification/${type}`, 'save');
+				}
+				return client.query( 'UPDATE verifynotice SET logchannel = $1, onsuccess = $2, onmatch = $3 WHERE guild = $4', [settings.channel, settings.success, settings.match, guild] ).then( () => {
+					console.log( `- Dashboard: Verification notices successfully updated: ${guild}` );
+					res(`/guild/${guild}/verification/${type}`, 'save');
+					return client.query( 'SELECT lang FROM discord WHERE guild = $1 AND channel IS NULL', [guild] ).then( ({rows:[channel]}) => {
+						var lang = new Lang(channel?.lang);
+						var text = lang.get('verification.dashboard.updated_notice', `<@${userSettings.user.id}>`) + '\n';
+						if ( settings.channel !== row.logchannel ) {
+							text += lang.get('verification.logging') + ' ~~' + ( row.logchannel ? `<#${row.logchannel}>` : `*\`${lang.get('verification.disabled')}\`*` ) + '~~ → ' + ( settings.channel ? `<#${settings.channel}>` : `*\`${lang.get('verification.disabled')}\`*` ) + '\n';
+						}
+						if ( settings.success !== row.onsuccess ) {
+							text += lang.get('verification.success') + ' ' + ( row.onsuccess ? '~~```md\n' + row.onsuccess.replace( /\\/g, '\\$&' ).replace( /```/g, '`ˋ`' ) + '\n```~~' : `~~*\`${lang.get('verification.disabled')}\`*~~ → ` ) + ( settings.success ? '```md\n' + settings.success.replace( /\\/g, '\\$&' ).replace( /```/g, '`ˋ`' ) + '\n```' : ` → *\`${lang.get('verification.disabled')}\`*\n` );
+						}
+						if ( settings.match !== row.onmatch ) {
+							text += lang.get('verification.match') + ' ' + ( row.onmatch ? '~~```md\n' + row.onmatch.replace( /\\/g, '\\$&' ).replace( /```/g, '`ˋ`' ) + '\n```~~' : `~~*\`${lang.get('verification.disabled')}\`*~~ → ` ) + ( settings.match ? '```md\n' + settings.match.replace( /\\/g, '\\$&' ).replace( /```/g, '`ˋ`' ) + '\n```' : ` → *\`${lang.get('verification.disabled')}\`*\n` );
+						}
+						text += `<${new URL(`/guild/${guild}/verification/${type}`, process.env.dashboard).href}>`;
+						if ( settings.success?.includes( '](' ) || settings.match?.includes( '](' ) ) {
+							text += '\n\n' + lang.get('verification.notice_embed');
+						}
+						sendMsg( {
+							type: 'notifyGuild', guild, text
+						} ).catch( error => {
+							console.log( '- Dashboard: Error while notifying the guild: ' + error );
+						} );
+					}, dberror => {
+						console.log( '- Dashboard: Error while notifying the guild: ' + dberror );
+					} );
+				}, dberror => {
+					console.log( '- Dashboard: Error while updating the verification notices: ' + dberror );
+					return res(`/guild/${guild}/verification/${type}`, 'savefail');
+				} );
+			}, dberror => {
+				console.log( '- Dashboard: Error while getting the current verification notices: ' + dberror );
+				return res(`/guild/${guild}/verification/${type}`, 'savefail');
+			} ).finally( () => {
+				client.release();
+			} );
+		}, dberror => {
+			console.log( '- Error while connecting to the database client: ' + dberror );
+			return res(`/guild/${guild}/verification/${type}`, 'savefail');
+		} );
+	}, error => {
+		console.log( '- Dashboard: Error while getting the member: ' + error );
+		return res(`/guild/${guild}/verification/${type}`, 'savefail');
+	} );
+}
+
 module.exports = {
 	get: dashboard_verification,
 	post: update_verification

+ 29 - 0
database.js

@@ -84,6 +84,18 @@ CREATE INDEX idx_verification_config ON verification (
     channel
 );
 
+CREATE TABLE verifynotice (
+    guild      TEXT    NOT NULL
+                       REFERENCES discord (main) ON DELETE CASCADE,
+    logchannel TEXT,
+    onsuccess  TEXT,
+    onmatch    TEXT
+);
+
+CREATE INDEX idx_verifynotice_guild ON verifynotice (
+    guild
+);
+
 CREATE TABLE rcgcdw (
     guild    TEXT    NOT NULL
                      REFERENCES discord (main) ON DELETE CASCADE,
@@ -128,6 +140,23 @@ CREATE INDEX idx_blocklist_wiki ON blocklist (
 
 COMMIT TRANSACTION;
 ALTER DATABASE "${process.env.PGDATABASE}" SET my.version TO 1;
+`,`
+BEGIN TRANSACTION;
+
+CREATE TABLE verifynotice (
+    guild      TEXT    NOT NULL
+                       REFERENCES discord (main) ON DELETE CASCADE,
+    logchannel TEXT,
+    onsuccess  TEXT,
+    onmatch    TEXT
+);
+
+CREATE INDEX idx_verifynotice_guild ON verifynotice (
+    guild
+);
+
+COMMIT TRANSACTION;
+ALTER DATABASE "${process.env.PGDATABASE}" SET my.version TO 2;
 `];
 
 module.exports = db.connect().then( () => {

+ 132 - 22
functions/verify.js

@@ -1,5 +1,6 @@
 const cheerio = require('cheerio');
 const {MessageEmbed} = require('discord.js');
+var db = require('../util/database.js');
 const logging = require('../util/logging.js');
 const {escapeFormatting} = require('../util/functions.js');
 const toTitle = require('../util/wiki.js').toTitle;
@@ -13,13 +14,18 @@ const toTitle = require('../util/wiki.js').toTitle;
  * @param {import('../util/wiki.js')} wiki - The wiki for the message.
  * @param {Object[]} rows - The verification settings.
  * @param {String} [old_username] - The username before the search.
- * @returns {Promise<{content:String,embed:MessageEmbed,reaction:String}>}
+ * @returns {Promise<{content:String,embed:MessageEmbed,reaction:String,logging:{channel:String,content:String,embed?:MessageEmbed}}>}
  */
 function verify(lang, channel, member, username, wiki, rows, old_username = '') {
 	var embed = new MessageEmbed().setFooter( lang.get('verify.footer') ).setTimestamp();
 	var result = {
 		content: '', embed,
-		reaction: ''
+		reaction: '',
+		logging: {
+			channel: '',
+			content: '',
+			embed: null
+		}
 	};
 	return got.get( wiki + 'api.php?action=query&meta=siteinfo&siprop=general&list=users&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json' ).then( response => {
 		var body = response.body;
@@ -110,16 +116,18 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 				queryuser.postcount = ucbody.userData.posts;
 				if ( ucbody.userData.discordHandle ) discordname = escapeFormatting(ucbody.userData.discordHandle).replace( /^\s*([^@#:]{2,32}?)\s*#(\d{4,6})\s*$/u, '$1#$2' );
 				
-				if ( wiki.isGamepedia() ) return got.get( wiki + 'api.php?action=profile&do=getPublicProfile&user_name=' + encodeURIComponent( username ) + '&format=json&cache=' + Date.now() ).then( presponse => {
+				if ( wiki.isGamepedia() || !discordname ) return got.get( wiki + 'api.php?action=profile&do=getPublicProfile&user_name=' + encodeURIComponent( username ) + '&format=json&cache=' + Date.now() ).then( presponse => {
 					var pbody = presponse.body;
 					if ( presponse.statusCode !== 200 || !pbody || pbody.error || pbody.errormsg || !pbody.profile ) {
 						console.log( '- ' + presponse.statusCode + ': Error while getting the Discord tag: ' + ( pbody?.error?.info || pbody?.errormsg ) );
-						return Promise.reject();
+						if ( wiki.isGamepedia() ) return Promise.reject();
+					}
+					else if ( pbody.profile['link-discord'] ) {
+						discordname = escapeFormatting(pbody.profile['link-discord']).replace( /^\s*([^@#:]{2,32}?)\s*#(\d{4,6})\s*$/u, '$1#$2' );
 					}
-					if ( pbody.profile['link-discord'] ) discordname = escapeFormatting(pbody.profile['link-discord']).replace( /^\s*([^@#:]{2,32}?)\s*#(\d{4,6})\s*$/u, '$1#$2' );
 				}, error => {
 					console.log( '- Error while getting the Discord tag: ' + error );
-					return Promise.reject();
+					if ( wiki.isGamepedia() ) return Promise.reject();
 				} );
 			}, ucerror => {
 				console.log( '- Error while getting the user profile: ' + ucerror );
@@ -174,23 +182,64 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 							comment.push(lang.get('verify.failed_roles'));
 						} )
 					];
-					if ( rename ) {
-						verify_promise.push(member.setNickname( username.substring(0, 32), lang.get('verify.audit_reason', username) ).catch( error => {
-							log_error(error);
+					var verifynotice = {
+						logchannel: '',
+						onsuccess: ''
+					};
+					if ( patreons.hasOwnProperty(channel.guild.id) ) {
+						verify_promise.push(db.query( 'SELECT logchannel, onsuccess FROM verifynotice WHERE guild = $1', [channel.guild.id] ).then( ({rows:[row]}) => {
+							if ( row ) verifynotice = row;
+						}, dberror => {
+							console.log( '- Error while getting the notices: ' + dberror );
+						} ));
+					}
+					if ( rename && member.displayName !== username ) {
+						if ( channel.guild.me.roles.highest.comparePositionTo(member.roles.highest) > 0 ) {
+							verify_promise.push(member.setNickname( username.substring(0, 32), lang.get('verify.audit_reason', username) ).catch( error => {
+								log_error(error);
+								embed.setColor('#008800');
+								comment.push(lang.get('verify.failed_rename', queryuser.gender));
+							} ));
+						}
+						else {
 							embed.setColor('#008800');
 							comment.push(lang.get('verify.failed_rename', queryuser.gender));
-						} ));
+						}
 					}
 					return Promise.all(verify_promise).then( () => {
+						var logchannel = ( verifynotice.logchannel ? channel.guild.channels.cache.get(verifynotice.logchannel) : null );
+						var useLogging = false;
+						if ( logchannel && logchannel.isGuild() && logchannel.permissionsFor(channel.guild.me).has(['VIEW_CHANNEL', 'SEND_MESSAGES']) ) {
+							useLogging = true;
+							result.logging.channel = logchannel.id;
+							if ( logchannel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) {
+								var logembed = new MessageEmbed(embed);
+								if ( roles.length ) logembed.addField( lang.get('verify.qualified'), roles.map( role => '<@&' + role + '>' ).join('\n') );
+								if ( missing.length ) logembed.setColor('#008800').addField( lang.get('verify.qualified_error'), missing.map( role => '<@&' + role + '>' ).join('\n') );
+								if ( comment.length ) logembed.setColor('#008800').addField( lang.get('verify.notice'), comment.join('\n') );
+								result.logging.embed = logembed;
+							}
+							else {
+								var logtext = '🔸 ' + lang.get('verify.user_verified', member.toString(), escapeFormatting(username), queryuser.gender);
+								if ( rename ) logtext += '\n' + lang.get('verify.user_renamed', queryuser.gender);
+								logtext += '\n<' + pagelink + '>';
+								if ( roles.length ) logtext += '\n**' + lang.get('verify.qualified') + '** ' + roles.map( role => '<@&' + role + '>' ).join(', ');
+								if ( missing.length ) logtext += '\n**' + lang.get('verify.qualified_error') + '** ' + missing.map( role => '<@&' + role + '>' ).join(', ');
+								if ( comment.length ) logtext += '\n**' + lang.get('verify.notice') + '** ' + comment.join('\n**' + lang.get('verify.notice') + '** ');
+								result.logging.content = logtext;
+							}
+						}
 						if ( channel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) {
 							if ( roles.length ) embed.addField( lang.get('verify.qualified'), roles.map( role => '<@&' + role + '>' ).join('\n') );
-							if ( missing.length ) embed.setColor('#008800').addField( lang.get('verify.qualified_error'), missing.map( role => '<@&' + role + '>' ).join('\n') );
-							if ( comment.length ) embed.setColor('#008800').addField( lang.get('verify.notice'), comment.join('\n') );
+							if ( missing.length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.qualified_error'), missing.map( role => '<@&' + role + '>' ).join('\n') );
+							if ( comment.length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.notice'), comment.join('\n') );
+							if ( verifynotice.onsuccess ) embed.addField( lang.get('verify.notice'), verifynotice.onsuccess );
 						}
 						else {
 							if ( roles.length ) text += '\n\n' + lang.get('verify.qualified') + ' ' + roles.map( role => '<@&' + role + '>' ).join(', ');
-							if ( missing.length ) text += '\n\n' + lang.get('verify.qualified_error') + ' ' + missing.map( role => '<@&' + role + '>' ).join(', ');
-							if ( comment.length ) text += '\n\n' + comment.join('\n');
+							if ( missing.length && !useLogging ) text += '\n\n' + lang.get('verify.qualified_error') + ' ' + missing.map( role => '<@&' + role + '>' ).join(', ');
+							if ( comment.length && !useLogging ) text += '\n\n' + comment.join('\n');
+							if ( verifynotice.onsuccess ) text += '\n\n**' + lang.get('verify.notice') + '** ' + verifynotice.onsuccess;
 						}
 						result.content = text;
 					}, log_error );
@@ -198,6 +247,16 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 				
 				embed.setColor('#FFFF00').setDescription( lang.get('verify.user_matches', member.toString(), '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
 				result.content = lang.get('verify.user_matches_reply', escapeFormatting(username), queryuser.gender);
+				
+				if ( patreons.hasOwnProperty(channel.guild.id) ) {
+					return db.query( 'SELECT onmatch FROM verifynotice WHERE guild = $1', [channel.guild.id] ).then( ({rows:[row]}) => {
+						if ( !row?.onmatch ) return;
+						if ( channel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) embed.addField( lang.get('verify.notice'), row.onmatch );
+						else result.content += '\n\n**' + lang.get('verify.notice') + '** ' + verifynotice.onmatch;
+					}, dberror => {
+						console.log( '- Error while getting the notices: ' + dberror );
+					} );
+				}
 			}, error => {
 				if ( error ) console.log( '- Error while getting the Discord tag: ' + error );
 				embed.setColor('#000000').setDescription( lang.get('verify.error') );
@@ -273,23 +332,64 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 						comment.push(lang.get('verify.failed_roles'));
 					} )
 				];
-				if ( rename ) {
-					verify_promise.push(member.setNickname( username.substring(0, 32), lang.get('verify.audit_reason', username) ).catch( error => {
-						log_error(error);
+				var verifynotice = {
+					logchannel: '',
+					onsuccess: ''
+				};
+				if ( patreons.hasOwnProperty(channel.guild.id) ) {
+					verify_promise.push(db.query( 'SELECT logchannel, onsuccess FROM verifynotice WHERE guild = $1', [channel.guild.id] ).then( ({rows:[row]}) => {
+						if ( row ) verifynotice = row;
+					}, dberror => {
+						console.log( '- Error while getting the notices: ' + dberror );
+					} ));
+				}
+				if ( rename && member.displayName !== username ) {
+					if ( channel.guild.me.roles.highest.comparePositionTo(member.roles.highest) > 0 ) {
+						verify_promise.push(member.setNickname( username.substring(0, 32), lang.get('verify.audit_reason', username) ).catch( error => {
+							log_error(error);
+							embed.setColor('#008800');
+							comment.push(lang.get('verify.failed_rename', queryuser.gender));
+						} ));
+					}
+					else {
 						embed.setColor('#008800');
 						comment.push(lang.get('verify.failed_rename', queryuser.gender));
-					} ));
+					}
 				}
 				return Promise.all(verify_promise).then( () => {
+					var logchannel = ( verifynotice.logchannel ? channel.guild.channels.cache.get(verifynotice.logchannel) : null );
+					var useLogging = false;
+					if ( logchannel && logchannel.isGuild() && logchannel.permissionsFor(channel.guild.me).has(['VIEW_CHANNEL', 'SEND_MESSAGES']) ) {
+						useLogging = true;
+						result.logging.channel = logchannel.id;
+						if ( logchannel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) {
+							var logembed = new MessageEmbed(embed);
+							if ( roles.length ) logembed.addField( lang.get('verify.qualified'), roles.map( role => '<@&' + role + '>' ).join('\n') );
+							if ( missing.length ) logembed.setColor('#008800').addField( lang.get('verify.qualified_error'), missing.map( role => '<@&' + role + '>' ).join('\n') );
+							if ( comment.length ) logembed.setColor('#008800').addField( lang.get('verify.notice'), comment.join('\n') );
+							result.logging.embed = logembed;
+						}
+						else {
+							var logtext = '🔸 ' + lang.get('verify.user_verified', member.toString(), escapeFormatting(username), queryuser.gender);
+							if ( rename ) logtext += '\n' + lang.get('verify.user_renamed', queryuser.gender);
+							logtext += '\n<' + pagelink + '>';
+							if ( roles.length ) logtext += '\n**' + lang.get('verify.qualified') + '** ' + roles.map( role => '<@&' + role + '>' ).join(', ');
+							if ( missing.length ) logtext += '\n**' + lang.get('verify.qualified_error') + '** ' + missing.map( role => '<@&' + role + '>' ).join(', ');
+							if ( comment.length ) logtext += '\n**' + lang.get('verify.notice') + '** ' + comment.join('\n**' + lang.get('verify.notice') + '** ');
+							result.logging.content = logtext;
+						}
+					}
 					if ( channel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) {
 						if ( roles.length ) embed.addField( lang.get('verify.qualified'), roles.map( role => '<@&' + role + '>' ).join('\n') );
-						if ( missing.length ) embed.setColor('#008800').addField( lang.get('verify.qualified_error'), missing.map( role => '<@&' + role + '>' ).join('\n') );
-						if ( comment.length ) embed.setColor('#008800').addField( lang.get('verify.notice'), comment.join('\n') );
+						if ( missing.length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.qualified_error'), missing.map( role => '<@&' + role + '>' ).join('\n') );
+						if ( comment.length && !useLogging ) embed.setColor('#008800').addField( lang.get('verify.notice'), comment.join('\n') );
+						if ( verifynotice.onsuccess ) embed.addField( lang.get('verify.notice'), verifynotice.onsuccess );
 					}
 					else {
 						if ( roles.length ) text += '\n\n' + lang.get('verify.qualified') + ' ' + roles.map( role => '<@&' + role + '>' ).join(', ');
-						if ( missing.length ) text += '\n\n' + lang.get('verify.qualified_error') + ' ' + missing.map( role => '<@&' + role + '>' ).join(', ');
-						if ( comment.length ) text += '\n\n' + comment.join('\n');
+						if ( missing.length && !useLogging ) text += '\n\n' + lang.get('verify.qualified_error') + ' ' + missing.map( role => '<@&' + role + '>' ).join(', ');
+						if ( comment.length && !useLogging ) text += '\n\n' + comment.join('\n');
+						if ( verifynotice.onsuccess ) text += '\n\n**' + lang.get('verify.notice') + '** ' + verifynotice.onsuccess;
 					}
 					result.content = text;
 				}, log_error );
@@ -297,6 +397,16 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
 			
 			embed.setColor('#FFFF00').setDescription( lang.get('verify.user_matches', member.toString(), '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
 			result.content = lang.get('verify.user_matches_reply', escapeFormatting(username), queryuser.gender);
+				
+			if ( patreons.hasOwnProperty(channel.guild.id) ) {
+				return db.query( 'SELECT onmatch FROM verifynotice WHERE guild = $1', [channel.guild.id] ).then( ({rows:[row]}) => {
+					if ( !row?.onmatch ) return;
+					if ( channel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) embed.addField( lang.get('verify.notice'), row.onmatch );
+					else result.content += '\n\n**' + lang.get('verify.notice') + '** ' + verifynotice.onmatch;
+				}, dberror => {
+					console.log( '- Error while getting the notices: ' + dberror );
+				} );
+			}
 		}, error => {
 			console.log( '- Error while getting the Discord tag: ' + error );
 			embed.setColor('#000000').setDescription( lang.get('verify.error') );

+ 7 - 1
i18n/en.json

@@ -739,8 +739,10 @@
         "current_selected": "this is the verification `$1` for this server:",
         "dashboard": {
             "added": "$1 added the verification with id `$2`.",
+            "added_notice": "$1 added some verification notices.",
             "removed": "$1 removed the verification with id `$2`.",
-            "updated": "$1 updated the verification with id `$2`."
+            "updated": "$1 updated the verification with id `$2`.",
+            "updated_notice": "$1 updated some verification notices."
         },
         "delete_current": "Delete this verification:",
         "deleted": "the verification has been deleted.",
@@ -748,6 +750,8 @@
         "editcount": "Edit count:",
         "enabled": "enabled",
         "indays": "(in days)",
+        "logging": "Logging channel:",
+        "match": "Missing requirements notice:",
         "max_entries": "you already reached the maximal amount of verifications.",
         "missing": "there are no verifications for this server yet.",
         "new_accountage": "<new account age>",
@@ -757,6 +761,7 @@
         "new_role": "<new role>",
         "new_usergroup": "<new user group>",
         "no_role": "please provide a role for the new verification.",
+        "notice_embed": "Some notices include masked markdown links. Make sure the bot has the `Embed Links` permissions in all verification channels for them to work properly.",
         "or": "or",
         "postcount": "Post count (only Fandom wikis):",
         "postcount_or": "(alternative to edit count)",
@@ -770,6 +775,7 @@
         "role_missing": "the provided role does not exist.",
         "role_too_high": "**The role $1 is too high for $2 to assign!**",
         "save_failed": "sadly the verification couldn't be saved, please try again later.",
+        "success": "Success notice:",
         "toggle": "(toggle)",
         "updated": "the verification has been updated:",
         "usergroup": "User group:",

+ 39 - 22
interactions/verify.js

@@ -52,6 +52,34 @@ function slash_verify(interaction, lang, wiki, channel) {
 			}
 		} ).catch(log_error);
 		
+		var username = ( interaction.data.options?.[0]?.value || '' ).replace( /^\s*<@!?(\d+)>\s*$/, (mention, id) => {
+			if ( id === interaction.user.id ) {
+				return ( interaction.member?.nick || interaction.user.username );
+			}
+			let user = channel.guild.members.cache.get(id);
+			if ( user ) return user.displayName;
+			else {
+				user = interaction.client.users.cache.get(user);
+				if ( user ) return user.username;
+			}
+			return mention;
+		} ).replace( /_/g, ' ' ).trim().replace( /^<\s*(.*)\s*>$/, '$1' ).split('#')[0].substring(0, 250).trim();
+		if ( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.(?:gamepedia\.com\/|(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?(?:wiki\/)?)/.test(username) ) {
+			username = decodeURIComponent( username.replace( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.(?:gamepedia\.com\/|(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?(?:wiki\/)?)/, '' ) );
+		}
+		if ( wiki.isGamepedia() ) username = username.replace( /^userprofile\s*:\s*/i, '' );
+		
+		if ( !username.trim() ) return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
+			data: {
+				type: 4,
+				data: {
+					content: lang.get('interaction.verify'),
+					allowed_mentions,
+					flags: 64
+				}
+			}
+		} ).catch(log_error);
+
 		return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
 			data: {
 				type: 5,
@@ -62,27 +90,6 @@ function slash_verify(interaction, lang, wiki, channel) {
 			}
 		} ).then( () => {
 			return channel.guild.members.fetch(interaction.user.id).then( member => {
-				var username = ( interaction.data.options?.[0]?.value || '' ).replace( /^\s*<@!?(\d+)>\s*$/, (mention, id) => {
-					if ( id === interaction.user.id ) {
-						return ( interaction.member?.nick || interaction.user.username );
-					}
-					let user = channel.guild.members.cache.get(id);
-					if ( user ) return user.displayName;
-					else {
-						user = interaction.client.users.cache.get(user);
-						if ( user ) return user.username;
-					}
-				} ).replace( /_/g, ' ' ).trim().replace( /^<\s*(.*)\s*>$/, '$1' ).split('#')[0].substring(0, 250).trim();
-				if ( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.(?:gamepedia\.com\/|(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?(?:wiki\/)?)/.test(username) ) {
-					username = decodeURIComponent( username.replace( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.(?:gamepedia\.com\/|(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?(?:wiki\/)?)/, '' ) );
-				}
-				if ( wiki.isGamepedia() ) username = username.replace( /^userprofile\s*:\s*/i, '' );
-				
-				if ( !username.trim() ) return sendMessage(interaction, {
-					content: lang.get('interaction.verify'),
-					allowed_mentions
-				}, channel);
-
 				return verify(lang, channel, member, username, wiki, rows).then( result => {
 					var message = {
 						content: reply + result.content,
@@ -94,7 +101,17 @@ function slash_verify(interaction, lang, wiki, channel) {
 						else message.content = reply + lang.get('verify.error_reply');
 						message.embeds = [];
 					}
-					return sendMessage(interaction, message);
+					return sendMessage(interaction, message, channel, false).then( msg => {
+						if ( !result.logging.channel || !channel.guild.channels.cache.has(result.logging.channel) ) return;
+						if ( msg ) {
+							if ( result.logging.embed ) result.logging.embed.addField(msg.url, '<#' + channel.id + '>');
+							else result.logging.content += '\n<#' + channel.id + '> – <' + msg.url + '>';
+						}
+						channel.guild.channels.cache.get(result.logging.channel).send(result.logging.content, {
+							embed: result.logging.embed,
+							allowedMentions: {parse: []}
+						}).catch(log_error);
+					} );
 				}, error => {
 					console.log( '- Error during the verifications: ' + error );
 					return sendMessage(interaction, {

+ 8 - 3
util/functions.js

@@ -439,13 +439,18 @@ function allowDelete(msg, author) {
  * @param {Object} message - The message.
  * @param {String} message.content - The message content.
  * @param {{parse: String[], roles?: String[], users?: String[]}} message.allowed_mentions - The allowed mentions.
- * @param {import('discord.js').TextChannel} [channel] - The channel for the interaction.
+ * @param {import('discord.js').TextChannel} channel - The channel for the interaction.
+ * @param {Boolean} [letDelete] - Let the interaction user delete the message.
+ * @returns {Promise<import('discord.js').Message?>}
  */
-function sendMessage(interaction, message, channel) {
+function sendMessage(interaction, message, channel, letDelete = true) {
 	return interaction.client.api.webhooks(interaction.application_id, interaction.token).messages('@original').patch( {
 		data: message
 	} ).then( msg => {
-		if ( channel ) allowDelete(channel.messages.add(msg), ( interaction.member?.user.id || interaction.user.id ));
+		if ( !channel ) return;
+		var responseMessage = channel.messages.add(msg);
+		if ( letDelete ) allowDelete(responseMessage, ( interaction.member?.user.id || interaction.user.id ));
+		return responseMessage;
 	}, log_error );
 };