Files
UrgentNotification/release/UrgentNotification.plugin.js

703 lines
23 KiB
JavaScript
Raw Permalink Normal View History

2022-10-31 22:09:32 +07:00
/**
* @name UrgentNotification
* @description Allow urgent messages to bypass do not disturb mode, streamer mode and others.
* @version 0.0.2
* @author YuzuZensai
* @website https://github.com/YuzuZensai/UrgentNotification
* @source https://raw.githubusercontent.com/YuzuZensai/UrgentNotification/main/release/UrgentNotification.plugin.js
*/
/*@cc_on
@if (@_jscript)
// Offer to self-install for clueless users that try to run this directly.
var shell = WScript.CreateObject("WScript.Shell");
var fs = new ActiveXObject("Scripting.FileSystemObject");
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins");
var pathSelf = WScript.ScriptFullName;
// Put the user at ease by addressing them in the first person
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
} else if (!fs.FolderExists(pathPlugins)) {
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
// Show the user where to put plugins in the future
shell.Exec("explorer " + pathPlugins);
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
}
WScript.Quit();
@else@*/
const config = {
info: {
name: "UrgentNotification",
authors: [
{
name: "YuzuZensai",
github_username: "YuzuZensai"
}
],
version: "0.0.2",
description: "Allow urgent messages to bypass do not disturb mode, streamer mode and others.",
github: "https://github.com/YuzuZensai/UrgentNotification",
github_raw: "https://raw.githubusercontent.com/YuzuZensai/UrgentNotification/main/release/UrgentNotification.plugin.js"
},
changelog: [
{
title: "Complete rewrite",
type: "improved",
items: [
"The plugin is now a complete rewrite of the original plugin. It is now a lot more efficient and has a lot more features.",
"Check out settings to see what you can do with it.",
"Fixed tons of bugs."
]
}
],
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"
};
class Dummy {
constructor() {this._config = config;}
start() {}
stop() {}
}
if (!global.ZeresPluginLibrary) {
BdApi.showConfirmationModal("Library Missing", `The library plugin needed for ${config.name ?? config.info.name} is missing. Please click Download Now to install it.`, {
confirmText: "Download Now",
cancelText: "Cancel",
onConfirm: () => {
require("request").get("https://betterdiscord.app/gh-redirect?id=9", async (err, resp, body) => {
if (err) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
if (resp.statusCode === 302) {
require("request").get(resp.headers.location, async (error, response, content) => {
if (error) return require("electron").shell.openExternal("https://betterdiscord.app/Download?id=9");
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), content, r));
});
}
else {
await new Promise(r => require("fs").writeFile(require("path").join(BdApi.Plugins.folder, "0PluginLibrary.plugin.js"), body, r));
}
});
}
});
}
module.exports = !global.ZeresPluginLibrary ? Dummy : (([Plugin, Api]) => {
const plugin = (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;
}
};
};
return plugin(Plugin, Api);
})(global.ZeresPluginLibrary.buildPlugin(config));
/*@end@*/