├── .github └── stale.yml ├── .gitignore ├── .prettierrc.js ├── ChangeLog.js ├── Components ├── Settings.jsx └── Tag.jsx ├── LICENSE ├── README.md ├── changelog.json ├── constants.js ├── index.js ├── manifest.json └── style.css /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - enhancement 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. 15 | # Comment to post when closing a stale issue. Set to `false` to disable 16 | closeComment: true 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debug.log 2 | node_modules/ -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | jsxSingleQuote: true, 4 | trailingComma: "none", 5 | useTabs: false, 6 | tabWidth: 4, 7 | semi: true, 8 | arrowParens: "avoid", 9 | }; 10 | -------------------------------------------------------------------------------- /ChangeLog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is from https://github.com/powerfart-plugins/image-tools/blob/main/modules/ChangeLog.js 3 | */ 4 | 5 | const { 6 | React, 7 | getModule, 8 | getModuleByDisplayName 9 | } = require('powercord/webpack'); 10 | const { findInReactTree } = require('powercord/util'); 11 | const { open: openModal } = require('powercord/modal'); 12 | 13 | const ChangelogStandardTemplate = getModuleByDisplayName( 14 | 'ChangelogStandardTemplate', 15 | false 16 | ); 17 | const { parse: toMarkdown } = getModule(['parse', 'parseTopic'], false); 18 | 19 | /** 20 | * @typedef ChangelogConfig 21 | * @type {Object} args. 22 | * @property {String} args.date - format: YYYY-MM-DD. 23 | * @property {String} [args.image] - link to image (1920x1080, (16:9)). 24 | * @property {String} [args.locale = 'en-us'] - text's locale. 25 | * @property {Number} [args.revision = 1] - idk what this. 26 | * @property {ChangelogConfigBodyItem} args.body - body. 27 | * @property {String} [args.footer] - modal footer. 28 | */ 29 | 30 | /** 31 | * @typedef ChangelogConfigBodyItem 32 | * @type {Object} args 33 | * @property {String} args.type - types: added, fixed, improved, and progress. 34 | * @property {String} args.header - category name. 35 | * @property {Array} args.content - If the String is text if an Array is a list, support sub lists 36 | */ 37 | 38 | /* eslint-disable no-use-before-define, no-undefined */ 39 | // noinspection JSUnusedGlobalSymbols 40 | module.exports = class ChangelogManager { 41 | /** 42 | * @param {Object} args 43 | * @param {ChangelogConfig} args.config 44 | * @param {String} args.currentVer type 1.2.3 45 | * @param {String} args.lastCheckedVer type 1.2.3 46 | * @param {Function} args.updateLastCheckedVer 47 | */ 48 | constructor(args) { 49 | this.args = args; 50 | } 51 | 52 | /** 53 | * @return {boolean} 54 | */ 55 | get needChangeLog() { 56 | const get = s => s.split(/\./g); 57 | const currVers = get(this.args.currentVer); 58 | const lastVers = get(this.args.lastCheckedVer); 59 | 60 | while (currVers.length || lastVers.length) { 61 | const a = Number(currVers.shift()); 62 | const b = Number(lastVers.shift()); 63 | 64 | if (a === b) { 65 | continue; 66 | } 67 | return a > b || isNaN(b); 68 | } 69 | return false; 70 | } 71 | 72 | /** 73 | * @return Void 74 | */ 75 | init() { 76 | if (this.needChangeLog) { 77 | openModal(() => 78 | React.createElement(Changelog, { 79 | changeLog: this.parseChangeLog(this.args.config), 80 | onClose: () => 81 | this.args.updateLastCheckedVer(this.args.currentVer), 82 | onScroll: () => null 83 | }) 84 | ); 85 | } 86 | } 87 | 88 | /** 89 | * @param {ChangelogConfig} json 90 | * @return {Object} acceptable data for ChangelogStandardTemplate 91 | */ 92 | parseChangeLog(json) { 93 | const defaultParams = { 94 | locale: 'en-us', 95 | revision: 1, 96 | version: this.args.currentVer 97 | }; 98 | const parseContent = (content, depp = 0) => { 99 | if (Array.isArray(content)) { 100 | const prefix = `${' '.repeat(depp)} * `; 101 | return content 102 | .map( 103 | e => 104 | `${Array.isArray(e) ? '' : prefix}${parseContent( 105 | e, 106 | depp + 1 107 | )}` 108 | ) 109 | .join(''); 110 | } 111 | return `${content}\n`; 112 | }; 113 | 114 | return { 115 | ...defaultParams, 116 | ...json, 117 | 118 | body: json.body.reduce((acc, item) => { 119 | acc += `${item.header.toUpperCase()} {${ 120 | item.type 121 | }}\n======================\n\n`; 122 | item.content.forEach(e => { 123 | acc += `${parseContent(e)}\n`; 124 | }); 125 | return acc; 126 | }, '') 127 | }; 128 | } 129 | }; 130 | 131 | class Changelog extends ChangelogStandardTemplate { 132 | constructor(props) { 133 | super(props); 134 | this.superRenderHeader = this.renderHeader; 135 | this.renderHeader = this.customRenderHeader.bind(this); 136 | this.renderFooter = this.renderFooter.bind(this); 137 | } 138 | 139 | customRenderHeader() { 140 | const res = this.superRenderHeader(); 141 | const Header = findInReactTree( 142 | res, 143 | ({ type }) => type?.displayName === 'Heading' 144 | ); 145 | const Text = findInReactTree( 146 | res, 147 | ({ type }) => type?.displayName === 'Text' 148 | ); 149 | 150 | Header.props.children = `Staff Tags - ${Header.props.children}`; 151 | Text.props.children = `${Text.props.children} - v${this.props.changeLog.version}`; 152 | return res; 153 | } 154 | 155 | renderFooter() { 156 | const footer = super.renderFooter(); 157 | footer.props.children = React.createElement('span', { 158 | style: { color: 'var(--text-normal)' }, 159 | children: toMarkdown(this.props.changeLog.footer) 160 | }); 161 | return footer; 162 | } 163 | 164 | componentWillUnmount() { 165 | this.props.onClose(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Components/Settings.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable indent */ 2 | /* Essential Packages */ 3 | const { React, constants } = require('powercord/webpack'); 4 | const Permissions = constants.Permissions; 5 | 6 | /* Plugin Specific Packages */ 7 | // There are many more componenets available in "powercord/components/settings". 8 | const { 9 | SwitchItem, 10 | TextInput, 11 | ColorPickerInput 12 | } = require('powercord/components/settings'); 13 | 14 | module.exports = class Settings extends React.Component { 15 | constructor(props) { 16 | super(props); 17 | 18 | this.state = {}; 19 | } 20 | 21 | /** 22 | * Renderer, this is what's being executed on line 22 of index.js 23 | * The example here displays a toggle between displaying a cat or a dog. 24 | * */ 25 | 26 | render() { 27 | return ( 28 |
29 | { 32 | this.props.toggleSetting('showOwnerTags', true); 33 | }} 34 | note="If disabled, owner tags won't show anywhere" 35 | > 36 | Show Owner Tags 37 | 38 | { 41 | this.props.toggleSetting('showAdminTags', true); 42 | }} 43 | note="If disabled, admin tags won't be shown anywhere. Admin tags look for the Administrator permission" 44 | > 45 | Show Admin Tags 46 | 47 | { 50 | this.props.toggleSetting('showModTags', true); 51 | }} 52 | note="If disabled, mod tags won't be shown anywhere. Mod tags look for kick/ban members and manage message permission" 53 | > 54 | Show Mod Tags 55 | 56 | { 59 | this.props.toggleSetting('showStaffTags', true); 60 | }} 61 | note="If disabled, staff tags won't be shown anywhere. Staff tags look for manage channels, manage server, or manage roles" 62 | > 63 | Show Staff Tags 64 | 65 | {/* {this.props.getSetting("showStaffTags") && ( 66 | 73 | this.setState({ categoryOpened: !this.state.categoryOpened }) 74 | } 75 | > 76 | // TODO 77 |
78 |
79 | )} */} 80 | { 83 | this.props.toggleSetting('displayMessages', true); 84 | }} 85 | note="If disabled, badges won't be shown in chat." 86 | > 87 | Show in Chat 88 | 89 | 90 | { 93 | this.props.toggleSetting('showCrowns', false); 94 | }} 95 | note='If enabled, Crowns will be displayed instead of Tags' 96 | > 97 | Show crowns instead of Tags 98 | 99 | 100 | { 103 | this.props.toggleSetting('displayMembers', true); 104 | }} 105 | note="If disabled, badges won't be shown in the member list." 106 | > 107 | Show in Member List 108 | 109 | { 112 | this.props.toggleSetting('showForBots', true); 113 | }} 114 | note="If disabled, badges won't be shown anywhere for bots." 115 | > 116 | Show for Bots 117 | 118 | 119 | { 122 | this.props.toggleSetting('customTagColors', false); 123 | }} 124 | note='Enable to select custom colors for tags. Enable to show settings.' 125 | > 126 | Change Tag Colors 127 | 128 | 129 | {this.props.getSetting('customTagColors') && ( 130 | <> 131 | { 137 | this.props.toggleSetting('useCustomOwnerColor'); 138 | }} 139 | note='If enabled, custom colors entered below will be used (if color box is empty, highest role color is used)' 140 | > 141 | Use Custom Owner Color 142 | 143 | {this.props.getSetting('useCustomOwnerColor') && ( 144 | 149 | this.props.updateSetting( 150 | 'ownerTagColor', 151 | c ? this._numberToHex(c) : null 152 | ) 153 | } 154 | default={parseInt('ED9F1B', 16)} 155 | value={this.getColorSetting('ownerTagColor')} 156 | > 157 | Owner Tag Color 158 | 159 | )} 160 | {this.props.getSetting('useCustomOwnerColor') && ( 161 | { 167 | this.props.toggleSetting( 168 | 'GroupOwnerColor', 169 | true 170 | ); 171 | }} 172 | note='If enabled, Group Owner tag color will be same as Server Owner tag color' 173 | > 174 | Use Custom Group owner Color 175 | 176 | )} 177 | 178 | { 184 | this.props.toggleSetting('useCustomAdminColor'); 185 | }} 186 | note='If enabled, custom colors entered below will be used (if color box is empty, highest role color is used)' 187 | > 188 | Use Custom Admin Color 189 | 190 | {this.props.getSetting('useCustomAdminColor') && ( 191 | 196 | this.props.updateSetting( 197 | 'adminTagColor', 198 | c ? this._numberToHex(c) : null 199 | ) 200 | } 201 | default={parseInt('B4B4B4', 16)} 202 | value={this.getColorSetting('adminTagColor')} 203 | > 204 | Admin Tag Color 205 | 206 | )} 207 | 208 | { 214 | this.props.toggleSetting('useCustomStaffColor'); 215 | }} 216 | note='If enabled, custom colors entered below will be used (if color box is empty, highest role color is used)' 217 | > 218 | Use Custom Staff Color 219 | 220 | {this.props.getSetting('useCustomStaffColor') && ( 221 | 226 | this.props.updateSetting( 227 | 'staffTagColor', 228 | c ? this._numberToHex(c) : null 229 | ) 230 | } 231 | default={parseInt('8D5C51', 16)} 232 | value={this.getColorSetting('staffTagColor')} 233 | > 234 | Staff Tag Color 235 | 236 | )} 237 | 238 | { 244 | this.props.toggleSetting('useCustomModColor'); 245 | }} 246 | note='If enabled, custom colors entered below will be used (if color box is empty, highest role color is used)' 247 | > 248 | Use Custom Mod Color 249 | 250 | {this.props.getSetting('useCustomModColor') && ( 251 | 256 | this.props.updateSetting( 257 | 'modTagColor', 258 | c ? this._numberToHex(c) : null 259 | ) 260 | } 261 | default={parseInt('C8682E', 16)} 262 | value={this.getColorSetting('modTagColor')} 263 | > 264 | Mod Tag Color 265 | 266 | )} 267 | 268 | )} 269 | 270 | { 273 | this.props.toggleSetting('customTagText'); 274 | }} 275 | note='Enables customizing text of the tags. Enable to show settings.' 276 | > 277 | Enable Custom Tag Text 278 | 279 | 280 | {this.props.getSetting('customTagText') && ( 281 | <> 282 | 287 | this.props.updateSetting('ownerTagText', c) 288 | } 289 | defaultValue={this.props.getSetting( 290 | 'ownerTagText', 291 | 'Owner' 292 | )} 293 | required={true} 294 | > 295 | Owner Tag Text 296 | 297 | 302 | this.props.updateSetting('adminTagText', c) 303 | } 304 | defaultValue={this.props.getSetting( 305 | 'adminTagText', 306 | 'Admin' 307 | )} 308 | required={true} 309 | > 310 | Admin Tag Text 311 | 312 | 317 | this.props.updateSetting('modTagText', c) 318 | } 319 | defaultValue={this.props.getSetting( 320 | 'modTagText', 321 | 'Mod' 322 | )} 323 | required={true} 324 | > 325 | Mod Tag Text 326 | 327 | 332 | this.props.updateSetting('staffTagText', c) 333 | } 334 | defaultValue={this.props.getSetting( 335 | 'staffTagText', 336 | 'Staff' 337 | )} 338 | required={true} 339 | > 340 | Staff Tag Text 341 | 342 | 343 | )} 344 | 345 |
346 |

