settings.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. const cheerio = require('cheerio');
  2. const {MessageEmbed} = require('discord.js');
  3. const {defaultSettings} = require('../util/default.json');
  4. const Lang = require('../util/i18n.js');
  5. const allLangs = Lang.allLangs();
  6. const Wiki = require('../util/wiki.js');
  7. var db = require('../util/database.js');
  8. var allSites = [];
  9. const getAllSites = require('../util/allSites.js');
  10. getAllSites.then( sites => allSites = sites );
  11. /**
  12. * Processes the "settings" command.
  13. * @param {Lang} lang - The user language.
  14. * @param {import('discord.js').Message} msg - The Discord message.
  15. * @param {String[]} args - The command arguments.
  16. * @param {String} line - The command as plain text.
  17. * @param {Wiki} wiki - The wiki for the message.
  18. */
  19. function cmd_settings(lang, msg, args, line, wiki) {
  20. if ( !allSites.length ) getAllSites.update();
  21. if ( !msg.isAdmin() ) return msg.reactEmoji('❌');
  22. db.all( 'SELECT channel, wiki, lang, role, inline, prefix FROM discord WHERE guild = ? ORDER BY channel DESC', [msg.guild.id], (error, rows) => {
  23. if ( error ) {
  24. console.log( '- Error while getting the settings: ' + error );
  25. msg.reactEmoji('error', true);
  26. return error;
  27. }
  28. var guild = rows.find( row => !row.channel );
  29. if ( !guild ) guild = Object.assign({
  30. role: null, inline: null,
  31. prefix: process.env.prefix
  32. }, defaultSettings);
  33. var prefix = guild.prefix;
  34. var inlinepage = ( lang.localNames.page || 'page' );
  35. var text = lang.get('settings.missing', '`' + prefix + 'settings lang`', '`' + prefix + 'settings wiki`');
  36. if ( rows.length ) {
  37. text = lang.get('settings.current') + '\n' + lang.get('settings.currentlang') + ' `' + allLangs.names[guild.lang] + '` - `' + prefix + 'settings lang`';
  38. if ( msg.guild.id in patreons ) text += '\n' + lang.get('settings.currentprefix') + ' `' + prefix + '` - `' + prefix + 'settings prefix`';
  39. text += '\n' + lang.get('settings.currentinline') + ' ' + ( guild.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( guild.inline ? '~~' : '' ) + ' - `' + prefix + 'settings inline`';
  40. text += '\n' + lang.get('settings.currentwiki') + ' ' + guild.wiki + ' - `' + prefix + 'settings wiki`';
  41. text += '\n' + lang.get('settings.currentchannel') + ' `' + prefix + 'settings channel`\n';
  42. if ( rows.length === 1 ) text += lang.get('settings.nochannels');
  43. else text += rows.filter( row => row !== guild ).map( row => '<#' + row.channel + '>: ' + ( msg.guild.id in patreons ? '`' + allLangs.names[row.lang] + '` - ' : '' ) + '<' + row.wiki + '>' + ( msg.guild.id in patreons ? ' - ' + ( row.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( row.inline ? '~~' : '' ) : '' ) ).join('\n');
  44. }
  45. if ( !args.length ) {
  46. return msg.replyMsg( text, {split:true}, true );
  47. }
  48. var prelang = '';
  49. args[0] = args[0].toLowerCase();
  50. if ( args[0] === 'channel' ) {
  51. prelang = 'channel ';
  52. if ( !rows.length ) return msg.replyMsg( text, {split:true}, true );
  53. var channel = rows.find( row => row.channel === msg.channel.id );
  54. if ( !channel ) channel = Object.assign({channel:msg.channel.id}, guild);
  55. text = lang.get('settings.' + prelang + 'current');
  56. if ( msg.guild.id in patreons ) {
  57. text += '\n' + lang.get('settings.currentlang') + ' `' + allLangs.names[channel.lang] + '` - `' + prefix + 'settings channel lang`';
  58. text += '\n' + lang.get('settings.currentinline') + ' ' + ( channel.inline ? '~~' : '' ) + '`[[' + inlinepage + ']]`' + ( channel.inline ? '~~' : '' ) + ' - `' + prefix + 'settings channel inline`';
  59. }
  60. text += '\n' + lang.get('settings.currentwiki') + ' ' + channel.wiki + ' - `' + prefix + 'settings channel wiki`';
  61. if ( !args[1] ) return msg.replyMsg( text, {}, true );
  62. args[0] = args[1].toLowerCase();
  63. args[1] = args.slice(2).join(' ').toLowerCase().trim().replace( /^<\s*(.*)>$/, '$1' );
  64. }
  65. else args[1] = args.slice(1).join(' ').toLowerCase().trim().replace( /^<\s*(.*)>$/, '$1' );
  66. if ( args[0] === 'wiki' ) {
  67. prelang += 'wiki';
  68. var wikihelp = '\n' + lang.get('settings.wikihelp', prefix + 'settings ' + prelang);
  69. if ( !args[1] ) {
  70. if ( !rows.length ) return msg.replyMsg( lang.get('settings.wikimissing') + wikihelp, {}, true );
  71. else return msg.replyMsg( lang.get('settings.' + prelang) + ' ' + ( channel || guild ).wiki + wikihelp, {}, true );
  72. }
  73. if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
  74. var isForced = false;
  75. if ( /^<?(?:https?:)?\/\//.test(args[1]) ) {
  76. args[1] = args[1].replace( /^<?(?:https?:)?\/\//, 'https://' );
  77. let value = [];
  78. [args[1], ...value] = args[1].split(/>? /);
  79. if ( value.join(' ') === '--force' ) isForced = true;
  80. }
  81. var wikinew = Wiki.fromInput(args[1]);
  82. if ( !wikinew ) {
  83. var text = lang.get('settings.wikiinvalid') + wikihelp;
  84. var sites = allSites.filter( site => site.wiki_display_name.toLowerCase().includes( args[1] ) );
  85. if ( 0 < sites.length && sites.length < 21 ) {
  86. text += '\n\n' + lang.get('settings.foundwikis') + '\n' + sites.map( site => site.wiki_display_name + ': `' + site.wiki_domain + '`' ).join('\n');
  87. }
  88. return msg.replyMsg( text, {split:true}, true );
  89. }
  90. if ( wikinew.isGamepedia() && !isForced ) {
  91. let site = allSites.find( site => site.wiki_domain === wikinew.hostname );
  92. if ( site ) wikinew = new Wiki('https://' + ( site.wiki_crossover || site.wiki_domain ) + '/');
  93. }
  94. return msg.reactEmoji('⏳', true).then( reaction => {
  95. got.get( wikinew + 'api.php?&action=query&meta=allmessages|siteinfo&ammessages=custom-GamepediaNotice|custom-FandomMergeNotice&amenableparser=true&siprop=general|extensions&format=json' ).then( response => {
  96. if ( !isForced && response.statusCode === 404 && typeof response.body === 'string' ) {
  97. let api = cheerio.load(response.body)('head link[rel="EditURI"]').prop('href');
  98. if ( api ) {
  99. wikinew = new Wiki(api.split('api.php?')[0], wikinew);
  100. return got.get( wikinew + 'api.php?action=query&meta=allmessages|siteinfo&ammessages=custom-GamepediaNotice|custom-FandomMergeNotice&amenableparser=true&siprop=general|extensions&format=json' );
  101. }
  102. }
  103. return response;
  104. } ).then( response => {
  105. var body = response.body;
  106. if ( response.statusCode !== 200 || !body?.query?.allmessages || !body?.query?.general || !body?.query?.extensions ) {
  107. console.log( '- ' + response.statusCode + ': Error while testing the wiki: ' + body?.error?.info );
  108. if ( reaction ) reaction.removeEmoji();
  109. msg.reactEmoji('nowiki', true);
  110. return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
  111. }
  112. if ( !isForced ) wikinew.updateWiki(body.query.general);
  113. if ( wikinew.isGamepedia() && !isForced ) {
  114. let site = allSites.find( site => site.wiki_domain === wikinew.hostname );
  115. if ( site ) wikinew = new Wiki('https://' + ( site.wiki_crossover || site.wiki_domain ) + '/');
  116. }
  117. else if ( wikinew.isFandom(false) && !isForced ) {
  118. let crossover = '';
  119. if ( body.query.allmessages[0]['*'] ) {
  120. crossover = 'https://' + body.query.allmessages[0]['*'] + '.gamepedia.com/';
  121. }
  122. else if ( body.query.allmessages[1]['*'] ) {
  123. let merge = body.query.allmessages[1]['*'].split('/');
  124. crossover = 'https://' + merge[0] + '.fandom.com/' + ( merge[1] ? merge[1] + '/' : '' );
  125. }
  126. if ( crossover ) wikinew = new Wiki(crossover);
  127. }
  128. var embed;
  129. if ( !wikinew.isFandom() ) {
  130. var notice = [];
  131. if ( body.query.general.generator.replace( /^MediaWiki 1\.(\d\d).*$/, '$1' ) < 30 ) {
  132. console.log( '- This wiki is using ' + body.query.general.generator + '.' );
  133. notice.push({
  134. name: 'MediaWiki',
  135. value: lang.get('test.MediaWiki', '[MediaWiki 1.30](https://www.mediawiki.org/wiki/MediaWiki_1.30)', body.query.general.generator)
  136. });
  137. }
  138. if ( !body.query.extensions.some( extension => extension.name === 'TextExtracts' ) ) {
  139. console.log( '- This wiki is missing Extension:TextExtracts.' );
  140. notice.push({
  141. name: 'TextExtracts',
  142. value: lang.get('test.TextExtracts', '[TextExtracts](https://www.mediawiki.org/wiki/Extension:TextExtracts)')
  143. });
  144. }
  145. if ( !body.query.extensions.some( extension => extension.name === 'PageImages' ) ) {
  146. console.log( '- This wiki is missing Extension:PageImages.' );
  147. notice.push({
  148. name: 'PageImages',
  149. value: lang.get('test.PageImages', '[PageImages](https://www.mediawiki.org/wiki/Extension:PageImages)')
  150. });
  151. }
  152. if ( notice.length ) {
  153. embed = new MessageEmbed().setAuthor( body.query.general.sitename ).setTitle( lang.get('test.notice') ).addFields( notice );
  154. }
  155. }
  156. var sql = 'UPDATE discord SET wiki = ? WHERE guild = ? AND wiki = ?';
  157. var sqlargs = [wikinew.href, msg.guild.id, guild.wiki];
  158. if ( !rows.length ) {
  159. sql = 'INSERT INTO discord(wiki, guild, main) VALUES(?, ?, ?)';
  160. sqlargs[2] = msg.guild.id;
  161. }
  162. else if ( channel ) {
  163. sql = 'UPDATE discord SET wiki = ? WHERE guild = ? AND channel = ?';
  164. sqlargs[2] = msg.channel.id;
  165. if ( !rows.includes( channel ) ) {
  166. if ( channel.wiki === wikinew.href ) {
  167. if ( reaction ) reaction.removeEmoji();
  168. return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + channel.wiki + wikihelp, {embed}, true );
  169. }
  170. sql = 'INSERT INTO discord(wiki, guild, channel, lang, role, inline, prefix) VALUES(?, ?, ?, ?, ?, ?, ?)';
  171. sqlargs.push(guild.lang, guild.role, guild.inline, guild.prefix);
  172. }
  173. }
  174. return db.run( sql, sqlargs, function (dberror) {
  175. if ( dberror ) {
  176. console.log( '- Error while editing the settings: ' + dberror );
  177. msg.replyMsg( lang.get('settings.save_failed'), {embed}, true );
  178. if ( reaction ) reaction.removeEmoji();
  179. return dberror;
  180. }
  181. console.log( '- Settings successfully updated.' );
  182. if ( channel ) channel.wiki = wikinew.href;
  183. else {
  184. rows.forEach( row => {
  185. if ( row.channel && row.wiki === guild.wiki ) row.wiki = wikinew.href;
  186. } );
  187. guild.wiki = wikinew.href;
  188. }
  189. if ( channel || !rows.some( row => row.channel === msg.channel.id ) ) wiki = new Wiki(wikinew);
  190. if ( reaction ) reaction.removeEmoji();
  191. msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' ' + wikinew + wikihelp, {embed}, true );
  192. var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
  193. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join(', ') + ')', channels, function (delerror) {
  194. if ( delerror ) {
  195. console.log( '- Error while removing the settings: ' + delerror );
  196. return delerror;
  197. }
  198. console.log( '- Settings successfully removed.' );
  199. } );
  200. } );
  201. }, ferror => {
  202. console.log( '- Error while testing the wiki: ' + ferror );
  203. if ( reaction ) reaction.removeEmoji();
  204. msg.reactEmoji('nowiki', true);
  205. return msg.replyMsg( lang.get('settings.wikiinvalid') + wikihelp, {}, true );
  206. } );
  207. } );
  208. }
  209. if ( args[0] === 'lang' ) {
  210. if ( channel && !( msg.guild.id in patreons ) ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', {}, true );
  211. prelang += 'lang';
  212. var langhelp = '\n' + lang.get('settings.langhelp', prefix + 'settings ' + prelang) + ' `' + Object.values(allLangs.names).join('`, `') + '`';
  213. if ( !args[1] ) {
  214. return msg.replyMsg( lang.get('settings.' + prelang) + ' `' + allLangs.names[( channel || guild ).lang] + '`' + langhelp, {files:( msg.uploadFiles() ? [`./i18n/widgets/${( channel || guild ).lang}.png`] : [] )}, true );
  215. }
  216. if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
  217. if ( !( args[1] in allLangs.map ) ) {
  218. return msg.replyMsg( lang.get('settings.langinvalid') + langhelp, {}, true );
  219. }
  220. var sql = 'UPDATE discord SET lang = ? WHERE guild = ? AND lang = ?';
  221. var sqlargs = [allLangs.map[args[1]], msg.guild.id, guild.lang];
  222. if ( !rows.length ) {
  223. sql = 'INSERT INTO discord(lang, guild, main) VALUES(?, ?, ?)';
  224. sqlargs[2] = msg.guild.id;
  225. }
  226. else if ( channel ) {
  227. sql = 'UPDATE discord SET lang = ? WHERE guild = ? AND channel = ?';
  228. sqlargs[2] = msg.channel.id;
  229. if ( !rows.includes( channel ) ) {
  230. if ( channel.lang === allLangs.map[args[1]] ) {
  231. return msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' `' + allLangs.names[channel.lang] + '`' + langhelp, {files:( msg.uploadFiles() ? [`./i18n/widgets/${channel.lang}.png`] : [] )}, true );
  232. }
  233. sql = 'INSERT INTO discord(lang, guild, channel, wiki, role, inline, prefix) VALUES(?, ?, ?, ?, ?, ?, ?)';
  234. sqlargs.push(guild.wiki, guild.role, guild.inline, guild.prefix);
  235. }
  236. }
  237. return db.run( sql, sqlargs, function (dberror) {
  238. if ( dberror ) {
  239. console.log( '- Error while editing the settings: ' + dberror );
  240. msg.replyMsg( lang.get('settings.save_failed'), {}, true );
  241. return dberror;
  242. }
  243. console.log( '- Settings successfully updated.' );
  244. if ( channel ) channel.lang = allLangs.map[args[1]];
  245. else {
  246. rows.forEach( row => {
  247. if ( row.channel && row.lang === guild.lang ) row.lang = allLangs.map[args[1]];
  248. } );
  249. guild.lang = allLangs.map[args[1]];
  250. if ( msg.guild.id in voice ) voice[msg.guild.id] = guild.lang;
  251. }
  252. if ( channel || !( msg.guild.id in patreons ) || !rows.some( row => row.channel === msg.channel.id ) ) lang = new Lang(allLangs.map[args[1]]);
  253. msg.replyMsg( lang.get('settings.' + prelang + 'changed') + ' `' + allLangs.names[allLangs.map[args[1]]] + '`\n' + lang.get('settings.langhelp', prefix + 'settings ' + prelang) + ' `' + Object.values(allLangs.names).join('`, `') + '`', {files:( msg.uploadFiles() ? [`./i18n/widgets/${allLangs.map[args[1]]}.png`] : [] )}, true );
  254. var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
  255. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join(', ') + ')', channels, function (delerror) {
  256. if ( delerror ) {
  257. console.log( '- Error while removing the settings: ' + delerror );
  258. return delerror;
  259. }
  260. console.log( '- Settings successfully removed.' );
  261. } );
  262. } );
  263. }
  264. if ( args[0] === 'prefix' && !channel ) {
  265. if ( !( msg.guild.id in patreons ) ) {
  266. return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', {}, true );
  267. }
  268. var prefixhelp = '\n' + lang.get('settings.prefixhelp', prefix + 'settings prefix');
  269. args[1] = args[1].replace( /(?<!\\)_$/, ' ' ).replace( /\\([_\W])/g, '$1' );
  270. if ( !args[1].trim() ) {
  271. return msg.replyMsg( lang.get('settings.prefix') + ' `' + prefix + '`' + prefixhelp, {}, true );
  272. }
  273. if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
  274. if ( args[1].includes( '`' ) || args[1].includes( '\\' ) || args[1].length > 100 ) {
  275. return msg.replyMsg( lang.get('settings.prefixinvalid') + prefixhelp, {}, true );
  276. }
  277. if ( args[1] === 'reset' || args[1] === 'default' ) args[1] = process.env.prefix;
  278. var sql = 'UPDATE discord SET prefix = ? WHERE guild = ?';
  279. var sqlargs = [args[1], msg.guild.id];
  280. if ( !rows.length ) {
  281. sql = 'INSERT INTO discord(prefix, guild, main) VALUES(?, ?, ?)';
  282. sqlargs.push(msg.guild.id);
  283. }
  284. return db.run( sql, sqlargs, function (dberror) {
  285. if ( dberror ) {
  286. console.log( '- Error while editing the settings: ' + dberror );
  287. msg.replyMsg( lang.get('settings.save_failed'), {}, true );
  288. return dberror;
  289. }
  290. console.log( '- Settings successfully updated.' );
  291. guild.prefix = args[1];
  292. msg.client.shard.broadcastEval( `global.patreons['${msg.guild.id}'] = '${args[1]}'` );
  293. msg.replyMsg( lang.get('settings.prefixchanged') + ' `' + args[1] + '`\n' + lang.get('settings.prefixhelp', args[1] + 'settings prefix'), {}, true );
  294. } );
  295. }
  296. if ( args[0] === 'inline' ) {
  297. if ( channel && !( msg.guild.id in patreons ) ) return msg.replyMsg( lang.get('general.patreon') + '\n<' + process.env.patreon + '>', {}, true );
  298. prelang += 'inline';
  299. var toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
  300. var inlinehelp = '\n' + lang.get('settings.' + toggle + '.help', prefix + 'settings ' + prelang + ' toggle', inlinepage);
  301. if ( args[1] !== 'toggle' ) {
  302. return msg.replyMsg( lang.get('settings.' + toggle + '.' + prelang) + inlinehelp, {}, true );
  303. }
  304. if ( process.env.READONLY ) return msg.replyMsg( lang.get('general.readonly') + '\n' + process.env.invite, {}, true );
  305. var value = ( ( channel || guild ).inline ? null : 1 );
  306. var sql = 'UPDATE discord SET inline = ? WHERE guild = ?';
  307. var sqlargs = [value, msg.guild.id];
  308. if ( !rows.length ) {
  309. sql = 'INSERT INTO discord(inline, guild, main) VALUES(?, ?, ?)';
  310. sqlargs.push(msg.guild.id);
  311. }
  312. else if ( channel ) {
  313. sql = 'UPDATE discord SET inline = ? WHERE guild = ? AND channel = ?';
  314. sqlargs.push(msg.channel.id);
  315. if ( !rows.includes( channel ) ) {
  316. sql = 'INSERT INTO discord(inline, guild, channel, wiki, lang, role, prefix) VALUES(?, ?, ?, ?, ?, ?, ?)';
  317. sqlargs.push(guild.wiki, guild.lang, guild.role, guild.prefix);
  318. }
  319. }
  320. return db.run( sql, sqlargs, function (dberror) {
  321. if ( dberror ) {
  322. console.log( '- Error while editing the settings: ' + dberror );
  323. msg.replyMsg( lang.get('settings.save_failed'), {}, true );
  324. return dberror;
  325. }
  326. console.log( '- Settings successfully updated.' );
  327. if ( channel ) channel.inline = value;
  328. else {
  329. rows.forEach( row => {
  330. if ( row.channel && row.inline === guild.inline ) row.inline = value;
  331. } );
  332. guild.inline = value;
  333. }
  334. toggle = 'inline ' + ( ( channel || guild ).inline ? 'disabled' : 'enabled' );
  335. msg.replyMsg( lang.get('settings.' + toggle + '.' + prelang + 'changed') + '\n' + lang.get('settings.' + toggle + '.help', prefix + 'settings ' + prelang + ' toggle', inlinepage), {}, true );
  336. var channels = rows.filter( row => row.channel && row.lang === guild.lang && row.wiki === guild.wiki && row.prefix === guild.prefix && row.role === guild.role && row.inline === guild.inline ).map( row => row.channel );
  337. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( row => '?' ).join(', ') + ')', channels, function (delerror) {
  338. if ( delerror ) {
  339. console.log( '- Error while removing the settings: ' + delerror );
  340. return delerror;
  341. }
  342. console.log( '- Settings successfully removed.' );
  343. } );
  344. } );
  345. }
  346. return msg.replyMsg( text, {split:true}, true );
  347. } );
  348. }
  349. module.exports = {
  350. name: 'settings',
  351. everyone: true,
  352. pause: true,
  353. owner: false,
  354. run: cmd_settings
  355. };