verify.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. const {randomBytes} = require('crypto');
  2. var db = require('../util/database.js');
  3. var verify = require('../functions/verify.js');
  4. const {oauthVerify, sendMessage} = require('../util/functions.js');
  5. /**
  6. * Wiki user verification.
  7. * @param {Object} interaction - The interaction.
  8. * @param {import('discord.js').Client} interaction.client - The client of the interaction.
  9. * @param {import('../util/i18n.js')} lang - The user language.
  10. * @param {import('../util/wiki.js')} wiki - The wiki for the interaction.
  11. * @param {import('discord.js').TextChannel} [channel] - The channel for the interaction.
  12. */
  13. function slash_verify(interaction, lang, wiki, channel) {
  14. var reply = '<@' + ( interaction.member?.nick ? '!' : '' ) + interaction.user.id + '>, ';
  15. var allowed_mentions = {
  16. users: [interaction.user.id]
  17. };
  18. if ( !channel?.guild ) return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  19. data: {
  20. type: 4,
  21. data: {
  22. content: reply + lang.get('verify.missing'),
  23. allowed_mentions,
  24. flags: 64
  25. }
  26. }
  27. } ).catch(log_error);
  28. if ( !channel.guild.me.permissions.has('MANAGE_ROLES') ) {
  29. console.log( channel.guild.id + ': Missing permissions - MANAGE_ROLES' );
  30. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  31. data: {
  32. type: 4,
  33. data: {
  34. content: reply + lang.get('general.missingperm') + ' `MANAGE_ROLES`',
  35. allowed_mentions,
  36. flags: 64
  37. }
  38. }
  39. } ).catch(log_error);
  40. }
  41. return db.query( 'SELECT role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = $1 AND channel LIKE $2 ORDER BY configid ASC', [interaction.guild_id, '%|' + interaction.channel_id + '|%'] ).then( ({rows}) => {
  42. if ( !rows.length ) return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  43. data: {
  44. type: 4,
  45. data: {
  46. content: reply + lang.get('verify.missing') + ( interaction.member.permissions.has('MANAGE_GUILD') && process.env.dashboard ? '\n' + new URL(`/guild/${interaction.guild_id}/verification`, process.env.dashboard).href : '' ),
  47. allowed_mentions,
  48. flags: 64
  49. }
  50. }
  51. } ).catch(log_error);
  52. if ( wiki.hasOAuth2() && process.env.dashboard ) {
  53. let oauth = [wiki.hostname + wiki.pathname.slice(0, -1)];
  54. if ( wiki.isWikimedia() ) oauth.push('wikimedia');
  55. if ( wiki.isMiraheze() ) oauth.push('miraheze');
  56. if ( process.env['oauth_' + ( oauth[1] || oauth[0] )] && process.env['oauth_' + ( oauth[1] || oauth[0] ) + '_secret'] ) {
  57. let state = `${oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( oauth[1] ? ` ${oauth[1]}` : '' );
  58. while ( oauthVerify.has(state) ) {
  59. state = `${oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( oauth[1] ? ` ${oauth[1]}` : '' );
  60. }
  61. oauthVerify.set(state, {
  62. state, wiki: wiki.href, channel,
  63. user: interaction.user.id
  64. });
  65. interaction.client.shard.send({id: 'verifyUser', state});
  66. let oauthURL = wiki + 'rest.php/oauth2/authorize?' + new URLSearchParams({
  67. response_type: 'code', redirect_uri: new URL('/oauth/mw', process.env.dashboard).href,
  68. client_id: process.env['oauth_' + ( oauth[1] || oauth[0] )], state
  69. }).toString();
  70. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  71. data: {
  72. type: 4,
  73. data: {
  74. content: reply + lang.get('verify.oauth_message', '<' + oauthURL + '>'),
  75. allowed_mentions,
  76. components: [
  77. {
  78. type: 1,
  79. components: [
  80. {
  81. type: 2,
  82. style: 5,
  83. label: lang.get('verify.oauth_button'),
  84. emoji: {id: null, name: '🔗'},
  85. url: oauthURL,
  86. disabled: false
  87. }
  88. ]
  89. }
  90. ],
  91. flags: 64
  92. }
  93. }
  94. } ).catch(log_error);
  95. }
  96. }
  97. var username = ( interaction.data.options?.[0]?.value || '' ).replace( /^\s*<@!?(\d+)>\s*$/, (mention, id) => {
  98. if ( id === interaction.user.id ) {
  99. return ( interaction.member?.nick || interaction.user.username );
  100. }
  101. let user = channel.guild.members.cache.get(id);
  102. if ( user ) return user.displayName;
  103. else {
  104. user = interaction.client.users.cache.get(user);
  105. if ( user ) return user.username;
  106. }
  107. return mention;
  108. } ).replace( /_/g, ' ' ).trim().replace( /^<\s*(.*)\s*>$/, '$1' ).split('#')[0].substring(0, 250).trim();
  109. if ( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.(?:gamepedia\.com\/|(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?(?:wiki\/)?)/.test(username) ) {
  110. username = decodeURIComponent( username.replace( /^(?:https?:)?\/\/([a-z\d-]{1,50})\.(?:gamepedia\.com\/|(?:fandom\.com|wikia\.org)\/(?:[a-z-]{1,8}\/)?(?:wiki\/)?)/, '' ) );
  111. }
  112. if ( wiki.isGamepedia() ) username = username.replace( /^userprofile\s*:\s*/i, '' );
  113. if ( !username.trim() ) return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  114. data: {
  115. type: 4,
  116. data: {
  117. content: lang.get('interaction.verify'),
  118. allowed_mentions,
  119. flags: 64
  120. }
  121. }
  122. } ).catch(log_error);
  123. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  124. data: {
  125. type: 5,
  126. data: {
  127. allowed_mentions,
  128. flags: 0
  129. }
  130. }
  131. } ).then( () => {
  132. return channel.guild.members.fetch(interaction.user.id).then( member => {
  133. return verify(lang, channel, member, username, wiki, rows).then( result => {
  134. if ( result.oauth ) {
  135. let state = `${result.oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( result.oauth[1] ? ` ${result.oauth[1]}` : '' );
  136. while ( oauthVerify.has(state) ) {
  137. state = `${result.oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( result.oauth[1] ? ` ${result.oauth[1]}` : '' );
  138. }
  139. oauthVerify.set(state, {
  140. state, wiki: wiki.href, channel,
  141. user: interaction.user.id
  142. });
  143. interaction.client.shard.send({id: 'verifyUser', state});
  144. let oauthURL = wiki + 'rest.php/oauth2/authorize?' + new URLSearchParams({
  145. response_type: 'code', redirect_uri: new URL('/oauth/mw', process.env.dashboard).href,
  146. client_id: process.env['oauth_' + ( result.oauth[1] || result.oauth[0] )], state
  147. }).toString();
  148. return interaction.client.api.webhooks(interaction.application_id, interaction.token).messages('@original').delete().then( () => {
  149. return interaction.client.api.webhooks(interaction.application_id, interaction.token).post( {
  150. data: {
  151. content: reply + lang.get('verify.oauth_message', '<' + oauthURL + '>'),
  152. allowed_mentions,
  153. components: [
  154. {
  155. type: 1,
  156. components: [
  157. {
  158. type: 2,
  159. style: 5,
  160. label: lang.get('verify.oauth_button'),
  161. emoji: {id: null, name: '🔗'},
  162. url: oauthURL,
  163. disabled: false
  164. }
  165. ]
  166. }
  167. ],
  168. flags: 64
  169. }
  170. } ).catch(log_error);
  171. }, log_error );
  172. }
  173. var message = {
  174. content: reply + result.content,
  175. embeds: [result.embed],
  176. allowed_mentions,
  177. components: []
  178. };
  179. if ( result.add_button ) message.components.push({
  180. type: 1,
  181. components: [
  182. {
  183. type: 2,
  184. style: 1,
  185. label: lang.get('verify.button_again'),
  186. emoji: {id: null, name: '🔂'},
  187. custom_id: 'verify_again',
  188. disabled: false
  189. }
  190. ]
  191. });
  192. if ( result.reaction ) {
  193. if ( result.reaction === 'nowiki' ) message.content = lang.get('interaction.nowiki');
  194. else message.content = reply + lang.get('verify.error_reply');
  195. message.embeds = [];
  196. }
  197. return sendMessage(interaction, message, channel, false).then( msg => {
  198. if ( !result.logging.channel || !channel.guild.channels.cache.has(result.logging.channel) ) return;
  199. if ( msg ) {
  200. if ( result.logging.embed ) result.logging.embed.addField(msg.url, '<#' + channel.id + '>');
  201. else result.logging.content += '\n<#' + channel.id + '> – <' + msg.url + '>';
  202. }
  203. channel.guild.channels.cache.get(result.logging.channel).send(result.logging.content, {
  204. embed: result.logging.embed,
  205. allowedMentions: {parse: []}
  206. }).catch(log_error);
  207. } );
  208. }, error => {
  209. console.log( '- Error during the verifications: ' + error );
  210. return sendMessage(interaction, {
  211. content: reply + lang.get('verify.error_reply'),
  212. allowed_mentions
  213. }, channel);
  214. } );
  215. }, error => {
  216. console.log( '- Error while getting the member: ' + error );
  217. return sendMessage(interaction, {
  218. content: reply + lang.get('verify.error_reply'),
  219. allowed_mentions
  220. }, channel);
  221. } );
  222. }, log_error );
  223. }, dberror => {
  224. console.log( '- Error while getting the verifications: ' + dberror );
  225. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  226. data: {
  227. type: 4,
  228. data: {
  229. content: reply + lang.get('verify.error_reply'),
  230. allowed_mentions,
  231. flags: 64
  232. }
  233. }
  234. } ).catch(log_error);
  235. } );
  236. }
  237. /**
  238. * Wiki user verification.
  239. * @param {Object} interaction - The interaction.
  240. * @param {import('discord.js').Client} interaction.client - The client of the interaction.
  241. * @param {import('../util/i18n.js')} lang - The user language.
  242. * @param {import('../util/wiki.js')} wiki - The wiki for the interaction.
  243. * @param {import('discord.js').TextChannel} [channel] - The channel for the interaction.
  244. */
  245. function button_verify(interaction, lang, wiki, channel) {
  246. var username = interaction?.message?.embeds?.[0]?.title?.replace( /\\(\\)?/g, '$1' );
  247. if ( !username || !channel?.guild || !interaction.message?.mentions?.[0]?.id ) {
  248. interaction.message.allowed_mentions = {
  249. users: [interaction.user.id]
  250. };
  251. interaction.message.components = [];
  252. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  253. data: {
  254. type: 7,
  255. data: interaction.message
  256. }
  257. } ).catch(log_error);
  258. }
  259. if ( interaction.user.id !== interaction.message.mentions[0].id ) {
  260. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  261. data: {type: 6}
  262. } ).then( () => {
  263. interaction.client.api.webhooks(interaction.application_id, interaction.token).post( {
  264. data: {
  265. content: lang.get('verify.button_wrong_user', `<@${interaction.message.mentions[0].id}>`),
  266. allowed_mentions: {
  267. parse: []
  268. },
  269. flags: 64
  270. }
  271. } ).catch(log_error);
  272. }, log_error);
  273. }
  274. return db.query( 'SELECT role, editcount, postcount, usergroup, accountage, rename FROM verification WHERE guild = $1 AND channel LIKE $2 ORDER BY configid ASC', [interaction.guild_id, '%|' + interaction.channel_id + '|%'] ).then( ({rows}) => {
  275. if ( !rows.length || !channel.guild.me.permissions.has('MANAGE_ROLES') ) {
  276. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  277. data: {type: 6}
  278. } ).catch(log_error);
  279. }
  280. var reply = '<@' + ( interaction.member?.nick ? '!' : '' ) + interaction.user.id + '>, ';
  281. var allowed_mentions = {
  282. users: [interaction.user.id]
  283. };
  284. interaction.message.allowed_mentions = allowed_mentions;
  285. if ( interaction?.message?.embeds?.[0]?.fields?.[1]?.value === lang.get('verify.oauth_used') && interaction?.message?.embeds?.[0]?.url?.startsWith( wiki.origin ) ) {
  286. console.log( interaction.guild_id + ': Button: ' + interaction.data.custom_id + ': OAuth2: ' + username );
  287. interaction.message.components[0].components[0].disabled = true;
  288. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  289. data: {
  290. type: 7,
  291. data: interaction.message
  292. }
  293. } ).then( () => {
  294. return global.verifyOauthUser('', '', {
  295. channel, username, user: interaction.user.id,
  296. send: function(content, options) {
  297. if ( !content && !options ) {
  298. interaction.message.components = [];
  299. return sendMessage(interaction, interaction.message, channel, false);
  300. }
  301. var message = {
  302. content, allowed_mentions,
  303. embeds: ( options.embed ? [options.embed] : [] ),
  304. components: ( options.components ? options.components : [] )
  305. };
  306. sendMessage(interaction, message, channel, false);
  307. return interaction.client.api.webhooks(interaction.application_id, interaction.token).post( {
  308. data: {
  309. content, allowed_mentions,
  310. embeds: ( options.embed ? [options.embed] : [] ),
  311. components: [],
  312. flags: 64
  313. }
  314. } ).catch(log_error);
  315. }
  316. });
  317. }, log_error );
  318. }
  319. if ( wiki.hasOAuth2() && process.env.dashboard ) {
  320. let oauth = [wiki.hostname + wiki.pathname.slice(0, -1)];
  321. if ( wiki.isWikimedia() ) oauth.push('wikimedia');
  322. if ( wiki.isMiraheze() ) oauth.push('miraheze');
  323. if ( process.env['oauth_' + ( oauth[1] || oauth[0] )] && process.env['oauth_' + ( oauth[1] || oauth[0] ) + '_secret'] ) {
  324. console.log( interaction.guild_id + ': Button: ' + interaction.data.custom_id + ': OAuth2' );
  325. let state = `${oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( oauth[1] ? ` ${oauth[1]}` : '' );
  326. while ( oauthVerify.has(state) ) {
  327. state = `${oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( oauth[1] ? ` ${oauth[1]}` : '' );
  328. }
  329. oauthVerify.set(state, {
  330. state, wiki: wiki.href, channel,
  331. user: interaction.user.id
  332. });
  333. interaction.client.shard.send({id: 'verifyUser', state});
  334. let oauthURL = wiki + 'rest.php/oauth2/authorize?' + new URLSearchParams({
  335. response_type: 'code', redirect_uri: new URL('/oauth/mw', process.env.dashboard).href,
  336. client_id: process.env['oauth_' + ( oauth[1] || oauth[0] )], state
  337. }).toString();
  338. interaction.message.components = [];
  339. interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  340. data: {
  341. type: 7,
  342. data: interaction.message
  343. }
  344. } ).catch(log_error);
  345. return interaction.client.api.webhooks(interaction.application_id, interaction.token).post( {
  346. data: {
  347. content: reply + lang.get('verify.oauth_message', '<' + oauthURL + '>'),
  348. allowed_mentions,
  349. components: [
  350. {
  351. type: 1,
  352. components: [
  353. {
  354. type: 2,
  355. style: 5,
  356. label: lang.get('verify.oauth_button'),
  357. emoji: {id: null, name: '🔗'},
  358. url: oauthURL,
  359. disabled: false
  360. }
  361. ]
  362. }
  363. ],
  364. flags: 64
  365. }
  366. } ).catch(log_error);
  367. }
  368. }
  369. interaction.message.components[0].components[0].disabled = true;
  370. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  371. data: {
  372. type: 7,
  373. data: interaction.message
  374. }
  375. } ).then( () => {
  376. return channel.guild.members.fetch(interaction.user.id).then( member => {
  377. console.log( interaction.guild_id + ': Button: ' + interaction.data.custom_id + ' ' + username );
  378. return verify(lang, channel, member, username, wiki, rows).then( result => {
  379. if ( result.oauth ) {
  380. let state = `${result.oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( result.oauth[1] ? ` ${result.oauth[1]}` : '' );
  381. while ( oauthVerify.has(state) ) {
  382. state = `${result.oauth[0]} ${global.shardId}` + Date.now().toString(16) + randomBytes(16).toString('hex') + ( result.oauth[1] ? ` ${result.oauth[1]}` : '' );
  383. }
  384. oauthVerify.set(state, {
  385. state, wiki: wiki.href, channel,
  386. user: interaction.user.id
  387. });
  388. interaction.client.shard.send({id: 'verifyUser', state});
  389. let oauthURL = wiki + 'rest.php/oauth2/authorize?' + new URLSearchParams({
  390. response_type: 'code', redirect_uri: new URL('/oauth/mw', process.env.dashboard).href,
  391. client_id: process.env['oauth_' + ( result.oauth[1] || result.oauth[0] )], state
  392. }).toString();
  393. interaction.message.components = [];
  394. interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  395. data: {
  396. type: 7,
  397. data: interaction.message
  398. }
  399. } ).catch(log_error);
  400. return interaction.client.api.webhooks(interaction.application_id, interaction.token).post( {
  401. data: {
  402. content: reply + lang.get('verify.oauth_message', '<' + oauthURL + '>'),
  403. allowed_mentions,
  404. components: [
  405. {
  406. type: 1,
  407. components: [
  408. {
  409. type: 2,
  410. style: 5,
  411. label: lang.get('verify.oauth_button'),
  412. emoji: {id: null, name: '🔗'},
  413. url: oauthURL,
  414. disabled: false
  415. }
  416. ]
  417. }
  418. ],
  419. flags: 64
  420. }
  421. } ).catch(log_error);
  422. }
  423. var message = {
  424. content: reply + result.content,
  425. embeds: [result.embed],
  426. allowed_mentions,
  427. components: []
  428. };
  429. if ( result.reaction ) {
  430. if ( result.reaction === 'nowiki' ) message.content = lang.get('interaction.nowiki');
  431. else message.content = reply + lang.get('verify.error_reply');
  432. message.embeds = [];
  433. }
  434. else if ( result.add_button ) message.components.push({
  435. type: 1,
  436. components: [
  437. {
  438. type: 2,
  439. style: 1,
  440. label: lang.get('verify.button_again'),
  441. emoji: {id: null, name: '🔂'},
  442. custom_id: 'verify_again',
  443. disabled: false
  444. }
  445. ]
  446. });
  447. sendMessage(interaction, message, channel, false);
  448. if ( result.logging.channel && channel.guild.channels.cache.has(result.logging.channel) ) {
  449. let msg_url = `https://discord.com/channels/${channel.guild.id}/${channel.id}/${interaction.message.id}`;
  450. if ( result.logging.embed ) result.logging.embed.addField(msg_url, '<#' + channel.id + '>');
  451. else result.logging.content += '\n<#' + channel.id + '> – <' + msg_url + '>';
  452. channel.guild.channels.cache.get(result.logging.channel).send(result.logging.content, {
  453. embed: result.logging.embed,
  454. allowedMentions: {parse: []}
  455. }).catch(log_error);
  456. }
  457. interaction.client.api.webhooks(interaction.application_id, interaction.token).post( {
  458. data: {
  459. content: message.content,
  460. embeds: message.embeds,
  461. allowed_mentions,
  462. components: [],
  463. flags: 64
  464. }
  465. } ).catch(log_error);
  466. }, error => {
  467. console.log( '- Error during the verifications: ' + error );
  468. return sendMessage(interaction, {
  469. content: reply + lang.get('verify.error_reply'),
  470. allowed_mentions
  471. }, channel);
  472. } );
  473. }, error => {
  474. console.log( '- Error while getting the member: ' + error );
  475. return sendMessage(interaction, {
  476. content: reply + lang.get('verify.error_reply'),
  477. allowed_mentions
  478. }, channel);
  479. } );
  480. }, log_error);
  481. }, dberror => {
  482. console.log( '- Error while getting the verifications: ' + dberror );
  483. return interaction.client.api.interactions(interaction.id, interaction.token).callback.post( {
  484. data: {type: 6}
  485. } ).catch(log_error);
  486. } );
  487. }
  488. module.exports = {
  489. name: 'verify',
  490. run: slash_verify,
  491. button: button_verify
  492. };