|
@@ -1,177 +1,118 @@
|
|
|
const http = require('http');
|
|
|
-const crypto = require('crypto');
|
|
|
-const cheerio = require('cheerio');
|
|
|
-const {defaultPermissions} = require('../util/default.json');
|
|
|
+const {parse} = require('querystring');
|
|
|
+const pages = require('./oauth.js');
|
|
|
+const dashboard = require('./guilds.js');
|
|
|
+const {db, settingsData} = require('./util.js');
|
|
|
|
|
|
-const sqlite3 = require('sqlite3').verbose();
|
|
|
-const mode = ( process.env.READONLY ? sqlite3.OPEN_READONLY : sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE );
|
|
|
-const db = new sqlite3.Database( './wikibot.db', mode, dberror => {
|
|
|
- if ( dberror ) {
|
|
|
- console.log( '- Dashboard: Error while connecting to the database: ' + dberror );
|
|
|
- return dberror;
|
|
|
- }
|
|
|
- console.log( '- Dashboard: Connected to the database.' );
|
|
|
-} );
|
|
|
-
|
|
|
-const DiscordOauth2 = require('discord-oauth2');
|
|
|
-const oauth = new DiscordOauth2( {
|
|
|
- clientId: process.env.bot,
|
|
|
- clientSecret: process.env.secret,
|
|
|
- redirectUri: process.env.dashboard
|
|
|
-} );
|
|
|
+const posts = {
|
|
|
+ settings: require('./settings.js').post,
|
|
|
+ verification: require('./verification.js').post,
|
|
|
+ rcscript: require('./rcscript.js').post
|
|
|
+};
|
|
|
|
|
|
const fs = require('fs');
|
|
|
-const files = {
|
|
|
- index: fs.readFileSync('./dashboard/index.html'),
|
|
|
- login: fs.readFileSync('./dashboard/login.html')
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * @type {Map<Number, PromiseConstructor>}
|
|
|
- */
|
|
|
-var messages = new Map();
|
|
|
-var messageId = 1;
|
|
|
-
|
|
|
-process.on( 'message', message => {
|
|
|
- if ( message.id ) {
|
|
|
- if ( message.data.error ) messages.get(message.id).reject(message.data.error);
|
|
|
- else messages.get(message.id).resolve(message.data.response);
|
|
|
- return messages.delete(message.id);
|
|
|
+const path = require('path');
|
|
|
+const files = new Map(fs.readdirSync( './dashboard/src' ).map( file => {
|
|
|
+ let contentType = 'text/html';
|
|
|
+ switch ( path.extname(file) ) {
|
|
|
+ case '.css':
|
|
|
+ contentType = 'text/css';
|
|
|
+ break;
|
|
|
+ case '.js':
|
|
|
+ contentType = 'text/javascript';
|
|
|
+ break;
|
|
|
+ case '.json':
|
|
|
+ contentType = 'application/json';
|
|
|
+ break;
|
|
|
+ case '.svg':
|
|
|
+ contentType = 'image/svg+xml';
|
|
|
+ break;
|
|
|
+ case '.png':
|
|
|
+ contentType = 'image/png';
|
|
|
+ break;
|
|
|
+ case '.jpg':
|
|
|
+ contentType = 'image/jpg';
|
|
|
+ break;
|
|
|
}
|
|
|
- console.log( '- [Dashboard]: Message received!', message );
|
|
|
-} );
|
|
|
-
|
|
|
-/**
|
|
|
- * Send messages to the manager.
|
|
|
- * @param {Object} [message] - The message.
|
|
|
- * @returns {Promise<Object>}
|
|
|
- */
|
|
|
-function sendMsg(message) {
|
|
|
- var id = messageId++;
|
|
|
- var promise = new Promise( (resolve, reject) => {
|
|
|
- messages.set(id, {resolve, reject});
|
|
|
- process.send( {id, data: message} );
|
|
|
- } );
|
|
|
- return promise;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * @typedef Settings
|
|
|
- * @property {String} state
|
|
|
- * @property {String} access_token
|
|
|
- * @property {User} user
|
|
|
- * @property {Object} guilds
|
|
|
- * @property {Map<String, Guild>} guilds.isMember
|
|
|
- * @property {Map<String, Guild>} guilds.notMember
|
|
|
- */
|
|
|
-
|
|
|
-/**
|
|
|
- * @typedef User
|
|
|
- * @property {String} id
|
|
|
- * @property {String} username
|
|
|
- * @property {String} discriminator
|
|
|
- * @property {String} avatar
|
|
|
- * @property {String} locale
|
|
|
- */
|
|
|
-
|
|
|
-/**
|
|
|
- * @typedef Guild
|
|
|
- * @property {String} id
|
|
|
- * @property {String} name
|
|
|
- * @property {String} acronym
|
|
|
- * @property {String} [icon]
|
|
|
- * @property {String} permissions
|
|
|
- */
|
|
|
-
|
|
|
-/**
|
|
|
- * @type {Map<String, Settings>}
|
|
|
- */
|
|
|
-var settingsData = new Map();
|
|
|
+ return [`/src/${file}`, {
|
|
|
+ name: file, contentType,
|
|
|
+ path: `./dashboard/src/${file}`
|
|
|
+ }];
|
|
|
+} ));
|
|
|
|
|
|
const server = http.createServer((req, res) => {
|
|
|
+ if ( req.method === 'POST' && req.url.startsWith( '/guild/' ) ) {
|
|
|
+ let args = req.url.split('/');
|
|
|
+ let state = req.headers.cookie?.split('; ')?.filter( cookie => {
|
|
|
+ return cookie.split('=')[0] === 'wikibot';
|
|
|
+ } )?.map( cookie => cookie.replace( /^wikibot="(\w*(?:-\d+)?)"$/, '$1' ) )?.join();
|
|
|
+
|
|
|
+ if ( args.length <= 4 && ['settings', 'verification', 'rcscript'].incluses( args[3] )
|
|
|
+ && settingsData.has(state) && settingsData.get(state).guilds.isMember.has(args[2]) ) {
|
|
|
+ let body = '';
|
|
|
+ req.on( 'data', chunk => {
|
|
|
+ body += chunk.toString();
|
|
|
+ } );
|
|
|
+ req.on( 'error', () => {
|
|
|
+ console.log( error );
|
|
|
+ res.end('error');
|
|
|
+ } );
|
|
|
+ return req.on( 'end', () => {
|
|
|
+ console.log( parse(body) );
|
|
|
+ //return posts[args[3]](res, settingsData.get(state).user.id, args[2], parse(body));
|
|
|
+ res.writeHead(302, {Location: req.url});
|
|
|
+ res.end();
|
|
|
+ } );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if ( req.method !== 'GET' ) {
|
|
|
let body = '<img width="400" src="https://http.cat/418"><br><strong>' + http.STATUS_CODES[418] + '</strong>';
|
|
|
- res.writeHead(418, {'Content-Length': body.length});
|
|
|
+ res.writeHead(418, {
|
|
|
+ 'Content-Type': 'text/html',
|
|
|
+ 'Content-Length': body.length
|
|
|
+ });
|
|
|
res.write( body );
|
|
|
return res.end();
|
|
|
}
|
|
|
|
|
|
- if ( req.url === '/favicon.ico' ) {
|
|
|
+ var reqURL = new URL(req.url, process.env.dashboard);
|
|
|
+
|
|
|
+ if ( reqURL.pathname === '/favicon.ico' ) {
|
|
|
res.writeHead(302, {Location: 'https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64'});
|
|
|
return res.end();
|
|
|
}
|
|
|
|
|
|
+ if ( files.has(reqURL.pathname) ) {
|
|
|
+ let file = files.get(reqURL.pathname);
|
|
|
+ res.writeHead(200, {'Content-Type': file.contentType});
|
|
|
+ return fs.createReadStream(file.path).pipe(res);
|
|
|
+ }
|
|
|
+
|
|
|
res.setHeader('Content-Type', 'text/html');
|
|
|
res.setHeader('Content-Language', ['en']);
|
|
|
|
|
|
var lastGuild = req.headers?.cookie?.split('; ')?.filter( cookie => {
|
|
|
return cookie.split('=')[0] === 'guild';
|
|
|
- } )?.map( cookie => cookie.replace( /^guild="(\w+)"$/, '$1' ) )?.join();
|
|
|
- if ( lastGuild ) res.setHeader('Set-Cookie', [`guild="${lastGuild}"; Max-Age=0; HttpOnly; Path=/`]);
|
|
|
+ } )?.map( cookie => cookie.replace( /^guild="(\w*)"$/, '$1' ) )?.join();
|
|
|
+ if ( lastGuild ) res.setHeader('Set-Cookie', ['guild=""; HttpOnly; Path=/; Max-Age=0']);
|
|
|
|
|
|
var state = req.headers.cookie?.split('; ')?.filter( cookie => {
|
|
|
return cookie.split('=')[0] === 'wikibot';
|
|
|
- } )?.map( cookie => cookie.replace( /^wikibot="(\w+(?:-\d+)?)"$/, '$1' ) )?.join();
|
|
|
-
|
|
|
- var reqURL = new URL(req.url, process.env.dashboard);
|
|
|
+ } )?.map( cookie => cookie.replace( /^wikibot="(\w*(?:-\d+)?)"$/, '$1' ) )?.join();
|
|
|
|
|
|
if ( reqURL.pathname === '/login' ) {
|
|
|
- if ( settingsData.has(state) ) {
|
|
|
- res.writeHead(302, {Location: '/'});
|
|
|
- return res.end();
|
|
|
- }
|
|
|
- if ( state ) res.setHeader('Set-Cookie', [`wikibot="${state}"; Max-Age=0; HttpOnly`]);
|
|
|
- var $ = cheerio.load(files.login);
|
|
|
- $('.guild#invite a').attr('href', oauth.generateAuthUrl( {
|
|
|
- scope: ['identify', 'guilds', 'bot'],
|
|
|
- permissions: defaultPermissions, state
|
|
|
- } ));
|
|
|
- let responseCode = 200;
|
|
|
- let notice = '';
|
|
|
- if ( reqURL.searchParams.get('action') === 'failed' ) {
|
|
|
- responseCode = 400;
|
|
|
- notice = createNotice($, {
|
|
|
- title: 'Login failed!',
|
|
|
- text: 'An error occurred while logging you in, please try again.'
|
|
|
- });
|
|
|
- }
|
|
|
- if ( reqURL.searchParams.get('action') === 'unauthorized' ) {
|
|
|
- responseCode = 401;
|
|
|
- notice = createNotice($, {
|
|
|
- title: 'Not logged in!',
|
|
|
- text: 'Please login before you can change any settings.'
|
|
|
- });
|
|
|
- }
|
|
|
- if ( reqURL.searchParams.get('action') === 'logout' ) {
|
|
|
- notice = createNotice($, {
|
|
|
- title: 'Successfully logged out!',
|
|
|
- text: 'You have been successfully logged out. To change any settings you need to login again.'
|
|
|
- });
|
|
|
- }
|
|
|
- $('replace#notice').replaceWith(notice);
|
|
|
- state = crypto.randomBytes(16).toString("hex");
|
|
|
- while ( settingsData.has(state) ) {
|
|
|
- state = crypto.randomBytes(16).toString("hex");
|
|
|
- }
|
|
|
- let url = oauth.generateAuthUrl( {
|
|
|
- scope: ['identify', 'guilds'],
|
|
|
- prompt: 'none', state
|
|
|
- } );
|
|
|
- $('replace#text').replaceWith(`<a href="${url}">Login</a>`);
|
|
|
- let body = $.html();
|
|
|
- res.writeHead(responseCode, {
|
|
|
- 'Set-Cookie': [`wikibot="${state}"; HttpOnly`],
|
|
|
- 'Content-Length': body.length
|
|
|
- });
|
|
|
- res.write( body );
|
|
|
- return res.end();
|
|
|
+ return pages.login(res, state, reqURL.searchParams.get('action'));
|
|
|
}
|
|
|
|
|
|
if ( reqURL.pathname === '/logout' ) {
|
|
|
settingsData.delete(state);
|
|
|
res.writeHead(302, {
|
|
|
Location: '/login?action=logout',
|
|
|
- 'Set-Cookie': [`wikibot="${state}"; Max-Age=0; HttpOnly`]
|
|
|
+ 'Set-Cookie': [
|
|
|
+ ...( res.getHeader('Set-Cookie') || [] ),
|
|
|
+ 'wikibot=""; HttpOnly; Path=/; Max-Age=0'
|
|
|
+ ]
|
|
|
});
|
|
|
return res.end();
|
|
|
}
|
|
@@ -184,83 +125,7 @@ const server = http.createServer((req, res) => {
|
|
|
}
|
|
|
|
|
|
if ( reqURL.pathname === '/oauth' ) {
|
|
|
- if ( settingsData.has(state) ) {
|
|
|
- res.writeHead(302, {Location: '/'});
|
|
|
- return res.end();
|
|
|
- }
|
|
|
- if ( state !== reqURL.searchParams.get('state') || !reqURL.searchParams.get('code') ) {
|
|
|
- res.writeHead(302, {Location: '/login?action=unauthorized'});
|
|
|
- return res.end();
|
|
|
- }
|
|
|
- return oauth.tokenRequest( {
|
|
|
- scope: ['identify', 'guilds'],
|
|
|
- code: reqURL.searchParams.get('code'),
|
|
|
- grantType: 'authorization_code'
|
|
|
- } ).then( ({access_token}) => {
|
|
|
- return Promise.all([
|
|
|
- oauth.getUser(access_token),
|
|
|
- oauth.getUserGuilds(access_token)
|
|
|
- ]).then( ([user, guilds]) => {
|
|
|
- guilds = guilds.filter( guild => {
|
|
|
- return ( guild.owner || hasPerm(guild.permissions, 'MANAGE_GUILD') );
|
|
|
- } ).map( guild => {
|
|
|
- return {
|
|
|
- id: guild.id,
|
|
|
- name: guild.name,
|
|
|
- acronym: guild.name.replace( /'s /g, ' ' ).replace( /\w+/g, e => e[0] ).replace( /\s/g, '' ),
|
|
|
- icon: ( guild.icon ? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.`
|
|
|
- + ( guild.icon.startsWith( 'a_' ) ? 'gif' : 'png' ) + '?size=128' : null ),
|
|
|
- permissions: guild.permissions
|
|
|
- };
|
|
|
- } );
|
|
|
- sendMsg( {
|
|
|
- type: 'isMemberAll',
|
|
|
- guilds: guilds.map( guild => guild.id )
|
|
|
- } ).then( response => {
|
|
|
- let isMember = new Map();
|
|
|
- let notMember = new Map();
|
|
|
- response.forEach( (guild, i) => {
|
|
|
- if ( guild ) isMember.set(guilds[i].id, guilds[i]);
|
|
|
- else notMember.set(guilds[i].id, guilds[i]);
|
|
|
- } );
|
|
|
- settingsData.set(`${state}-${user.id}`, {
|
|
|
- state: `${state}-${user.id}`,
|
|
|
- access_token,
|
|
|
- user: {
|
|
|
- id: user.id,
|
|
|
- username: user.username,
|
|
|
- discriminator: user.discriminator,
|
|
|
- avatar: 'https://cdn.discordapp.com/' + ( user.avatar ?
|
|
|
- `avatars/${user.id}/${user.avatar}.` +
|
|
|
- ( user.avatar.startsWith( 'a_' ) ? 'gif' : 'png' ) :
|
|
|
- `embed/avatars/${user.discriminator % 5}.png` ) + '?size=64',
|
|
|
- locale: user.locale
|
|
|
- },
|
|
|
- guilds: {isMember, notMember}
|
|
|
- });
|
|
|
- res.writeHead(302, {
|
|
|
- Location: ( lastGuild ? '/guild/' + lastGuild : '/' ),
|
|
|
- 'Set-Cookie': [
|
|
|
- `wikibot="${state}"; Max-Age=0; HttpOnly`,
|
|
|
- `wikibot="${state}-${user.id}"; HttpOnly`
|
|
|
- ]
|
|
|
- });
|
|
|
- return res.end();
|
|
|
- }, error => {
|
|
|
- console.log( '- Dashboard: Error while checking the guilds:', error );
|
|
|
- res.writeHead(302, {Location: '/login?action=failed'});
|
|
|
- return res.end();
|
|
|
- } );
|
|
|
- }, error => {
|
|
|
- console.log( '- Dashboard: Error while getting user and guilds: ' + error );
|
|
|
- res.writeHead(302, {Location: '/login?action=failed'});
|
|
|
- return res.end();
|
|
|
- } );
|
|
|
- }, error => {
|
|
|
- console.log( '- Dashboard: Error while getting the token: ' + error );
|
|
|
- res.writeHead(302, {Location: '/login?action=failed'});
|
|
|
- return res.end();
|
|
|
- } );
|
|
|
+ return pages.oauth(res, state, reqURL.searchParams, lastGuild);
|
|
|
}
|
|
|
|
|
|
if ( !settingsData.has(state) ) {
|
|
@@ -269,126 +134,16 @@ const server = http.createServer((req, res) => {
|
|
|
});
|
|
|
return res.end();
|
|
|
}
|
|
|
- var settings = settingsData.get(state);
|
|
|
|
|
|
if ( reqURL.pathname === '/refresh' ) {
|
|
|
- return oauth.getUserGuilds(settings.access_token).then( guilds => {
|
|
|
- guilds = guilds.filter( guild => {
|
|
|
- return ( guild.owner || hasPerm(guild.permissions, 'MANAGE_GUILD') );
|
|
|
- } ).map( guild => {
|
|
|
- return {
|
|
|
- id: guild.id,
|
|
|
- name: guild.name,
|
|
|
- acronym: guild.name.replace( /'s /g, ' ' ).replace( /\w+/g, e => e[0] ).replace( /\s/g, '' ),
|
|
|
- icon: ( guild.icon ? `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.`
|
|
|
- + ( guild.icon.startsWith( 'a_' ) ? 'gif' : 'png' ) + '?size=128' : null ),
|
|
|
- permissions: guild.permissions
|
|
|
- };
|
|
|
- } );
|
|
|
- sendMsg( {
|
|
|
- type: 'isMemberAll',
|
|
|
- guilds: guilds.map( guild => guild.id )
|
|
|
- } ).then( response => {
|
|
|
- let isMember = new Map();
|
|
|
- let notMember = new Map();
|
|
|
- response.forEach( (guild, i) => {
|
|
|
- if ( guild ) isMember.set(guilds[i].id, guilds[i]);
|
|
|
- else notMember.set(guilds[i].id, guilds[i]);
|
|
|
- } );
|
|
|
- settings.guilds = {isMember, notMember};
|
|
|
- res.writeHead(302, {
|
|
|
- Location: ( reqURL.searchParams.get('return') || '/' )
|
|
|
- });
|
|
|
- return res.end();
|
|
|
- }, error => {
|
|
|
- console.log( '- Dashboard: Error while checking refreshed guilds:', error );
|
|
|
- res.writeHead(302, {Location: '/login?action=failed'});
|
|
|
- return res.end();
|
|
|
- } );
|
|
|
- }, error => {
|
|
|
- console.log( '- Dashboard: Error while refreshing guilds: ' + error );
|
|
|
- res.writeHead(302, {Location: '/login?action=failed'});
|
|
|
- return res.end();
|
|
|
- } );
|
|
|
+ return pages.refresh(res, state, reqURL.searchParams.get('return'));
|
|
|
}
|
|
|
|
|
|
- var $ = cheerio.load(files.index);
|
|
|
- let notice = '';
|
|
|
- if ( process.env.READONLY ) {
|
|
|
- notice = createNotice($, {
|
|
|
- title: 'Read-only database!',
|
|
|
- text: 'You can currently only view your settings but not change them.'
|
|
|
- });
|
|
|
- }
|
|
|
- $('replace#notice').replaceWith(notice);
|
|
|
- $('.navbar #logout img').attr('src', settings.user.avatar);
|
|
|
- $('.navbar #logout span').text(`${settings.user.username} #${settings.user.discriminator}`);
|
|
|
- $('.guild#invite a').attr('href', oauth.generateAuthUrl( {
|
|
|
- scope: ['identify', 'guilds', 'bot'],
|
|
|
- permissions: defaultPermissions, state
|
|
|
- } ));
|
|
|
- $('.guild#refresh a').attr('href', '/refresh?return=' + reqURL.pathname);
|
|
|
- let guilds = $('<div>');
|
|
|
- if ( settings.guilds.isMember.size ) {
|
|
|
- $('<div class="guild">').append(
|
|
|
- $('<div class="separator">')
|
|
|
- ).appendTo(guilds);
|
|
|
- settings.guilds.isMember.forEach( guild => {
|
|
|
- $('<div class="guild">').attr('id', guild.id).append(
|
|
|
- $('<div class="bar">'),
|
|
|
- $('<a>').attr('href', `/guild/${guild.id}`).attr('alt', guild.name).append(
|
|
|
- ( guild.icon ?
|
|
|
- $('<img class="avatar" width="48" height="48">').attr('src', guild.icon).attr('alt', guild.name)
|
|
|
- : $('<div class="avatar noicon">').text(guild.acronym) )
|
|
|
- )
|
|
|
- ).appendTo(guilds);
|
|
|
- } );
|
|
|
+ if ( reqURL.pathname === '/' || reqURL.pathname.startsWith( '/guild/' ) ) {
|
|
|
+ return dashboard(res, state, reqURL);
|
|
|
}
|
|
|
- if ( settings.guilds.notMember.size ) {
|
|
|
- $('<div class="guild">').append(
|
|
|
- $('<div class="separator">')
|
|
|
- ).appendTo(guilds);
|
|
|
- settings.guilds.notMember.forEach( guild => {
|
|
|
- $('<div class="guild">').attr('id', guild.id).append(
|
|
|
- $('<div class="bar">'),
|
|
|
- $('<a>').attr('href', `/guild/${guild.id}`).attr('alt', guild.name).append(
|
|
|
- ( guild.icon ?
|
|
|
- $('<img class="avatar" width="48" height="48">').attr('src', guild.icon).attr('alt', guild.name)
|
|
|
- : $('<div class="avatar noicon">').text(guild.acronym) )
|
|
|
- )
|
|
|
- ).appendTo(guilds);
|
|
|
- } );
|
|
|
- }
|
|
|
- $('replace#guilds').replaceWith(guilds.children());
|
|
|
|
|
|
- if ( reqURL.pathname.startsWith( '/guild/' ) ) {
|
|
|
- let id = reqURL.pathname.replace( '/guild/', '' );
|
|
|
- if ( settings.guilds.isMember.has(id) ) {
|
|
|
- $(`.guild#${id}`).addClass('selected');
|
|
|
- let guild = settings.guilds.isMember.get(id);
|
|
|
- $('head title').text(`${guild.name} – ` + $('head title').text());
|
|
|
- res.setHeader('Set-Cookie', [`guild="${id}"; HttpOnly; Path=/`]);
|
|
|
- $('replace#text').replaceWith(`${guild.permissions}`);
|
|
|
- }
|
|
|
- if ( settings.guilds.notMember.has(id) ) {
|
|
|
- $(`.guild#${id}`).addClass('selected');
|
|
|
- let guild = settings.guilds.notMember.get(id);
|
|
|
- $('head title').text(`${guild.name} – ` + $('head title').text());
|
|
|
- res.setHeader('Set-Cookie', [`guild="${id}"; HttpOnly; Path=/`]);
|
|
|
- let url = oauth.generateAuthUrl( {
|
|
|
- scope: ['identify', 'guilds', 'bot'],
|
|
|
- permissions: defaultPermissions,
|
|
|
- guild_id: id, state
|
|
|
- } );
|
|
|
- $('replace#text').replaceWith($('<a>').attr('href', url).text(guild.permissions));
|
|
|
- }
|
|
|
- $('replace#text').replaceWith('You are missing the <code>MANAGE_GUILD</code> permission.');
|
|
|
- }
|
|
|
-
|
|
|
- $('replace#text').replaceWith('Keks');
|
|
|
- let body = $.html();
|
|
|
- res.writeHead(200, {'Content-Length': body.length});
|
|
|
- res.write( body );
|
|
|
+ res.writeHead(302, {Location: '/'});
|
|
|
return res.end();
|
|
|
});
|
|
|
|
|
@@ -396,46 +151,6 @@ server.listen(8080, 'localhost', () => {
|
|
|
console.log( '- Dashboard: Server running at http://localhost:8080/' );
|
|
|
});
|
|
|
|
|
|
-/**
|
|
|
- * Create a red notice
|
|
|
- * @param {CheerioStatic} $ - The cheerio static
|
|
|
- * @param {{title: String, text: String}[]} notices - The notices to create
|
|
|
- * @returns {Cheerio}
|
|
|
- */
|
|
|
-function createNotice($, ...notices) {
|
|
|
- return notices.map( notice => {
|
|
|
- return $('<div class="notice">').append(
|
|
|
- $('<b>').text(notice.title),
|
|
|
- $('<div>').text(notice.text)
|
|
|
- );
|
|
|
- } );
|
|
|
-}
|
|
|
-
|
|
|
-const permissions = {
|
|
|
- ADMINISTRATOR: 1 << 3,
|
|
|
- MANAGE_CHANNELS: 1 << 4,
|
|
|
- MANAGE_GUILD: 1 << 5,
|
|
|
- MANAGE_MESSAGES: 1 << 13,
|
|
|
- MENTION_EVERYONE: 1 << 17,
|
|
|
- MANAGE_NICKNAMES: 1 << 27,
|
|
|
- MANAGE_ROLES: 1 << 28,
|
|
|
- MANAGE_WEBHOOKS: 1 << 29,
|
|
|
- MANAGE_EMOJIS: 1 << 30
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * Check if a permission is included in the BitField
|
|
|
- * @param {String|Number} all - BitField of multiple permissions
|
|
|
- * @param {String} permission - Name of the permission to check for
|
|
|
- * @param {Boolean} [admin] - If administrator permission can overwrite
|
|
|
- * @returns {Boolean}
|
|
|
- */
|
|
|
-function hasPerm(all, permission, admin = true) {
|
|
|
- var bit = permissions[permission];
|
|
|
- var adminOverwrite = ( admin && (all & permissions.ADMINISTRATOR) === permissions.ADMINISTRATOR );
|
|
|
- return ( adminOverwrite || (all & bit) === bit )
|
|
|
-}
|
|
|
-
|
|
|
|
|
|
/**
|
|
|
* End the process gracefully.
|