Owner: Is the owner of a Server or Group Chat

347 |

Admin: Has the Administrator permission

348 | 349 |

350 | Mod: Has one of the following permissions 351 |

    352 |
  • - Kick Members
  • 353 |
  • - Ban Members
  • 354 |
  • - Manage Messages
  • 355 |
356 |

357 | 358 |

359 | Staff: Has one of the following permissions 360 |

    361 |
  • - Manage Server
  • 362 |
  • - Manage Channels
  • 363 |
  • - Manage Roles
  • 364 |
365 |

366 |
367 |
368 | ); 369 | } 370 | 371 | getTagTextSetting(setting, def) { 372 | const string = this.props.getSetting(setting); 373 | return string ? string : def; 374 | } 375 | 376 | getColorSetting(setting) { 377 | const hex = this.props.getSetting(setting); 378 | return hex ? parseInt(hex.slice(1), 16) : 0; 379 | } 380 | 381 | _numberToHex(color) { 382 | const r = (color & 0xff0000) >>> 16; 383 | const g = (color & 0xff00) >>> 8; 384 | const b = color & 0xff; 385 | return `#${r.toString(16).padStart(2, '0')}${g 386 | .toString(16) 387 | .padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; 388 | } 389 | }; 390 | -------------------------------------------------------------------------------- /Components/Tag.jsx: -------------------------------------------------------------------------------- 1 | const { React } = require('powercord/webpack'); 2 | 3 | const userTypes = { 4 | NONE: 'None', 5 | STAFF: 'Staff', 6 | MOD: 'Mod', 7 | ADMIN: 'Admin', 8 | SOWNER: 'Server Owner', 9 | GOWNER: 'Group Owner' 10 | }; 11 | const DEFAULT_TAG_TEXTS = { 12 | staff: 'Staff', 13 | mod: 'Mod', 14 | admin: 'Admin', 15 | owner: 'Owner' 16 | }; 17 | 18 | class Tag extends React.PureComponent { 19 | constructor(props) { 20 | super(props); 21 | this.state = { userType: userTypes.NONE }; 22 | } 23 | 24 | getTagText(tagType) { 25 | const customTextEnabled = this.props.settings.get( 26 | 'customTagText', 27 | false 28 | ); 29 | const tagText = this.props.settings.get(`${tagType}TagText`); 30 | return customTextEnabled ? tagText : DEFAULT_TAG_TEXTS[tagType]; 31 | } 32 | 33 | render() { 34 | if (!this.props.className || !this.props.userType) return null; 35 | if ( 36 | this.props.userType === userTypes.SOWNER || 37 | this.props.userType === userTypes.GOWNER 38 | ) { 39 | return ( 40 |
41 | {this.getTagText('owner')} 42 |
43 | ); 44 | } else if (this.props.userType === userTypes.ADMIN) { 45 | return ( 46 |
47 | {this.getTagText('admin')} 48 |
49 | ); 50 | } else if (this.props.userType === userTypes.MOD) { 51 | return ( 52 |
53 | {this.getTagText('mod')} 54 |
55 | ); 56 | } else if (this.props.userType === userTypes.STAFF) { 57 | return ( 58 |
59 | {this.getTagText('staff')} 60 |
61 | ); 62 | } 63 | return null; 64 | } 65 | } 66 | 67 | Tag.cache = {}; 68 | module.exports = Tag; 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Puyodead1 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 | # pc-stafftags 2 | 3 | Displays owner, admin, and staff tags similar to bot tags, they can appear in the member list and/or chat. 4 | 5 | | Preview | Settings | 6 | | ------------------------------------ | ------------------------------------ | 7 | | ![](https://i.imgur.com/4x8dnFA.png) | ![](https://i.imgur.com/seVBXw1.png) | 8 | | ![](https://i.imgur.com/6NwdauH.png) | | 9 | 10 | --- 11 | 12 | dis is my first plugin, be kind plz <3 13 | -------------------------------------------------------------------------------- /changelog.json: -------------------------------------------------------------------------------- 1 | { 2 | "date": "2022-08-23", 3 | "image": "https://c.tenor.com/4blWuIh5MIYAAAAC/baby-yoda.gif", 4 | "body": [ 5 | { 6 | "type": "fixed", 7 | "header": "Fixed", 8 | "content": ["- **Owner Tag** Fixed `showOwnerTag` setting."] 9 | }, 10 | { 11 | "type": "added", 12 | "header": "Added", 13 | "content": [ 14 | "- **Bot Tags** Bot tags will now show in chat when `showForBots` setting is enabled." 15 | ] 16 | } 17 | ], 18 | "footer": "" 19 | } 20 | -------------------------------------------------------------------------------- /constants.js: -------------------------------------------------------------------------------- 1 | module.exports.userTypes = { 2 | NONE: 'None', 3 | STAFF: 'Staff', 4 | MOD: 'Mod', 5 | ADMIN: 'Admin', 6 | SOWNER: 'Server Owner', 7 | GOWNER: 'Group Owner' 8 | }; 9 | 10 | module.exports.DEFAULT_TAG_TEXTS = { 11 | staff: 'Staff', 12 | mod: 'Mod', 13 | admin: 'Admin', 14 | owner: 'Owner' 15 | }; 16 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { Plugin } = require('powercord/entities'); 2 | const { inject, uninject } = require('powercord/injector'); 3 | const { findInReactTree } = require('powercord/util'); 4 | const { 5 | React, 6 | getModule, 7 | getModuleByDisplayName, 8 | constants: { 9 | Permissions 10 | }, 11 | } = require('powercord/webpack'); 12 | const { getGuild } = getModule(['getGuild'], false); 13 | const { 14 | default: { getMember } 15 | } = getModule(m => m.default && m.default.getMember, false); 16 | 17 | const parseBitFieldPermissions = allowed => { 18 | const permissions = {}; 19 | for (const perm of Object.keys(Permissions)) { 20 | if (!perm.startsWith('all')) { 21 | if (BigInt(allowed) & BigInt(Permissions[perm])) { 22 | permissions[perm] = true; 23 | } 24 | } 25 | } 26 | return permissions; 27 | }; 28 | 29 | const { userTypes, DEFAULT_TAG_TEXTS } = require('./constants'); 30 | 31 | function getPermissionsRaw(guild, user_id) { 32 | let permissions = 0n; 33 | 34 | const member = getMember(guild.id, user_id); 35 | 36 | if (guild && member) { 37 | if (guild.ownerId === user_id) { 38 | permissions = BigInt(Permissions.ADMINISTRATOR); 39 | } else { 40 | /* @everyone is not inlcuded in the member's roles */ 41 | permissions |= BigInt(guild.roles[guild.id]?.permissions); 42 | 43 | for (const roleId of member.roles) { 44 | const rolePerms = guild.roles[roleId]?.permissions; 45 | if (rolePerms !== undefined) { 46 | permissions |= BigInt(rolePerms); 47 | } 48 | } 49 | } 50 | 51 | /* If they have administrator they have every permission */ 52 | if ( 53 | (BigInt(permissions) & BigInt(Permissions.ADMINISTRATOR)) === 54 | BigInt(Permissions.ADMINISTRATOR) 55 | ) { 56 | return Object.values(Permissions).reduce( 57 | (a, b) => BigInt(a) | BigInt(b), 58 | 0n 59 | ); 60 | } 61 | } 62 | 63 | return permissions; 64 | } 65 | 66 | const Tooltip = getModule(['TooltipContainer'], false).TooltipContainer; 67 | 68 | const Settings = require('./Components/Settings.jsx'); 69 | const Tag = require('./Components/Tag'); 70 | const ChangeLog = require('./ChangeLog'); 71 | const changelog = require('./changelog.json'); 72 | 73 | module.exports = class StaffTags extends Plugin { 74 | constructor() { 75 | super(); 76 | this.ChangeLog = new ChangeLog({ 77 | config: changelog, 78 | currentVer: this.manifest.version, 79 | lastCheckedVer: this.settings.get('lastChangeLogVersion', '0'), 80 | updateLastCheckedVer: v => 81 | this.settings.set('lastChangeLogVersion', v) 82 | }); 83 | } 84 | 85 | async startPlugin() { 86 | this.loadStylesheet('style.css'); 87 | powercord.api.settings.registerSettings(this.entityID, { 88 | category: this.entityID, 89 | label: this.manifest.name, // Label that appears in the settings menu 90 | render: Settings // The React component to render. In this case, the imported Settings file 91 | }); 92 | 93 | this.injectMessages(); 94 | this.injectMembers(); 95 | 96 | this.ChangeLog.init(); 97 | } 98 | 99 | getTagText(tagType) { 100 | switch (tagType) { 101 | case userTypes.ADMIN: 102 | tagType = 'admin'; 103 | break; 104 | case userTypes.GOWNER: 105 | case userTypes.SOWNER: 106 | tagType = 'owner'; 107 | break; 108 | case userTypes.MOD: 109 | tagType = 'mod'; 110 | break; 111 | case userTypes.STAFF: 112 | tagType = 'staff'; 113 | break; 114 | } 115 | const customTextEnabled = this.settings.get('customTagText', false); 116 | const tagText = this.settings.get(`${tagType}TagText`); 117 | return customTextEnabled ? tagText : DEFAULT_TAG_TEXTS[tagType]; 118 | } 119 | 120 | async injectMessages() { 121 | const { getGuildId } = await getModule(['getLastSelectedGuildId']); 122 | const _this = this; 123 | const MessageTimestamp = 124 | getModule(['MessageTimestamp'], false) || 125 | getModule( 126 | m => 127 | typeof (m?.__powercordOriginal_default || m.default) === 128 | 'function' && 129 | (m?.__powercordOriginal_default || m.default) 130 | .toString() 131 | .includes('showTimestampOnHover'), 132 | false 133 | ); // credit to M|-|4r13y ツ#1051 for this snippet 134 | const botTagRegularClasses = await getModule(['botTagRegular']); 135 | const botTagCozyClasses = await getModule(['botTagCozy']); 136 | const remClasses = await getModule(['rem']); 137 | 138 | /** 139 | * The following injects a function into the specified module. 140 | * Parameter 1: The InjectionID, used to uninject. 141 | * 2: The module you want to inject into. 142 | * 3: The function name you want to target. 143 | * 4: The function you want to inject. 144 | */ 145 | inject( 146 | 'ownertag-messages', 147 | MessageTimestamp, 148 | 'default', 149 | (args, res) => { 150 | if (!_this.settings.get('displayMessages', true)) { 151 | return res; 152 | } 153 | 154 | if ( 155 | !_this.settings.get('showForBots', true) && 156 | args[0].message.author.bot 157 | ) { 158 | return res; 159 | } 160 | 161 | const id = args[0].message.author.id; 162 | 163 | const header = findInReactTree( 164 | res.props.username, 165 | e => 166 | Array.isArray(e.props?.children) && 167 | e.props.children.find(c => c?.props?.message) 168 | ); 169 | if (!header) return res; 170 | let data; 171 | 172 | const channel = args[0].channel; 173 | const guild = getGuild(getGuildId()); 174 | if (!guild && !channel) return res; 175 | 176 | if (guild) { 177 | const member = getMember(guild.id, id); 178 | if (!member) return res; 179 | const permissions = getPermissionsRaw(guild, id); 180 | const parsedPermissions = 181 | parseBitFieldPermissions(permissions); 182 | 183 | if (guild.ownerId === id) { 184 | // is guild owner 185 | const tagColor = _this.settings.get( 186 | 'ownerTagColor', 187 | '#ED9F1B' 188 | ); 189 | const useCustomColor = _this.settings.get( 190 | 'useCustomOwnerColor' 191 | ); 192 | data = { 193 | userType: _this.settings.get('showOwnerTags', true) 194 | ? userTypes.SOWNER 195 | : userTypes.NONE, 196 | color: 197 | useCustomColor && tagColor 198 | ? tagColor 199 | : member.colorString, 200 | textColor: _this._numberToTextColor( 201 | useCustomColor && tagColor 202 | ? tagColor 203 | : member.colorString 204 | ) 205 | }; 206 | } else if (parsedPermissions['ADMINISTRATOR']) { 207 | const tagColor = _this.settings.get( 208 | 'adminTagColor', 209 | '#B4B4B4' 210 | ); 211 | const useCustomColor = _this.settings.get( 212 | 'useCustomAdminColor' 213 | ); 214 | data = { 215 | userType: _this.settings.get('showAdminTags', true) 216 | ? userTypes.ADMIN 217 | : userTypes.NONE, 218 | color: 219 | useCustomColor && tagColor 220 | ? tagColor 221 | : member.colorString, 222 | textColor: _this._numberToTextColor( 223 | useCustomColor && tagColor 224 | ? tagColor 225 | : member.colorString 226 | ) 227 | }; 228 | } else if ( 229 | parsedPermissions['MANAGE_SERVER'] || 230 | parsedPermissions['MANAGE_CHANNELS'] || 231 | parsedPermissions['MANAGE_ROLES'] 232 | ) { 233 | const tagColor = _this.settings.get( 234 | 'staffTagColor', 235 | '#8D5C51' 236 | ); 237 | const useCustomColor = _this.settings.get( 238 | 'useCustomStaffColor' 239 | ); 240 | data = { 241 | userType: _this.settings.get('showStaffTags', true) 242 | ? userTypes.STAFF 243 | : userTypes.NONE, 244 | color: 245 | useCustomColor && tagColor 246 | ? tagColor 247 | : member.colorString, 248 | textColor: _this._numberToTextColor( 249 | useCustomColor && tagColor 250 | ? tagColor 251 | : member.colorString 252 | ) 253 | }; 254 | } else if ( 255 | parsedPermissions['KICK_MEMBERS'] || 256 | parsedPermissions['BAN_MEMBERS'] || 257 | parsedPermissions['MANAGE_MESSAGES'] 258 | ) { 259 | const tagColor = _this.settings.get( 260 | 'modTagColor', 261 | '#C8682E' 262 | ); 263 | const useCustomColor = 264 | _this.settings.get('useCustomModColor'); 265 | data = { 266 | userType: _this.settings.get('showModTags', true) 267 | ? userTypes.MOD 268 | : userTypes.NONE, 269 | color: 270 | useCustomColor && tagColor 271 | ? tagColor 272 | : member.colorString, 273 | textColor: _this._numberToTextColor( 274 | useCustomColor && tagColor 275 | ? tagColor 276 | : member.colorString 277 | ) 278 | }; 279 | } 280 | } else if (channel.type === 3 && channel.ownerId === id) { 281 | // group channel 282 | const tagColor = _this.settings.get( 283 | 'ownerTagColor', 284 | '#ED9F1B' 285 | ); 286 | const useCustomColor = _this.settings.get( 287 | 'useCustomOwnerColor' 288 | ); 289 | data = { 290 | userType: userTypes.GOWNER, 291 | color: useCustomColor && tagColor ? tagColor : null 292 | }; 293 | } 294 | 295 | //const element = React.createElement(Tag, { userid: id }); 296 | if (data && data.userType !== userTypes.NONE) { 297 | // const textColor = _this.settings.get('textColor'); 298 | if (_this.settings.get('showCrowns', false)) { 299 | const element = React.createElement( 300 | Tooltip, 301 | { 302 | text: `${data.userType}`, 303 | className: 'OwnerTag-13h21hk' 304 | }, 305 | React.createElement( 306 | 'svg', 307 | { 308 | className: `${botTagCozyClasses.botTagCozy} ${botTagRegularClasses.botTagRegular} ${remClasses.rem} ownertag`, 309 | 'aria-label': `${data.userType}`, 310 | 'aria-hidden': 'false', 311 | width: 14, 312 | height: 14, 313 | viewBox: '0 0 16 16', 314 | style: { 315 | color: data.color, 316 | backgroundColor: 'transparent' 317 | } 318 | }, 319 | React.createElement('path', { 320 | fillRule: 'evenodd', 321 | clipRule: 'evenodd', 322 | d: 'M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z', 323 | fill: 'currentColor', 324 | 'aria-hidden': 'true' 325 | }) 326 | ) 327 | ); 328 | header.props.children.push(element); 329 | } else { 330 | const element = React.createElement( 331 | 'span', 332 | { 333 | className: `${botTagCozyClasses.botTagCozy} ${botTagRegularClasses.botTagRegular} ${remClasses.rem} ownertag`, 334 | style: { 335 | backgroundColor: data.color, 336 | color: data.textColor 337 | } 338 | }, 339 | React.createElement(Tag, { 340 | className: botTagRegularClasses.botText, 341 | userType: data.userType, 342 | settings: _this.settings 343 | }) 344 | ); 345 | header.props.children.push(element); 346 | } 347 | } 348 | return res; 349 | } 350 | ); 351 | } 352 | 353 | async injectMembers() { 354 | const _this = this; 355 | const MemberListItem = 356 | getModuleByDisplayName('MemberListItem', false) || 357 | getModule(['AVATAR_DECORATION_PADDING'], false)?.default; 358 | const MemberListItemTarget = { 359 | module: MemberListItem.prototype || MemberListItem, 360 | method: MemberListItem.prototype ? 'renderDecorators' : 'type' 361 | }; 362 | const botTagRegularClasses = await getModule(['botTagRegular']); 363 | const remClasses = await getModule(['rem']); 364 | 365 | inject( 366 | 'ownertag-members-1', 367 | MemberListItemTarget.module, 368 | MemberListItemTarget.method, 369 | function (_, res) { 370 | const MemberListItem = res.type; 371 | inject( 372 | 'ownertag-members-2', 373 | MemberListItem.prototype, 374 | 'renderDecorators', 375 | function (_, res) { 376 | if (!_this.settings.get('displayMembers', true)) { 377 | return res; 378 | } 379 | 380 | const user = this.props.user; 381 | if (!user) return res; 382 | 383 | // check if the user is a bot and rendering bots is enabled 384 | if ( 385 | !_this.settings.get('showForBots', true) && 386 | user.bot 387 | ) { 388 | return res; 389 | } 390 | 391 | let data; 392 | 393 | const id = user.id; 394 | const guild = getGuild(this.props.channel.guild_id); 395 | if (guild) { 396 | const member = getMember(guild.id, id); 397 | const permissions = getPermissionsRaw(guild, id); 398 | const parsedPermissions = 399 | parseBitFieldPermissions(permissions); 400 | 401 | if (guild.ownerId === id) { 402 | // is guild owner 403 | const tagColor = _this.settings.get( 404 | 'ownerTagColor', 405 | '#ED9F1B' 406 | ); 407 | const useCustomColor = _this.settings.get( 408 | 'useCustomOwnerColor' 409 | ); 410 | data = { 411 | userType: _this.settings.get( 412 | 'showOwnerTags', 413 | true 414 | ) 415 | ? userTypes.SOWNER 416 | : userTypes.NONE, 417 | color: 418 | useCustomColor && tagColor 419 | ? tagColor 420 | : member.colorString, 421 | textColor: _this._numberToTextColor( 422 | useCustomColor && tagColor 423 | ? tagColor 424 | : member.colorString 425 | ) 426 | }; 427 | } else if (parsedPermissions['ADMINISTRATOR']) { 428 | const tagColor = _this.settings.get( 429 | 'adminTagColor', 430 | '#B4B4B4' 431 | ); 432 | const useCustomColor = _this.settings.get( 433 | 'useCustomAdminColor' 434 | ); 435 | data = { 436 | userType: _this.settings.get( 437 | 'showAdminTags', 438 | true 439 | ) 440 | ? userTypes.ADMIN 441 | : userTypes.NONE, 442 | color: 443 | useCustomColor && tagColor 444 | ? tagColor 445 | : member.colorString, 446 | textColor: _this._numberToTextColor( 447 | useCustomColor && tagColor 448 | ? tagColor 449 | : member.colorString 450 | ) 451 | }; 452 | } else if ( 453 | parsedPermissions['MANAGE_SERVER'] || 454 | parsedPermissions['MANAGE_CHANNELS'] || 455 | parsedPermissions['MANAGE_ROLES'] 456 | ) { 457 | const tagColor = _this.settings.get( 458 | 'staffTagColor', 459 | '#8D5C51' 460 | ); 461 | const useCustomColor = _this.settings.get( 462 | 'useCustomStaffColor' 463 | ); 464 | data = { 465 | userType: _this.settings.get( 466 | 'showStaffTags', 467 | true 468 | ) 469 | ? userTypes.STAFF 470 | : userTypes.NONE, 471 | color: 472 | useCustomColor && tagColor 473 | ? tagColor 474 | : member.colorString, 475 | textColor: _this._numberToTextColor( 476 | useCustomColor && tagColor 477 | ? tagColor 478 | : member.colorString 479 | ) 480 | }; 481 | } else if ( 482 | parsedPermissions['KICK_MEMBERS'] || 483 | parsedPermissions['BAN_MEMBERS'] || 484 | parsedPermissions['MANAGE_MESSAGES'] 485 | ) { 486 | const tagColor = _this.settings.get( 487 | 'modTagColor', 488 | '#C8682E' 489 | ); 490 | const useCustomColor = 491 | _this.settings.get('useCustomModColor'); 492 | data = { 493 | userType: _this.settings.get( 494 | 'showModTags', 495 | true 496 | ) 497 | ? userTypes.MOD 498 | : userTypes.NONE, 499 | color: 500 | useCustomColor && tagColor 501 | ? tagColor 502 | : member.colorString, 503 | textColor: _this._numberToTextColor( 504 | useCustomColor && tagColor 505 | ? tagColor 506 | : member.colorString 507 | ) 508 | }; 509 | } 510 | } else if ( 511 | this.props.channel.type === 3 && 512 | this.props.channel.ownerId === id 513 | ) { 514 | // group channel 515 | const tagColor = _this.settings.get( 516 | 'ownerTagColor', 517 | '#ED9F1B' 518 | ); 519 | const useCustomColor = _this.settings.get( 520 | 'useCustomOwnerColor' 521 | ); 522 | data = { 523 | userType: userTypes.GOWNER, 524 | color: 525 | useCustomColor && tagColor ? tagColor : null 526 | }; 527 | } 528 | 529 | if (data && data.userType !== userTypes.NONE) { 530 | if (_this.settings.get('showCrowns', false)) { 531 | const element = React.createElement( 532 | Tooltip, 533 | { 534 | text: _this.getTagText(data.userType), 535 | className: 'OwnerTag-13h21hk' 536 | }, 537 | React.createElement( 538 | 'svg', 539 | { 540 | className: `${remClasses.botTag} ${botTagRegularClasses.botTagRegular} ${remClasses.px} ownertag-list`, 541 | 'aria-label': `${data.userType}`, 542 | 'aria-hidden': 'false', 543 | width: 14, 544 | height: 14, 545 | viewBox: '0 0 16 16', 546 | style: { 547 | color: data.color, 548 | backgroundColor: 'transparent' 549 | } 550 | }, 551 | React.createElement('path', { 552 | fillRule: 'evenodd', 553 | clipRule: 'evenodd', 554 | d: 'M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z', 555 | fill: 'currentColor', 556 | 'aria-hidden': 'true' 557 | }) 558 | ) 559 | ); 560 | const size = res.props.children.length; 561 | res.props.children[size] = 562 | res.props.children[size - 1]; 563 | res.props.children[size - 1] = element; 564 | } else { 565 | const element = React.createElement( 566 | 'span', 567 | { 568 | className: `${remClasses.botTag} ${botTagRegularClasses.botTagRegular} ${remClasses.px} ownertag-list`, 569 | style: { 570 | backgroundColor: data.color, 571 | color: data.textColor 572 | } 573 | }, 574 | React.createElement(Tag, { 575 | className: botTagRegularClasses.botText, 576 | userType: data.userType, 577 | settings: _this.settings 578 | }) 579 | ); 580 | const size = res.props.children.length; 581 | res.props.children[size] = 582 | res.props.children[size - 1]; 583 | res.props.children[size - 1] = element; 584 | } 585 | // res.props.children.unshift(element); 586 | } 587 | 588 | return res; 589 | } 590 | ); 591 | 592 | uninject('ownertag-members-1'); 593 | return res; 594 | } 595 | ); 596 | } 597 | 598 | pluginWillUnload() { 599 | powercord.api.settings.unregisterSettings(this.entityID); 600 | uninject('ownertag-members-1'); 601 | uninject('ownertag-members-2'); 602 | uninject('ownertag-messages'); 603 | } 604 | 605 | /* 606 | * Original code from https://github.com/powercord-community/rolecolor-everywhere. 607 | */ 608 | _numberToTextColor(color) { 609 | if (!color) { 610 | return; 611 | } // prevents errors from null colors which come from roles with no colors 612 | const colorInt = parseInt(color.slice(1), 16); 613 | const r = (colorInt & 0xff0000) >>> 16; 614 | const g = (colorInt & 0xff00) >>> 8; 615 | const b = colorInt & 0xff; 616 | const bgDelta = r * 0.299 + g * 0.587 + b * 0.114; 617 | return 255 - bgDelta < 105 ? '#000000' : '#ffffff'; 618 | } 619 | }; 620 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Staff Tags", 3 | "version": "1.0.17", 4 | "description": "Adds a tag or crown to Owners, Mods, Admins and Staff.", 5 | "author": "Puyodead1", 6 | "license": "MIT" 7 | } 8 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | .ownertag { 2 | color: #fff; 3 | } 4 | 5 | .ownertag-list { 6 | color: #fff; 7 | margin-left: 5px; 8 | } 9 | 10 | .botText-1526X_ { 11 | color: inherit; 12 | } 13 | 14 | [class*="ownerIcon-"][class*="icon-"] { 15 | display: none; 16 | } 17 | 18 | /* .ownertag.botTagRegular-2HEhHi:hover, 19 | .ownertag-list.botTagRegular-2HEhHi:hover { 20 | background-color: transparent !important; 21 | } */ 22 | 23 | .OwnerTag-13h21hk { 24 | display: inline; 25 | } 26 | --------------------------------------------------------------------------------