eval.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. const util = require('util');
  2. util.inspect.defaultOptions = {compact:false,breakLength:Infinity};
  3. const Discord = require('discord.js');
  4. const {limit: {verification: verificationLimit, rcgcdw: rcgcdwLimit}} = require('../util/default.json');
  5. const newMessage = require('../util/newMessage.js');
  6. const Wiki = require('../util/wiki.js');
  7. var db = require('../util/database.js');
  8. /**
  9. * Processes the "eval" command.
  10. * @param {import('../util/i18n.js')} lang - The user language.
  11. * @param {Discord.Message} msg - The Discord message.
  12. * @param {String[]} args - The command arguments.
  13. * @param {String} line - The command as plain text.
  14. * @param {Wiki} wiki - The wiki for the message.
  15. * @async
  16. */
  17. async function cmd_eval(lang, msg, args, line, wiki) {
  18. try {
  19. var text = util.inspect( await eval( args.join(' ') ) );
  20. } catch ( error ) {
  21. var text = error.toString();
  22. }
  23. if ( isDebug ) console.log( '--- EVAL START ---\n' + text + '\n--- EVAL END ---' );
  24. if ( text.length > 2000 ) msg.reactEmoji('✅', true);
  25. else msg.sendChannel( '```js\n' + text + '\n```', {split:{prepend:'```js\n',append:'\n```'},allowedMentions:{}}, true );
  26. /**
  27. * Runs a command with admin permissions.
  28. * @param {String} cmdline - The message text.
  29. */
  30. function backdoor(cmdline) {
  31. msg.evalUsed = true;
  32. newMessage(msg, lang, wiki, patreons[msg.guild.id], msg.noInline, cmdline);
  33. return cmdline;
  34. }
  35. }
  36. /**
  37. * Runs database queries.
  38. * @param {String} sql - The SQL command.
  39. * @param {String[]} [sqlargs] - The command arguments.
  40. */
  41. function database(sql, sqlargs = []) {
  42. return new Promise( function (resolve, reject) {
  43. db.all( sql, sqlargs, (error, rows) => {
  44. if (error) reject(error);
  45. resolve(rows);
  46. } );
  47. } );
  48. }
  49. /**
  50. * Checks a wiki and it's recent changes webhooks.
  51. * @param {Wiki} wiki - The wiki to check.
  52. */
  53. function checkWiki(wiki) {
  54. wiki = new Wiki(wiki);
  55. return got.get( wiki + 'api.php?action=query' + ( wiki.isFandom() ? '&meta=siteinfo&siprop=variables' : '' ) + '&list=recentchanges&rcshow=!bot&rctype=edit|new|log|categorize&rcprop=ids&rclimit=1&format=json' ).then( response => {
  56. var body = response.body;
  57. if ( response.statusCode !== 200 || !body?.query?.recentchanges ) {
  58. return response.statusCode + ': Error while getting the recent changes: ' + body?.error?.info;
  59. }
  60. var result = {
  61. wiki: wiki.href,
  62. rcid: ( body.query.recentchanges[0]?.rcid || 0 ),
  63. wikiid: body.query.variables?.find?.( variable => variable?.id === 'wgCityId' )?.['*'],
  64. postid: null
  65. }
  66. return Promise.all([
  67. database('SELECT guild, lang, display, rcid, wikiid, postid FROM rcgcdw WHERE wiki = ?', [result.wiki]).then( rows => {
  68. result.rcgcdb = rows;
  69. }, error => {
  70. result.rcgcdb = error.toString();
  71. } ),
  72. ( result.wikiid ? got.get( 'https://services.fandom.com/discussion/' + result.wikiid + '/posts?limit=1&format=json&cache=' + Date.now(), {
  73. headers: {
  74. Accept: 'application/hal+json'
  75. }
  76. } ).then( dsresponse => {
  77. var dsbody = dsresponse.body;
  78. if ( dsresponse.statusCode !== 200 || !dsbody || dsbody.title ) {
  79. if ( dsbody?.title !== 'site doesn\'t exists' ) result.postid = dsresponse.statusCode + ': Error while getting the discussions: ' + dsbody?.title;
  80. }
  81. else result.postid = ( dsbody._embedded?.['doc:posts']?.id || 0 );
  82. }, error => {
  83. result.postid = 'Error while getting the discussions: ' + error;
  84. } ) : null )
  85. ]).then( () => {
  86. return result;
  87. } );
  88. }, error => {
  89. return 'Error while getting the recent changes: ' + error;
  90. } );
  91. }
  92. /**
  93. * Update the list of all sites.
  94. * @returns {Promise<Object[]>}
  95. */
  96. function updateAllSites() {
  97. return require('../util/allSites.js').update();
  98. }
  99. /**
  100. * Removes the patreon features for a guild.
  101. * @param {String} guild - The guild ID.
  102. * @param {Discord.Message} msg - The Discord message.
  103. */
  104. function removePatreons(guild, msg) {
  105. try {
  106. if ( !( typeof guild === 'string' || msg instanceof Discord.Message ) ) {
  107. return 'removePatreons(guild, msg) – No guild or message provided!';
  108. }
  109. db.get( 'SELECT lang, inline FROM discord WHERE guild = ? AND channel IS NULL', [guild], (dberror, row) => {
  110. try {
  111. if ( dberror ) {
  112. console.log( '- Error while getting the guild: ' + dberror );
  113. msg.replyMsg( 'I got an error while searching for the guild!', {}, true );
  114. return dberror;
  115. }
  116. if ( !row ) {
  117. msg.replyMsg( 'that guild doesn\'t exist!', {}, true );
  118. return;
  119. }
  120. db.run( 'UPDATE discord SET lang = ?, inline = ?, prefix = ?, patreon = NULL WHERE guild = ?', [row.lang, row.inline, process.env.prefix, guild], function (error) {
  121. try {
  122. if ( error ) {
  123. console.log( '- Error while updating the guild: ' + error );
  124. msg.replyMsg( 'I got an error while updating the guild!', {}, true );
  125. return error;
  126. }
  127. console.log( '- Guild successfully updated.' );
  128. msg.client.shard.broadcastEval( `delete global.patreons['${guild}']`);
  129. msg.replyMsg( 'the patreon features are now disabled on that guild.', {}, true );
  130. }
  131. catch ( tryerror ) {
  132. console.log( '- Error while removing the patreon features: ' + tryerror );
  133. }
  134. } );
  135. }
  136. catch ( tryerror ) {
  137. console.log( '- Error while removing the patreon features: ' + tryerror );
  138. }
  139. } );
  140. db.all( 'SELECT configid FROM verification WHERE guild = ? ORDER BY configid ASC', [guild], (dberror, rows) => {
  141. if ( dberror ) {
  142. console.log( '- Error while getting the verifications: ' + dberror );
  143. return dberror;
  144. }
  145. var ids = rows.slice(verificationLimit.default).map( row => row.configid );
  146. if ( ids.length ) db.run( 'DELETE FROM verification WHERE guild = ? AND configid IN (' + ids.map( configid => '?' ).join(', ') + ')', [guild, ...ids], function (error) {
  147. if ( error ) {
  148. console.log( '- Error while deleting the verifications: ' + error );
  149. return error;
  150. }
  151. console.log( '- Verifications successfully deleted.' );
  152. } );
  153. } );
  154. db.all( 'SELECT webhook FROM rcgcdw WHERE guild = ? ORDER BY configid ASC', [guild], (dberror, rows) => {
  155. if ( dberror ) {
  156. console.log( '- Error while getting the RcGcDw: ' + dberror );
  157. return dberror;
  158. }
  159. var webhooks = rows.slice(rcgcdwLimit.default).map( row => row.webhook );
  160. if ( webhooks.length ) db.run( 'DELETE FROM rcgcdw WHERE webhook IN (' + webhooks.map( webhook => '?' ).join(', ') + ')', webhooks, function (error) {
  161. if ( error ) {
  162. console.log( '- Error while deleting the RcGcDw: ' + error );
  163. return error;
  164. }
  165. console.log( '- RcGcDw successfully deleted.' );
  166. webhooks.forEach( hook => guild.client.fetchWebhook(...hook.split('/')).then( webhook => {
  167. webhook.delete('Removed extra recent changes webhook').catch(log_error);
  168. }, log_error ) );
  169. } );
  170. } );
  171. db.run( 'UPDATE rcgcdw SET display = ? WHERE guild = ? AND display > ?', [rcgcdwLimit.display, guild, rcgcdwLimit.display], function (dberror) {
  172. if ( dberror ) {
  173. console.log( '- Error while updating the RcGcDw: ' + dberror );
  174. return dberror;
  175. }
  176. console.log( '- RcGcDw successfully updated.' );
  177. } );
  178. }
  179. catch ( tryerror ) {
  180. console.log( '- Error while removing the patreon features: ' + tryerror );
  181. return 'removePatreons(guild, msg) – Error while removing the patreon features: ' + tryerror;
  182. }
  183. }
  184. /**
  185. * Removes the settings for deleted guilds and channels.
  186. * @param {Discord.Message} msg - The Discord message.
  187. */
  188. function removeSettings(msg) {
  189. if ( !msg ) return 'removeSettings(msg) – No message provided!';
  190. try {
  191. msg.client.shard.broadcastEval( `[[...this.guilds.cache.keys()], [...this.channels.cache.filter( channel => channel.isGuild() ).keys()]]` ).then( results => {
  192. var all_guilds = results.map( result => result[0] ).reduce( (acc, val) => acc.concat(val), [] );
  193. var all_channels = results.map( result => result[1] ).reduce( (acc, val) => acc.concat(val), [] );
  194. var guilds = [];
  195. var channels = [];
  196. db.each( 'SELECT guild, channel FROM discord', [], (dberror, row) => {
  197. if ( dberror ) {
  198. console.log( '- Error while getting the setting: ' + dberror );
  199. return dberror;
  200. }
  201. if ( !row.channel && !all_guilds.includes(row.guild) ) {
  202. if ( row.guild in patreons ) msg.client.shard.broadcastEval( `delete global.patreons['${row.guild}']` );
  203. if ( row.guild in voice ) delete voice[row.guild];
  204. return guilds.push(row.guild);
  205. }
  206. if ( row.channel && all_guilds.includes(row.guild) && !all_channels.includes(row.channel) ) return channels.push(row.channel);
  207. }, (error) => {
  208. if ( error ) {
  209. console.log( '- Error while getting the settings: ' + error );
  210. msg.replyMsg( 'I got an error while getting the settings!', {}, true );
  211. return error;
  212. }
  213. if ( guilds.length ) {
  214. db.run( 'DELETE FROM discord WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ')', guilds, function (dberror) {
  215. if ( dberror ) {
  216. console.log( '- Error while removing the guilds: ' + dberror );
  217. msg.replyMsg( 'I got an error while removing the guilds!', {}, true );
  218. return dberror;
  219. }
  220. console.log( '- Guilds successfully removed.' );
  221. } );
  222. db.run( 'DELETE FROM verification WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ')', guilds, function (dberror) {
  223. if ( dberror ) {
  224. console.log( '- Error while removing the verifications: ' + dberror );
  225. msg.replyMsg( 'I got an error while removing the verifications!', {}, true );
  226. return dberror;
  227. }
  228. console.log( '- Verifications successfully removed.' );
  229. } );
  230. db.run( 'DELETE FROM rcgcdw WHERE guild IN (' + guilds.map( guild => '?' ).join(', ') + ')', guilds, function (dberror) {
  231. if ( dberror ) {
  232. console.log( '- Error while removing the RcGcDw: ' + dberror );
  233. msg.replyMsg( 'I got an error while removing the RcGcDw!', {}, true );
  234. return dberror;
  235. }
  236. console.log( '- Verifications successfully removed.' );
  237. } );
  238. }
  239. if ( channels.length ) db.run( 'DELETE FROM discord WHERE channel IN (' + channels.map( channel => '?' ).join(', ') + ')', channels, function (dberror) {
  240. if ( dberror ) {
  241. console.log( '- Error while removing the channels: ' + dberror );
  242. msg.replyMsg( 'I got an error while removing the channels!', {}, true );
  243. return dberror;
  244. }
  245. console.log( '- Channels successfully removed.' );
  246. } );
  247. if ( !guilds.length && !channels.length ) console.log( '- Settings successfully removed.' );
  248. } );
  249. } );
  250. }
  251. catch ( tryerror ) {
  252. console.log( '- Error while removing the settings: ' + tryerror );
  253. return 'removeSettings(msg) – Error while removing the settings: ' + tryerror;
  254. }
  255. }
  256. module.exports = {
  257. name: 'eval',
  258. everyone: false,
  259. pause: false,
  260. owner: true,
  261. run: cmd_eval
  262. };