feat(plugin): RelationshipNotifier (#450)
Co-authored-by: Ven <vendicated@riseup.net>
This commit is contained in:
parent
dae7cb67ef
commit
2c8ebdce7d
7 changed files with 465 additions and 0 deletions
40
src/plugins/relationshipNotifier/events.ts
Normal file
40
src/plugins/relationshipNotifier/events.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { FluxEvents } from "@webpack/types";
|
||||
|
||||
import { onChannelDelete, onGuildDelete, onRelationshipRemove } from "./functions";
|
||||
import { syncFriends, syncGroups, syncGuilds } from "./utils";
|
||||
|
||||
export const FluxHandlers: Partial<Record<FluxEvents, Array<(data: any) => void>>> = {
|
||||
GUILD_CREATE: [syncGuilds],
|
||||
GUILD_DELETE: [onGuildDelete],
|
||||
CHANNEL_CREATE: [syncGroups],
|
||||
CHANNEL_DELETE: [onChannelDelete],
|
||||
RELATIONSHIP_ADD: [syncFriends],
|
||||
RELATIONSHIP_UPDATE: [syncFriends],
|
||||
RELATIONSHIP_REMOVE: [syncFriends, onRelationshipRemove]
|
||||
};
|
||||
|
||||
export function forEachEvent(fn: (event: FluxEvents, handler: (data: any) => void) => void) {
|
||||
for (const event in FluxHandlers) {
|
||||
for (const cb of FluxHandlers[event]) {
|
||||
fn(event as FluxEvents, cb);
|
||||
}
|
||||
}
|
||||
}
|
87
src/plugins/relationshipNotifier/functions.ts
Normal file
87
src/plugins/relationshipNotifier/functions.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { UserUtils } from "@webpack/common";
|
||||
|
||||
import settings from "./settings";
|
||||
import { ChannelDelete, ChannelType, GuildDelete, RelationshipRemove, RelationshipType } from "./types";
|
||||
import { deleteGroup, deleteGuild, getGroup, getGuild, notify } from "./utils";
|
||||
|
||||
let manuallyRemovedFriend: string | undefined;
|
||||
let manuallyRemovedGuild: string | undefined;
|
||||
let manuallyRemovedGroup: string | undefined;
|
||||
|
||||
export const removeFriend = (id: string) => manuallyRemovedFriend = id;
|
||||
export const removeGuild = (id: string) => manuallyRemovedGuild = id;
|
||||
export const removeGroup = (id: string) => manuallyRemovedGroup = id;
|
||||
|
||||
export async function onRelationshipRemove({ relationship: { type, id } }: RelationshipRemove) {
|
||||
if (manuallyRemovedFriend === id) {
|
||||
manuallyRemovedFriend = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const user = await UserUtils.fetchUser(id)
|
||||
.catch(() => null);
|
||||
if (!user) return;
|
||||
|
||||
switch (type) {
|
||||
case RelationshipType.FRIEND:
|
||||
if (settings.store.friends)
|
||||
notify(`${user.tag} removed you as a friend.`, user.getAvatarURL(undefined, undefined, false));
|
||||
break;
|
||||
case RelationshipType.FRIEND_REQUEST:
|
||||
if (settings.store.friendRequestCancels)
|
||||
notify(`A friend request from ${user.tag} has been removed.`, user.getAvatarURL(undefined, undefined, false));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function onGuildDelete({ guild: { id, unavailable } }: GuildDelete) {
|
||||
if (!settings.store.servers) return;
|
||||
if (unavailable) return;
|
||||
|
||||
if (manuallyRemovedGuild === id) {
|
||||
deleteGuild(id);
|
||||
manuallyRemovedGuild = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const guild = getGuild(id);
|
||||
if (guild) {
|
||||
deleteGuild(id);
|
||||
notify(`You were removed from the server ${guild.name}.`, guild.iconURL);
|
||||
}
|
||||
}
|
||||
|
||||
export function onChannelDelete({ channel: { id, type } }: ChannelDelete) {
|
||||
if (!settings.store.groups) return;
|
||||
if (type !== ChannelType.GROUP_DM) return;
|
||||
|
||||
if (manuallyRemovedGroup === id) {
|
||||
deleteGroup(id);
|
||||
manuallyRemovedGroup = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const group = getGroup(id);
|
||||
if (group) {
|
||||
deleteGroup(id);
|
||||
notify(`You were removed from the group ${group.name}.`, group.iconURL);
|
||||
}
|
||||
}
|
70
src/plugins/relationshipNotifier/index.ts
Normal file
70
src/plugins/relationshipNotifier/index.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { FluxDispatcher } from "@webpack/common";
|
||||
|
||||
import { forEachEvent } from "./events";
|
||||
import { removeFriend, removeGroup, removeGuild } from "./functions";
|
||||
import settings from "./settings";
|
||||
import { syncAndRunChecks } from "./utils";
|
||||
|
||||
export default definePlugin({
|
||||
name: "RelationshipNotifier",
|
||||
description: "Notifies you when a friend, group chat, or server removes you.",
|
||||
authors: [Devs.nick],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "removeRelationship:function(",
|
||||
replacement: {
|
||||
match: /(removeRelationship:function\((\i),\i,\i\){)/,
|
||||
replace: "$1$self.removeFriend($2);"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "leaveGuild:function(",
|
||||
replacement: {
|
||||
match: /(leaveGuild:function\((\i)\){)/,
|
||||
replace: "$1$self.removeGuild($2);"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "closePrivateChannel:function(",
|
||||
replacement: {
|
||||
match: /(closePrivateChannel:function\((\i)\){)/,
|
||||
replace: "$1$self.removeGroup($2);"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
async start() {
|
||||
await syncAndRunChecks();
|
||||
forEachEvent((ev, cb) => FluxDispatcher.subscribe(ev, cb));
|
||||
},
|
||||
|
||||
stop() {
|
||||
forEachEvent((ev, cb) => FluxDispatcher.unsubscribe(ev, cb));
|
||||
},
|
||||
|
||||
removeFriend,
|
||||
removeGroup,
|
||||
removeGuild
|
||||
});
|
53
src/plugins/relationshipNotifier/settings.ts
Normal file
53
src/plugins/relationshipNotifier/settings.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/settings";
|
||||
import { OptionType } from "@utils/types";
|
||||
|
||||
export default definePluginSettings({
|
||||
notices: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Also show a notice at the top of your screen when removed (use this if you don't want to miss any notifications).",
|
||||
default: false
|
||||
},
|
||||
offlineRemovals: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Notify you when starting discord if you were removed while offline.",
|
||||
default: true
|
||||
},
|
||||
friends: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Notify when a friend removes you",
|
||||
default: true
|
||||
},
|
||||
friendRequestCancels: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Notify when a friend request is cancelled",
|
||||
default: true
|
||||
},
|
||||
servers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Notify when removed from a server",
|
||||
default: true
|
||||
},
|
||||
groups: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Notify when removed from a group chat",
|
||||
default: true
|
||||
}
|
||||
});
|
62
src/plugins/relationshipNotifier/types.ts
Normal file
62
src/plugins/relationshipNotifier/types.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Channel } from "discord-types/general";
|
||||
|
||||
export interface ChannelDelete {
|
||||
type: "CHANNEL_DELETE";
|
||||
channel: Channel;
|
||||
}
|
||||
|
||||
export interface GuildDelete {
|
||||
type: "GUILD_DELETE";
|
||||
guild: {
|
||||
id: string;
|
||||
unavailable?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RelationshipRemove {
|
||||
type: "RELATIONSHIP_REMOVE";
|
||||
relationship: {
|
||||
id: string;
|
||||
nickname: string;
|
||||
type: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SimpleGroupChannel {
|
||||
id: string;
|
||||
name: string;
|
||||
iconURL?: string;
|
||||
}
|
||||
|
||||
export interface SimpleGuild {
|
||||
id: string;
|
||||
name: string;
|
||||
iconURL?: string;
|
||||
}
|
||||
|
||||
export const enum ChannelType {
|
||||
GROUP_DM = 3,
|
||||
}
|
||||
|
||||
export const enum RelationshipType {
|
||||
FRIEND = 1,
|
||||
FRIEND_REQUEST = 3,
|
||||
}
|
149
src/plugins/relationshipNotifier/utils.ts
Normal file
149
src/plugins/relationshipNotifier/utils.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { DataStore, Notices } from "@api/index";
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { ChannelStore, GuildStore, RelationshipStore, UserUtils } from "@webpack/common";
|
||||
|
||||
import settings from "./settings";
|
||||
import { ChannelType, RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types";
|
||||
|
||||
const guilds = new Map<string, SimpleGuild>();
|
||||
const groups = new Map<string, SimpleGroupChannel>();
|
||||
const friends = {
|
||||
friends: [] as string[],
|
||||
requests: [] as string[]
|
||||
};
|
||||
|
||||
export async function syncAndRunChecks() {
|
||||
const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([
|
||||
"relationship-notifier-guilds",
|
||||
"relationship-notifier-groups",
|
||||
"relationship-notifier-friends"
|
||||
]) as [Map<string, SimpleGuild> | undefined, Map<string, SimpleGroupChannel> | undefined, Record<"friends" | "requests", string[]> | undefined];
|
||||
|
||||
await Promise.all([syncGuilds(), syncGroups(), syncFriends()]);
|
||||
|
||||
if (settings.store.offlineRemovals) {
|
||||
if (settings.store.groups && oldGroups?.size) {
|
||||
for (const [id, group] of oldGroups) {
|
||||
if (!groups.has(id))
|
||||
notify(`You are no longer in the group ${group.name}.`, group.iconURL);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.store.servers && oldGuilds?.size) {
|
||||
for (const [id, guild] of oldGuilds) {
|
||||
if (!guilds.has(id))
|
||||
notify(`You are no longer in the server ${guild.name}.`, guild.iconURL);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.store.friends && oldFriends?.friends.length) {
|
||||
for (const id of oldFriends.friends) {
|
||||
if (friends.friends.includes(id)) continue;
|
||||
|
||||
const user = await UserUtils.fetchUser(id).catch(() => void 0);
|
||||
if (user)
|
||||
notify(`You are no longer friends with ${user.tag}.`, user.getAvatarURL(undefined, undefined, false));
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.store.friendRequestCancels && oldFriends?.requests?.length) {
|
||||
for (const id of oldFriends.requests) {
|
||||
if (friends.requests.includes(id)) continue;
|
||||
|
||||
const user = await UserUtils.fetchUser(id).catch(() => void 0);
|
||||
if (user)
|
||||
notify(`Friend request from ${user.tag} has been revoked.`, user.getAvatarURL(undefined, undefined, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function notify(text: string, icon?: string) {
|
||||
if (settings.store.notices)
|
||||
Notices.showNotice(text, "OK", () => Notices.popNotice());
|
||||
|
||||
showNotification({
|
||||
title: "Relationship Notifier",
|
||||
body: text,
|
||||
icon
|
||||
});
|
||||
}
|
||||
|
||||
export function getGuild(id: string) {
|
||||
return guilds.get(id);
|
||||
}
|
||||
|
||||
export function deleteGuild(id: string) {
|
||||
guilds.delete(id);
|
||||
syncGuilds();
|
||||
}
|
||||
|
||||
export async function syncGuilds() {
|
||||
for (const [id, { name, icon }] of Object.entries(GuildStore.getGuilds())) {
|
||||
guilds.set(id, {
|
||||
id,
|
||||
name,
|
||||
iconURL: icon && `https://cdn.discordapp.com/icons/${id}/${icon}.png`
|
||||
});
|
||||
}
|
||||
await DataStore.set("relationship-notifier-guilds", guilds);
|
||||
}
|
||||
|
||||
export function getGroup(id: string) {
|
||||
return groups.get(id);
|
||||
}
|
||||
|
||||
export function deleteGroup(id: string) {
|
||||
groups.delete(id);
|
||||
syncGroups();
|
||||
}
|
||||
|
||||
export async function syncGroups() {
|
||||
for (const { type, id, name, rawRecipients, icon } of ChannelStore.getSortedPrivateChannels()) {
|
||||
if (type === ChannelType.GROUP_DM)
|
||||
groups.set(id, {
|
||||
id,
|
||||
name: name || rawRecipients.map(r => r.username).join(", "),
|
||||
iconURL: icon && `https://cdn.discordapp.com/channel-icons/${id}/${icon}.png`
|
||||
});
|
||||
}
|
||||
|
||||
await DataStore.set("relationship-notifier-groups", groups);
|
||||
}
|
||||
|
||||
export async function syncFriends() {
|
||||
friends.friends = [];
|
||||
friends.requests = [];
|
||||
|
||||
const relationShips = RelationshipStore.getRelationships();
|
||||
for (const id in relationShips) {
|
||||
switch (relationShips[id]) {
|
||||
case RelationshipType.FRIEND:
|
||||
friends.friends.push(id);
|
||||
break;
|
||||
case RelationshipType.FRIEND_REQUEST:
|
||||
friends.requests.push(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
await DataStore.set("relationship-notifier-friends", friends);
|
||||
}
|
|
@ -194,6 +194,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "Captain",
|
||||
id: 347366054806159360n
|
||||
},
|
||||
nick: {
|
||||
name: "nick",
|
||||
id: 347884694408265729n
|
||||
},
|
||||
whqwert: {
|
||||
name: "whqwert",
|
||||
id: 586239091520176128n
|
||||
|
|
Loading…
Reference in a new issue