├── LICENSE ├── README.md ├── assets ├── css │ └── main.css └── js │ ├── permissions.js │ └── themes.js └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Finite Reality 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Discord Permissions Calculator # 2 | 3 | A permission calculator for Discord, for use with Apps and bots. 4 | 5 | ## Features ## 6 | 7 | - Simple calculation of permissions using checkboxes. 8 | - Generate invite links through the calculator. 9 | - Specify additional scopes which may be required for more complex apps. 10 | 11 | ## Contributing ## 12 | 13 | PRs are welcome. If there is something missing, feel free to open an issue 14 | about it. -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | body{ 2 | display:flex; 3 | min-height:100vh; 4 | flex-direction:column; 5 | } 6 | body.theme-dark{ 7 | background: #2c2f33 url(https://discordapp.com/assets/530280c4e76a27a6dd43c0ecabb96e7d.png) fixed; 8 | } 9 | main{ 10 | flex:1 0 auto; 11 | } 12 | .filled-in[type="checkbox"]:checked + label::after{ 13 | border-color:#66bb6a !important; 14 | background-color:#66bb6a !important; 15 | } 16 | .row.centered{ 17 | /* TODO: edge support */ 18 | width: fit-content; 19 | width: -moz-fit-content; 20 | width: -webkit-fit-content; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | -------------------------------------------------------------------------------- /assets/js/permissions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // behold, my beautiful code. 4 | // also: behold the field where my fucks are grown, and thy shall see that it is barren. 5 | 6 | angular.module('permissionsCalc', ['themes']) 7 | .config(['$locationProvider', function($locationProvider) 8 | { 9 | $locationProvider.html5Mode(true); 10 | }]) 11 | .controller('calc', ['$scope', '$location', function($scope, $location) 12 | { 13 | let perms = parseInt($location.search().v); 14 | 15 | let generateScopes = function(scopes) 16 | { 17 | if (scopes === undefined) 18 | return 0; 19 | 20 | const map = { 21 | '' : 0x0, // sentinel 22 | 'bot': 0x1, 23 | 'connections': 0x2, 24 | 'email': 0x4, 25 | 'identify': 0x8, 26 | 'guilds': 0x10, 27 | 'guilds.join': 0x20, 28 | 'gdm.join': 0x40, 29 | 'messages.read': 0x80, 30 | 'rpc': 0x100, 31 | 'rpc.api': 0x200, 32 | 'rpc.notifications.read': 0x400, 33 | 'webhook.incoming': 0x800 34 | } 35 | 36 | return scopes.match(/(?:[\w.]+,?)/g).reduce((acc, val) => acc | map[val.replace(',','')], ''); 37 | } 38 | 39 | $scope.generateInvite = function(provider, info, permissions) 40 | { 41 | if (info === undefined || 42 | info.id === undefined || 43 | permissions === undefined || 44 | provider === undefined) 45 | return ''; 46 | 47 | if (provider === 'discord') 48 | { 49 | if (!info.hasScope && info.hasCode) 50 | return `https://discord.com/oauth2/authorize/?permissions=${permissions}&scope=bot&client_id=${info.id}&response_type=code`; 51 | else if(!info.hasScope) 52 | return `https://discord.com/oauth2/authorize/?permissions=${permissions}&scope=bot&client_id=${info.id}`; 53 | else if (info.hasCode) 54 | return `https://discord.com/oauth2/authorize/?permissions=${permissions}&scope=${info.scope}&client_id=${info.id}&response_type=code`; 55 | else 56 | return `https://discord.com/oauth2/authorize/?permissions=${permissions}&scope=${info.scope}&client_id=${info.id}`; 57 | } 58 | else if (provider === 'embed') 59 | { 60 | if (!info.hasScope && info.hasCode) 61 | return `https://discord.now.sh/${info.id}?p${permissions}&t1`; 62 | else if (!info.hasScope) 63 | return `https://discord.now.sh/${info.id}?p${permissions}`; 64 | else if (info.hasCode) 65 | return `https://discord.now.sh/${info.id}?p${permissions}&s${generateScopes(info.scope)}&t1`; 66 | else 67 | return `https://discord.now.sh/${info.id}?p${permissions}&s${generateScopes(info.scope)}`; 68 | } 69 | } 70 | 71 | $scope.calculatePermissions = function() 72 | { 73 | let value = 0; 74 | for (let sectionId in $scope.permissions) 75 | { 76 | let section = $scope.permissions[sectionId]; 77 | for (let permissionId in section.permissions) 78 | { 79 | let permission = section.permissions[permissionId]; 80 | if (permission.active) 81 | { 82 | value |= permission.value; 83 | } 84 | } 85 | } 86 | $location.search('v', value); 87 | return value; 88 | } 89 | $scope.calculateExplanation = function() 90 | { 91 | let resultSects = [ ]; 92 | for (let sectionId in $scope.permissions) 93 | { 94 | let section = $scope.permissions[sectionId]; 95 | for (let permissionId in section.permissions) 96 | { 97 | let permission = section.permissions[permissionId]; 98 | if (permission.active) 99 | { 100 | resultSects.push('0x'+permission.value.toString(16)); 101 | } 102 | } 103 | } 104 | if (resultSects.length == 0) { resultSects.push('0x0'); } 105 | return resultSects.join(' | '); 106 | } 107 | 108 | $scope.toggle = function(section) 109 | { 110 | for (let permissionId in section.permissions) 111 | { 112 | let permission = section.permissions[permissionId]; 113 | if (permission.auto && section.active) 114 | permission.active = true; 115 | } 116 | } 117 | 118 | $scope.disableActive = function(section) 119 | { 120 | section.active = false; 121 | } 122 | 123 | $scope.permissions = [ 124 | { 125 | name: 'General', 126 | active: false, 127 | permissions: [ 128 | {active: false, id: 'administrator', name: 'Administrator', value: 0x8, auto: false}, 129 | {active: false, id: 'manage_roles', name: 'Manage Roles', value: 0x10000000, auto: false}, 130 | {active: false, id: 'kick_members', name: 'Kick Members', value: 0x2, auto: false}, 131 | {active: false, id: 'instant_invite', name: 'Create Instant Invite', value: 0x1, auto: true }, 132 | {active: false, id: 'manage_nicknames', name: 'Manage Nicknames', value: 0x8000000, auto: true }, 133 | {active: false, id: 'manage_server', name: 'Manage Server', value: 0x20, auto: false}, 134 | {active: false, id: 'manage_channels', name: 'Manage Channels', value: 0x10, auto: false}, 135 | {active: false, id: 'ban_members', name: 'Ban Members', value: 0x4, auto: false}, 136 | {active: false, id: 'change_nickname', name: 'Change Nickname', value: 0x4000000, auto: true }, 137 | {active: false, id: 'manage_webhooks', name: 'Manage Webhooks', value: 0x20000000, auto: false}, 138 | {active: false, id: 'manage_emojis', name: 'Manage Emojis', value: 0x40000000, auto: true }, 139 | {active: false, id: 'view_audit_log', name: 'View Audit Log', value: 0x80, auto: true }, 140 | {active: false, id: 'view_guild_insights', name: 'View Server Insights', value: 0x80000, auto: true }, 141 | {active: false, id: 'view_channel', name: 'View Channel', value: 0x400, auto: true } 142 | ] 143 | }, 144 | { 145 | name: 'Text', 146 | active: false, 147 | permissions: [ 148 | {active: false, id: 'send_tts_messages', name: 'Send TTS Messages', value: 0x1000, auto: true }, 149 | {active: false, id: 'embed_links', name: 'Embed Links', value: 0x4000, auto: true }, 150 | {active: false, id: 'read_message_history', name: 'Read Message History', value: 0x10000, auto: true }, 151 | {active: false, id: 'use_external_emojis', name: 'Use External Emojis', value: 0x40000, auto: true }, 152 | {active: false, id: 'send_messages', name: 'Send Messages', value: 0x800, auto: true }, 153 | {active: false, id: 'manage_messaes', name: 'Manage Messages', value: 0x2000, auto: false}, 154 | {active: false, id: 'attach_files', name: 'Attach Files', value: 0x8000, auto: true }, 155 | {active: false, id: 'mention_everyone', name: 'Mention Everyone', value: 0x20000, auto: true }, 156 | {active: false, id: 'add_reactions', name: 'Add Reactions', value: 0x40, auto: true }, 157 | {active: false, id: 'use_slash_commands', name: 'Use Slash Commands', value: 0x80000000, auto: true } 158 | ] 159 | }, 160 | { 161 | name: 'Voice', 162 | active: false, 163 | permissions: [ 164 | {active: false, id: 'connect', name: 'Connect', value: 0x100000, auto: true }, 165 | {active: false, id: 'mute_members', name: 'Mute Members', value: 0x400000, auto: true }, 166 | {active: false, id: 'move_members', name: 'Move Members', value: 0x1000000, auto: true }, 167 | {active: false, id: 'speak', name: 'Speak', value: 0x200000, auto: true }, 168 | {active: false, id: 'deafen_members', name: 'Deafen Members', value: 0x800000, auto: true }, 169 | {active: false, id: 'use_voice_activity', name: 'Use Voice Activity', value: 0x2000000, auto: true }, 170 | {active: false, id: 'go_live', name: 'Go Live', value: 0x200, auto: true }, 171 | {active: false, id: 'priority_speaker', name: 'Priority Speaker', value: 0x100, auto: true }, 172 | {active: false, id: 'request_to_speak', name: 'Request to Speak', value: 0x100000000, auto: true } 173 | ] 174 | } 175 | ]; 176 | 177 | if (!isNaN(perms)) 178 | { 179 | for (let sectionId in $scope.permissions) 180 | { 181 | let section = $scope.permissions[sectionId]; 182 | for (let permissionId in section.permissions) 183 | { 184 | let permission = section.permissions[permissionId]; 185 | permission.active = (perms & permission.value) != 0; 186 | } 187 | } 188 | } 189 | }]); 190 | -------------------------------------------------------------------------------- /assets/js/themes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // this is a very over-engineered solution that took a long time to make. 4 | // derp. 5 | 6 | $(document).ready(function() 7 | { 8 | $('.button-collapse').sideNav(); 9 | }); 10 | 11 | angular.module('themes', []) 12 | .controller('themer', ['$scope', function($scope) 13 | { 14 | $scope.themedItems = $scope.themedItems || []; 15 | $scope.theme = $scope.theme || localStorage.theme || "Dark"; 16 | 17 | // add and set the current theme 18 | this.addThemedItem = function(item) 19 | { 20 | $scope.themedItems.push(item); 21 | } 22 | this.setTheme = function(theme) 23 | { 24 | $scope.theme = theme; 25 | localStorage.theme = theme; 26 | 27 | angular.forEach($scope.themedItems, function(item) 28 | { 29 | item.e.attr('class', item.a['theme' + theme]); 30 | }); 31 | } 32 | 33 | // apply initial theming 34 | $scope.$watch('themedItems', function() 35 | { 36 | angular.forEach($scope.themedItems, function(item) 37 | { 38 | item.e.attr('class', item.a['theme' + $scope.theme]); 39 | }); 40 | }); 41 | }]) 42 | .directive('theme', function() 43 | { 44 | // this element is themed 45 | let link = function(scope, element, attrs, controller) 46 | { 47 | controller.addThemedItem({e: element, a: attrs}); 48 | } 49 | 50 | return { 51 | link: link, 52 | controller: 'themer' 53 | } 54 | }) 55 | .directive('themeSet', function() 56 | { 57 | // this element sets the theme 58 | let link = function(scope, element, attrs, controller) 59 | { 60 | element.on('click', function() 61 | { 62 | controller.setTheme(attrs.themeSet); 63 | }); 64 | } 65 | 66 | return { 67 | link: link, 68 | controller: 'themer' 69 | }; 70 | }); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Discord Permissions Calculator 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 37 | 41 |
42 |
43 |
44 |
45 |

Calculated permissions: {{calculatePermissions()}}

46 | 0 = 0x0 47 |
48 |
49 |
50 |
Unknown Permissions
51 | 52 |
    53 |
  • 54 | 55 |
  • 56 |
57 |
58 |
59 |
60 |
61 |

62 | Permissions highlighted in orange require that the bot owner has two factor authentication enabled. 63 |

64 |

65 | View Channel and Read Messages are synonymous; they are both the same permission. 66 |

67 |
68 |
69 |
70 |

Invite a bot

71 |
72 |
73 |
74 | 75 |
76 |
77 | 78 |
79 |
80 |
81 |
82 | 83 | 84 |
85 |
86 |
87 |
88 | 89 | 90 |
91 |
92 |
93 |
94 | Add 95 |
96 |
97 | 98 | Embed 99 |
100 |
101 |
102 |
103 |
104 |
105 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 130 | 131 | 132 | --------------------------------------------------------------------------------