├── .d.ts ├── .gitattributes ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .prettierrc.js ├── 404pagedefault.js ├── ExternalIntegration └── index.js ├── ExternalStatistics └── index.js ├── HandlerIntegration ├── index.js └── structures │ ├── Category.js │ └── Option.js ├── InitFunctions ├── initExampleDash.js ├── initPpCheck.js └── initServer.js ├── LICENSE ├── LicenseSystem └── index.js ├── ModuleExportsFunctions ├── customPagesTypes.js ├── discordPermissions.js └── formTypes.js ├── README.MD ├── Routes ├── dashboard.js ├── discord.js └── main.js ├── SECURITY.md ├── __ModuleExample └── index.js ├── index.js ├── licensedDashboardClass.js ├── package-lock.json ├── package.json ├── router.js ├── underMaintenancePageDefault.js └── yarn.lock /.d.ts: -------------------------------------------------------------------------------- 1 | declare module "discord-dashboard" { 2 | const licenseInfo: () => string | boolean 3 | const useLicense: (licenseId: string) => Promise 4 | const UpdatedClass: () => Dashboard 5 | 6 | let Dashboard: any 7 | 8 | const initDashboard: (options: { 9 | fileName: string 10 | domain: string 11 | port: number 12 | token: string 13 | clientSecret: string 14 | clientId: string 15 | }) => any 16 | 17 | const formTypes: formTypes 18 | const customPagesTypes: customPagesTypes 19 | const DISCORD_FLAGS: { 20 | Permissions: Permissions 21 | } 22 | 23 | const version: string 24 | } 25 | 26 | interface Permissions { 27 | CREATE_INSTANT_INVITE: ["CREATE_INSTANT_INVITE", 0x1] 28 | KICK_MEMBERS: ["KICK_MEMBERS", 0x2] 29 | BAN_MEMBERS: ["BAN_MEMBERS", 0x4] 30 | ADMINISTRATOR: ["ADMINISTRATOR", 0x8] 31 | MANAGE_CHANNELS: ["MANAGE_CHANNELS", 0x10] 32 | MANAGE_GUILD: ["MANAGE_GUILD", 0x20] 33 | ADD_REACTIONS: ["ADD_REACTIONS", 0x40] 34 | VIEW_AUDIT_LOG: ["VIEW_AUDIT_LOG", 0x80] 35 | PRIORITY_SPEAKER: ["PRIORITY_SPEAKER", 0x100] 36 | STREAM: ["STREAM", 0x200] 37 | VIEW_CHANNEL: ["VIEW_CHANNEL", 0x400] 38 | SEND_MESSAGES: ["SEND_MESSAGES", 0x800] 39 | SEND_TTS_MESSAGES: ["SEND_TTS_MESSAGES", 0x1000] 40 | MANAGE_MESSAGES: ["MANAGE_MESSAGES", 0x2000] 41 | EMBED_LINKS: ["EMBED_LINKS", 0x4000] 42 | ATTACH_FILES: ["ATTACH_FILES", 0x8000] 43 | READ_MESSAGE_HISTORY: ["READ_MESSAGE_HISTORY", 0x10000] 44 | MENTION_EVERYONE: ["MENTION_EVERYONE", 0x20000] 45 | USE_EXTERNAL_EMOJIS: ["USE_EXTERNAL_EMOJIS", 0x40000] 46 | VIEW_GUILD_INSIGHTS: ["VIEW_GUILD_INSIGHTS", 0x80000] 47 | CONNECT: ["CONNECT", 0x100000] 48 | SPEAK: ["SPEAK", 0x200000] 49 | MUTE_MEMBERS: ["MUTE_MEMBERS", 0x400000] 50 | DEAFEN_MEMBERS: ["DEAFEN_MEMBERS", 0x800000] 51 | MOVE_MEMBERS: ["MOVE_MEMBERS", 0x1000000] 52 | USE_VAD: ["USE_VAD", 0x2000000] 53 | CHANGE_NICKNAME: ["CHANGE_NICKNAME", 0x4000000] 54 | MANAGE_NICKNAMES: ["MANAGE_NICKNAMES", 0x8000000] 55 | MANAGE_ROLES: ["MANAGE_ROLES", 0x10000000] 56 | MANAGE_WEBHOOKS: ["MANAGE_WEBHOOKS", 0x20000000] 57 | MANAGE_EMOJIS_AND_STICKERS: ["MANAGE_EMOJIS_AND_STICKERS", 0x40000000] 58 | USE_APPLICATION_COMMANDS: ["USE_APPLICATION_COMMANDS", 0x80000000] 59 | REQUEST_TO_SPEAK: ["REQUEST_TO_SPEAK", 0x100000000] 60 | MANAGE_EVENTS: ["MANAGE_EVENTS", 0x200000000] 61 | MANAGE_THREADS: ["MANAGE_THREADS", 0x400000000] 62 | CREATE_PUBLIC_THREADS: ["CREATE_PUBLIC_THREADS", 0x800000000] 63 | CREATE_PRIVATE_THREADS: ["CREATE_PRIVATE_THREADS", 0x1000000000] 64 | USE_EXTERNAL_STICKERS: ["USE_EXTERNAL_STICKERS", 0x2000000000] 65 | SEND_MESSAGES_IN_THREADS: ["SEND_MESSAGES_IN_THREADS", 0x4000000000] 66 | START_EMBEDDED_ACTIVITIES: ["START_EMBEDDED_ACTIVITIES", 0x8000000000] 67 | MODERATE_MEMBERS: ["MODERATE_MEMBERS", 0x10000000000] 68 | } 69 | 70 | interface RateLimitSettingsObject { 71 | windowMs: Number 72 | max: Number 73 | message: String 74 | store?: any 75 | } 76 | 77 | interface Dashboard { 78 | new (config: { 79 | port: number 80 | client: { 81 | id: string 82 | secret: string 83 | } 84 | redirectUri: string 85 | domain: string 86 | bot: any 87 | theme: any 88 | settings: category[] 89 | requiredPermissions?: object, 90 | ownerIDs: array, 91 | useTheme404: boolean, 92 | useThemeMaintenance: boolean, 93 | acceptPrivacyPolicy?: boolean 94 | noCreateServer?: boolean 95 | SSL?: { 96 | enabled: boolean 97 | key: string 98 | cert: string 99 | } 100 | minimizedConsoleLogs?: boolean 101 | rateLimits?: { 102 | manage?: RateLimitSettingsObject 103 | guildPage?: RateLimitSettingsObject 104 | settingsUpdatePostAPI?: RateLimitSettingsObject 105 | discordOAuth2?: RateLimitSettingsObject 106 | } 107 | invite?: { 108 | clientId: string 109 | scopes: object 110 | permissions: string 111 | redirectUri: string 112 | otherParams: string 113 | } 114 | supportServer?: { 115 | slash: string 116 | inviteUrl: string 117 | } 118 | guildAfterAuthorization?: { 119 | use: boolean 120 | guildId: string 121 | options?: { 122 | nickname?: string 123 | roles?: [string] 124 | mute?: boolean 125 | deaf?: boolean 126 | } 127 | } 128 | reportError?: (where: string, what: any) => any 129 | assistantsSecureStorageKey?: string 130 | }): any 131 | DBDEvents: () => any 132 | init: () => Promise 133 | getApp: () => any 134 | useThirdPartyModule: (module: any) => any 135 | } 136 | 137 | interface category { 138 | categoryId: string 139 | categoryName: string 140 | categoryDescription: string 141 | categoryOptionsList: option[] 142 | } 143 | 144 | interface option { 145 | optionId?: string 146 | optionName?: string 147 | optionDescription?: string 148 | title?: string 149 | description?: string 150 | optionType: 151 | | { 152 | type: string 153 | data?: string | null 154 | function?: any 155 | min?: number | null 156 | max?: number | null 157 | disabled?: boolean | null 158 | required?: boolean | null 159 | themeOptions?: object | null 160 | } 161 | | string 162 | getActualSet?: (options: optionOptions) => Promise 163 | setNew?: (options: optionOptions) => Promise 164 | allowedCheck?: (options: allowedCheckOption) => Promise 165 | themeOptions?: Object 166 | } 167 | 168 | interface optionOptions { 169 | guild: { id: string } 170 | user: { id: string } 171 | newData: any 172 | } 173 | 174 | interface allowedCheckOption { 175 | guild: { id: string } 176 | user: { id: string } 177 | } 178 | 179 | interface formTypes { 180 | select: ( 181 | list: object, 182 | disabled?: boolean, 183 | themeOptions?: object 184 | ) => { 185 | type: string 186 | data: { 187 | keys: object 188 | values: object 189 | } 190 | disabled: boolean 191 | themeOptions: object 192 | } 193 | 194 | multiSelect: ( 195 | list: object, 196 | disabled?: boolean, 197 | required?: boolean, 198 | themeOptions?: object 199 | ) => { 200 | type: string 201 | data: { 202 | keys: object 203 | values: object 204 | } 205 | disabled: boolean | null 206 | required: boolean | null 207 | themeOptions: object 208 | } 209 | 210 | input: ( 211 | placeholder?: string, 212 | min?: number, 213 | max?: number, 214 | disabled?: boolean, 215 | required?: boolean, 216 | themeOptions?: object 217 | ) => { 218 | type: string 219 | data: string | null 220 | min: number | null 221 | max: number | null 222 | disabled: boolean | null 223 | required: boolean | null 224 | themeOptions: object | null 225 | } 226 | 227 | textarea: ( 228 | placeholder?: string, 229 | min?: number, 230 | max?: number, 231 | disabled?: boolean, 232 | required?: boolean, 233 | themeOptions?: object 234 | ) => { 235 | type: string 236 | data: string | null 237 | min: number | null 238 | max: number | null 239 | disabled: boolean | null 240 | required: boolean | null 241 | themeOptions: object | null 242 | } 243 | 244 | switch: ( 245 | disabled?: boolean, 246 | themeOptions?: object 247 | ) => { 248 | type: string 249 | disabled: boolean 250 | themeOptions: object 251 | } 252 | 253 | checkbox: ( 254 | disabled?: boolean, 255 | themeOptions?: object 256 | ) => { 257 | type: string 258 | disabled: boolean 259 | themeOptions: object 260 | } 261 | 262 | channelsSelect: ( 263 | disabled?: boolean, 264 | channelTypes?: string[], 265 | hideNSFW?: boolean, 266 | onlyNSFW?: boolean, 267 | hideNoAccess?: boolean, 268 | themeOptions?: object 269 | ) => { 270 | type: string 271 | function: (client: string, guildid: string) => any 272 | disabled: boolean 273 | themeOptions: object 274 | } 275 | 276 | channelsMultiSelect: ( 277 | disabled?: boolean, 278 | required?: boolean, 279 | channelTypes?: string[], 280 | hideNSFW?: boolean, 281 | onlyNSFW?: boolean, 282 | hideNoAccess?: boolean, 283 | themeOptions?: object 284 | ) => { 285 | type: string 286 | function: (client: string, guildid: string) => any 287 | disabled: boolean 288 | required: boolean 289 | themeOptions: object 290 | } 291 | 292 | rolesSelect: ( 293 | includeBots: boolean, 294 | disabled?: boolean, 295 | hideHigherRoles?: boolean, 296 | themeOptions?: object 297 | ) => { 298 | type: string 299 | function: (client: string, guildid: string) => any 300 | disabled: boolean 301 | themeOptions: object 302 | } 303 | 304 | rolesMultiSelect: ( 305 | includeBots: boolean, 306 | disabled?: boolean, 307 | required?: boolean, 308 | hideHigherRoles?: boolean, 309 | themeOptions?: object 310 | ) => { 311 | type: string 312 | function: (client: string, guildid: string) => any 313 | disabled: boolean 314 | required: boolean 315 | themeOptions: object 316 | } 317 | 318 | colorSelect: ( 319 | defaultState: string, 320 | disabled?: boolean, 321 | themeOptions?: object 322 | ) => { 323 | type: string 324 | data: string 325 | disabled: boolean 326 | themeOptions: object 327 | } 328 | 329 | embedBuilder: ( 330 | defaultSettings: object, 331 | themeOptions?: object 332 | ) => { 333 | type: string 334 | data: object 335 | themeOptions: object 336 | } 337 | } 338 | 339 | interface EmbedBuilder { 340 | content?: string 341 | embed: { 342 | title?: string 343 | description: string 344 | color?: string | number 345 | timestamp?: any 346 | url?: string 347 | author?: { 348 | name?: string 349 | url?: string 350 | icon_url?: string 351 | } 352 | thumbnail?: { 353 | url?: string 354 | } 355 | image?: { 356 | url?: string 357 | } 358 | footer?: { 359 | text?: string 360 | icon_url?: string 361 | } 362 | fields?: EmbedBuilderField[] 363 | } 364 | } 365 | 366 | interface EmbedBuilderField { 367 | name?: string 368 | value?: string 369 | inline?: boolean 370 | } 371 | 372 | interface customPagesTypes { 373 | redirectToUrl: ( 374 | endpoint: string, 375 | getDataFunction: any 376 | ) => { 377 | type: string 378 | endpoint: string 379 | getEndpoint: any 380 | } 381 | 382 | renderHtml: ( 383 | endpoint: string, 384 | getDataFunction: any 385 | ) => { 386 | type: string 387 | endpoint: string 388 | getHtml: any 389 | } 390 | 391 | sendJson: ( 392 | endpoint: string, 393 | getDataFunction: any 394 | ) => { 395 | type: string 396 | endpoint: string 397 | getJson: any 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['https://paypal.me/breftejk', 'https://www.buymeacoffee.com/breftejk'] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ./node_modules/ 2 | node_modules/ 3 | test/ 4 | /test 5 | ./test/ 6 | ./test/ 7 | ./.idea 8 | discord-dashboard-pp 9 | ./discord-dashboard-pp 10 | .idea 11 | .devChannel 12 | ./.devChannel 13 | ./ExternalStatistics/index_unobfuscated.js 14 | ExternalStatistics/index_unobfuscated.js 15 | ./dev_project.json 16 | dev_project.json 17 | altometra-dashboard 18 | ./altometra-dashboard 19 | index_unobf.js 20 | ./index_unobf.js 21 | /altometra-dashoard 22 | altometra-dashboard/ 23 | ./altometra-dashboard/ 24 | /altometra-dashoard/ 25 | ./licensedDashboardClass_unobfuscated.js 26 | ./npmPublish.js 27 | licensedDashboardClass_unobfuscated.js 28 | npmPublish.js 29 | updateObfuscatedIndex.js 30 | ./updateObfuscatedIndex.js 31 | .idea 32 | .idea 33 | yarn-error.log -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: "es5", 3 | tabWidth: 4, 4 | semi: false, 5 | singleQuote: false, 6 | } 7 | -------------------------------------------------------------------------------- /404pagedefault.js: -------------------------------------------------------------------------------- 1 | module.exports = (websiteTitle) => ` 2 | 3 | 4 | 5 | 6 | 7 | ${websiteTitle} - 404 Not Found 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |

404

23 |

Oops! Error 404 Page Not Found

24 |

Sorry but the page you are looking for does not exist, have been removed. name changed or is temporarily unavailable

25 | Back to homepage 26 |
27 |
28 | 29 | 30 | 31 | 32 | ` 33 | -------------------------------------------------------------------------------- /ExternalIntegration/index.js: -------------------------------------------------------------------------------- 1 | class Client { 2 | constructor({url,token}) { 3 | this.url = url; 4 | try { 5 | //connect with socket using url specified 6 | }catch(err){ 7 | throw new TypeError('Discord-Dashboard External Integration failure: URL specified is wrong or token specified is wrong.'); 8 | } 9 | } 10 | 11 | socket: ()=>{ 12 | //socket with 'settingUpdated', 'settingRequested' 13 | } 14 | } 15 | 16 | class Server { 17 | constructor(config) { 18 | this.app = ()=>{}; 19 | this.server = ({io,server,config,themeConfig}) => { 20 | const ExternalSocket = io.of('/externalIntegration'); 21 | ExternalSocket.on('settingUpdated', (data) => { 22 | console.log(`${data.action} got updated: ${JSON.stringify(data.)}`); 23 | }); 24 | return ExternalSocket; 25 | }; 26 | this.server.on('settingRequested', (data)=>{ 27 | console.log(`${data.option} has been requested.`); 28 | }); 29 | }; 30 | 31 | UpdatedSettingEmit: (data)=>{ 32 | this.server.emit('settingUpdated', data); 33 | }; 34 | 35 | RequestDataEmit: (data)=>{ 36 | this.server.emit('settingsRequested', data); 37 | } 38 | }; 39 | 40 | module.exports = { 41 | Client, 42 | Server, 43 | }; 44 | -------------------------------------------------------------------------------- /ExternalStatistics/index.js: -------------------------------------------------------------------------------- 1 | const _0x1c811c=_0x109d;(function(_0x359c7e,_0x1eeab5){const _0x3573de=_0x109d,_0x418c34=_0x359c7e();while(!![]){try{const _0x23d247=parseInt(_0x3573de(0x1a3))/0x1*(-parseInt(_0x3573de(0x1b5))/0x2)+-parseInt(_0x3573de(0x1ac))/0x3+-parseInt(_0x3573de(0x1ab))/0x4+parseInt(_0x3573de(0x19e))/0x5+parseInt(_0x3573de(0x1b1))/0x6+parseInt(_0x3573de(0x1b3))/0x7+parseInt(_0x3573de(0x1aa))/0x8*(parseInt(_0x3573de(0x1b2))/0x9);if(_0x23d247===_0x1eeab5)break;else _0x418c34['push'](_0x418c34['shift']());}catch(_0x306517){_0x418c34['push'](_0x418c34['shift']());}}}(_0x3a2b,0xc3499));const fetch=require(_0x1c811c(0x1a6)),fs=require('fs'),DiscordDashboardPP=require(_0x1c811c(0x1af)),PPManager=new DiscordDashboardPP[(_0x1c811c(0x1a7))]({},{}),projectData=PPManager[_0x1c811c(0x1a1)]();function send(_0x5c317f,_0x2addc9){const _0x197388=_0x1c811c;try{fetch(_0x197388(0x1a2)+_0x5c317f,{'method':_0x197388(0x19f),'body':JSON[_0x197388(0x1a0)](_0x2addc9),'headers':{'Content-Type':'application/json'}})[_0x197388(0x1a8)](_0xf760df=>_0xf760df[_0x197388(0x1ad)]())[_0x197388(0x1a8)](_0x8029fe=>{})[_0x197388(0x1a4)](_0x53b4d3=>{});}catch(_0xaf8d52){}}function _0x109d(_0x584b3e,_0x1a74e2){const _0x3a2ba3=_0x3a2b();return _0x109d=function(_0x109dec,_0x14b473){_0x109dec=_0x109dec-0x19e;let _0x1dbfe7=_0x3a2ba3[_0x109dec];return _0x1dbfe7;},_0x109d(_0x584b3e,_0x1a74e2);}function _0x3a2b(){const _0x4a7b36=['PPManager','then','version','16qhVXeG','5948904oyFyVY','2301012dxNLDh','json','discord-dashboard','discord-dashboard-pp-system','exports','4292130gVGkAh','11997243GuMAAk','3220581tZFnbk','/registerProject','18tgqDOF','2422395VtzisY','POST','stringify','GetProjectData','https://dbd-external-stats.assistantscenter.com','141316gijWNs','catch','name','node-fetch'];_0x3a2b=function(){return _0x4a7b36;};return _0x3a2b();}module[_0x1c811c(0x1b0)]={'registerProject':(_0x57a975,_0x254002=projectData['id'],_0x37e8dd=projectData[_0x1c811c(0x1a5)],_0x263f96=require(_0x1c811c(0x1ae))[_0x1c811c(0x1a9)])=>{const _0x3aec38=_0x1c811c;send(_0x3aec38(0x1b4),{'cId':_0x57a975,'pId':_0x254002,'pN':_0x37e8dd,'v':_0x263f96});},'registerUser':(_0x4ae6ba,_0x4fbfb4=projectData['id'])=>{send('/registerUser',{'uId':_0x4ae6ba,'pId':_0x4fbfb4});},'pD':projectData}; -------------------------------------------------------------------------------- /HandlerIntegration/index.js: -------------------------------------------------------------------------------- 1 | const prefix = "[DBD-Storage-Handler]" 2 | const colors = require("colors") 3 | const Keyv = require("keyv") 4 | const { join } = require("path") 5 | 6 | const err = (text) => { 7 | return `🐧${text} Do you need help? Join our Discord server: ${"https://discord.gg/CzfMGtrdaA".blue 8 | }` 9 | } 10 | 11 | class Handler { 12 | constructor(keyvAdapter) { 13 | this.db = new Keyv( 14 | keyvAdapter || `sqlite://${join(__dirname, "/database.sqlite")}` 15 | ) 16 | 17 | this.db.on("error", (err) => 18 | console.error(`${prefix} ${`Keyv connection error: ${err}`.red}`) 19 | ) 20 | 21 | this.Category = require(`${__dirname}/structures/Category`)(this.db) 22 | this.Option = require(`${__dirname}/structures/Option`)(this.db) 23 | 24 | console.log(`${prefix} Database successfully initialized!`) 25 | } 26 | 27 | async fetch(guildId, optionId) { 28 | return await this.db.get(`${guildId}.options.${optionId}`) 29 | } 30 | 31 | db() { 32 | return this.db 33 | } 34 | } 35 | 36 | module.exports = Handler 37 | -------------------------------------------------------------------------------- /HandlerIntegration/structures/Category.js: -------------------------------------------------------------------------------- 1 | module.exports = (db) => { 2 | return class Category { 3 | constructor( 4 | options = { categoryId: "", categoryName: "", categoryDescription: "" } 5 | ) { 6 | this.categoryId = options.categoryId 7 | this.categoryName = options.categoryName 8 | this.categoryDescription = options.categoryDescription 9 | this.categoryOptionsList = [] 10 | 11 | // const db = Handler.getDB() 12 | this.db = db 13 | } 14 | 15 | /** 16 | * 17 | * @param {string} id - The id of the category, must be unique 18 | * @returns 19 | */ 20 | setId(id) { 21 | this.categoryId = id 22 | 23 | return this 24 | } 25 | 26 | /** 27 | * 28 | * @param {string} name - The name of the category displayed in the dashboard 29 | * @returns 30 | */ 31 | setName(name) { 32 | this.categoryName = name 33 | 34 | return this 35 | } 36 | 37 | /** 38 | * 39 | * @param {string} description - The description of the category displayed in the dashboard 40 | * @returns 41 | */ 42 | setDescription(description) { 43 | this.categoryDescription = description 44 | 45 | return this 46 | } 47 | 48 | /** 49 | * 50 | * @param {string} image - Set the image for a Soft UI category, must be a link 51 | * @returns 52 | */ 53 | setImage(image) { 54 | this.categoryImageURL = image 55 | 56 | return this 57 | } 58 | 59 | /** 60 | * 61 | * @param {boolean} toggleable - Allows Soft UI category to be toggleable 62 | * @returns 63 | */ 64 | setToggleable(toggleable) { 65 | this.toggleable = toggleable 66 | 67 | this.getActualSet = async ({ guild }) => { 68 | return await this.db.get( 69 | `${guild.id}.categories.${this.categoryId}.toggle` 70 | ) 71 | } 72 | 73 | this.setNew = async ({ guild, newData }) => { 74 | await this.db.set( 75 | `${guild.id}.categories.${this.categoryId}.toggle`, 76 | newData 77 | ) 78 | } 79 | 80 | return this 81 | } 82 | 83 | /** 84 | * @param {import('./Option')[]} options - The options of the category 85 | * @example 86 | * new Category() 87 | * .setId('setup') 88 | * .setName("Setup") 89 | * .setDescription("Setup your bot with default settings!") 90 | * .addOptions( 91 | * new Option() 92 | * .setId('lang') 93 | * .setName("Language") 94 | * .setDescription("Change bot's language easily") 95 | * .setType(dbd.formTypes.select({"Polish": 'pl', "English": 'en', "French": 'fr'})) 96 | * ) 97 | */ 98 | addOptions() { 99 | this.categoryOptionsList.push(...arguments) 100 | 101 | return this 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /HandlerIntegration/structures/Option.js: -------------------------------------------------------------------------------- 1 | const { formTypes } = require("discord-dashboard") 2 | 3 | const ids = [] 4 | 5 | const err = (text) => { 6 | return `🐧${text} Do you need help? Join our Discord server: ${ 7 | "https://discord.gg/CzfMGtrdaA".blue 8 | }` 9 | } 10 | 11 | module.exports = (db) => { 12 | return class Option { 13 | constructor( 14 | options = { 15 | optionId: "", 16 | optionName: "", 17 | optionDescription: "", 18 | optionType: formTypes, 19 | } 20 | ) { 21 | // 22 | this.optionId = options.optionId 23 | this.optionName = options.optionName 24 | this.optionDescription = options.optionDescription 25 | this.optionType = options.optionType 26 | 27 | this.categoryId = "default" 28 | 29 | this.setNew = async ({ guild, newData }) => { 30 | await db.set(`${guild.id}.options.${this.optionId}`, newData) 31 | } 32 | 33 | this.getActualSet = async ({ guild }) => { 34 | return await db.get(`${guild.id}.options.${this.optionId}`) 35 | } 36 | } 37 | 38 | /** 39 | * 40 | * @param {string} id - The id of the option, must be unique 41 | * @returns 42 | */ 43 | setId(id) { 44 | this.optionId = id 45 | 46 | if (ids.includes(id)) 47 | throw new Error(err(`Option id ${id} already exists`)) 48 | else ids.push(this.optionId) 49 | 50 | return this 51 | } 52 | 53 | /** 54 | * 55 | * @param {string} name - The name of the option displayed in the dashboard 56 | * @returns 57 | */ 58 | setName(name) { 59 | this.optionName = name 60 | 61 | return this 62 | } 63 | 64 | /** 65 | * 66 | * @param {string} description - The description of the option displayed in the dashboard 67 | * @returns 68 | */ 69 | setDescription(description) { 70 | this.optionDescription = description 71 | 72 | return this 73 | } 74 | 75 | /** 76 | * 77 | * @param {object} options - Set custom options for the formType 78 | * @returns 79 | */ 80 | setOptions(options) { 81 | this.themeOptions = options 82 | } 83 | 84 | /** 85 | * 86 | * @param {formTypes} type - The type of the option 87 | * @returns 88 | */ 89 | setType(type) { 90 | this.optionType = type 91 | 92 | return this 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /InitFunctions/initExampleDash.js: -------------------------------------------------------------------------------- 1 | module.exports = ({ 2 | fileName, 3 | domain, 4 | port, 5 | token, 6 | licenseId, 7 | clientSecret, 8 | clientId, 9 | }) => { 10 | require("fs").writeFileSync( 11 | `${fileName}.js`, 12 | ` 13 | 14 | /* --- DISCORD.JS CLIENT --- */ 15 | 16 | const {Client, Intents} = require('discord.js'); 17 | const client = new Client({ intents: [Intents.FLAGS.GUILDS] }); 18 | client.login('${token}'); 19 | 20 | /* --- DASHBOARD THEME & SETTINGS 'DATABASE' --- */ 21 | 22 | const DarkDashboard = require('dbd-dark-dashboard'); 23 | let langsSettings = {}; 24 | 25 | /* --- DASHBOARD --- */ 26 | (async ()=>{ 27 | let DBD = require('discord-dashboard'); 28 | await DBD.useLicense('${licenseId}'); 29 | DBD.Dashboard = DBD.UpdatedClass(); 30 | 31 | const Dashboard = new DBD.Dashboard({ 32 | port: ${port || 80}, 33 | client: { 34 | id: '${clientId}', 35 | secret: '${clientSecret}' 36 | }, 37 | redirectUri: '${domain}/discord/callback', 38 | domain: '${domain}', 39 | bot: client, 40 | theme: DarkDashboard({ 41 | information: { 42 | createdBy: "Assistants Center", 43 | websiteTitle: "Assistants Center", 44 | websiteName: "Discord-Dashboard", 45 | websiteUrl: "${domain}", 46 | dashboardUrl: "${domain}", 47 | supporteMail: "support@${domain}", 48 | supportServer: "", 49 | imageFavicon: "https://www.imidnight.ml/assets/img/logo-circular.png", 50 | iconURL: "https://www.imidnight.ml/assets/img/logo-circular.png", 51 | pageBackGround: "linear-gradient(#2CA8FF, #155b8d)", 52 | loggedIn: "Successfully signed in.", 53 | mainColor: "#2CA8FF", 54 | subColor: "#ebdbdb", 55 | }, 56 | index: { 57 | card: { 58 | category: "Assistants Center - The center of everything", 59 | title: "Welcome to the iMidnight discord where you can control the core features to the bot.", 60 | image: "https://i.imgur.com/axnP93g.png", 61 | footer: "Footer", 62 | }, 63 | 64 | information: { 65 | category: "Category", 66 | title: "Information", 67 | description: "This bot and panel is currently a work in progress so contact me if you find any issues on discord.", 68 | footer: "Footer", 69 | }, 70 | 71 | feeds: { 72 | category: "Category", 73 | title: "Information", 74 | description: "This bot and panel is currently a work in progress so contact me if you find any issues on discord.", 75 | footer: "Footer", 76 | }, 77 | }, 78 | commands: [ 79 | { 80 | category: "Starting Up", 81 | subTitle: "All helpful commands", 82 | list: [{ 83 | commandName: 'bug', 84 | commandUsage: ";bug ", 85 | commandDescription: "test", 86 | commandAlias: 'No aliases' 87 | }, 88 | { 89 | commandName: "2nd command", 90 | commandUsage: "oto.nd [op]", 91 | commandDescription: "Lorem ipsum dolor sth, arg sth arg2 stuff", 92 | commandAlias: "Alias", 93 | }, 94 | { 95 | commandName: "Test command", 96 | commandUsage: "prefix.test [op]", 97 | commandDescription: "Lorem ipsum dolor sth", 98 | commandAlias: "Alias", 99 | }, 100 | ], 101 | }, 102 | ], 103 | }), 104 | settings: [ 105 | { 106 | categoryId: 'setup', 107 | categoryName: "Setup", 108 | categoryDescription: "Setup your bot with default settings!", 109 | categoryOptionsList: [ 110 | { 111 | optionId: 'lang', 112 | optionName: "Language", 113 | optionDescription: "Change bot's language easily", 114 | optionType: DBD.formTypes.select({"Polish": 'pl', "English": 'en', "French": 'fr'}), 115 | getActualSet: async ({guild}) => { 116 | return langsSettings[guild.id] || null; 117 | }, 118 | setNew: async ({guild,newData}) => { 119 | langsSettings[guild.id] = newData; 120 | return; 121 | } 122 | }, 123 | ] 124 | }, 125 | ] 126 | }); 127 | 128 | Dashboard.init(); 129 | })(); 130 | 131 | ` 132 | ) 133 | } 134 | -------------------------------------------------------------------------------- /InitFunctions/initPpCheck.js: -------------------------------------------------------------------------------- 1 | const DBDStats = require("../ExternalStatistics") 2 | const fs = require("fs") 3 | const { v4: uuidv4 } = require("uuid") 4 | const readline = require("readline-sync") 5 | 6 | const DiscordDashboardPP = require("discord-dashboard-pp-system") 7 | 8 | module.exports = ( 9 | config, 10 | themeConfig, 11 | DBDStats, 12 | secretInit, 13 | modules, 14 | aaThis, 15 | license 16 | ) => { 17 | let externalStatsDisabled = false 18 | if (config.disableExternalStatistics) { 19 | if (license.type == "production" || license.type == "personal") { 20 | externalStatsDisabled = true 21 | } else { 22 | console.log( 23 | `${"[Discord-dashboard v".red}${ 24 | `${require("../package.json").version}]:`.red 25 | }: You can't disable External Stats without Personal/Production License.` 26 | ) 27 | } 28 | } 29 | const PPManager = new DiscordDashboardPP.PPManager(config, themeConfig) 30 | PPManager.SaveProjectData() 31 | if (!externalStatsDisabled) DBDStats.registerProject(config.client.id) 32 | if (config.acceptPrivacyPolicy) return aaThis.secretInit(aaThis.modules) 33 | const ppAccepted = PPManager.PP_GetAccepted() 34 | if (ppAccepted == "accepted") return aaThis.secretInit(aaThis.modules) 35 | let oThis = { secretInit, modules } 36 | const readline = require("readline-sync") 37 | 38 | setTimeout(function () { 39 | console.log( 40 | `${"[Discord-dashboard v".blue}${ 41 | `${require("../package.json").version}]:`.blue 42 | } Hello! First of all, we would like to thank you for your trust and choosing the ${ 43 | "discord-dashboard".rainbow 44 | }.` 45 | ) 46 | }, 2000) 47 | setTimeout(function () { 48 | console.log( 49 | `${"[Discord-dashboard v".blue}${ 50 | `${require("../package.json").version}]:`.blue 51 | } However, we must familiarize you with our privacy policy and describe to you how we collect your data.` 52 | ) 53 | }, 4000) 54 | setTimeout(function () { 55 | console.log(` 56 | ${ 57 | "[Discord-dashboard v".blue 58 | }${`${require("../package.json").version}]:`.blue} To maintain the quality of our services at the highest level, we collect from you: 59 | ${ 60 | "[Discord-dashboard v".blue 61 | }${`${require("../package.json").version}]:`.blue} - The ID of your Discord-Client, 62 | ${ 63 | "[Discord-dashboard v".blue 64 | }${`${require("../package.json").version}]:`.blue} - The number of users who log in to your panel (we also collect their IDs, but only to distinguish them from other, same login sessions), 65 | ${ 66 | "[Discord-dashboard v".blue 67 | }${`${require("../package.json").version}]:`.blue} - The types of settings you use that go beyond the basic ones. It does not include settings such as sensitive settings, e.g. your bot data. 68 | ${ 69 | "[Discord-dashboard v".blue 70 | }${`${require("../package.json").version}]:`.blue} We must add that your data is available only to the Project Administrator - breathtake. Nobody else can see it. Your data is not transferred anywhere either. 71 | 72 | ${ 73 | "[Discord-dashboard v".red 74 | }${`${require("../package.json").version}]:`.red} If you can't type in the console, pass 'acceptPrivacyPolicy: true,' to the discord-dashboard config.`) 75 | let iCount = 0 76 | 77 | function ask() { 78 | if (iCount > 0) 79 | console.log( 80 | `${"[Discord-dashboard v".red}${ 81 | `${require("../package.json").version}]:`.red 82 | }: You must accept our privacy policy to be able to use the module. Otherwise, you must delete the module.` 83 | ) 84 | iCount++ 85 | const rlResponse = readline.question( 86 | `${"[Discord-dashboard v".blue}${ 87 | `${require("../package.json").version}]:`.blue 88 | } Do you accept it? (y/n) ` 89 | ) 90 | 91 | if (rlResponse == "y" || rlResponse == "yes") { 92 | console.log( 93 | `${"[Discord-dashboard v".green}${ 94 | `${require("../package.json").version}]:`.green 95 | } Thank you. Now we will run the module for you. You will not need to re-approve our privacy policy again.` 96 | ) 97 | PPManager.PP_Accept() 98 | setTimeout(function () { 99 | aaThis.secretInit(aaThis.modules) 100 | }, 1000) 101 | } else ask() 102 | } 103 | ask() 104 | }, 6000) 105 | } 106 | -------------------------------------------------------------------------------- /InitFunctions/initServer.js: -------------------------------------------------------------------------------- 1 | const https = require("https") 2 | const http = require("http") 3 | const { Server: SocketServer } = require("socket.io") 4 | 5 | const err = (text) => { 6 | return ( 7 | text + 8 | ` Do you need help? Join our Discord server: ${ 9 | "https://discord.gg/CzfMGtrdaA".blue 10 | }` 11 | ) 12 | } 13 | 14 | module.exports = (app, config, themeConfig, modules) => { 15 | if (config.noCreateServer) return { io: null, server: null } 16 | let server 17 | 18 | if (!config.SSL) config.SSL = {} 19 | if (config.SSL.enabled) { 20 | if (!config.SSL.key || !config.SSL.cert) 21 | console.log( 22 | err( 23 | `${ 24 | "discord-dashboard issue:".red 25 | } The SSL preference for Dashboard is selected (config.SSL.enabled), but config does not include key or cert (config.SSL.key, config.SSL.cert).` 26 | ) 27 | ) 28 | let options = { 29 | key: config.SSL.key || "", 30 | cert: config.SSL.cert || "", 31 | } 32 | try { 33 | const https = require("https") 34 | server = https.createServer(options, app) 35 | } catch (e) { 36 | console.log( 37 | err( 38 | `${ 39 | "discord-dashboard issue:".red 40 | } There's a problem while creating server, check if the port specified is already on use.` 41 | ) 42 | ) 43 | } 44 | } else { 45 | const http = require("http") 46 | server = http.createServer(app) 47 | } 48 | 49 | let pport = "" 50 | if (config.port != 80 && config.port != 443) { 51 | pport = `:${config.port}` 52 | } 53 | 54 | if (!config.minimizedConsoleLogs) { 55 | console.log( 56 | ` 57 | ██████╗ ██████╗ ██████╗ 58 | ██╔══██╗██╔══██╗██╔══██╗ 59 | ██║ ██║██████╔╝██║ ██║ 60 | ██║ ██║██╔══██╗██║ ██║ 61 | ██████╔╝██████╔╝██████╔╝ 62 | ╚═════╝ ╚═════╝ ╚═════╝ 63 | Discord Bot Dashboard 64 | `.rainbow + 65 | ` 66 | Thanks for using ${ 67 | "discord-dashboard".rainbow 68 | } module! The server is up and running, so head over to the ${ 69 | `${(config.domain || "domain.com") + pport}`.blue 70 | } website and start your fun. 71 | 72 | Remember that there are ${ 73 | "themes".rainbow 74 | } available to make the Dashboard look better: ${ 75 | "https://dbd-docs.assistantscenter.com/#/?id=themes".blue 76 | } 77 | 78 | If you need help with something or you don't understand something, please visit our ${ 79 | "Discord Support Server".rainbow 80 | }: ${"https://discord.gg/CzfMGtrdaA".blue} 81 | ` 82 | ) 83 | } else { 84 | console.log( 85 | `DBD Dashboard running on ${ 86 | `${(config.domain || "domain.com") + pport}`.blue 87 | } !` 88 | ) 89 | } 90 | 91 | const SocketServer = require("socket.io").Server 92 | const io = new SocketServer(server, { 93 | cors: { 94 | origin: "*", 95 | }, 96 | }) 97 | 98 | modules.forEach((module) => { 99 | module.server({ 100 | io: io, 101 | server: server, 102 | config: config, 103 | themeConfig: themeConfig, 104 | }) 105 | }) 106 | 107 | server.listen(config.port) 108 | return { server, io } 109 | } 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public 421 | licenses. Notwithstanding, Creative Commons may elect to apply one of 422 | its public licenses to material it publishes and in those instances 423 | will be considered the “Licensor.” The text of the Creative Commons 424 | public licenses is dedicated to the public domain under the CC0 Public 425 | Domain Dedication. Except for the limited purpose of indicating that 426 | material is shared under a Creative Commons public license or as 427 | otherwise permitted by the Creative Commons policies published at 428 | creativecommons.org/policies, Creative Commons does not authorize the 429 | use of the trademark "Creative Commons" or any other trademark or logo 430 | of Creative Commons without its prior written consent including, 431 | without limitation, in connection with any unauthorized modifications 432 | to any of its public licenses or any other arrangements, 433 | understandings, or agreements concerning use of licensed material. For 434 | the avoidance of doubt, this paragraph does not form part of the 435 | public licenses. 436 | 437 | Creative Commons may be contacted at creativecommons.org. -------------------------------------------------------------------------------- /LicenseSystem/index.js: -------------------------------------------------------------------------------- 1 | const _0x11e2b8=_0x4ef1;function _0x4ef1(_0x25c410,_0x358db5){const _0x29a8e6=_0x29a8();return _0x4ef1=function(_0x4ef111,_0x4067f7){_0x4ef111=_0x4ef111-0x14c;let _0x4796d9=_0x29a8e6[_0x4ef111];return _0x4796d9;},_0x4ef1(_0x25c410,_0x358db5);}function _0x29a8(){const _0x77775c=['2904430BCDYuv','9ceBTKm','731330jzyhAt','1137520MhgEHw','376cQveQv','POST','Discord-dashboard\x20License\x20ID\x20is\x20not\x20valid.','licenseId','ValidateLicense','node-fetch','https://licenses.assistantscenter.com/validate','176xSOpMV','466968xQLxio','6Uyrrnc','Discord-Dashboard\x20License\x20ID\x20is\x20not\x20defined!\x20Go\x20to\x20https://licenses.assistants.ga/\x20and\x20generate/buy\x20one\x20for\x20you.','35756yBzcPP','then','304918QtlMjH','exports','170581JeXJRS','6QtFvLt'];_0x29a8=function(){return _0x77775c;};return _0x29a8();}(function(_0x576ba9,_0x27eee6){const _0x45302b=_0x4ef1,_0x59594e=_0x576ba9();while(!![]){try{const _0x43186f=parseInt(_0x45302b(0x150))/0x1+parseInt(_0x45302b(0x14e))/0x2*(-parseInt(_0x45302b(0x15f))/0x3)+parseInt(_0x45302b(0x155))/0x4+parseInt(_0x45302b(0x154))/0x5*(parseInt(_0x45302b(0x151))/0x6)+parseInt(_0x45302b(0x14c))/0x7*(parseInt(_0x45302b(0x156))/0x8)+-parseInt(_0x45302b(0x153))/0x9*(-parseInt(_0x45302b(0x152))/0xa)+-parseInt(_0x45302b(0x15d))/0xb*(parseInt(_0x45302b(0x15e))/0xc);if(_0x43186f===_0x27eee6)break;else _0x59594e['push'](_0x59594e['shift']());}catch(_0x59c126){_0x59594e['push'](_0x59594e['shift']());}}}(_0x29a8,0x31dac));const fetch=require(_0x11e2b8(0x15b));class License{constructor(_0x1dec84){const _0x31f658=_0x11e2b8;if(!_0x1dec84)throw new TypeError(_0x31f658(0x160));this[_0x31f658(0x159)]=_0x1dec84;}async[_0x11e2b8(0x15a)](){const _0x115f1c=_0x11e2b8;let _0x469e87;return await fetch(_0x115f1c(0x15c),{'method':_0x115f1c(0x157),'headers':{'Content-Type':'application/json'},'body':JSON['stringify']({'licenseId':this[_0x115f1c(0x159)]})})[_0x115f1c(0x14d)](_0x264618=>_0x264618['json']())[_0x115f1c(0x14d)](_0x1adf24=>{const _0x2b7298=_0x115f1c;if(_0x1adf24['error'])throw new TypeError(_0x2b7298(0x158));_0x469e87=_0x1adf24;}),_0x469e87;}}module[_0x11e2b8(0x14f)]=License; 2 | module.exports = License; -------------------------------------------------------------------------------- /ModuleExportsFunctions/customPagesTypes.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | redirectToUrl: (endpoint, getDataFunction) => { 3 | return { 4 | type: "redirect", 5 | endpoint: endpoint, 6 | getEndpoint: getDataFunction, 7 | } 8 | }, 9 | renderHtml: (endpoint, getDataFunction) => { 10 | return { 11 | type: "html", 12 | endpoint: endpoint, 13 | getHtml: getDataFunction, 14 | } 15 | }, 16 | sendJson: (endpoint, getDataFunction) => { 17 | return { 18 | type: "json", 19 | endpoint: endpoint, 20 | getJson: getDataFunction, 21 | } 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /ModuleExportsFunctions/discordPermissions.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CREATE_INSTANT_INVITE: ["CREATE_INSTANT_INVITE", 0x1], 3 | KICK_MEMBERS: ["KICK_MEMBERS", 0x2], 4 | BAN_MEMBERS: ["BAN_MEMBERS", 0x4], 5 | ADMINISTRATOR: ["ADMINISTRATOR", 0x8], 6 | MANAGE_CHANNELS: ["MANAGE_CHANNELS", 0x10], 7 | MANAGE_GUILD: ["MANAGE_GUILD", 0x20], 8 | ADD_REACTIONS: ["ADD_REACTIONS", 0x40], 9 | VIEW_AUDIT_LOG: ["VIEW_AUDIT_LOG", 0x80], 10 | PRIORITY_SPEAKER: ["PRIORITY_SPEAKER", 0x100], 11 | STREAM: ["STREAM", 0x200], 12 | VIEW_CHANNEL: ["VIEW_CHANNEL", 0x400], 13 | SEND_MESSAGES: ["SEND_MESSAGES", 0x800], 14 | SEND_TTS_MESSAGES: ["SEND_TTS_MESSAGES", 0x1000], 15 | MANAGE_MESSAGES: ["MANAGE_MESSAGES", 0x2000], 16 | EMBED_LINKS: ["EMBED_LINKS", 0x4000], 17 | ATTACH_FILES: ["ATTACH_FILES", 0x8000], 18 | READ_MESSAGE_HISTORY: ["READ_MESSAGE_HISTORY", 0x10000], 19 | MENTION_EVERYONE: ["MENTION_EVERYONE", 0x20000], 20 | USE_EXTERNAL_EMOJIS: ["USE_EXTERNAL_EMOJIS", 0x40000], 21 | VIEW_GUILD_INSIGHTS: ["VIEW_GUILD_INSIGHTS", 0x80000], 22 | CONNECT: ["CONNECT", 0x100000], 23 | SPEAK: ["SPEAK", 0x200000], 24 | MUTE_MEMBERS: ["MUTE_MEMBERS", 0x400000], 25 | DEAFEN_MEMBERS: ["DEAFEN_MEMBERS", 0x800000], 26 | MOVE_MEMBERS: ["MOVE_MEMBERS", 0x1000000], 27 | USE_VAD: ["USE_VAD", 0x2000000], 28 | CHANGE_NICKNAME: ["CHANGE_NICKNAME", 0x4000000], 29 | MANAGE_NICKNAMES: ["MANAGE_NICKNAMES", 0x8000000], 30 | MANAGE_ROLES: ["MANAGE_ROLES", 0x10000000], 31 | MANAGE_WEBHOOKS: ["MANAGE_WEBHOOKS", 0x20000000], 32 | MANAGE_EMOJIS_AND_STICKERS: ["MANAGE_EMOJIS_AND_STICKERS", 0x40000000], 33 | USE_APPLICATION_COMMANDS: ["USE_APPLICATION_COMMANDS", 0x80000000], 34 | REQUEST_TO_SPEAK: ["REQUEST_TO_SPEAK", 0x100000000], 35 | MANAGE_EVENTS: ["MANAGE_EVENTS", 0x200000000], 36 | MANAGE_THREADS: ["MANAGE_THREADS", 0x400000000], 37 | CREATE_PUBLIC_THREADS: ["CREATE_PUBLIC_THREADS", 0x800000000], 38 | CREATE_PRIVATE_THREADS: ["CREATE_PRIVATE_THREADS", 0x1000000000], 39 | USE_EXTERNAL_STICKERS: ["USE_EXTERNAL_STICKERS", 0x2000000000], 40 | SEND_MESSAGES_IN_THREADS: ["SEND_MESSAGES_IN_THREADS", 0x4000000000], 41 | START_EMBEDDED_ACTIVITIES: ["START_EMBEDDED_ACTIVITIES", 0x8000000000], 42 | MODERATE_MEMBERS: ["MODERATE_MEMBERS", 0x10000000000], 43 | } 44 | -------------------------------------------------------------------------------- /ModuleExportsFunctions/formTypes.js: -------------------------------------------------------------------------------- 1 | const discordPermissions = require("./discordPermissions") 2 | 3 | module.exports = { 4 | select: (list, disabled, themeOptions = {}) => { 5 | if (!list) 6 | throw new Error( 7 | err("List in the 'select' form type cannot be empty.") 8 | ) 9 | if (typeof list != "object") 10 | throw new Error( 11 | err("List in the 'select' form type should be an JSON object.") 12 | ) 13 | let keys = Object.keys(list) 14 | let values = Object.values(list) 15 | return { 16 | type: "select", 17 | data: { 18 | keys, 19 | values, 20 | }, 21 | disabled: disabled || false, 22 | themeOptions, 23 | } 24 | }, 25 | multiSelect: (list, disabled, required, themeOptions = {}) => { 26 | if (!list) 27 | throw new Error( 28 | err("List in the 'select' form type cannot be empty.") 29 | ) 30 | if (typeof list != "object") 31 | throw new Error( 32 | err("List in the 'select' form type should be an JSON object.") 33 | ) 34 | let keys = Object.keys(list) 35 | let values = Object.values(list) 36 | return { 37 | type: "multiSelect", 38 | data: { 39 | keys, 40 | values, 41 | }, 42 | disabled: disabled || false, 43 | required: required || false, 44 | themeOptions, 45 | } 46 | }, 47 | input: (placeholder, min, max, disabled, required, themeOptions = {}) => { 48 | if (min) { 49 | if (isNaN(min)) 50 | throw new Error( 51 | err("'min' in the 'input' form type should be an number.") 52 | ) 53 | } 54 | if (max) { 55 | if (isNaN(max)) 56 | throw new Error( 57 | err("'max' in the 'input' form type should be an number.") 58 | ) 59 | } 60 | if (min && max) { 61 | if (min > max) 62 | throw new Error( 63 | err( 64 | "'min' in the 'input' form type cannot be higher than 'max'." 65 | ) 66 | ) 67 | } 68 | return { 69 | type: "input", 70 | data: placeholder, 71 | min: min || null, 72 | max: max || null, 73 | disabled: disabled || false, 74 | required: required || false, 75 | themeOptions, 76 | } 77 | }, 78 | textarea: ( 79 | placeholder, 80 | min, 81 | max, 82 | disabled, 83 | required, 84 | themeOptions = {} 85 | ) => { 86 | if (min) { 87 | if (isNaN(min)) 88 | throw new Error( 89 | err("'min' in the 'input' form type should be an number.") 90 | ) 91 | } 92 | if (max) { 93 | if (isNaN(max)) 94 | throw new Error( 95 | err("'max' in the 'input' form type should be an number.") 96 | ) 97 | } 98 | if (min && max) { 99 | if (min > max) 100 | throw new Error( 101 | err( 102 | "'min' in the 'input' form type cannot be higher than 'max'." 103 | ) 104 | ) 105 | } 106 | return { 107 | type: "textarea", 108 | data: placeholder, 109 | min: min || null, 110 | max: max || null, 111 | disabled: disabled || false, 112 | required: required || false, 113 | themeOptions, 114 | } 115 | }, 116 | switch: (disabled, themeOptions = {}) => { 117 | return { 118 | type: "switch", 119 | disabled: disabled, 120 | themeOptions, 121 | } 122 | }, 123 | checkbox: (disabled, themeOptions = {}) => { 124 | return { 125 | type: "checkbox", 126 | disabled: disabled, 127 | themeOptions, 128 | } 129 | }, 130 | channelsSelect: ( 131 | disabled, 132 | channelTypes = ["GUILD_TEXT"], 133 | hideNSFW, 134 | onlyNSFW, 135 | hideNoAccess, 136 | themeOptions = {} 137 | ) => { 138 | return { 139 | type: "channelsSelect", 140 | function: (client, guildid, userid) => { 141 | let listCount = {} 142 | let list = { 143 | "-": "", 144 | } 145 | const guild = client.guilds.cache.get(guildid) 146 | const user = guild.members.cache.get(userid) 147 | const bot = guild.members.cache.get(client.user.id) 148 | client.guilds.cache 149 | .get(guildid) 150 | .channels.cache.forEach((channel) => { 151 | if (!channelTypes.includes(channel.type)) return 152 | if (hideNSFW && channel.nsfw) return 153 | if (onlyNSFW && !channel.nsfw) return 154 | if (hideNoAccess) { 155 | if (!user.permissionsIn(channel).has('0x800') || !user.permissionsIn(channel).has('0x400')) return 156 | if (!bot.permissionsIn(channel).has('0x800') || !bot.permissionsIn(channel).has('0x800')) return 157 | } 158 | listCount[channel.name] 159 | ? (listCount[channel.name] = 160 | listCount[channel.name] + 1) 161 | : (listCount[channel.name] = 1) 162 | if (list[channel.name]) 163 | list[ 164 | `${channel.name} (${listCount[channel.name]})` 165 | ] = channel.id 166 | else list[channel.name] = channel.id 167 | }) 168 | 169 | let myObj = list 170 | let keys = Object.keys(myObj), 171 | i = null, 172 | len = keys.length 173 | 174 | keys.sort() 175 | list = {} 176 | 177 | for (i = 0; i < len; i++) { 178 | k = keys[i] 179 | list[k] = myObj[k] 180 | } 181 | 182 | return { 183 | values: Object.values(list), 184 | keys: Object.keys(list), 185 | } 186 | }, 187 | disabled, 188 | themeOptions, 189 | } 190 | }, 191 | channelsMultiSelect: ( 192 | disabled, 193 | required, 194 | channelTypes = ["GUILD_TEXT"], 195 | hideNSFW, 196 | onlyNSFW, 197 | hideNoAccess, 198 | themeOptions = {} 199 | ) => { 200 | return { 201 | type: "channelsMultiSelect", 202 | function: (client, guildid, userid) => { 203 | let listCount = {} 204 | let list = {} 205 | const guild = client.guilds.cache.get(guildid) 206 | const user = guild.members.cache.get(userid) 207 | const bot = guild.members.cache.get(client.user.id) 208 | client.guilds.cache 209 | .get(guildid) 210 | .channels.cache.forEach((channel) => { 211 | if (!channelTypes.includes(channel.type)) return 212 | if (hideNSFW && channel.nsfw) return 213 | if (onlyNSFW && !channel.nsfw) return 214 | if (hideNoAccess) { 215 | if (!user.permissionsIn(channel).has('0x800') || !user.permissionsIn(channel).has('0x400')) return 216 | if (!bot.permissionsIn(channel).has('0x800') || !bot.permissionsIn(channel).has('0x800')) return 217 | } 218 | listCount[channel.name] 219 | ? (listCount[channel.name] = 220 | listCount[channel.name] + 1) 221 | : (listCount[channel.name] = 1) 222 | if (list[channel.name]) 223 | list[ 224 | `${channel.name} (${listCount[channel.name]})` 225 | ] = channel.id 226 | else list[channel.name] = channel.id 227 | }) 228 | 229 | let myObj = list 230 | let keys = Object.keys(myObj), 231 | i = null, 232 | len = keys.length 233 | 234 | keys.sort() 235 | list = {} 236 | 237 | for (i = 0; i < len; i++) { 238 | k = keys[i] 239 | list[k] = myObj[k] 240 | } 241 | 242 | return { 243 | values: Object.values(list), 244 | keys: Object.keys(list), 245 | } 246 | }, 247 | disabled, 248 | required, 249 | themeOptions, 250 | } 251 | }, 252 | rolesMultiSelect: (disabled, required, includeBots, hideHigherRoles, themeOptions = {}) => { 253 | return { 254 | type: "rolesMultiSelect", 255 | function: (client, guildid, userid) => { 256 | let listCount = {} 257 | const list = [] 258 | const guild = client.guilds.cache.get(guildid) 259 | const user = guild.members.cache.get(userid) 260 | const bot = guild.members.cache.get(client.user.id) 261 | 262 | client.guilds.cache.get(guildid).roles.cache.forEach((role) => { 263 | if (role.managed && !includeBots) return 264 | if (role.id === guildid) return // @everyone role 265 | if (hideHigherRoles) { 266 | if (role.position >= user.roles.highest.position) return 267 | if (role.position >= bot.roles.highest.position) return 268 | } 269 | listCount[role.name] 270 | ? (listCount[role.name] = listCount[role.name] + 1) 271 | : (listCount[role.name] = 1) 272 | if (listCount[role.name] > 1) 273 | list.push({ key: `${role.name} (${listCount[role.name]})`, value: role.id, position: role.position }) 274 | else list.push({ key: role.name, value: role.id, position: role.position }) 275 | }) 276 | 277 | list.sort((a, b) => b.position - a.position) 278 | 279 | const sortedList = {} 280 | list.forEach(({ key, value }) => (sortedList[key] = value)) 281 | 282 | return { 283 | values: Object.values(sortedList), 284 | keys: Object.keys(sortedList), 285 | } 286 | }, 287 | disabled, 288 | required, 289 | themeOptions, 290 | } 291 | }, 292 | rolesSelect: (disabled, includeBots, hideHigherRoles, themeOptions = {}) => { 293 | return { 294 | type: "rolesSelect", 295 | function: (client, guildid, userid) => { 296 | let listCount = {} 297 | const list = [{ key: '-', value: '' }] 298 | const guild = client.guilds.cache.get(guildid) 299 | const user = guild.members.cache.get(userid) 300 | const bot = guild.members.cache.get(client.user.id) 301 | client.guilds.cache.get(guildid).roles.cache.forEach((role) => { 302 | if (role.managed && !includeBots) return 303 | if (role.id === guildid) return // @everyone role 304 | if (hideHigherRoles) { 305 | if (role.position >= user.roles.highest.position) return 306 | if (role.position >= bot.roles.highest.position) return 307 | } 308 | listCount[role.name] 309 | ? (listCount[role.name] = listCount[role.name] + 1) 310 | : (listCount[role.name] = 1) 311 | if (listCount[role.name] > 1) 312 | list.push({ key: `${role.name} (${listCount[role.name]})`, value: role.id, position: role.position }) 313 | else list.push({ key: role.name, value: role.id, position: role.position }) 314 | }) 315 | 316 | list.sort((a, b) => b.position - a.position) 317 | 318 | const sortedList = {} 319 | list.forEach(({ key, value }) => (sortedList[key] = value)) 320 | 321 | return { 322 | values: Object.values(sortedList), 323 | keys: Object.keys(sortedList), 324 | } 325 | }, 326 | disabled, 327 | themeOptions, 328 | } 329 | }, 330 | colorSelect: (defaultState, disabled, themeOptions = {}) => { 331 | return { 332 | type: "colorSelect", 333 | data: defaultState, 334 | disabled, 335 | themeOptions, 336 | } 337 | }, 338 | embedBuilder: (defaultSettings, disabled, themeOptions = {}) => { 339 | return { 340 | type: "embedBuilder", 341 | data: defaultSettings, 342 | disabled, 343 | themeOptions, 344 | } 345 | }, 346 | } 347 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 |

5 |

Discord server

6 | 7 | # Discord-Dashboard 8 | 9 | - Easier creation of a Dashboard for Bot using Node.js 10 | - No need to know how the API works, no need to write it 11 | - Easy configuration 12 | - Community support 13 | - Free for no-profit usage 14 | - Paid for profit usage 15 | 16 | # Install 17 | 18 | ```js 19 | npm i discord-dashboard 20 | //or 21 | yarn add discord-dashboard 22 | ``` 23 | 24 | # Get help 25 | 26 | Join our Discord Support Server 27 | 28 | Discord server 29 | 30 | # Documentation 31 | 32 | **Tutorial:** 33 | - **#1** [Introduction](https://learnit.assistantscenter.com/discord-dashboard-tutorial-1-introduction/) 34 | - **#2** [Setting up a project](https://learnit.assistantscenter.com/discord-dashboard-tutorial-2-setting-up-a-project/) 35 | - ***#2.1.*** [Let's make it work](https://learnit.assistantscenter.com/discord-dashboard-tutorial-2-1-lets-make-it-work/) 36 | - ***#2.2.*** [Creating custom options](https://learnit.assistantscenter.com/discord-dashboard-tutorial-2-2-creating-custom-options/) 37 | - ***#2.3.*** [FormTypes](https://learnit.assistantscenter.com/discord-dashboard-tutorial-2-3-formtypes/) 38 | - ***#2.4.*** [Theme customization](https://learnit.assistantscenter.com/discord-dashboard-tutorial-2-3-theme-customization/) 39 | - **#3** [How to...? ](https://learnit.assistantscenter.com/discord-dashboard-tutorial-3-how-to/) 40 | - ***#3.1.*** [Connect domain with the dashboard](https://learnit.assistantscenter.com/discord-dashboard-tutorial-3-1-connect-domain-with-the-dashboard/) 41 | - ***#2.2.*** [Make dashboard HTTPS (SSL)](https://learnit.assistantscenter.com/discord-dashboard-tutorial-3-2-make-dashboard-https-ssl/) 42 | 43 | Straight-written DOCS can be found at the URL: https://dbd-docs.assistantscenter.com/ 44 | 45 | We'd prefer using Tutorial 😅 46 | 47 | # Contact 48 | 49 | - **E-mail**: contact@assistantscenter.com, support@assistantscenter.com, billing@assistantscenter.com 50 | - **Discord**: https://discord.gg/Nkc8MWxHRD 51 | 52 | # Licenses 53 | 54 | The project is covered by a **Shareware (CC BY-NC-SA 4.0) licence**. This means that the project is theoretically free and limited, but it has a paid versions. 55 | 56 | ### What types of shareware license does the discord-dashboard project fall under? 57 | 58 | - **OpenSource License**: Free. Personal usage only. Non-profit. 59 | 60 | **For profit usage**: Please contact use us to resell rights. Email: *contact@assistantscenter.com* 61 | 62 | #### You can generate OpenSource License there: https://licenses.assistantscenter.com/ 63 | 64 | # Star History 65 | 66 | 67 | 68 | 69 | 70 | Star History Chart 71 | 72 | 73 | 74 | # v3 Info 75 | 76 | The v3 version of the project designed using solutions like Fastify+Next.js is coming! 77 | 78 | ### **Stay tuned:** 79 | 80 | **Discord v3 Updates**: https://discord.gg/Nkc8MWxHRD 81 | 82 |
83 | 84 | © 2020-2023 Assistants Center, All rights reserved 85 | -------------------------------------------------------------------------------- /Routes/dashboard.js: -------------------------------------------------------------------------------- 1 | const { PermissionFlagsBits } = require("discord.js") 2 | const Discord = require("discord.js") 3 | const router = require("express").Router() 4 | 5 | module.exports = (app, config, themeConfig) => { 6 | const RL = require("express-rate-limit") 7 | const RateLimits = config.rateLimits || {} 8 | let RateFunctions = {} 9 | 10 | const NoRL = (req, res, next) => next() 11 | 12 | if (RateLimits.manage) { 13 | RateFunctions.manage = RL.rateLimit({ 14 | windowMs: RateLimits.manage.windowMs, 15 | max: RateLimits.manage.max, 16 | message: RateLimits.manage.message, 17 | store: RateLimits.manage.store || new RL.MemoryStore(), 18 | }) 19 | } 20 | 21 | if (RateLimits.guildPage) { 22 | RateFunctions.guildPage = RL.rateLimit({ 23 | windowMs: RateLimits.guildPage.windowMs, 24 | max: RateLimits.guildPage.max, 25 | message: RateLimits.guildPage.message, 26 | store: RateLimits.guildPage.store || new RL.MemoryStore(), 27 | }) 28 | } 29 | 30 | if (RateLimits.settingsUpdatePostAPI) { 31 | RateFunctions.settingsUpdatePostAPI = RL.rateLimit({ 32 | windowMs: RateLimits.settingsUpdatePostAPI.windowMs, 33 | max: RateLimits.settingsUpdatePostAPI.max, 34 | message: RateLimits.settingsUpdatePostAPI.message, 35 | store: 36 | RateLimits.settingsUpdatePostAPI.store || new RL.MemoryStore(), 37 | }) 38 | } 39 | 40 | router.get( 41 | "/manage", 42 | RateFunctions.manage ? RateFunctions.manage : NoRL, 43 | async (req, res) => { 44 | if (!req.session.user) return res.redirect("/discord?r=/manage") 45 | let customThemeOptions 46 | if (themeConfig?.customThemeOptions?.manage) { 47 | customThemeOptions = 48 | await themeConfig.customThemeOptions.manage({ 49 | req: req, 50 | res: res, 51 | config: config, 52 | }) 53 | } 54 | res.render("guilds", { 55 | req: req, 56 | bot: config.bot, 57 | themeConfig: req.themeConfig, 58 | customThemeOptions: customThemeOptions || {}, 59 | config, 60 | }) 61 | } 62 | ) 63 | 64 | router.get( 65 | "/guild/:id", 66 | RateFunctions.guildPage ? RateFunctions.guildPage : NoRL, 67 | async (req, res) => { 68 | res.redirect("/settings/" + req.params.id) 69 | 70 | if (!req.session.user) 71 | return res.redirect("/discord?r=/guild/" + req.params.id) 72 | let customThemeOptions 73 | if (themeConfig?.customThemeOptions?.getGuild) { 74 | customThemeOptions = 75 | await themeConfig.customThemeOptions.getGuild({ 76 | req: req, 77 | res: res, 78 | config: config, 79 | guildId: req.params.id, 80 | }) 81 | } 82 | let bot = config.bot 83 | if (!bot.guilds.cache.get(req.params.id)) { 84 | try { 85 | await bot.guilds.fetch(req.params.id) 86 | } catch (err) { } 87 | } 88 | 89 | if (!bot.guilds.cache.get(req.params.id)) 90 | return res.redirect("/manage?error=noPermsToManageGuild") 91 | if ( 92 | !bot.guilds.cache 93 | .get(req.params.id) 94 | .members.cache.get(req.session.user.id) 95 | ) { 96 | try { 97 | await bot.guilds.cache 98 | .get(req.params.id) 99 | .members.fetch(req.session.user.id) 100 | } catch (err) { } 101 | } 102 | for (let PermissionRequired of req.requiredPermissions) { 103 | let converted = PermissionRequired[0] 104 | const DiscordJsVersion = Discord.version.split(".")[0] 105 | 106 | if (DiscordJsVersion === "14") 107 | converted = convert14(PermissionRequired[0]) 108 | 109 | if ( 110 | !bot.guilds.cache 111 | .get(req.params.id) 112 | .members.cache.get(req.session.user.id) 113 | .permissions.has(converted) 114 | ) 115 | return res.redirect("/manage?error=noPermsToManageGuild") 116 | } 117 | 118 | if (bot.guilds.cache.get(req.params.id).channels.cache.size < 1) { 119 | try { 120 | await bot.guilds.cache.get(req.params.id).channels.fetch() 121 | } catch (err) { } 122 | } 123 | 124 | if (bot.guilds.cache.get(req.params.id).roles.cache.size < 2) { 125 | try { 126 | await bot.guilds.cache.get(req.params.id).roles.fetch() 127 | } catch (err) { } 128 | } 129 | 130 | let actual = {} 131 | 132 | let canUseList = {} 133 | if (!config.useCategorySet) 134 | for (const s of config.settings) { 135 | if (!canUseList[s.categoryId]) canUseList[s.categoryId] = {} 136 | for (const c of s.categoryOptionsList) { 137 | if (c.allowedCheck) { 138 | const canUse = await c.allowedCheck({ 139 | guild: { id: req.params.id }, 140 | user: { id: req.session.user.id }, 141 | }) 142 | if (typeof canUse != "object") 143 | throw new TypeError( 144 | `${s.categoryId} category option with id ${c.optionId} allowedCheck function need to return {allowed: Boolean, errorMessage: String | null}` 145 | ) 146 | canUseList[s.categoryId][c.optionId] = canUse 147 | } else { 148 | canUseList[s.categoryId][c.optionId] = { 149 | allowed: true, 150 | errorMessage: null, 151 | } 152 | } 153 | 154 | if (c.optionType == "spacer") { 155 | } else { 156 | if (!actual[s.categoryId]) { 157 | actual[s.categoryId] = {} 158 | } 159 | if (!actual[s.categoryId][c.optionId]) { 160 | actual[s.categoryId][c.optionId] = 161 | await c.getActualSet({ 162 | guild: { 163 | id: req.params.id, 164 | object: bot.guilds.cache.get( 165 | req.params.id 166 | ), 167 | }, 168 | user: { 169 | id: req.session.user.id, 170 | object: bot.guilds.cache 171 | .get(req.params.id) 172 | .members.cache.get( 173 | req.session.user.id 174 | ), 175 | }, 176 | }) 177 | } 178 | } 179 | } 180 | } 181 | else 182 | for (const category of config.settings) { 183 | if (!canUseList[category.categoryId]) 184 | canUseList[category.categoryId] = {} 185 | const catGAS = await category.getActualSet({ 186 | guild: { 187 | id: req.params.id, 188 | object: bot.guilds.cache.get(req.params.id), 189 | }, 190 | user: { 191 | id: req.session.user.id, 192 | object: bot.guilds.cache 193 | .get(req.params.id) 194 | .members.cache.get(req.session.user.id), 195 | }, 196 | }) 197 | 198 | for (const o of catGAS) { 199 | if (!o || !o?.optionId) 200 | console.log( 201 | "WARNING: You haven't set the optionId for a category option in your config. This is required for the category option to work." 202 | ) 203 | else { 204 | const option = category.categoryOptionsList.find( 205 | (c) => c.optionId == o.optionId 206 | ) 207 | if (option) { 208 | if (option.allowedCheck) { 209 | const canUse = await option.allowedCheck({ 210 | guild: { 211 | id: req.params.id, 212 | }, 213 | user: { 214 | id: req.session.user.id, 215 | }, 216 | }) 217 | if (typeof canUse != "object") 218 | throw new TypeError( 219 | `${category.categoryId} category option with id ${option.optionId} allowedCheck function need to return {allowed: Boolean, errorMessage: String | null}` 220 | ) 221 | canUseList[category.categoryId][ 222 | option.optionId 223 | ] = canUse 224 | } else { 225 | canUseList[category.categoryId][ 226 | option.optionId 227 | ] = { 228 | allowed: true, 229 | errorMessage: null, 230 | } 231 | } 232 | 233 | if (option.optionType !== "spacer") { 234 | if (!actual[category.categoryId]) { 235 | actual[category.categoryId] = {} 236 | } 237 | if ( 238 | !actual[category.categoryId][ 239 | option.optionId 240 | ] 241 | ) { 242 | actual[category.categoryId][ 243 | option.optionId 244 | ] = o.data 245 | } 246 | } 247 | } else 248 | console.log( 249 | `WARNING: Option ${o.optionId} in category ${category.categoryId} doesn't exist in your config.` 250 | ) 251 | } 252 | } 253 | } 254 | 255 | let errors 256 | let success 257 | 258 | if (req.session.errors) { 259 | if (String(req.session.errors).includes("%is%")) { 260 | errors = req.session.errors.split("%and%") 261 | } 262 | } 263 | 264 | if (req.session.success) { 265 | if (typeof req.session.success == "boolean") { 266 | success = true 267 | } else { 268 | if (String(req.session.success).includes("%is%")) { 269 | success = req.session.success.split("%and%") 270 | } 271 | } 272 | } 273 | 274 | req.session.errors = null 275 | req.session.success = null 276 | 277 | res.render("guild", { 278 | successes: success, 279 | errors: errors, 280 | settings: config.settings, 281 | actual: actual, 282 | canUseList, 283 | bot: config.bot, 284 | req: req, 285 | guildid: req.params.id, 286 | themeConfig: req.themeConfig, 287 | customThemeOptions: customThemeOptions || {}, 288 | config, 289 | }) 290 | } 291 | ) 292 | 293 | router.post( 294 | "/settings/update/:guildId/:categoryId", 295 | RateFunctions.settingsUpdatePostAPI 296 | ? RateFunctions.settingsUpdatePostAPI 297 | : NoRL, 298 | async (req, res) => { 299 | if (!req.session.user) 300 | return res.redirect("/discord?r=/guild/" + req.params.guildId) 301 | 302 | let customThemeOptions 303 | if (themeConfig?.customThemeOptions?.settingsUpdate) { 304 | customThemeOptions = 305 | await themeConfig.customThemeOptions.settingsUpdate({ 306 | req: req, 307 | config: config, 308 | guildId: req.params.id, 309 | categoryId: req.params.categoryId, 310 | }) 311 | } 312 | let bot = config.bot 313 | 314 | if (!bot.guilds.cache.get(req.params.guildId)) 315 | return res.redirect("/manage?error=noPermsToManageGuild") 316 | await bot.guilds.cache 317 | .get(req.params.guildId) 318 | .members.fetch(req.session.user.id) 319 | for (let PermissionRequired of req.requiredPermissions) { 320 | let converted2 = PermissionRequired[0] 321 | const DiscordJsVersion2 = Discord.version.split(".")[0] 322 | 323 | if (DiscordJsVersion2 === "14") 324 | converted2 = await convert14(PermissionRequired[0]) 325 | 326 | if ( 327 | !bot.guilds.cache 328 | .get(req.params.guildId) 329 | .members.cache.get(req.session.user.id) 330 | .permissions.has(converted2) 331 | ) 332 | return res.redirect("/manage?error=noPermsToManageGuild") 333 | } 334 | 335 | let cid = req.params.categoryId 336 | let settings = config.settings 337 | 338 | let category = settings.find((c) => c.categoryId == cid) 339 | 340 | if (!category) 341 | return res.send({ 342 | error: true, 343 | message: "No category found", 344 | }) 345 | 346 | let setNewRes 347 | let errors = [] 348 | let successes = [] 349 | let catO = [] 350 | 351 | const userGuildMemberObject = bot.guilds.cache 352 | .get(req.params.guildId) 353 | .members.cache.get(req.session.user.id) 354 | const guildObject = bot.guilds.cache.get(req.params.guildId) 355 | 356 | for (let option of category.categoryOptionsList) { 357 | let canUse = {} 358 | 359 | if (option.allowedCheck) { 360 | canUse = await option.allowedCheck({ 361 | guild: { id: req.params.guildId }, 362 | user: { id: req.session.user.id }, 363 | }) 364 | } else { 365 | canUse = { allowed: true, errorMessage: null } 366 | } 367 | 368 | if (canUse.allowed == false) { 369 | setNewRes = { error: canUse.errorMessage } 370 | errors.push( 371 | option.optionName + 372 | "%is%" + 373 | setNewRes.error + 374 | "%is%" + 375 | option.optionId 376 | ) 377 | } else if (option.optionType != "spacer") { 378 | if (config.useCategorySet) { 379 | if ( 380 | option.optionType.type == "rolesMultiSelect" || 381 | option.optionType.type == "channelsMultiSelect" || 382 | option.optionType.type == "multiSelect" 383 | ) { 384 | if ( 385 | !req.body[option.optionId] || 386 | req.body[option.optionId] == null || 387 | req.body[option.optionId] == undefined 388 | ) 389 | catO.push({ 390 | optionId: option.optionId, 391 | data: [], 392 | }) 393 | else if ( 394 | typeof req.body[option.optionId] != "object" 395 | ) 396 | catO.push({ 397 | optionId: option.optionId, 398 | data: [req.body[option.optionId]], 399 | }) 400 | else 401 | catO.push({ 402 | optionId: option.optionId, 403 | data: req.body[option.optionId], 404 | }) 405 | } else if (option.optionType.type == "switch") { 406 | if ( 407 | req.body[option.optionId] || 408 | req.body[option.optionId] == null || 409 | req.body[option.optionId] == undefined 410 | ) { 411 | if ( 412 | req.body[option.optionId] == null || 413 | req.body[option.optionId] == undefined 414 | ) 415 | catO.push({ 416 | optionId: option.optionId, 417 | data: false, 418 | }) 419 | else 420 | catO.push({ 421 | optionId: option.optionId, 422 | data: true, 423 | }) 424 | } 425 | } else if (option.optionType.type == "embedBuilder") { 426 | if ( 427 | req.body[option.optionId] == null || 428 | req.body[option.optionId] == undefined 429 | ) 430 | catO.push({ 431 | optionId: option.optionId, 432 | data: option.optionType.data, 433 | }) 434 | else { 435 | try { 436 | const parsedResponse = JSON.parse( 437 | req.body[option.optionId] 438 | ) 439 | catO.push({ 440 | optionId: option.optionId, 441 | data: parsedResponse, 442 | }) 443 | } catch (err) { 444 | catO.push({ 445 | optionId: option.optionId, 446 | data: option.optionType.data, 447 | }) 448 | } 449 | } 450 | } else { 451 | if ( 452 | req.body[option.optionId] == undefined || 453 | req.body[option.optionId] == null 454 | ) 455 | catO.push({ 456 | optionId: option.optionId, 457 | data: null, 458 | }) 459 | else 460 | catO.push({ 461 | optionId: option.optionId, 462 | data: req.body[option.optionId], 463 | }) 464 | } 465 | } else { 466 | if ( 467 | option.optionType.type == "rolesMultiSelect" || 468 | option.optionType.type == "channelsMultiSelect" || 469 | option.optionType.type == "multiSelect" 470 | ) { 471 | if ( 472 | !req.body[option.optionId] || 473 | req.body[option.optionId] == null || 474 | req.body[option.optionId] == undefined 475 | ) { 476 | setNewRes = await option.setNew({ 477 | guild: { 478 | id: req.params.guildId, 479 | object: guildObject, 480 | }, 481 | user: { 482 | id: req.session.user.id, 483 | object: userGuildMemberObject, 484 | }, 485 | newData: [], 486 | }) 487 | setNewRes ? null : (setNewRes = {}) 488 | if (setNewRes.error) { 489 | errors.push( 490 | option.optionName + 491 | "%is%" + 492 | setNewRes.error + 493 | "%is%" + 494 | option.optionId 495 | ) 496 | } else { 497 | successes.push(option.optionName) 498 | } 499 | } else if ( 500 | typeof req.body[option.optionId] != "object" 501 | ) { 502 | setNewRes = await option.setNew({ 503 | guild: { 504 | id: req.params.guildId, 505 | object: guildObject, 506 | }, 507 | user: { 508 | id: req.session.user.id, 509 | object: userGuildMemberObject, 510 | }, 511 | newData: [req.body[option.optionId]], 512 | }) 513 | setNewRes ? null : (setNewRes = {}) 514 | if (setNewRes.error) { 515 | errors.push( 516 | option.optionName + 517 | "%is%" + 518 | setNewRes.error + 519 | "%is%" + 520 | option.optionId 521 | ) 522 | } else { 523 | successes.push(option.optionName) 524 | } 525 | } else { 526 | setNewRes = await option.setNew({ 527 | guild: { 528 | id: req.params.guildId, 529 | object: guildObject, 530 | }, 531 | user: { 532 | id: req.session.user.id, 533 | object: userGuildMemberObject, 534 | }, 535 | newData: req.body[option.optionId], 536 | }) 537 | setNewRes ? null : (setNewRes = {}) 538 | if (setNewRes.error) { 539 | errors.push( 540 | option.optionName + 541 | "%is%" + 542 | setNewRes.error + 543 | "%is%" + 544 | option.optionId 545 | ) 546 | } else { 547 | successes.push(option.optionName) 548 | } 549 | } 550 | } else if (option.optionType.type == "switch") { 551 | if ( 552 | req.body[option.optionId] || 553 | req.body[option.optionId] == null || 554 | req.body[option.optionId] == undefined 555 | ) { 556 | if ( 557 | req.body[option.optionId] == null || 558 | req.body[option.optionId] == undefined 559 | ) { 560 | setNewRes = 561 | (await option.setNew({ 562 | guild: { 563 | id: req.params.guildId, 564 | object: guildObject, 565 | }, 566 | user: { 567 | id: req.session.user.id, 568 | object: userGuildMemberObject, 569 | }, 570 | newData: false, 571 | })) || {} 572 | setNewRes ? null : (setNewRes = {}) 573 | if (setNewRes.error) { 574 | errors.push( 575 | option.optionName + 576 | "%is%" + 577 | setNewRes.error + 578 | "%is%" + 579 | option.optionId 580 | ) 581 | } else { 582 | successes.push(option.optionName) 583 | } 584 | } else { 585 | setNewRes = 586 | (await option.setNew({ 587 | guild: { 588 | id: req.params.guildId, 589 | object: guildObject, 590 | }, 591 | user: { 592 | id: req.session.user.id, 593 | object: userGuildMemberObject, 594 | }, 595 | newData: true, 596 | })) || {} 597 | setNewRes ? null : (setNewRes = {}) 598 | if (setNewRes.error) { 599 | errors.push( 600 | option.optionName + 601 | "%is%" + 602 | setNewRes.error + 603 | "%is%" + 604 | option.optionId 605 | ) 606 | } else { 607 | successes.push(option.optionName) 608 | } 609 | } 610 | } 611 | } else if (option.optionType.type == "embedBuilder") { 612 | if ( 613 | req.body[option.optionId] == null || 614 | req.body[option.optionId] == undefined 615 | ) { 616 | setNewRes = 617 | (await option.setNew({ 618 | guild: { 619 | id: req.params.guildId, 620 | object: guildObject, 621 | }, 622 | user: { 623 | id: req.session.user.id, 624 | object: userGuildMemberObject, 625 | }, 626 | newData: option.optionType.data, 627 | })) || {} 628 | setNewRes ? null : (setNewRes = {}) 629 | if (setNewRes.error) { 630 | errors.push( 631 | option.optionName + 632 | "%is%" + 633 | setNewRes.error + 634 | "%is%" + 635 | option.optionId 636 | ) 637 | } else { 638 | successes.push(option.optionName) 639 | } 640 | } else { 641 | try { 642 | const parsedResponse = JSON.parse( 643 | req.body[option.optionId] 644 | ) 645 | setNewRes = 646 | (await option.setNew({ 647 | guild: { 648 | id: req.params.guildId, 649 | object: guildObject, 650 | }, 651 | user: { 652 | id: req.session.user.id, 653 | object: userGuildMemberObject, 654 | }, 655 | newData: parsedResponse, 656 | })) || {} 657 | setNewRes ? null : (setNewRes = {}) 658 | if (setNewRes.error) { 659 | errors.push( 660 | option.optionName + 661 | "%is%" + 662 | setNewRes.error + 663 | "%is%" + 664 | option.optionId 665 | ) 666 | } else { 667 | successes.push(option.optionName) 668 | } 669 | } catch (err) { 670 | setNewRes = 671 | (await option.setNew({ 672 | guild: { 673 | id: req.params.guildId, 674 | object: guildObject, 675 | }, 676 | user: { 677 | id: req.session.user.id, 678 | object: userGuildMemberObject, 679 | }, 680 | newData: option.optionType.data, 681 | })) || {} 682 | setNewRes = { 683 | error: "JSON parse for embed builder went wrong, your settings have been reset.", 684 | } 685 | if (setNewRes.error) { 686 | errors.push( 687 | option.optionName + 688 | "%is%" + 689 | setNewRes.error + 690 | "%is%" + 691 | option.optionId 692 | ) 693 | } else { 694 | successes.push(option.optionName) 695 | } 696 | } 697 | } 698 | } else { 699 | if ( 700 | req.body[option.optionId] == undefined || 701 | req.body[option.optionId] == null 702 | ) { 703 | setNewRes = 704 | (await option.setNew({ 705 | guild: { 706 | id: req.params.guildId, 707 | object: guildObject, 708 | }, 709 | user: { 710 | id: req.session.user.id, 711 | object: userGuildMemberObject, 712 | }, 713 | newData: null, 714 | })) || {} 715 | setNewRes ? null : (setNewRes = {}) 716 | if (setNewRes.error) { 717 | errors.push( 718 | option.optionName + 719 | "%is%" + 720 | setNewRes.error + 721 | "%is%" + 722 | option.optionId 723 | ) 724 | } else { 725 | successes.push(option.optionName) 726 | } 727 | } else { 728 | setNewRes = 729 | (await option.setNew({ 730 | guild: { 731 | id: req.params.guildId, 732 | object: guildObject, 733 | }, 734 | user: { 735 | id: req.session.user.id, 736 | object: userGuildMemberObject, 737 | }, 738 | newData: req.body[option.optionId], 739 | })) || {} 740 | setNewRes ? null : (setNewRes = {}) 741 | if (setNewRes.error) { 742 | errors.push( 743 | option.optionName + 744 | "%is%" + 745 | setNewRes.error + 746 | "%is%" + 747 | option.optionId 748 | ) 749 | } else { 750 | successes.push(option.optionName) 751 | } 752 | } 753 | } 754 | } 755 | } 756 | } 757 | 758 | if (config.useCategorySet && catO.length) { 759 | let sNR = await category.setNew({ 760 | guild: { 761 | id: req.params.guildId, 762 | object: guildObject, 763 | }, 764 | user: { 765 | id: req.session.user.id, 766 | object: userGuildMemberObject, 767 | }, 768 | data: catO, 769 | }) 770 | sNR ? null : (sNR = {}) 771 | if (sNR.error) { 772 | errors.push(category.categoryId + "%is%" + sNR.error) 773 | } else { 774 | successes.push(category.categoryId) 775 | } 776 | } 777 | 778 | let successesForDBDEvent = [] 779 | let errorsForDBDEvent = [] 780 | 781 | successes.forEach((item) => { 782 | if (typeof item == "string") { 783 | successesForDBDEvent.push(item.split("%is%")) 784 | } 785 | }) 786 | 787 | errors.forEach((item) => { 788 | if (typeof item == "string") { 789 | errorsForDBDEvent.push(item.split("%is%")) 790 | } 791 | }) 792 | 793 | req.DBDEvents.emit("guildSettingsUpdated", { 794 | user: req.session.user, 795 | changes: { successesForDBDEvent, errorsForDBDEvent }, 796 | guildId: req.params.guildId, 797 | }) 798 | 799 | if (errors[0]) { 800 | if (!successes) successes = [] 801 | req.session.success = successes.join("%and%") 802 | req.session.errors = errors.join("%and%") 803 | return res.redirect("/guild/" + req.params.guildId) 804 | } else { 805 | req.session.success = true 806 | req.session.errors = false 807 | return res.redirect("/guild/" + req.params.guildId) 808 | } 809 | } 810 | ) 811 | 812 | return router 813 | } 814 | 815 | function convert14(perm) { 816 | let final = "NULL" 817 | 818 | switch (perm) { 819 | case "CREATE_INSTANT_INVITE": 820 | final = "CreateInstantInvite" 821 | break 822 | case "KICK_MEMBERS": 823 | final = "KickMembers" 824 | break 825 | case "BAN_MEMBERS": 826 | final = "BanMembers" 827 | break 828 | case "ADMINISTRATOR": 829 | final = "Administrator" 830 | break 831 | case "MANAGE_CHANNELS": 832 | final = "ManageChannels" 833 | break 834 | case "MANAGE_GUILD": 835 | final = "ManageGuild" 836 | break 837 | case "ADD_REACTIONS": 838 | final = "AddReactions" 839 | break 840 | case "VIEW_AUDIT_LOG": 841 | final = "ViewAuditLog" 842 | break 843 | case "PRIORITY_SPEAKER": 844 | final = "PrioritySpeaker" 845 | break 846 | case "STREAM": 847 | final = "Stream" 848 | break 849 | case "VIEW_CHANNEL": 850 | final = "ViewChannel" 851 | break 852 | case "SEND_MESSAGES": 853 | final = "SendMessages" 854 | break 855 | case "SEND_TTS_MESSAGES": 856 | final = "SendTTSMessages" 857 | break 858 | case "MANAGE_MESSAGES": 859 | final = "ManageMessages" 860 | break 861 | case "EMBED_LINKS": 862 | final = "ManageMessages" 863 | break 864 | case "ATTACH_FILES": 865 | final = "AttachFiles" 866 | break 867 | case "READ_MESSAGE_HISTORY": 868 | final = "ReadMessageHistory" 869 | break 870 | case "MENTION_EVERYONE": 871 | final = "MentionEveryone" 872 | break 873 | case "USE_EXTERNAL_EMOJIS": 874 | final = "UseExternalEmojis" 875 | break 876 | case "VIEW_GUILD_INSIGHTS": 877 | final = "ViewGuildInsughts" 878 | break 879 | case "CONNECT": 880 | final = "Connect" 881 | break 882 | case "SPEAK": 883 | final = "Speak" 884 | break 885 | } 886 | 887 | return final 888 | } 889 | -------------------------------------------------------------------------------- /Routes/discord.js: -------------------------------------------------------------------------------- 1 | const router = require("express").Router() 2 | const fetch = require("node-fetch") 3 | const DBDStats = require("../ExternalStatistics/index") 4 | 5 | const DiscordOauth2 = require("discord-oauth2") 6 | const oauth = new DiscordOauth2() 7 | 8 | module.exports = (app, config, themeConfig) => { 9 | const scopes = config.guildAfterAuthorization?.use 10 | ? ["identify", "guilds", "guilds.join"] 11 | : ["identify", "guilds"] 12 | const RL = require("express-rate-limit") 13 | const RateLimits = config.rateLimits || {} 14 | let RateFunctions = {} 15 | 16 | const NoRL = (req, res, next) => next() 17 | 18 | if (RateLimits.discordOAuth2) { 19 | RateFunctions.discordOAuth2 = RL.rateLimit({ 20 | windowMs: RateLimits.discordOAuth2.windowMs, 21 | max: RateLimits.discordOAuth2.max, 22 | message: RateLimits.discordOAuth2.message, 23 | store: RateLimits.discordOAuth2.store || new RL.MemoryStore(), 24 | }) 25 | } 26 | 27 | router.get("/", (req, res) => { 28 | const clientId = req.client.id 29 | const redirectUri = req.redirectUri 30 | 31 | let newPage = "/" 32 | if (themeConfig.landingPage?.enabled) newPage = "/dash" 33 | req.session.r = req.query.r || newPage 34 | 35 | const authorizeUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent( 36 | redirectUri 37 | )}&response_type=code&scope=${scopes.join("%20")}` 38 | res.redirect(authorizeUrl) 39 | }) 40 | 41 | router.get("/status", async (req, res) => { 42 | res.send(req.session?.discordAuthStatus) 43 | }) 44 | 45 | router.get( 46 | "/callback", 47 | RateFunctions.discordOAuth2 ? RateFunctions.discordOAuth2 : NoRL, 48 | async (req, res) => { 49 | req.session.discordAuthStatus = { 50 | loading: true, 51 | success: null, 52 | state: { 53 | error: null, 54 | data: null, 55 | }, 56 | } 57 | const clientId = req.client.id 58 | const clientSecret = req.client.secret 59 | const redirectUri = req.redirectUri 60 | 61 | const accessCode = req.query.code 62 | if (!accessCode) 63 | return res.redirect("/?error=NoAccessCodeReturnedFromDiscord") 64 | 65 | res.redirect("/loading") 66 | 67 | let OAuth2Response 68 | let OAuth2UserResponse 69 | let OAuth2GuildsResponse 70 | 71 | /* 72 | Get Discord OAuth2 API Response with Access Code gained 73 | */ 74 | try { 75 | req.session.discordAuthStatus = { 76 | loading: true, 77 | success: null, 78 | state: { 79 | error: null, 80 | data: "Requesting token...", 81 | }, 82 | } 83 | req.session.save(function (err) {}) 84 | OAuth2Response = await oauth.tokenRequest({ 85 | clientId, 86 | clientSecret, 87 | 88 | code: accessCode, 89 | scope: scopes.join(" "), 90 | grantType: "authorization_code", 91 | 92 | redirectUri, 93 | }) 94 | } catch (err) { 95 | req.config.reportError( 96 | "Discord.js Route - OAuth2Response (line 86)", 97 | err 98 | ) 99 | req.session.discordAuthStatus = { 100 | loading: false, 101 | success: false, 102 | state: { 103 | error: err, 104 | data: null, 105 | }, 106 | } 107 | req.session.save(function (err) {}) 108 | return 109 | } 110 | 111 | /* 112 | Get User from Discord OAuth2 API using gained access_token and update its values with tag and avatarURL 113 | */ 114 | 115 | try { 116 | req.session.discordAuthStatus = { 117 | loading: true, 118 | success: null, 119 | state: { 120 | error: null, 121 | data: "Getting User...", 122 | }, 123 | } 124 | req.session.save(function (err) {}) 125 | OAuth2UserResponse = await oauth.getUser( 126 | OAuth2Response.access_token 127 | ) 128 | } catch (err) { 129 | req.config.reportError( 130 | "Discord.js Route - OAuth2UserResponse (line 118)", 131 | err 132 | ) 133 | req.session.discordAuthStatus = { 134 | loading: false, 135 | success: false, 136 | state: { 137 | error: err, 138 | data: null, 139 | }, 140 | } 141 | req.session.save(function (err) {}) 142 | return 143 | } 144 | OAuth2UserResponse.tag = `${OAuth2UserResponse.username}#${OAuth2UserResponse.discriminator}` 145 | OAuth2UserResponse.avatarURL = OAuth2UserResponse.avatar 146 | ? `https://cdn.discordapp.com/avatars/${OAuth2UserResponse.id}/${OAuth2UserResponse.avatar}.png?size=1024` 147 | : null 148 | 149 | /* 150 | Save user token in Assistants Secure Storage 151 | */ 152 | 153 | const isBlacklisted = await config?.blacklisted({ userId: OAuth2UserResponse.id }) || false 154 | if (isBlacklisted) { 155 | req.session.discordAuthStatus = { 156 | loading: false, 157 | success: false, 158 | state: { 159 | error: "blacklisted", 160 | data: null, 161 | }, 162 | } 163 | req.session.user = { 164 | blacklisted: true 165 | } 166 | req.session.save(function (err) {}) 167 | return 168 | } 169 | 170 | 171 | try { 172 | req.AssistantsSecureStorage.SaveUser( 173 | OAuth2UserResponse.id, 174 | OAuth2Response.access_token 175 | ) 176 | } catch (err) { 177 | req.config.reportError( 178 | "Discord.js Route - Assistants Secure Storage (line 141)", 179 | err 180 | ) 181 | req.session.discordAuthStatus = { 182 | loading: false, 183 | success: false, 184 | state: { 185 | error: err, 186 | data: null, 187 | }, 188 | } 189 | req.session.save(function (err) {}) 190 | return 191 | } 192 | 193 | /* 194 | Save user in session 195 | */ 196 | 197 | req.session.user = OAuth2UserResponse 198 | req.session.loggedInLastTime = true 199 | 200 | /* 201 | Register user to DBD Stats and emit userLoggedIn event 202 | */ 203 | 204 | try { 205 | DBDStats.registerUser(OAuth2UserResponse.id) 206 | req.DBDEvents.emit("userLoggedIn", OAuth2UserResponse) 207 | } catch (err) { 208 | req.config.reportError( 209 | "Discord.js Route - DBDStats register and DBDEvent emit userLoggedIn (line 170)", 210 | err 211 | ) 212 | req.session.discordAuthStatus = { 213 | loading: false, 214 | success: false, 215 | state: { 216 | error: err, 217 | data: null, 218 | }, 219 | } 220 | req.session.save(function (err) {}) 221 | return 222 | } 223 | 224 | /* 225 | Gain and update session with user guilds 226 | */ 227 | 228 | try { 229 | req.session.discordAuthStatus = { 230 | loading: true, 231 | success: null, 232 | state: { 233 | error: null, 234 | data: "Getting List of User Guilds...", 235 | }, 236 | } 237 | req.session.save(function (err) {}) 238 | OAuth2GuildsResponse = await oauth.getUserGuilds( 239 | OAuth2Response.access_token 240 | ) 241 | } catch (err) { 242 | req.config.reportError( 243 | "Discord.js Route - OAuth2GuildsResponse (line 201)", 244 | err 245 | ) 246 | req.session.discordAuthStatus = { 247 | loading: false, 248 | success: false, 249 | state: { 250 | error: err, 251 | data: null, 252 | }, 253 | } 254 | req.session.save(function (err) {}) 255 | return 256 | } 257 | req.session.guilds = OAuth2GuildsResponse || [] 258 | 259 | /* 260 | Loop and fetch each guild into bots cache 261 | */ 262 | 263 | if (!req.config.disableResolvingGuildCache) { 264 | try { 265 | req.session.discordAuthStatus = { 266 | loading: true, 267 | success: null, 268 | state: { 269 | error: null, 270 | data: "Resolving guilds cache...", 271 | }, 272 | } 273 | req.session.save(function (err) {}) 274 | for (let g of OAuth2GuildsResponse) { 275 | try { 276 | await req.bot.guilds.fetch(g.id) 277 | } catch (err) {} 278 | } 279 | } catch (err) { 280 | req.config.reportError( 281 | "Discord.js Route - OAuth2GuildsResponse Whole Loop (line 239)", 282 | err 283 | ) 284 | req.session.discordAuthStatus = { 285 | loading: false, 286 | success: false, 287 | state: { 288 | error: err, 289 | data: null, 290 | }, 291 | } 292 | req.session.save(function (err) {}) 293 | return 294 | } 295 | } 296 | 297 | /* 298 | If joining specific guild after authorization is enabled, do it 299 | */ 300 | 301 | if (req.guildAfterAuthorization.use == true) { 302 | req.session.discordAuthStatus = { 303 | loading: true, 304 | success: null, 305 | state: { 306 | error: null, 307 | data: "Authorizing user with guild...", 308 | }, 309 | } 310 | req.session.save(function (err) {}) 311 | try { 312 | await oauth.addMember({ 313 | accessToken: OAuth2Response.access_token, 314 | botToken: req.botToken, 315 | guildId: req.guildAfterAuthorization.guildId, 316 | userId: OAuth2UserResponse.id, 317 | 318 | ...(req.guildAfterAuthorization.options || {}), 319 | /* 320 | options?: { 321 | nickname?: string, 322 | roles?: [string], 323 | mute?: boolean, 324 | deaf?: boolean, 325 | } 326 | */ 327 | }) 328 | } catch (err) { 329 | req.config.reportError( 330 | "Discord.js Route - guildAfterAuthorization (line 287)", 331 | err 332 | ) 333 | } 334 | } 335 | 336 | req.session.discordAuthStatus = { 337 | loading: false, 338 | success: true, 339 | state: { 340 | error: null, 341 | data: null, 342 | }, 343 | } 344 | req.session.save(function (err) {}) 345 | 346 | return 347 | } 348 | ) 349 | 350 | router.get("/logout", (req, res) => { 351 | let r = req.query.r || "/" 352 | req.session.destroy() 353 | res.redirect(r) 354 | }) 355 | 356 | router.get("/guilds/reload", async (req, res) => { 357 | if (!req.session.user) return res.redirect("/discord") 358 | 359 | /* 360 | Fetch user token 361 | */ 362 | 363 | const access_token = req.AssistantsSecureStorage.GetUser( 364 | req.session.user.id 365 | ) 366 | if (!access_token) 367 | return res.send({ 368 | error: true, 369 | message: "You don't have any access_token saved.", 370 | login_again_text: true, 371 | }) 372 | 373 | /* 374 | Gain and update session with user guilds 375 | */ 376 | 377 | let OAuth2GuildsResponse 378 | 379 | try { 380 | OAuth2GuildsResponse = await oauth.getUserGuilds(access_token) 381 | } catch (err) { 382 | req.config.reportError( 383 | "Discord.js Route - OAuth2GuildsResponse for ReloadGuilds (line 335)", 384 | err 385 | ) 386 | return res.send({ 387 | error: true, 388 | message: 389 | "An error occured. Access_token is wrong or you're being rate limited.", 390 | login_again_text: true, 391 | }) 392 | } 393 | req.session.guilds = OAuth2GuildsResponse || [] 394 | 395 | /* 396 | Loop and fetch each guild into bots cache 397 | */ 398 | 399 | try { 400 | const Promises = [] 401 | for (let g of OAuth2GuildsResponse) { 402 | Promises.push( 403 | new Promise(async (resolve, reject) => { 404 | try { 405 | await req.bot.guilds.fetch(g.id) 406 | } catch (err) {} 407 | resolve(1) 408 | }) 409 | ) 410 | try { 411 | await Promises.all(Promises) 412 | } catch (err) {} 413 | } 414 | } catch (err) { 415 | req.config.reportError( 416 | "Discord.js Route - OAuth2GuildsResponse Whole Loop for ReloadGuilds (line 363)", 417 | err 418 | ) 419 | return res.send({ 420 | error: true, 421 | message: 422 | "An error occured. Access_token is wrong or you're being rate limited.", 423 | login_again_text: true, 424 | }) 425 | } 426 | 427 | /* 428 | Success 429 | */ 430 | 431 | return res.send({ 432 | error: false, 433 | message: null, 434 | login_again_text: false, 435 | }) 436 | }) 437 | 438 | return router 439 | } 440 | -------------------------------------------------------------------------------- /Routes/main.js: -------------------------------------------------------------------------------- 1 | const Discord = require("discord.js") 2 | const router = require("express").Router() 3 | 4 | module.exports = (app, config, themeConfig, modules) => { 5 | router.get( 6 | themeConfig.landingPage?.enabled ? "/dash" : "/", 7 | async (req, res) => { 8 | let customThemeOptions 9 | if (themeConfig?.customThemeOptions?.index) { 10 | customThemeOptions = await themeConfig.customThemeOptions.index( 11 | { req: req, res: res, config: config } 12 | ) 13 | } 14 | res.render("index", { 15 | req: req, 16 | themeConfig: req.themeConfig, 17 | bot: config.bot, 18 | customThemeOptions: customThemeOptions || {}, 19 | config, 20 | require, 21 | }) 22 | } 23 | ) 24 | 25 | if (themeConfig.landingPage?.enabled) 26 | router.get("/", async (req, res) => { 27 | res.setHeader("Content-Type", "text/html") 28 | res.send(await themeConfig.landingPage.getLandingPage(req, res)) 29 | }) 30 | 31 | router.get("/loading", async (req, res) => { 32 | if (!req.session?.discordAuthStatus?.loading) 33 | return res.redirect("/manage") 34 | 35 | res.render("loading", { req, themeConfig, bot: config.bot }) 36 | }) 37 | 38 | router.get("/invite", (req, res) => { 39 | let config = req.config 40 | config.invite ? null : (config.invite = {}) 41 | const scopes = config.invite.scopes || ["bot"] 42 | 43 | if (req.query.redirect && !req.query.g) 44 | return res.redirect( 45 | `https://discord.com/oauth2/authorize?client_id=${ 46 | config.invite.clientId || config.bot.user.id 47 | }&scope=${scopes.join("%20")}&permissions=${ 48 | config.invite.permissions || "0" 49 | }&response_type=code&redirect_uri=${req.query.redirect}${ 50 | config.invite.otherParams || "" 51 | }` 52 | ) 53 | if (req.query.redirect && req.query.g) 54 | return res.redirect( 55 | `https://discord.com/oauth2/authorize?client_id=${ 56 | config.invite.clientId || config.bot.user.id 57 | }&scope=${scopes.join("%20")}&permissions=${ 58 | config.invite.permissions || "0" 59 | }&response_type=code&redirect_uri=${ 60 | req.query.redirect 61 | }&guild_id=${req.query.g}${config.invite.otherParams || ""}` 62 | ) 63 | 64 | if (req.query.g) { 65 | let thingymabob = config.invite.redirectUri 66 | ? `&response_type=code&redirect_uri=${config.invite.redirectUri}` 67 | : null; 68 | if(!thingymabob) thingymabob = config.invite.specialredirectUri 69 | ? `&response_type=code&redirect_uri=${config.invite.specialRedirectUri.replace("{SERVER}", req.query.g)}` 70 | : ""; 71 | 72 | return res.redirect( 73 | `https://discord.com/oauth2/authorize?client_id=${ 74 | config.invite.clientId || config.bot.user.id 75 | }&scope=${scopes.join("%20")}&permissions=${ 76 | config.invite.permissions || "0" 77 | }${thingymabob}&guild_id=${req.query.g}${config.invite.otherParams || ""}` 78 | ) 79 | } 80 | 81 | res.redirect( 82 | `https://discord.com/oauth2/authorize?client_id=${ 83 | config.invite.clientId || config.bot.user.id 84 | }&scope=${scopes.join("%20")}&permissions=${ 85 | config.invite.permissions || "0" 86 | }${ 87 | config.invite.redirectUri 88 | ? `&response_type=code&redirect_uri=${config.invite.redirectUri}` 89 | : "" 90 | }${config.invite.otherParams || ""}` 91 | ) 92 | }) 93 | 94 | if (!config.supportServer) config.supportServer = {} 95 | 96 | router.get(config.supportServer.slash || "/support-server", (req, res) => { 97 | let config = req.config 98 | config.supportServer ? null : (config.supportServer = {}) 99 | if (!config.supportServer.inviteUrl) 100 | return res.send({ 101 | error: true, 102 | message: 103 | "No inviteUrl defined (discord-dashboard config.supportServer).", 104 | }) 105 | if ( 106 | !config.supportServer.inviteUrl 107 | .toLowerCase() 108 | .startsWith("https://discord.gg/") && 109 | !config.supportServer.inviteUrl 110 | .toLowerCase() 111 | .startsWith("https://discord.com/") 112 | ) 113 | return res.send({ 114 | error: true, 115 | message: 116 | "Invite url should start with 'https://discord.gg/' or 'https://discord.com/'.", 117 | }) 118 | res.redirect(config.supportServer.inviteUrl) 119 | }) 120 | 121 | return router 122 | } 123 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Currently supported versions: 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | > 2.\* | :white_check_mark: | 10 | | < 2.\* | :x: | 11 | -------------------------------------------------------------------------------- /__ModuleExample/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | app: ({ app, config, themeConfig }) => { 3 | app.get("/moduleexample", (req, res) => { 4 | res.send("ModuleExample: Hello World!") 5 | }) 6 | }, 7 | server: ({ io, server, config, themeConfig }) => { 8 | const test = io.of("/moduleexample") 9 | test.on("connection", () => { 10 | console.log("ModuleExample socket.io connected.") 11 | }) 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Why is this obfuscated? 3 | The index.js file is obfuscated to hide the license system. 4 | However, this doesn't prevent you from modifying the Dashboard. 5 | Just check files like ./Routes/* and ./ModuleExportsFunctions/* 6 | and we assure you that they are enough to configure the module. 7 | */ 8 | 9 | function _0x3f31(_0x69d767,_0x157650){const _0x2d2dbe=_0x2d2d();return _0x3f31=function(_0x3f313e,_0x4a4225){_0x3f313e=_0x3f313e-0x8f;let _0x46eddb=_0x2d2dbe[_0x3f313e];return _0x46eddb;},_0x3f31(_0x69d767,_0x157650);}const _0x4d40c6=_0x3f31;(function(_0x3917a9,_0x9bc430){const _0x328fea=_0x3f31,_0x37cbb7=_0x3917a9();while(!![]){try{const _0x2239eb=parseInt(_0x328fea(0xd1))/0x1*(-parseInt(_0x328fea(0xbf))/0x2)+-parseInt(_0x328fea(0xac))/0x3+-parseInt(_0x328fea(0x91))/0x4*(parseInt(_0x328fea(0xc2))/0x5)+-parseInt(_0x328fea(0xc8))/0x6+parseInt(_0x328fea(0xae))/0x7*(-parseInt(_0x328fea(0xb0))/0x8)+-parseInt(_0x328fea(0xc5))/0x9*(parseInt(_0x328fea(0xa5))/0xa)+-parseInt(_0x328fea(0xa9))/0xb*(-parseInt(_0x328fea(0xa7))/0xc);if(_0x2239eb===_0x9bc430)break;else _0x37cbb7['push'](_0x37cbb7['shift']());}catch(_0x570953){_0x37cbb7['push'](_0x37cbb7['shift']());}}}(_0x2d2d,0xc1881));function _0x2d2d(){const _0x10bb5a=['mamma\x20mia\x20italiano','You\x20need\x20to\x20use\x20.useLicense(\x22licenseId\x22)\x20function\x20before\x20initing\x20Dashboard.','gtfvdlicensergtfdd','writeFileSync','\x27licenseId\x27\x20not\x20defined\x20in\x20the\x20.useLicense(\x27licenseId\x27)\x20function.','Successfully\x20signed\x20in.','Man,\x20this\x20was\x20just\x20check\x20if\x20variable\x20is\x20still\x20false\x20:oooo','4BCwWgR','man\x20stop\x20deobfuscating\x20this\x20source\x20code\x20🤷‍♂️🤷‍♂️🤷‍♂️','http://localhost:3000/','man\x20thats\x20just\x20a\x20function\x20doing\x20nothing...','/HandlerIntegration/index','./ModuleExportsFunctions/customPagesTypes','Discord-Dashboard\x20License\x20Error:\x20','/licensedDashboardClass.js','Default\x20DBD-Dark-Dashboard\x20config.','Loading...','https://discord.gg/yYq4UgRRzz','shift','7OzWizz','6552252jCWVcp','Category',';bug\x20','38592sbLQyd','\x0amodule.exports\x20=\x20Dashboard;','lmfaooo...\x20stop.','1853400SDxfnR','11596630EzQCtz','licenseClient','Test\x20command','access_token','test','./ModuleExportsFunctions/formTypes','222VTPBta','iMidnight','notlicense','4ijVdcT','./LicenseSystem/index','gtfvrthththrtdlicensed','support@imidnight.ml','Information','error','usedLicense','279670chJqCU','./InitFunctions/initExampleDash','56033148jtsIkI','#ebdbdb','11kTpdSX','log','oto.nd\x20\x20\x20[op]','2789085bTWQVe','Alias','100709rGYPjY','version','40drDnKp','aos!','push','No\x20aliases','u\x20aint\x20even\x20nrealy','Lorem\x20ipsum\x20dolor\x20sth','Starting\x20Up','\x20Assistants\x20will\x20sue\x20you\x20100%%%%,\x20just\x20use\x20free\x20OpenSource\x20license','gtfvdlicenhtgfdsed','This\x20bot\x20and\x20panel\x20is\x20currently\x20a\x20work\x20in\x20progress\x20so\x20contact\x20me\x20if\x20you\x20find\x20any\x20issues\x20on\x20discord.','colors','Footer','gtfvdlicensed','exports','message','584090iigBIQ','You\x20really\x20thought\x20thats\x20real\x20license\x20class?????????','lmfao...\x20stop.','345mGZBkl','text','You\x20nearly\x20got\x20it\x20:rofl:\x20Loser.','162pRMplf','./package.json','fakse;','3226776OnXxBz','All\x20helpful\x20commands'];_0x2d2d=function(){return _0x10bb5a;};return _0x2d2d();}let x_32fsfgd=![];const colors=require(_0x4d40c6(0xba)),fs=require('fs'),License=require(_0x4d40c6(0x9f)),{version}=require(_0x4d40c6(0xc6));class Dashboard{constructor(){const _0x44a370=_0x4d40c6;throw new TypeError(_0x44a370(0xcb));}}module[_0x4d40c6(0xbd)]={'default_configs':{'dbdDarkDashboard':{'information':{'createdBy':_0x4d40c6(0x9c),'websiteTitle':_0x4d40c6(0x9c),'websiteName':_0x4d40c6(0x9c),'websiteUrl':'https:/www.imidnight.ml/','dashboardUrl':_0x4d40c6(0xd3),'supporteMail':_0x4d40c6(0xa1),'supportServer':_0x4d40c6(0xdb),'imageFavicon':'https://www.imidnight.ml/assets/img/logo-circular.png','iconURL':'https://www.imidnight.ml/assets/img/logo-circular.png','loggedIn':_0x4d40c6(0xcf),'mainColor':'#2CA8FF','subColor':_0x4d40c6(0xa8),'preloader':_0x4d40c6(0xda)},'index':{'card':{'category':_0x4d40c6(0xd9),'title':'Please\x20customize\x20config\x20to\x20change\x20how\x20dashboard\x20looks\x20like.','image':'https://i.imgur.com/axnP93g.png','footer':'Footer'},'information':{'category':_0x4d40c6(0x8f),'title':'Information','description':_0x4d40c6(0xb9),'footer':'Footer'},'feeds':{'category':_0x4d40c6(0x8f),'title':_0x4d40c6(0xa2),'description':'This\x20bot\x20and\x20panel\x20is\x20currently\x20a\x20work\x20in\x20progress\x20so\x20contact\x20me\x20if\x20you\x20find\x20any\x20issues\x20on\x20discord.','footer':_0x4d40c6(0xbb)}},'commands':[{'category':_0x4d40c6(0xb6),'subTitle':_0x4d40c6(0xc9),'list':[{'commandName':'bug','commandUsage':_0x4d40c6(0x90),'commandDescription':_0x4d40c6(0x99),'commandAlias':_0x4d40c6(0xb3)},{'commandName':'2nd\x20command','commandUsage':_0x4d40c6(0xab),'commandDescription':'Lorem\x20ipsum\x20dolor\x20sth,\x20arg\x20sth\x20arg2\x20stuff','commandAlias':'Alias'},{'commandName':_0x4d40c6(0x97),'commandUsage':'prefix.test\x20\x20[op]','commandDescription':_0x4d40c6(0xb5),'commandAlias':_0x4d40c6(0xad)}]}]}},'licenseInfo':()=>{return console['log']('[Discord-Dashboard\x20Info]\x20Your\x20License\x20ID\x20has\x20been\x20fetched\x20by\x20*probably*\x20the\x20theme!'),x_32fsfgd;},'useLicense':async _0x550a87=>{const _0x3f8cd1=_0x4d40c6;if(!_0x550a87)throw new TypeError(_0x3f8cd1(0xce));const _0x4e4496=require(_0x3f8cd1(0x9f)),_0x518a4b=new _0x4e4496(_0x550a87),_0x31d1da=await _0x518a4b['ValidateLicense']();if(_0x31d1da[_0x3f8cd1(0xa3)])throw new TypeError(_0x3f8cd1(0xd7)+_0x31d1da[_0x3f8cd1(0xbe)]);x_32fsfgd=_0x31d1da;const _0x40e2f3=require(_0x3f8cd1(0xc6))['version'];let _0x19616b=await require('node-fetch')('https://licenses.assistantscenter.com/api/index/'+_0x40e2f3+'/'+_0x550a87+'/'+x_32fsfgd[_0x3f8cd1(0x98)]);return _0x19616b=await _0x19616b[_0x3f8cd1(0xc3)](),await require('fs')[_0x3f8cd1(0xcd)](__dirname+_0x3f8cd1(0xd8),'//'+_0x40e2f3+'\x0a'+_0x19616b+_0x3f8cd1(0x92)),_0x31d1da;},'UpdatedClass':()=>{const _0x17b0f9=_0x4d40c6,_0x371000=_0x17b0f9(0xbc);let _0x2641d4=![],_0x255626=![];const _0x3ba3c9=_0x17b0f9(0xbc),_0x44721e=_0x17b0f9(0xc0);let _0x1b39af=_0x17b0f9(0xc7);const _0xda87e=_0x17b0f9(0xb7);let _0x4b398c=![];_0x255626=!![];const _0x5295b7=_0x17b0f9(0xd4),_0x384866=_0x17b0f9(0xc4),_0x4c487e=_0x17b0f9(0xa0),_0x337c3b=_0x17b0f9(0xca);let _0x4d7768=_0x17b0f9(0xd2);if(!_0x255626)throw new TypeError(_0x4d7768+_0xda87e);let _0xe7882a=_0x17b0f9(0xcc),_0x1f71b0=_0x17b0f9(0xb8),_0x24e096=!![];if(x_32fsfgd){_0x24e096?_0x2641d4=![]:_0x2641d4=!![];if(x_32fsfgd==x_32fsfgd){const _0x32e7ac=_0x17b0f9(0x96),_0x5bdddc=_0x17b0f9(0xa4),_0x25d09e=_0x17b0f9(0xb1);if(_0x2641d4)throw new TypeError('Stop\x20editing\x20source\x20code,\x20that\x20will\x20give\x20you\x20an\x20brainfuck\x20moment.\x20Remember\x20that\x20Assistants\x20could\x20sue\x20you.');class _0x2f89db{constructor(){throw new TypeError('STop\x20🤣');}}if(!_0x24e096)new _0x2f89db();if(_0x24e096&&_0x2641d4)return console[_0x17b0f9(0xaa)](_0x384866);const _0x4a3d01=_0x567e15;(function(_0x5e53ad,_0x544bfe){const _0xa1e2e1=_0x17b0f9,_0x9c67c=_0x567e15,_0x53b902=_0x5e53ad();while(!![]){try{const _0x55649e=-parseInt(_0x9c67c(0x13e))/0x1*(parseInt(_0x9c67c(0x145))/0x2)+-parseInt(_0x9c67c(0x13f))/0x3+parseInt(_0x9c67c(0x143))/0x4+parseInt(_0x9c67c(0x13d))/0x5*(parseInt(_0x9c67c(0x140))/0x6)+parseInt(_0x9c67c(0x13a))/0x7*(-parseInt(_0x9c67c(0x142))/0x8)+-parseInt(_0x9c67c(0x13c))/0x9+parseInt(_0x9c67c(0x13b))/0xa;if(_0x55649e===_0x544bfe)break;else _0x53b902[_0xa1e2e1(0xb2)](_0x53b902[_0xa1e2e1(0xdc)]());}catch(_0x1ce62e){_0x53b902[_0xa1e2e1(0xb2)](_0x53b902[_0xa1e2e1(0xdc)]());}}}(_0x2c18c4,0x6dde6));function _0x567e15(_0x13f002,_0x9b0643){const _0x145cf6=_0x2c18c4();return _0x567e15=function(_0x474170,_0x98632b){_0x474170=_0x474170-0x13a;let _0x4b5a47=_0x145cf6[_0x474170];return _0x4b5a47;},_0x567e15(_0x13f002,_0x9b0643);}function _0x2c18c4(){const _0x432b5c=_0x17b0f9,_0x45da9b=['92375iWCWKi',_0x432b5c(0x9e),_0x432b5c(0x94),_0x432b5c(0x9b),'./licensedDashboardClass.js','3616112Tfevia','2509304crvKVh',_0x432b5c(0xd8),'111350xHjeKY',_0x432b5c(0xdd),_0x432b5c(0x95),_0x432b5c(0xde)];return _0x2c18c4=function(){return _0x45da9b;},_0x2c18c4();}const _0x356cc4=require(_0x4a3d01(0x141));fs[_0x17b0f9(0xcd)](__dirname+_0x4a3d01(0x144),'');return _0x356cc4;const _0x41125a='Haha\x20u\x20wont\x20get\x20this\x20working.';((()=>{const _0x6d6947=_0x17b0f9;if(_0x2641d4&&!_0x255626)return console[_0x6d6947(0xaa)](_0x5295b7);})());const _0x469f38=_0x17b0f9(0xc1),_0x32e03b=_0x17b0f9(0xb4),_0x5b9692=![];let _0x3c12c3=_0x17b0f9(0x93);const _0xe9088=_0x17b0f9(0xb4);if(!_0x3c12c3){;((()=>{const _0x550379=_0x3c12c3+_0xe9088+_0x41125a+_0xe7882a+String(_0x5b9692);throw new TypeError(_0x550379);})());}}}else{const _0x4d7ad0=_0x17b0f9(0x9d);return Dashboard;}if(_0x4b398c!=![])throw new TypeError(_0x17b0f9(0xd0));_0x255626=!![];if(_0x255626!=!![]){class _0x21910b{constructor(_0x20a4d0){const _0x3c7b54=_0x17b0f9;this['config']=_0x20a4d0,console[_0x3c7b54(0xaa)](_0x44721e);}['Gen']=()=>{return _0x1b39af;};['formTypes']=()=>{return _0x3ba3c9;};}return;}},'initDashboard':({fileName:_0x2d5863,domain:_0x9593f9,port:_0x1c094c,token:_0x593ba2,clientSecret:_0xc9184c,clientId:_0x485531})=>{const _0x3ebfde=_0x4d40c6;require(_0x3ebfde(0xa6))({'fileName':_0x2d5863,'domain':_0x9593f9,'port':_0x1c094c,'token':_0x593ba2,'clientSecret':_0xc9184c,'clientId':_0x485531});},'Handler':require(__dirname+_0x4d40c6(0xd5)),'formTypes':require(_0x4d40c6(0x9a)),'customPagesTypes':require(_0x4d40c6(0xd6)),'DISCORD_FLAGS':{'Permissions':require('./ModuleExportsFunctions/discordPermissions')},'version':require(_0x4d40c6(0xc6))[_0x4d40c6(0xaf)]}; -------------------------------------------------------------------------------- /licensedDashboardClass.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Discord-Dashboard/Core/64596b5070ddbec84f46af6aee50d656a61f2a5a/licensedDashboardClass.js -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "discord-dashboard", 3 | "version": "2.3.61", 4 | "description": "Create an Dashboard for your bot in 10 minutes without APIs knowledge and slapping code from scratch!", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "typings": ".d.ts", 10 | "dependencies": { 11 | "assistants-safe-storage": "^1.0.0", 12 | "body-parser": "^1.20.0", 13 | "colors": "^1.4.0", 14 | "cookie-parser": "^1.4.6", 15 | "discord-dashboard": "^2.3.39", 16 | "discord-dashboard-pp-system": "^1.0.2", 17 | "discord-oauth2": "^2.10.0", 18 | "discord.js": "*", 19 | "ejs": "^3.1.8", 20 | "express": "^4.17.3", 21 | "express-partials": "^0.3.0", 22 | "express-rate-limit": "^6.4.0", 23 | "express-session": "^1.17.3", 24 | "https": "^1.0.0", 25 | "keyv": "^4.5.2", 26 | "node-fetch": "^2.6.7", 27 | "readline-sync": "^1.4.10", 28 | "socket.io": "^4.5.1", 29 | "uuid": "^8.3.2" 30 | }, 31 | "scripts": { 32 | "test": "echo \"Error: no test specified\" && exit 1" 33 | }, 34 | "author": "breftejk", 35 | "license": "CC BY-NC-SA 4.0", 36 | "homepage": "https://dbd-docs.assistantscenter.com/", 37 | "keywords": [ 38 | "discord", 39 | "discord.js", 40 | "discordjs", 41 | "discord dashboard", 42 | "discord web dashboard", 43 | "web dashboard", 44 | "dashboard" 45 | ], 46 | "repository": { 47 | "type": "git", 48 | "url": "https://github.com/Assistants-Center/Discord.js-Web-Dashboard.git" 49 | }, 50 | "devDependencies": { 51 | "dbd-dark-dashboard": "^1.6.58", 52 | "javascript-obfuscator": "^4.0.0", 53 | "prettier": "2.7.1" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /router.js: -------------------------------------------------------------------------------- 1 | module.exports = (app, config, themeConfig, modules) => { 2 | app.use(require("cookie-parser")()) 3 | 4 | app.use((req, res, next) => { 5 | req.bot = config.bot 6 | if (req?.session?.user?.blacklisted && (req.url != "/blacklisted/") && (req.url != "/discord/logout/")) return res.redirect("/blacklisted") 7 | next() 8 | }) 9 | 10 | if (themeConfig.defaultLocales) { 11 | app.use((req, res, next) => { 12 | if (req.cookies?.lang) req.lang = req.cookies.lang 13 | else req.lang = req.acceptsLanguages()[0].replace("-", "") 14 | 15 | if (themeConfig.locales) { 16 | if (Object.keys(themeConfig.locales).includes(req.lang)) { 17 | req.locales = themeConfig.locales[req.lang] 18 | } else { 19 | req.locales = 20 | themeConfig.locales[Object.keys(themeConfig.locales)[0]] 21 | } 22 | } else { 23 | req.locales = 24 | themeConfig.defaultLocales[ 25 | Object.keys(themeConfig.defaultLocales).includes( 26 | req.lang 27 | ) 28 | ? req.lang 29 | : "enUS" 30 | ] 31 | } 32 | 33 | next() 34 | }) 35 | } 36 | 37 | app.use( 38 | "/discord", 39 | require("./Routes/discord")(app, config, themeConfig, modules) 40 | ) 41 | 42 | if (config.useUnderMaintenance) { 43 | app.get( 44 | config.underMaintenanceAccessPage || "/total-secret-get-access", 45 | (req, res) => { 46 | res.send(` 47 |
51 | 52 | 53 |
54 | `) 55 | } 56 | ) 57 | 58 | app.post( 59 | config.underMaintenanceAccessPage || "/total-secret-get-access", 60 | (req, res) => { 61 | if (!req.body) req.body = {} 62 | const accessKey = req.body.accessKey 63 | if (accessKey != config.underMaintenanceAccessKey) 64 | return res.send("Wrong key.") 65 | req.session.umaccess = true 66 | res.redirect("/") 67 | } 68 | ) 69 | 70 | app.use((req, res, next) => { 71 | if (req.originalUrl.startsWith("/loading")) return next() 72 | if (!req.session.umaccess && !req.session.user) { 73 | if (!config.useThemeMaintenance) 74 | return res.send( 75 | config.underMaintenanceCustomHtml || 76 | require("./underMaintenancePageDefault")( 77 | config.underMaintenance, 78 | false 79 | ) 80 | ) 81 | else 82 | res.render("maintenance", { 83 | req: req, 84 | bot: config.bot, 85 | themeConfig: req.themeConfig, 86 | loggedIn: false, 87 | defaultMaintenanceConfig: config.underMaintenance || {}, 88 | }) 89 | } else if ( 90 | !req.session.umaccess && 91 | config.ownerIDs && 92 | !config.ownerIDs.includes(req.session.user.id) 93 | ) { 94 | if (!config.useThemeMaintenance) 95 | return res.send( 96 | config.underMaintenanceCustomHtml || 97 | require("./underMaintenancePageDefault")( 98 | config.underMaintenance, 99 | true 100 | ) 101 | ) 102 | else 103 | res.render("maintenance", { 104 | req: req, 105 | bot: config.bot, 106 | themeConfig: req.themeConfig, 107 | loggedIn: true, 108 | defaultMaintenanceConfig: config.underMaintenance || {}, 109 | }) 110 | } else next() 111 | }) 112 | } 113 | 114 | app.use("/", require("./Routes/main")(app, config, themeConfig, modules)) 115 | app.use( 116 | "/", 117 | require("./Routes/dashboard")(app, config, themeConfig, modules) 118 | ) 119 | 120 | config.theme.init(app, config) 121 | 122 | let customPages = config.customPages || [] 123 | customPages.forEach((p) => { 124 | if (p.type == "redirect") { 125 | app.get(p.endpoint, async (req, res) => { 126 | let endpoint = await p.getEndpoint({ 127 | user: req.session.user || {}, 128 | req, 129 | }) 130 | res.redirect(endpoint) 131 | }) 132 | } else if (p.type == "html") { 133 | app.get(p.endpoint, async (req, res) => { 134 | let html = await p.getHtml({ 135 | user: req.session.user || {}, 136 | req, 137 | }) 138 | res.send(html) 139 | }) 140 | } else if (p.type == "json") { 141 | app.get(p.endpoint, async (req, res) => { 142 | let json = await p.getJson({ 143 | user: req.session.user || {}, 144 | req, 145 | }) 146 | res.send(json) 147 | }) 148 | } 149 | }) 150 | 151 | modules.forEach((module) => { 152 | module.app({ 153 | app: app, 154 | config: this.config, 155 | themeConfig: themeConfig, 156 | }) 157 | }) 158 | 159 | if (!config.useTheme404) { 160 | app.get("*", (req, res) => { 161 | let text = 162 | config.html404 || 163 | require("./404pagedefault")( 164 | config.websiteTitle || themeConfig.websiteName 165 | ) 166 | res.send( 167 | text.replace( 168 | "{{websiteTitle}}", 169 | config.websiteTitle || themeConfig.websiteName 170 | ) 171 | ) 172 | }) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /underMaintenancePageDefault.js: -------------------------------------------------------------------------------- 1 | module.exports = ( 2 | { 3 | title, 4 | contentTitle, 5 | texts = [], 6 | bodyBackgroundColors = ["#ffa191", "#ffc247"], 7 | buildingsColor = "#ff6347", 8 | craneDivBorderColor = "#ff6347", 9 | craneArmColor = "#f88f7c", 10 | craneWeightColor = "#f88f7c", 11 | outerCraneColor = "#ff6347", 12 | craneLineColor = "#ff6347", 13 | craneCabinColor = "#f88f7c", 14 | craneStandColors = ["#ff6347", "#f29b8b"], 15 | }, 16 | loggedIn 17 | ) => { 18 | let logged = "Login" 19 | if (loggedIn) logged = "Logout" 20 | return ` 21 | 22 | 23 | 24 | ${title} 25 | 371 | 640 | 641 | 642 |
643 |

${contentTitle}

644 | ${texts 645 | .map((text) => '

' + text + "

") 646 | .join("")} 647 |


648 | ${logged} 649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 | 687 | 688 | ` 689 | } 690 | --------------------------------------------------------------------------------