1
0

util.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. const got = require('got').extend( {
  2. throwHttpErrors: false,
  3. timeout: 5000,
  4. headers: {
  5. 'User-Agent': 'Wiki-Bot/dashboard (Discord; ' + process.env.npm_package_name + ')'
  6. },
  7. responseType: 'json'
  8. } );
  9. const sqlite3 = require('sqlite3').verbose();
  10. const mode = ( process.env.READONLY ? sqlite3.OPEN_READONLY : sqlite3.OPEN_READWRITE );
  11. const db = new sqlite3.Database( './wikibot.db', mode, dberror => {
  12. if ( dberror ) {
  13. console.log( '- Dashboard: Error while connecting to the database: ' + dberror );
  14. return dberror;
  15. }
  16. db.exec( 'PRAGMA foreign_keys = ON;', function (error) {
  17. if ( error ) {
  18. console.log( '- Dashboard: Error while enabling the foreign key constraint: ' + error );
  19. }
  20. console.log( '- Dashboard: Connected to the database.' );
  21. } );
  22. } );
  23. /**
  24. * @typedef Settings
  25. * @property {String} state
  26. * @property {String} access_token
  27. * @property {User} user
  28. * @property {Object} guilds
  29. * @property {Number} guilds.count
  30. * @property {Map<String, Guild>} guilds.isMember
  31. * @property {Map<String, Guild>} guilds.notMember
  32. */
  33. /**
  34. * @typedef User
  35. * @property {String} id
  36. * @property {String} username
  37. * @property {String} discriminator
  38. * @property {String} avatar
  39. * @property {String} locale
  40. */
  41. /**
  42. * @typedef Guild
  43. * @property {String} id
  44. * @property {String} name
  45. * @property {String} acronym
  46. * @property {String} [icon]
  47. * @property {String} userPermissions
  48. * @property {Boolean} [patreon]
  49. * @property {String} [botPermissions]
  50. * @property {Channel[]} [channels]
  51. * @property {Role[]} [roles]
  52. * @property {String} [locale]
  53. */
  54. /**
  55. * @typedef Channel
  56. * @property {String} id
  57. * @property {String} name
  58. * @property {Boolean} isCategory
  59. * @property {Number} userPermissions
  60. * @property {Number} botPermissions
  61. */
  62. /**
  63. * @typedef Role
  64. * @property {String} id
  65. * @property {String} name
  66. * @property {Boolean} lower
  67. */
  68. /**
  69. * @type {Map<String, Settings>}
  70. */
  71. const settingsData = new Map();
  72. /**
  73. * @type {Map<Number, PromiseConstructor>}
  74. */
  75. const messages = new Map();
  76. var messageId = 1;
  77. process.on( 'message', message => {
  78. if ( message.id ) {
  79. if ( message.data.error ) messages.get(message.id).reject(message.data.error);
  80. else messages.get(message.id).resolve(message.data.response);
  81. return messages.delete(message.id);
  82. }
  83. console.log( '- [Dashboard]: Message received!', message );
  84. } );
  85. /**
  86. * Send messages to the manager.
  87. * @param {Object} [message] - The message.
  88. * @returns {Promise<Object>}
  89. */
  90. function sendMsg(message) {
  91. var id = messageId++;
  92. var promise = new Promise( (resolve, reject) => {
  93. messages.set(id, {resolve, reject});
  94. process.send( {id, data: message} );
  95. } );
  96. return promise;
  97. }
  98. /**
  99. * Create a red notice
  100. * @param {import('cheerio')} $ - The cheerio static
  101. * @param {String} notice - The notice to create
  102. * @param {import('./i18n.js')} dashboardLang - The user language
  103. * @param {String[]} [args] - The arguments for the notice
  104. * @returns {import('cheerio')}
  105. */
  106. function createNotice($, notice, dashboardLang, args = []) {
  107. if ( !notice ) return;
  108. var type = 'info';
  109. var title = $('<b>');
  110. var text = $('<div>');
  111. var note;
  112. switch (notice) {
  113. case 'unauthorized':
  114. type = 'info';
  115. title.text(dashboardLang.get('notice.unauthorized.title'));
  116. text.text(dashboardLang.get('notice.unauthorized.text'));
  117. break;
  118. case 'save':
  119. type = 'success';
  120. title.text(dashboardLang.get('notice.save.title'));
  121. text.text(dashboardLang.get('notice.save.text'));
  122. break;
  123. case 'nosettings':
  124. type = 'info';
  125. title.text(dashboardLang.get('notice.nosettings.title'));
  126. text.text(dashboardLang.get('notice.nosettings.text'));
  127. note = $('<a>').text(dashboardLang.get('notice.nosettings.note')).attr('href', `/guild/${args[0]}/settings`);
  128. break;
  129. case 'logout':
  130. type = 'success';
  131. title.text(dashboardLang.get('notice.logout.title'));
  132. text.text(dashboardLang.get('notice.logout.text'));
  133. break;
  134. case 'refresh':
  135. type = 'success';
  136. title.text(dashboardLang.get('notice.refresh.title'));
  137. text.text(dashboardLang.get('notice.refresh.text'));
  138. break;
  139. case 'missingperm':
  140. type = 'error';
  141. title.text(dashboardLang.get('notice.missingperm.title'));
  142. text.html(dashboardLang.get('notice.missingperm.text', true, $('<code>').text(args[0])));
  143. break;
  144. case 'loginfail':
  145. type = 'error';
  146. title.text(dashboardLang.get('notice.loginfail.title'));
  147. text.text(dashboardLang.get('notice.loginfail.text'));
  148. break;
  149. case 'sysmessage':
  150. type = 'info';
  151. title.text(dashboardLang.get('notice.sysmessage.title'));
  152. text.html(dashboardLang.get('notice.sysmessage.text', true, $('<a target="_blank">').append(
  153. $('<code>').text('MediaWiki:Custom-RcGcDw')
  154. ).attr('href', args[1]), $('<code class="user-select">').text(args[0])));
  155. note = $('<a target="_blank">').text(args[1]).attr('href', args[1]);
  156. break;
  157. case 'mwversion':
  158. type = 'error';
  159. title.text(dashboardLang.get('notice.mwversion.title'));
  160. text.text(dashboardLang.get('notice.mwversion.text', false, args[0], args[1]));
  161. note = $('<a target="_blank">').text('https://www.mediawiki.org/wiki/MediaWiki_1.30').attr('href', 'https://www.mediawiki.org/wiki/MediaWiki_1.30');
  162. break;
  163. case 'nochange':
  164. type = 'info';
  165. title.text(dashboardLang.get('notice.nochange.title'));
  166. text.text(dashboardLang.get('notice.nochange.text'));
  167. break;
  168. case 'invalidusergroup':
  169. type = 'error';
  170. title.text(dashboardLang.get('notice.invalidusergroup.title'));
  171. text.text(dashboardLang.get('notice.invalidusergroup.text'));
  172. break;
  173. case 'wikiblocked':
  174. type = 'error';
  175. title.text(dashboardLang.get('notice.wikiblocked.title'));
  176. text.text(dashboardLang.get('notice.wikiblocked.text', false, args[0]));
  177. if ( args[1] ) note = $('<div>').append(
  178. dashboardLang.get('notice.wikiblocked.note', true) + ' ',
  179. $('<code>').text(args[1])
  180. );
  181. break;
  182. case 'savefail':
  183. type = 'error';
  184. title.text(dashboardLang.get('notice.savefail.title'));
  185. text.text(dashboardLang.get('notice.savefail.text'));
  186. if ( typeof args[0] === 'string' ) {
  187. note = $('<div>').text(dashboardLang.get('notice.savefail.note_' + args[0]));
  188. }
  189. break;
  190. case 'movefail':
  191. type = 'info';
  192. title.text(dashboardLang.get('notice.movefail.title'));
  193. text.text(dashboardLang.get('notice.movefail.text'));
  194. note = $('<div>').text(dashboardLang.get('notice.movefail.note'));
  195. break;
  196. case 'refreshfail':
  197. type = 'error';
  198. title.text(dashboardLang.get('notice.refreshfail.title'));
  199. text.text(dashboardLang.get('notice.refreshfail.text'));
  200. break;
  201. case 'error':
  202. type = 'error';
  203. title.text(dashboardLang.get('notice.error.title'));
  204. text.text(dashboardLang.get('notice.error.text'));
  205. break;
  206. case 'readonly':
  207. type = 'info';
  208. title.text(dashboardLang.get('notice.readonly.title'));
  209. text.text(dashboardLang.get('notice.readonly.text'));
  210. break;
  211. default:
  212. return;
  213. }
  214. return $(`<div class="notice notice-${type}">`).append(
  215. title,
  216. text,
  217. note
  218. ).appendTo('#text #notices');
  219. }
  220. /**
  221. * HTML escape text
  222. * @param {String} text - The text to escape
  223. * @returns {String}
  224. */
  225. function escapeText(text) {
  226. return text.replace( /&/g, '&amp;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' );
  227. }
  228. const permissions = {
  229. ADMINISTRATOR: 1 << 3,
  230. MANAGE_CHANNELS: 1 << 4,
  231. MANAGE_GUILD: 1 << 5,
  232. ADD_REACTIONS: 1 << 6,
  233. VIEW_CHANNEL: 1 << 10,
  234. SEND_MESSAGES: 1 << 11,
  235. MANAGE_MESSAGES: 1 << 13,
  236. EMBED_LINKS: 1 << 14,
  237. ATTACH_FILES: 1 << 15,
  238. READ_MESSAGE_HISTORY: 1 << 16,
  239. MENTION_EVERYONE: 1 << 17,
  240. USE_EXTERNAL_EMOJIS: 1 << 18,
  241. MANAGE_NICKNAMES: 1 << 27,
  242. MANAGE_ROLES: 1 << 28,
  243. MANAGE_WEBHOOKS: 1 << 29
  244. }
  245. /**
  246. * Check if a permission is included in the BitField
  247. * @param {String|Number} all - BitField of multiple permissions
  248. * @param {String[]} permission - Name of the permission to check for
  249. * @returns {Boolean}
  250. */
  251. function hasPerm(all = 0, ...permission) {
  252. if ( (all & permissions.ADMINISTRATOR) === permissions.ADMINISTRATOR ) return true;
  253. return permission.map( perm => {
  254. let bit = permissions[perm];
  255. return ( (all & bit) === bit );
  256. } ).every( perm => perm );
  257. }
  258. module.exports = {got, db, settingsData, sendMsg, createNotice, escapeText, hasPerm};