2024-04-17 14:29:47 -04:00
/ *
* Vencord , a Discord client mod
* Copyright ( c ) 2024 Vendicated and contributors
* SPDX - License - Identifier : GPL - 3.0 - or - later
* /
import { type NavContextMenuPatchCallback } from "@api/ContextMenu" ;
import { Notifications } from "@api/index" ;
2024-08-18 00:20:36 -04:00
import { definePluginSettings , migratePluginSettings } from "@api/Settings" ;
2024-04-17 14:29:47 -04:00
import { Devs } from "@utils/constants" ;
import { getCurrentChannel } from "@utils/discord" ;
import { Logger } from "@utils/Logger" ;
import definePlugin , { OptionType } from "@utils/types" ;
import { ChannelStore , Menu , MessageStore , NavigationRouter , PresenceStore , PrivateChannelsStore , UserStore , WindowStore } from "@webpack/common" ;
2024-06-01 14:32:22 -04:00
import type { Message } from "discord-types/general" ;
2025-01-04 17:57:05 -05:00
import { JSX } from "react" ;
2024-04-17 14:29:47 -04:00
interface IMessageCreate {
channelId : string ;
guildId : string ;
message : Message ;
}
function Icon ( enabled? : boolean ) : JSX . Element {
return < svg
width = "18"
height = "18"
>
< circle cx = "9" cy = "9" r = "8" fill = { ! enabled ? "var(--status-danger)" : "currentColor" } / >
< circle cx = "9" cy = "9" r = "3.75" fill = { ! enabled ? "white" : "black" } / >
< / svg > ;
}
function processIds ( value : string ) : string {
return value . replace ( /\s/g , "" ) . split ( "," ) . filter ( id = > id . trim ( ) !== "" ) . join ( ", " ) ;
}
async function showNotification ( message : Message , guildId : string | undefined ) : Promise < void > {
2024-06-01 14:32:22 -04:00
try {
const channel = ChannelStore . getChannel ( message . channel_id ) ;
const channelRegex = /<#(\d{19})>/g ;
const userRegex = /<@(\d{18})>/g ;
2024-04-17 14:29:47 -04:00
2024-06-01 14:32:22 -04:00
message . content = message . content . replace ( channelRegex , ( match : any , channelId : string ) = > {
return ` # ${ ChannelStore . getChannel ( channelId ) ? . name } ` ;
} ) ;
2024-04-17 14:29:47 -04:00
2024-06-01 14:32:22 -04:00
message . content = message . content . replace ( userRegex , ( match : any , userId : string ) = > {
return ` @ ${ ( UserStore . getUser ( userId ) as any ) . globalName } ` ;
} ) ;
await Notifications . showNotification ( {
title : ` ${ ( message . author as any ) . globalName } ${ guildId ? ` (# ${ channel ? . name } , ${ ChannelStore . getChannel ( channel ? . parent_id ) ? . name } ) ` : "" } ` ,
body : message.content ,
icon : UserStore.getUser ( message . author . id ) . getAvatarURL ( undefined , undefined , false ) ,
onClick : function ( ) : void {
NavigationRouter . transitionTo ( ` /channels/ ${ guildId ? ? "@me" } / ${ message . channel_id } / ${ message . id } ` ) ;
}
} ) ;
2024-04-17 14:29:47 -04:00
2024-06-01 14:32:22 -04:00
if ( settings . store . notificationSound ) {
new Audio ( "https://discord.com/assets/9422aef94aa931248105.mp3" ) . play ( ) ;
2024-04-17 14:29:47 -04:00
}
2024-06-01 14:32:22 -04:00
} catch ( error ) {
2024-08-18 00:24:00 -04:00
new Logger ( "BypassStatus" ) . error ( "Failed to notify user: " , error ) ;
2024-06-01 14:32:22 -04:00
}
2024-04-17 14:29:47 -04:00
}
function ContextCallback ( name : "guild" | "user" | "channel" ) : NavContextMenuPatchCallback {
return ( children , props ) = > {
const type = props [ name ] ;
if ( ! type ) return ;
const enabled = settings . store [ ` ${ name } s ` ] . split ( ", " ) . includes ( type . id ) ;
if ( name === "user" && type . id === UserStore . getCurrentUser ( ) . id ) return ;
children . splice ( - 1 , 0 , (
< Menu.MenuGroup >
< Menu.MenuItem
2024-08-18 00:24:00 -04:00
id = { ` status- ${ name } -bypass ` }
label = { ` ${ enabled ? "Remove" : "Add" } Status Bypass ` }
2024-04-17 14:29:47 -04:00
icon = { ( ) = > Icon ( enabled ) }
action = { ( ) = > {
let bypasses : string [ ] = settings . store [ ` ${ name } s ` ] . split ( ", " ) ;
if ( enabled ) bypasses = bypasses . filter ( id = > id !== type . id ) ;
else bypasses . push ( type . id ) ;
settings . store [ ` ${ name } s ` ] = bypasses . filter ( id = > id . trim ( ) !== "" ) . join ( ", " ) ;
} }
/ >
< / Menu.MenuGroup >
) ) ;
} ;
}
const settings = definePluginSettings ( {
guilds : {
type : OptionType . STRING ,
description : "Guilds to let bypass (notified when pinged anywhere in guild)" ,
default : "" ,
placeholder : "Separate with commas" ,
onChange : value = > settings . store . guilds = processIds ( value )
} ,
channels : {
type : OptionType . STRING ,
description : "Channels to let bypass (notified when pinged in that channel)" ,
default : "" ,
placeholder : "Separate with commas" ,
onChange : value = > settings . store . channels = processIds ( value )
} ,
users : {
type : OptionType . STRING ,
description : "Users to let bypass (notified for all messages sent in DMs)" ,
default : "" ,
placeholder : "Separate with commas" ,
onChange : value = > settings . store . users = processIds ( value )
} ,
allowOutsideOfDms : {
type : OptionType . BOOLEAN ,
2024-08-18 00:24:00 -04:00
description : "Allow selected users to bypass status outside of DMs too (acts like a channel/guild bypass, but it's for all messages sent by the selected users)"
2024-06-01 14:32:22 -04:00
} ,
notificationSound : {
type : OptionType . BOOLEAN ,
description : "Whether the notification sound should be played" ,
default : true ,
2024-08-18 00:20:36 -04:00
} ,
statusToUse : {
type : OptionType . SELECT ,
description : "Status to use for whitelist" ,
options : [
{
label : "Online" ,
value : "online" ,
} ,
{
label : "Idle" ,
value : "idle" ,
} ,
{
label : "Do Not Disturb" ,
value : "dnd" ,
default : true
} ,
{
label : "Invisible" ,
value : "invisible" ,
}
]
2024-04-17 14:29:47 -04:00
}
} ) ;
2024-08-18 00:20:36 -04:00
migratePluginSettings ( "BypassStatus" , "BypassDND" ) ;
2024-04-17 14:29:47 -04:00
export default definePlugin ( {
2024-08-18 00:20:36 -04:00
name : "BypassStatus" ,
2024-04-17 14:29:47 -04:00
description : "Still get notifications from specific sources when in do not disturb mode. Right-click on users/channels/guilds to set them to bypass do not disturb mode." ,
authors : [ Devs . Inbestigator ] ,
flux : {
async MESSAGE_CREATE ( { message , guildId , channelId } : IMessageCreate ) : Promise < void > {
try {
const currentUser = UserStore . getCurrentUser ( ) ;
const userStatus = await PresenceStore . getStatus ( currentUser . id ) ;
const currentChannelId = getCurrentChannel ( ) ? . id ? ? "0" ;
2024-08-18 00:20:36 -04:00
if ( message . state === "SENDING" || message . content === "" || message . author . id === currentUser . id || ( channelId === currentChannelId && WindowStore . isFocused ( ) ) || userStatus !== settings . store . statusToUse ) {
2024-04-17 14:29:47 -04:00
return ;
}
const mentioned = MessageStore . getMessage ( channelId , message . id ) ? . mentioned ;
if ( ( settings . store . guilds . split ( ", " ) . includes ( guildId ) || settings . store . channels . split ( ", " ) . includes ( channelId ) ) && mentioned ) {
await showNotification ( message , guildId ) ;
} else if ( settings . store . users . split ( ", " ) . includes ( message . author . id ) ) {
const userChannelId = await PrivateChannelsStore . getOrEnsurePrivateChannel ( message . author . id ) ;
if ( channelId === userChannelId || ( mentioned && settings . store . allowOutsideOfDms === true ) ) {
await showNotification ( message , guildId ) ;
}
}
} catch ( error ) {
2024-08-18 00:24:00 -04:00
new Logger ( "BypassStatus" ) . error ( "Failed to handle message: " , error ) ;
2024-04-17 14:29:47 -04:00
}
}
} ,
settings ,
contextMenus : {
"guild-context" : ContextCallback ( "guild" ) ,
"channel-context" : ContextCallback ( "channel" ) ,
"user-context" : ContextCallback ( "user" ) ,
}
} ) ;