├── 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 |
38 | - Light
39 | - Dark
40 |
41 |
42 |
43 |
44 |
45 |
Calculated permissions: {{calculatePermissions()}}
46 | 0 = 0x0
47 |
48 |
49 |
50 |
Unknown Permissions
51 |
52 |
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 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
101 |
102 |
103 |
104 |
105 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
130 |
131 |
132 |
--------------------------------------------------------------------------------