From e2f419e0d98e4146d020e2e785d97444bc6a5a9b Mon Sep 17 00:00:00 2001 From: Yuzu Date: Mon, 31 Oct 2022 21:57:20 +0700 Subject: [PATCH] First rewrite release --- src/UrgentNotification/config.json | 250 ++++++++++++++++++++ src/UrgentNotification/index.js | 361 +++++++++++++++++++++++++++++ 2 files changed, 611 insertions(+) create mode 100644 src/UrgentNotification/config.json create mode 100644 src/UrgentNotification/index.js diff --git a/src/UrgentNotification/config.json b/src/UrgentNotification/config.json new file mode 100644 index 0000000..ad572f1 --- /dev/null +++ b/src/UrgentNotification/config.json @@ -0,0 +1,250 @@ +{ + "info": { + "name": "UrgentNotification", + "authors": [ + { + "name": "YuzuZensai", + "discord_id": "", + "github_username": "YuzuZensai", + "twitter_username": "YuzuZensai" + } + ], + "version": "0.0.2", + "description": "Allow urgent messages to bypass do not disturb mode, streamer mode and others.", + "github": "", + "github_raw": "" + }, + "changelog": [], + "defaultConfig": [ + { + "type": "textbox", + "id": "triggerCommand", + "name": "Trigger command", + "note": "Trigger command for the urgent notification", + "value": "!urgent" + }, + { + "type": "switch", + "id": "commandResponseEnabled", + "name": "๐Ÿ’ฌ Enable urgent command reply", + "note": "The plugin will send a confirmation message when the urgent command is triggered back to the user", + "value": true + }, + { + "type": "switch", + "id": "autoReplyEnabled", + "name": "โœ‰๏ธ Enable AutoReply", + "note": "When a new message is received, the plugin will automatically reply with information about current status and how to trigger the urgent notification", + "value": true + }, + { + "type": "switch", + "id": "onlyFromFriends", + "name": "๐Ÿง‘โ€๐Ÿคโ€๐Ÿง‘ Friends only mode", + "note": "Only reply back and allow urgent notifications from friends", + "value": true + }, + { + "type": "category", + "id": "timer", + "name": "โŒ› Timer", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "slider", + "id": "commandResponseCooldown", + "name": "Urgent command cooldown", + "note": "How long until the user can trigger the urgent command again (in minutes)", + "value": 5, + "min": 1, + "max": 120, + "markers": [1, 5, 10, 15, 20, 30, 60, 90, 120], + "stickToMarkers": true + }, + { + "type": "slider", + "id": "autoReplyCooldown", + "name": "AutoReply cooldown", + "note": "How long until AutoReply can be triggered again (in minutes)", + "value": 30, + "min": 5, + "max": 120, + "markers": [5, 10, 15, 20, 30, 60, 90, 120], + "stickToMarkers": true + }, + { + "type": "slider", + "id": "autoReplySelfDeleteCountdown", + "name": "AutoReply Self Delete Countdown", + "note": "How long until AutoReply will self delete the message (in minutes)", + "value": 5, + "min": 1, + "max": 120, + "markers": [1, 5, 10, 15, 20, 30, 60, 90, 120], + "stickToMarkers": true + } + ] + }, + { + "type": "category", + "id": "commandResponseMessage", + "name": "โœ๏ธ Customize command response message", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "textbox", + "id": "header", + "name": "Header", + "note": "", + "value": "โ—โœ‰๏ธใ…ค**__URGENT NOTIFICATION SENT__**" + }, + { + "type": "textbox", + "id": "body", + "name": "Body", + "note": "", + "value": "If I'm taking too long to respond, please send send ``!urgent`` again (there is a cooldown)\\nPlease don't abuse the urgent tag. Thanks you" + } + ] + }, + { + "type": "category", + "id": "streaming", + "name": "๐Ÿ”ด [AutoReply] When streaming", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "switch", + "id": "enabled", + "name": "Enable", + "note": "", + "value": true + }, + { + "type": "textbox", + "id": "header", + "name": "Header", + "note": "", + "value": "๐ŸŒ™ใ…ค**${username} has notifications silenced**" + }, + { + "type": "textbox", + "id": "body", + "name": "Body", + "note": "", + "value": "๐Ÿ”ดใ…คI'm currently streaming or recording, in order to prevent any accidental notifications being shown\\nใ…ค ใ…ค and other reasons. I'll check my notifications periodically." + } + ] + }, + { + "type": "category", + "id": "dnd", + "name": "๐Ÿ”• [AutoReply] When do not disturb enabled", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "switch", + "id": "enabled", + "name": "Enable", + "note": "", + "value": true + }, + { + "type": "textbox", + "id": "header", + "name": "Header", + "note": "", + "value": "๐ŸŒ™ใ…ค**${username} has notifications silenced**" + }, + { + "type": "textbox", + "id": "body", + "name": "Body", + "note": "", + "value": "๐Ÿ”•ใ…คI'm currently busy, I'll respond to you later." + } + ] + }, + { + "type": "category", + "id": "idle", + "name": "๐ŸŒ™ [AutoReply] Idle", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "switch", + "id": "enabled", + "name": "Enable", + "note": "", + "value": true + }, + { + "type": "textbox", + "id": "header", + "name": "Header", + "note": "", + "value": "๐ŸŒ™ใ…ค**${username} is currently away**" + }, + { + "type": "textbox", + "id": "body", + "name": "Body", + "note": "", + "value": "๐Ÿ’คใ…คI'm currently AFK right now, doing things in real life, or just taking a break." + } + ] + }, + { + "type": "category", + "id": "inVR", + "name": "๐Ÿฅฝ [AutoReply] When using VR headset", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "switch", + "id": "enabled", + "name": "Enable", + "note": "", + "value": true + }, + { + "type": "textbox", + "id": "header", + "name": "Header", + "note": "", + "value": "๐Ÿฅฝใ…ค**${username} is currently in VR**" + }, + { + "type": "textbox", + "id": "body", + "name": "Body", + "note": "", + "value": " ๐Ÿ‘๏ธโ€๐Ÿ—จ๏ธใ…คI'm currently wearing a VR headset. I might not be able to respond to you right away\\nใ…ค ใ…ค I'll check my notifications periodically." + } + ] + }, + { + "type": "category", + "id": "general", + "name": "โœ‰๏ธ [AutoReply] Footer text", + "collapsible": true, + "shown": false, + "settings": [ + { + "type": "textbox", + "id": "footer", + "name": "Footer text", + "note": "", + "value": "โš ๏ธใ…คIf this is an urgent matter or need to contact me ASAP, please send ``!urgent``." + } + ] + } + ], + "main": "index.js" +} diff --git a/src/UrgentNotification/index.js b/src/UrgentNotification/index.js new file mode 100644 index 0000000..f7bd365 --- /dev/null +++ b/src/UrgentNotification/index.js @@ -0,0 +1,361 @@ +/** + * + * @param {import("zerespluginlibrary").Plugin} Plugin + * @param {import("zerespluginlibrary").BoundAPI} Library + * @returns + */ + +module.exports = (Plugin, Library) => { + const { DiscordModules, Logger } = Library; + + const { + UserStore, + ChannelStore, + MessageStore, + RelationshipStore, + UserStatusStore, + StreamerModeStore, + Dispatcher, + MessageQueue, + APIModule, + MessageActions, + NavigationUtils, + NotificationModule, + } = DiscordModules; + + return class extends Plugin { + onStart() { + Logger.info("Plugin enabled!"); + Dispatcher.subscribe("MESSAGE_CREATE", this.onMessage.bind(this)); + + if (!global.UrgentNotification) + global.UrgentNotification = { + recently_sent: [], + cooldown: [], + resetCooldowns: () => { + global.UrgentNotification.recently_sent = []; + global.UrgentNotification.cooldown = []; + Logger.info("Recently sent and cooldown list cleared"); + }, + resetAll: () => { + global.UrgentNotification.resetCooldowns(); + global.UrgentNotification.purgePendingDeleteNow(); + }, + purgePendingDeleteNow: () => { + Logger.info("Pending delete messages cleared"); + for (let x in global.UrgentNotification.pending_delete) { + clearTimeout(x); + global.UrgentNotification.pending_delete[x].f(); + } + global.UrgentNotification.pending_delete = {}; + }, + }; + if (!global.UrgentNotification.cooldown) + global.UrgentNotification.cooldown = []; + if (!global.UrgentNotification.recently_sent) + global.UrgentNotification.recently_sent = []; + if (!global.UrgentNotification.pending_delete) + global.UrgentNotification.pending_delete = {}; + } + + onStop() { + Dispatcher.unsubscribe("MESSAGE_CREATE", this.onMessage.bind(this)); + Logger.info("Plugin disabled!"); + } + + getSettings() { + return this.settings; + } + + onMessage(data) { + let channel = ChannelStore.getChannel(data.channelId); + let isGuild = channel.guild_id != null; + let isDM = channel.type == 1; + + if (isGuild || !isDM || !data.message) return; + + let message = data.message; + let isNormalMessage = message.type == 0; + + if (!isNormalMessage) return; + + if (!message.author) + message = MessageStore.getMessage(channel.id, message.id); + + if (!message.author) return; + + let author = message.author; + if (author.id === this.getUserID()) return; + + let isFriend = RelationshipStore.isFriend(message.author.id); + + let streamerModeSettings = StreamerModeStore.getSettings(); + let status = UserStatusStore.getStatus(this.getUserID()); + let isStreamerModeEnabled = + streamerModeSettings.enabled || status === "streaming"; + + let activities = UserStatusStore.getActivities(this.getUserID()); + let isVR = activities.some((x) => x.name === "SteamVR"); + + if (status === "invisible") return; + if (status === "online" && !isStreamerModeEnabled && !isVR) return; + //if (status === "idle") return; + + if (message.content === this.settings.triggerCommand) { + if (this.settings.onlyFromFriends && !isFriend) { + Logger.info( + message.author.username, + "is not your friend, urgent message ignored" + ); + return; + } + + let isCooldown = global.UrgentNotification.cooldown.includes(author.id); + if (isCooldown) return; + + global.UrgentNotification.cooldown.push(author.id); + Logger.info("Added", author.username, "to cooldown list"); + setTimeout(() => { + global.UrgentNotification.cooldown = + global.UrgentNotification.cooldown.filter((e) => e !== author.id); + Logger.info("Removed from cooldown", message.author.username); + }, this.getCommandResponseCooldown()); + + this.forceNotify(channel, message); + + if (!this.settings.commandResponseEnabled) return; + MessageQueue.enqueue( + { + type: 0, + message: { + channelId: channel.id, + content: this.makeCommandResponseMessage( + { username: this.getLocalUsername() }, + this.settings + ), + tts: false, + }, + }, + (data) => { + Logger.info("Message sent to", message.author.username); + let newMessage = data.body; + this.scheduleDelete( + channel.id, + newMessage.id, + this.getAutoReplySelfDeleteCountdown() + ); + this.sendACK(channel.id, message.id, 2); + } + ); + } else { + if (!this.settings.autoReplyEnabled) return; + if (this.settings.onlyFromFriends && !isFriend) return; + let isRecentlySent = global.UrgentNotification.recently_sent.includes( + author.id + ); + if (isRecentlySent) return; + + let isCooldown = global.UrgentNotification.cooldown.includes(author.id); + if (isCooldown) return; + + let messageToSend; + if (this.settings.streaming.enabled && isStreamerModeEnabled) + messageToSend = this.makeStreamingMessage( + { username: this.getLocalUsername() }, + this.settings + ); + else if (this.settings.dnd.enabled && status === "dnd") + messageToSend = this.makeDNDMessage( + { username: this.getLocalUsername() }, + this.settings + ); + else if (this.settings.inVR.enabled && isVR) + messageToSend = this.makeInVRMessage( + { username: this.getLocalUsername() }, + this.settings + ); + else if (this.settings.idle.enabled && status === "idle") + messageToSend = this.makeIdleMessage( + { username: this.getLocalUsername() }, + this.settings + ); + else return; + + global.UrgentNotification.recently_sent.push(author.id); + Logger.info("Added", author.username, "to recently sent list"); + setTimeout(() => { + global.UrgentNotification.recently_sent = + global.UrgentNotification.recently_sent.filter( + (e) => e !== author.id + ); + Logger.info("Removed from recently sent", message.author.username); + }, this.getAutoReplyCooldown()); + + MessageQueue.enqueue( + { + type: 0, + message: { + channelId: channel.id, + content: messageToSend, + tts: false, + }, + }, + (data) => { + Logger.info("Message sent to", message.author.username); + let newMessage = data.body; + this.scheduleDelete( + channel.id, + newMessage.id, + this.getAutoReplySelfDeleteCountdown() + ); + this.sendACK(channel.id, message.id, 2); + } + ); + } + } + + getSettingsPanel() { + return this.buildSettingsPanel().getElement(); + } + + makeStreamingMessage(data, settings) { + return this.makeMessage( + data, + settings.streaming.header, + settings.streaming.body, + settings.general.footer + ); + } + + makeDNDMessage(data, settings) { + return this.makeMessage( + data, + settings.dnd.header, + settings.dnd.body, + settings.general.footer + ); + } + + makeIdleMessage(data, settings) { + return this.makeMessage( + data, + settings.idle.header, + settings.idle.body, + settings.general.footer + ); + } + + makeInVRMessage(data, settings) { + return this.makeMessage( + data, + settings.inVR.header, + settings.inVR.body, + settings.general.footer + ); + } + + makeFancyMessage(text) { + text = text.replaceAll("\n", "\n> "); + text = text.replaceAll("\\n", "\n> "); + if (text.charAt(text.length - 1) == " ") { + text = text.substring(0, text.length - 1) + " ใ…ค\n"; + } + return `> ${text}`; + } + + makeCommandResponseMessage(data, settings) { + return this.makeFancyMessage( + `\n${settings.commandResponseMessage.header.replaceAll( + "${username}", + data.username + )}\n\n${settings.commandResponseMessage.body.replaceAll( + "${username}", + data.username + )}\n` + ); + } + + makeMessage(data, header, body, footer) { + return this.makeFancyMessage( + `\n${header.replaceAll( + "${username}", + data.username + )}\n\n${body.replaceAll( + "${username}", + data.username + )}\n\n${footer.replaceAll("${username}", data.username)}\n` + ); + } + + forceNotify(channel, message) { + NotificationModule.showNotification( + `https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.webp?size=96`, + `[URGENT] Notification`, + `from ${message.author.username}`, + { + notif_type: "MESSAGE_CREATE", + notif_user_id: message.author.id, + message_id: message.id, + message_type: message.type, + channel_id: channel.id, + channel_type: channel.type, + guild_id: null, + }, + { + omitViewTracking: true, + overrideStreamerMode: true, + sound: "message1", + volume: 0.4, + onClick: () => { + NavigationUtils.transitionTo( + `/channels/@me/${channel.id}/${message.id}`, + undefined, + undefined + ); + }, + } + ); + } + + sendACK(channelID, messageID, count) { + APIModule.post({ + url: `/channels/${channelID}/messages/${messageID}/ack`, + body: { + manual: true, + mention_count: count, + }, + }); + } + + scheduleDelete(channelID, messageID, timeout) { + const f = () => { + MessageActions.deleteMessage(channelID, messageID); + delete global.UrgentNotification.pending_delete[x]; + }; + + let x = setTimeout(f, timeout); + + global.UrgentNotification.pending_delete[x] = { channelID, messageID, f }; + } + + getAutoReplySelfDeleteCountdown() { + return this.settings.timer.autoReplySelfDeleteCountdown * 60 * 1000; + } + + getCommandResponseCooldown() { + return this.settings.timer.commandResponseCooldown * 60 * 1000; + } + + getAutoReplyCooldown() { + return this.settings.timer.autoReplyCooldown * 60 * 1000; + } + + getUserID() { + return UserStore.getCurrentUser().id; + } + + getLocalUsername() { + return UserStore.getCurrentUser().username; + } + }; +};