|
@@ -1,8 +1,10 @@
|
|
|
const cheerio = require('cheerio');
|
|
|
const {MessageEmbed} = require('discord.js');
|
|
|
var db = require('../util/database.js');
|
|
|
+const Lang = require('../util/i18n.js');
|
|
|
+const Wiki = require('../util/wiki.js');
|
|
|
const logging = require('../util/logging.js');
|
|
|
-const {escapeFormatting} = require('../util/functions.js');
|
|
|
+const {oauthVerify, escapeFormatting} = require('../util/functions.js');
|
|
|
const toTitle = require('../util/wiki.js').toTitle;
|
|
|
|
|
|
/**
|
|
@@ -14,14 +16,14 @@ 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,logging:{channel:String,content:String,embed?:MessageEmbed}}>}
|
|
|
+ * @returns {Promise<{content:String,embed:MessageEmbed,add_button:Boolean,reaction:String,oauth: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,
|
|
|
add_button: channel.permissionsFor(channel.guild.me).has('EMBED_LINKS'),
|
|
|
- reaction: '',
|
|
|
+ reaction: '', oauth: '',
|
|
|
logging: {
|
|
|
channel: '',
|
|
|
content: '',
|
|
@@ -51,6 +53,15 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
|
|
|
return;
|
|
|
}
|
|
|
wiki.updateWiki(body.query.general);
|
|
|
+ if ( ( wiki.isWikimedia() || wiki.isMiraheze() ) && process.env.dashboard ) {
|
|
|
+ let oauth = '';
|
|
|
+ if ( wiki.isWikimedia() ) oauth = 'wikimedia';
|
|
|
+ if ( wiki.isMiraheze() ) oauth = 'miraheze';
|
|
|
+ if ( process.env[`oauth-${oauth}`] && process.env[`oauth-${oauth}-secret`] ) {
|
|
|
+ result.oauth = oauth;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
if ( !old_username ) logging(wiki, channel.guild.id, 'verification');
|
|
|
var queryuser = body.query.users[0];
|
|
|
embed.setAuthor( body.query.general.sitename );
|
|
@@ -455,6 +466,234 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
|
|
|
} );
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Oauth wiki user verification.
|
|
|
+ * @param {String} state - Unique state for the authorization.
|
|
|
+ * @param {String} access_token - Access token.
|
|
|
+ * @param {Object} [settings] - Settings to skip oauth.
|
|
|
+ * @param {import('discord.js').TextChannel} settings.channel - The channel.
|
|
|
+ * @param {String} settings.username - The username.
|
|
|
+ * @param {String} settings.user - The user id.
|
|
|
+ * @param {Function} settings.edit - The function to edit the message.
|
|
|
+ */
|
|
|
+global.verifyOauthUser = function(state, access_token, settings) {
|
|
|
+ if ( state && access_token && oauthVerify.has(state) ) settings = oauthVerify.get(state);
|
|
|
+ if ( !settings?.channel ) return;
|
|
|
+ var channel = settings.channel;
|
|
|
+ var username = settings.username;
|
|
|
+ if ( !username && !channel.permissionsFor(channel.guild.me).has(['VIEW_CHANNEL', 'SEND_MESSAGES']) ) return;
|
|
|
+ Promise.all([
|
|
|
+ db.query( 'SELECT configid, channel, role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = $1 AND channel LIKE $2 ORDER BY configid ASC', [channel.guild.id, '%|' + channel.id + '|%'] ).then( ({rows}) => {
|
|
|
+ if ( !rows.length ) return Promise.reject();
|
|
|
+ return db.query( 'SELECT wiki, lang FROM discord WHERE guild = $1 AND (channel = $2 OR channel = $3 OR channel IS NULL) ORDER BY channel DESC NULLS LAST LIMIT 1', [channel.guild.id, channel.id, '#' + channel.parentID] ).then( ({rows: [row]}) => {
|
|
|
+ return {
|
|
|
+ rows, wiki: new Wiki(row?.wiki),
|
|
|
+ lang: new Lang(( row?.lang || channel?.guild?.preferredLocale ))
|
|
|
+ };
|
|
|
+ } );
|
|
|
+ } ),
|
|
|
+ channel.guild.members.fetch(settings.user),
|
|
|
+ ( !username ? got.get( 'https://meta.' + settings.wiki + '.org/w/rest.php/oauth2/resource/profile', {
|
|
|
+ Authorization: `Bearer ${access_token}`
|
|
|
+ } ).then( response => {
|
|
|
+ var body = response.body;
|
|
|
+ console.log(body)
|
|
|
+ if ( response.statusCode !== 200 || !body?.username ) {
|
|
|
+ console.log( '- ' + response.statusCode + ': Error while getting the mediawiki profile: ' + ( body?.message || body?.error ) );
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ username = body.username;
|
|
|
+ }, error => {
|
|
|
+ console.log( '- Error while getting the mediawiki profile: ' + error );
|
|
|
+ } ) : null )
|
|
|
+ ]).then( ([{rows, wiki, lang}, member]) => {
|
|
|
+ if ( !username ) return settings.edit?.();
|
|
|
+ got.get( wiki + 'api.php?action=query&meta=siteinfo|globaluserinfo&siprop=general&guiprop=groups&guiuser=' + encodeURIComponent( username ) + '&list=users&usprop=blockinfo|groups|editcount|registration|gender&ususers=' + encodeURIComponent( username ) + '&format=json' ).then( response => {
|
|
|
+ var body = response.body;
|
|
|
+ if ( body && body.warnings ) log_warn(body.warnings);
|
|
|
+ if ( response.statusCode !== 200 || body?.batchcomplete === undefined || !body?.query?.users?.[0] ) {
|
|
|
+ if ( wiki.noWiki(response.url, response.statusCode) ) {
|
|
|
+ console.log( '- This wiki doesn\'t exist!' );
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.log( '- ' + response.statusCode + ': Error while getting the user: ' + body?.error?.info );
|
|
|
+ }
|
|
|
+ return settings.edit?.();
|
|
|
+ }
|
|
|
+ wiki.updateWiki(body.query.general);
|
|
|
+ if ( settings.wiki === 'wikimedia' && !wiki.isWikimedia() ) return settings.edit?.();
|
|
|
+ if ( settings.wiki === 'miraheze' && !wiki.isMiraheze() ) return settings.edit?.();
|
|
|
+ logging(wiki, channel.guild.id, 'verification');
|
|
|
+ var queryuser = body.query.users[0];
|
|
|
+ if ( body.query.users.length !== 1 || queryuser.missing !== undefined || queryuser.invalid !== undefined ) return settings.edit?.();
|
|
|
+ var allowedMentions = {
|
|
|
+ users: [
|
|
|
+ member.id
|
|
|
+ ]
|
|
|
+ };
|
|
|
+ var embed = new MessageEmbed().setFooter( lang.get('verify.footer') ).setTimestamp().setAuthor( body.query.general.sitename ).addField( lang.get('verify.discord', queryuser.gender), escapeFormatting(member.user.tag), true ).addField( lang.get('verify.wiki', queryuser.gender), lang.get('verify.oauth_used'), true );
|
|
|
+ var pagelink = wiki.toLink('User:' + username, '', '', true);
|
|
|
+ embed.setTitle( escapeFormatting(username) ).setURL( pagelink );
|
|
|
+ if ( queryuser.blockexpiry ) {
|
|
|
+ embed.setColor('#FF0000').setDescription( lang.get('verify.user_blocked', '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
|
|
|
+ return ( settings.edit || channel.send )(member.toString() + ', ' + lang.get('verify.user_blocked_reply', escapeFormatting(username), queryuser.gender), {embed, allowedMentions}).catch(log_error);
|
|
|
+ }
|
|
|
+ if ( body.query.globaluserinfo.locked !== undefined ) {
|
|
|
+ embed.setColor('#FF0000').setDescription( lang.get('verify.user_gblocked', '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
|
|
|
+ return ( settings.edit || channel.send )(member.toString() + ', ' + lang.get('verify.user_gblocked_reply', escapeFormatting(username), queryuser.gender), {embed, allowedMentions}).catch(log_error);
|
|
|
+ }
|
|
|
+ queryuser.groups.push(...body.query.globaluserinfo.groups);
|
|
|
+
|
|
|
+ var roles = [];
|
|
|
+ var missing = [];
|
|
|
+ var verified = false;
|
|
|
+ var rename = false;
|
|
|
+ var accountage = ( Date.now() - new Date(queryuser.registration) ) / 86400000;
|
|
|
+ rows.forEach( row => {
|
|
|
+ var and_or = 'some';
|
|
|
+ if ( row.usergroup.startsWith( 'AND|' ) ) {
|
|
|
+ row.usergroup = row.usergroup.replace( 'AND|', '' );
|
|
|
+ and_or = 'every';
|
|
|
+ }
|
|
|
+ if ( queryuser.editcount >= row.editcount && row.usergroup.split('|')[and_or]( usergroup => queryuser.groups.includes( usergroup ) ) && accountage >= row.accountage && row.role.split('|').some( role => !roles.includes( role ) ) ) {
|
|
|
+ verified = true;
|
|
|
+ if ( row.rename ) rename = true;
|
|
|
+ row.role.split('|').forEach( role => {
|
|
|
+ if ( !roles.includes( role ) ) {
|
|
|
+ if ( channel.guild.roles.cache.has(role) && channel.guild.me.roles.highest.comparePositionTo(role) > 0 ) roles.push(role);
|
|
|
+ else if ( !missing.includes( role ) ) missing.push(role);
|
|
|
+ }
|
|
|
+ } );
|
|
|
+ }
|
|
|
+ } );
|
|
|
+ if ( verified ) {
|
|
|
+ embed.setColor('#00FF00').setDescription( lang.get('verify.user_verified', member.toString(), '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) + ( rename ? '\n' + lang.get('verify.user_renamed', queryuser.gender) : '' ) );
|
|
|
+ var text = lang.get('verify.user_verified_reply', escapeFormatting(username), queryuser.gender);
|
|
|
+ var verifynotice = {
|
|
|
+ logchannel: '',
|
|
|
+ onsuccess: ''
|
|
|
+ };
|
|
|
+ var verify_promise = [
|
|
|
+ member.roles.add( roles, lang.get('verify.audit_reason', username) ).catch( error => {
|
|
|
+ log_error(error);
|
|
|
+ embed.setColor('#008800');
|
|
|
+ comment.push(lang.get('verify.failed_roles'));
|
|
|
+ } ),
|
|
|
+ db.query( 'SELECT logchannel, onsuccess FROM verifynotice WHERE guild = $1', [channel.guild.id] ).then( ({rows:[row]}) => {
|
|
|
+ if ( !row ) return;
|
|
|
+ verifynotice.logchannel = row.logchannel;
|
|
|
+ if ( row.onsuccess ) verifynotice.onsuccess = parseNotice(row.onsuccess, {
|
|
|
+ editcount: queryuser.editcount,
|
|
|
+ accountage: Math.trunc(accountage),
|
|
|
+ dateformat: lang.get('dateformat')
|
|
|
+ }).trim();
|
|
|
+ }, 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;
|
|
|
+ var logembed;
|
|
|
+ var logtext = '';
|
|
|
+ if ( logchannel && logchannel.isGuild() && logchannel.permissionsFor(channel.guild.me).has(['VIEW_CHANNEL', 'SEND_MESSAGES']) ) {
|
|
|
+ useLogging = true;
|
|
|
+ if ( logchannel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) {
|
|
|
+ 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') );
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ 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') + '** ');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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 && !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 && !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;
|
|
|
+ }
|
|
|
+ return ( settings.edit || channel.send )(member.toString() + ', ' + text, {embed, allowedMentions}).then( msg => {
|
|
|
+ if ( !useLogging ) return;
|
|
|
+ if ( msg ) {
|
|
|
+ if ( logembed ) logembed.addField(msg.url, '<#' + channel.id + '>');
|
|
|
+ else logtext += '\n<#' + channel.id + '> – <' + msg.url + '>';
|
|
|
+ }
|
|
|
+ logchannel.send(logtext, {
|
|
|
+ embed: logembed,
|
|
|
+ allowedMentions: {parse: []}
|
|
|
+ }).catch(log_error);
|
|
|
+ }, log_error );
|
|
|
+ }, log_error );
|
|
|
+ }
|
|
|
+
|
|
|
+ embed.setColor('#FFFF00').setDescription( lang.get('verify.user_matches', member.toString(), '[' + escapeFormatting(username) + '](' + pagelink + ')', queryuser.gender) );
|
|
|
+
|
|
|
+ return db.query( 'SELECT onmatch FROM verifynotice WHERE guild = $1', [channel.guild.id] ).then( ({rows:[row]}) => {
|
|
|
+ if ( !row?.onmatch ) return;
|
|
|
+ var onmatch = parseNotice(row.onmatch, {
|
|
|
+ editcount: queryuser.editcount,
|
|
|
+ accountage: Math.trunc(accountage),
|
|
|
+ dateformat: lang.get('dateformat')
|
|
|
+ });
|
|
|
+ if ( !onmatch.trim() ) return;
|
|
|
+ if ( channel.permissionsFor(channel.guild.me).has('EMBED_LINKS') ) embed.addField( lang.get('verify.notice'), onmatch );
|
|
|
+ else return '\n\n**' + lang.get('verify.notice') + '** ' + onmatch;
|
|
|
+ }, dberror => {
|
|
|
+ console.log( '- Error while getting the notices: ' + dberror );
|
|
|
+ } ).then( (noticeContent = '') => {
|
|
|
+ var components = [
|
|
|
+ {
|
|
|
+ type: 1,
|
|
|
+ components: [
|
|
|
+ {
|
|
|
+ type: 2,
|
|
|
+ style: 1,
|
|
|
+ label: lang.get('verify.button_again'),
|
|
|
+ emoji: {id: null, name: '🔂'},
|
|
|
+ custom_id: 'verify_again',
|
|
|
+ disabled: false
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ return ( settings.edit || channel.send )(member.toString() + ', ' + lang.get('verify.user_matches_reply', escapeFormatting(username), queryuser.gender) + noticeContent, {embed, allowedMentions, components}).catch(log_error);
|
|
|
+ } );
|
|
|
+ }, error => {
|
|
|
+ console.log( '- Error while getting the user: ' + error );
|
|
|
+ settings.edit?.();
|
|
|
+ } );
|
|
|
+ }, error => {
|
|
|
+ if ( error ) console.log( '- Error while preparing oauth verification: ' + error );
|
|
|
+ settings.edit?.();
|
|
|
+ } );
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Parse variables in a verification notice.
|
|
|
* @param {String} [text] The notice to parse.
|
|
@@ -468,7 +707,7 @@ function verify(lang, channel, member, username, wiki, rows, old_username = '')
|
|
|
function parseNotice(text = '', variables = {editcount: 0, postcount: 0, accountage: 0, dateformat: 'en-US'}) {
|
|
|
if ( !text.includes( '$' ) ) return ( text.length > 1000 ? text.substring(0, 1000) + '\u2026' : text );
|
|
|
text = text.replace( /\$(editcount|postcount|accountage)/g, (variable, key, offset, fulltext) => {
|
|
|
- var value = variables[key];
|
|
|
+ var value = ( variables[key] ?? 0 );
|
|
|
if ( typeof value === 'string' ) return value;
|
|
|
if ( /#(?:if)?expr:[^{|}]*$/.test(fulltext.substring(0, offset)) ) return ( value > 1000000000 ? 1000000000 : value );
|
|
|
return value.toLocaleString(variables.dateformat);
|