├── LICENSE.md ├── README.md ├── demo ├── 1.png ├── 2.png └── 3.png ├── index.js ├── manifest.json ├── ui └── Settings.jsx └── utils ├── addUser.js └── removeUser.js /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 RazerMoon 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 | # User Notifs 2 | 3 | Shows a notification when a specified user sends a message 4 | 5 |  6 | 7 |  8 | 9 |  10 | -------------------------------------------------------------------------------- /demo/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazerMoon/userNotifs/b6b2123ceb2ddf343d0c2831a5ec6d44494703df/demo/1.png -------------------------------------------------------------------------------- /demo/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazerMoon/userNotifs/b6b2123ceb2ddf343d0c2831a5ec6d44494703df/demo/2.png -------------------------------------------------------------------------------- /demo/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RazerMoon/userNotifs/b6b2123ceb2ddf343d0c2831a5ec6d44494703df/demo/3.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { Plugin } = require('powercord/entities'); 2 | const { React, FluxDispatcher, getModule } = require('powercord/webpack'); 3 | const { inject, uninject } = require('powercord/injector'); 4 | const { findInReactTree } = require('powercord/util'); 5 | 6 | const Settings = require('./ui/Settings.jsx'); 7 | const addUser = require('./utils/addUser'); 8 | const removeUser = require('./utils/removeUser'); 9 | 10 | /** 11 | * Creates a notification when a specified user sends a message 12 | * @link https://github.com/RazerMoon/userNotifs 13 | * @license MIT 14 | * @extends Plugin 15 | */ 16 | module.exports = class UserNotifs extends Plugin { 17 | startPlugin () { 18 | powercord.api.settings.registerSettings(this.entityID, { 19 | category: this.entityID, 20 | label: 'User Notifs', 21 | render: Settings 22 | }); 23 | 24 | FluxDispatcher.subscribe('MESSAGE_CREATE', this.handleMessage.bind(this)); 25 | 26 | this.patchListener = this.patchListener.bind(this); 27 | 28 | document.addEventListener('mousedown', this.patchListener); 29 | } 30 | 31 | patchListener (e) { 32 | if (e.button === 2 && e.target?.tagName === 'IMG' && e.target.className.includes('avatar')) { 33 | setTimeout(() => { 34 | this.patchUserCM(); 35 | document.removeEventListener('mousedown', this.patchListener); 36 | }, 500); 37 | } 38 | } 39 | 40 | async patchUserCM () { 41 | const Menu = await getModule((m) => (m.__powercordOriginal_default || m.default)?.displayName === 'Menu'); 42 | const mod = await getModule((m) => (m.__powercordOriginal_default || m.default)?.displayName === 'GuildChannelUserContextMenu'); 43 | 44 | inject('usernotifs-usercm-patch', mod, 'default', ([ { user } ], res) => { 45 | if (!res) { 46 | return res; 47 | } 48 | 49 | const hasNotifyButton = findInReactTree(res.children, child => child.props && child.props.id === 'notify'); 50 | 51 | if (!hasNotifyButton) { 52 | const userOnList = this.settings.get('idlist', []).includes(user.id); 53 | 54 | const addUserButton = React.createElement(Menu.MenuItem, { 55 | id: 'notify', 56 | label: userOnList ? 'Stop Message Notifs' : 'Notify on Message', 57 | action: () => userOnList ? removeUser(user.id, this.settings) : addUser(user, this.settings) 58 | }); 59 | 60 | const devmodeItem = findInReactTree(res.props, child => child.props && child.props.id === 'devmode-copy-id'); 61 | const developerGroup = findInReactTree(res.props, child => child.props && child.props.children === devmodeItem); 62 | 63 | if (developerGroup) { 64 | if (!Array.isArray(developerGroup.props.children)) { 65 | developerGroup.props.children = [ developerGroup.props.children ]; 66 | } 67 | 68 | developerGroup.props.children.splice(developerGroup.props.children.length - 1, 0, addUserButton); 69 | } else { 70 | res.props.children.props.children.splice(res.props.children.props.children.length - 1, 0, [ React.createElement(Menu.MenuSeparator), React.createElement(Menu.MenuGroup, {}, addUserButton) ]); 71 | } 72 | } 73 | 74 | return res; 75 | }); 76 | } 77 | 78 | async handleMessage ({ message }) { 79 | // ! Small errors here cause discord to crash loop 80 | const idlist = this.settings.get('idlist', []); 81 | if (idlist.includes(message.author.id)) { 82 | const user = this.settings.get('details', []).find(item => item.id === message.author.id); 83 | 84 | const modules = await Promise.all([ getModule([ 'showNotification' ]), getModule([ 'getUserAvatarURL', 'getGuildIconURL' ]), getModule([ 'transitionTo' ]), getModule([ 'getChannel', 'getDMFromUserId' ]), getModule([ 'getGuild' ]) ]); 85 | 86 | if (!user) { 87 | console.log('[userNotifs] Something went wrong when fetching the user!'); 88 | return null; 89 | } 90 | 91 | const channel = modules[3].getChannel(message.channel_id); 92 | 93 | const getChannel = () => { 94 | if (channel.isDM()) { 95 | return 'DM'; 96 | } 97 | return `#${channel.name}, ${modules[4].getGuild(channel.getGuildId())}`; 98 | }; 99 | 100 | const getUsername = ({ username, discriminator }) => `${username}#${discriminator}`; 101 | 102 | if (!this.settings.get('dm', false) && channel.isDM()) { 103 | return null; 104 | } 105 | 106 | // ! Doesn't work with animated avatars 107 | modules[0].showNotification(modules[1].getUserAvatarURL(message.author, 'png'), `${getUsername(message.author)} (${getChannel()})`, message.content, { onClick: () => { 108 | // Yoinked from https://gist.github.com/jiangzhuo/793f6d120607bb71f30c45f4fa6ea00a 109 | modules[2].transitionTo(`/channels/${message.guild_id ? message.guild_id : '@me'}/${message.channel_id}/${message.id}`); 110 | } }, {}); 111 | } 112 | } 113 | 114 | pluginWillUnload () { 115 | powercord.api.settings.unregisterSettings(this.entityID); 116 | uninject('usernotifs-usercm-patch'); 117 | FluxDispatcher.unsubscribe('MESSAGE_CREATE', this.handleMessage); 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "User Notifs", 3 | "version": "0.9.6", 4 | "description": "Shows a notification when a specified user sends a message", 5 | "author": "RazerMoon", 6 | "license": "MIT" 7 | } 8 | -------------------------------------------------------------------------------- /ui/Settings.jsx: -------------------------------------------------------------------------------- 1 | const { React } = require('powercord/webpack'); 2 | const { Button } = require('powercord/components'); 3 | const { TextInput, ButtonItem, Category, SwitchItem } = require('powercord/components/settings'); 4 | const addUser = require('../utils/addUser'); 5 | const removeUser = require('../utils/removeUser'); 6 | 7 | // eslint-disable-next-line no-warning-comments 8 | // TODO: Make the channel items more distinct/seperate from the actual settings 9 | // eslint-disable-next-line no-warning-comments 10 | // TODO: Add avatar preview 11 | module.exports = ({ getSetting, updateSetting, settings, toggleSetting }) => { 12 | const removeAll = () => { 13 | updateSetting('idlist', []); 14 | updateSetting('details', []); 15 | }; 16 | 17 | const [ userField, setUserField ] = React.useState(''); 18 | const [ idField, setIdField ] = React.useState(''); 19 | 20 | const [ opened, onChange ] = React.useState(true); 21 | 22 | return ( 23 |