diff --git a/src/equicordplugins/vencordRPC/index.ts b/src/equicordplugins/vencordRPC/index.ts deleted file mode 100644 index ba985418a..000000000 --- a/src/equicordplugins/vencordRPC/index.ts +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2023 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { definePluginSettings } from "@api/Settings"; -import { Devs } from "@utils/constants"; -import { isTruthy } from "@utils/guards"; -import definePlugin, { OptionType } from "@utils/types"; -import { Activity, Channel, FluxStore } from "@vencord/discord-types"; -import { ActivityType } from "@vencord/discord-types/enums"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; -import { ApplicationAssetUtils, ChannelStore, FluxDispatcher, GuildStore, IconUtils, PresenceStore, RelationshipStore, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; - -const presenceStore = findByPropsLazy("getLocalPresence"); -const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; }; -const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & { - getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; }; -}; -const VoiceStates = findByPropsLazy("getVoiceStatesForChannel"); -const chino = "https://i.imgur.com/Dsa2rQy.png"; -const wysi = "https://i.imgur.com/uKtXde9.gif"; - -async function getApplicationAsset(key: string): Promise { - if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); - return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0]; -} - -const enum TimestampMode { - NONE, - NOW, - TIME, - CUSTOM, -} - -const settings = definePluginSettings({ - appID: { - type: OptionType.STRING, - description: "Application ID (required)", - onChange: onChange, - isValid: (value: string) => { - if (!value) return "Application ID is required."; - if (value && !/^\d+$/.test(value)) return "Application ID must be a number."; - return true; - } - }, - userAvatarAsSmallImage: { - type: OptionType.BOOLEAN, - description: "Use your avatar as small image", - onChange: onChange, - default: false - }, - exposeDmsUsername: { - type: OptionType.BOOLEAN, - description: "Expose current DMs username", - onChange: onChange, - default: false - }, - type: { - type: OptionType.SELECT, - description: "Activity type", - onChange: onChange, - options: [ - { - label: "Playing", - value: ActivityType.PLAYING, - default: true - }, - { - label: "Streaming", - value: ActivityType.STREAMING - }, - { - label: "Listening", - value: ActivityType.LISTENING - }, - { - label: "Watching", - value: ActivityType.WATCHING - }, - { - label: "Competing", - value: ActivityType.COMPETING - } - ] - }, - streamLink: { - type: OptionType.STRING, - description: "Twitch.tv or Youtube.com link (only for Streaming activity type)", - onChange: onChange, - disabled: isStreamLinkDisabled, - isValid: isStreamLinkValid - }, - timestampMode: { - type: OptionType.SELECT, - description: "Timestamp mode", - onChange: onChange, - options: [ - { - label: "None", - value: TimestampMode.NONE, - default: true - }, - { - label: "Since discord open", - value: TimestampMode.NOW - }, - { - label: "Same as your current time", - value: TimestampMode.TIME - }, - { - label: "Custom", - value: TimestampMode.CUSTOM - } - ] - }, - startTime: { - type: OptionType.NUMBER, - description: "Start timestamp (only for custom timestamp mode)", - onChange: onChange, - disabled: isTimestampDisabled, - isValid: (value: number) => { - if (value && value < 0) return "Start timestamp must be greater than 0."; - return true; - } - }, - endTime: { - type: OptionType.NUMBER, - description: "End timestamp (only for custom timestamp mode)", - onChange: onChange, - disabled: isTimestampDisabled, - isValid: (value: number) => { - if (value && value < 0) return "End timestamp must be greater than 0."; - return true; - } - }, - secretStuff: { - type: OptionType.STRING, - description: "A list of server/channel ids separated by commas, which are hidden from the RPC.", - onChange: onChange, - default: "" - } -}); - -function onChange() { - setRpc(true); - setRpc(); -} - -function isStreamLinkDisabled() { - return settings.store.type !== ActivityType.STREAMING; -} - -function isStreamLinkValid(value: string) { - if (!isStreamLinkDisabled() && !/https?:\/\/(www\.)?(twitch\.tv|youtube\.com)\/\w+/.test(value)) return "Streaming link must be a valid URL."; - return true; -} - -function isTimestampDisabled() { - return settings.store.timestampMode !== TimestampMode.CUSTOM; -} - -function onlineFriendCount(): number { - let onlineFriends = 0; - const relationships = RelationshipStore.getFriendIDs(); - for (const id of relationships) { - if (PresenceStore.getStatus(id) !== "offline") { - onlineFriends++; - } - } - return onlineFriends; -} - -function totalFriendCount(): number { - return RelationshipStore.getFriendCount(); -} - -function memberCount(): string { - const channelId = SelectedChannelStore.getChannelId(); - const guildId = SelectedGuildStore.getGuildId()!; - const { groups } = ChannelMemberStore.getProps(guildId, channelId); - const total = GuildMemberCountStore.getMemberCount(guildId); - - if (total == null) - return ""; - - const online = - (groups.length === 1 && groups[0].id === "unknown") - ? 0 - : groups.reduce((count, curr) => count + (curr.id === "offline" ? 0 : curr.count), 0); - - return online === 0 ? `${total} members` : `${online} online / ${total} total`; -} - -function getChannelIconURL(channel: Channel): string { - if (channel.icon) return `https://cdn.discordapp.com/channel-icons/${channel.id}/${channel.icon}.webp?size=128`; - return chino; -} - -function getSecretStuff(): string[] { - return settings.store.secretStuff.split(",").map(id => id.trim()).filter(Boolean); -} - -async function createActivity(): Promise { - - const { - appID, - userAvatarAsSmallImage, - exposeDmsUsername, - streamLink, - timestampMode, - startTime, - endTime, - } = settings.store; - - let { type } = settings.store; - - let appName = "Equicord"; - let details = ""; - let state = ""; - let imageBig = ""; - const imageBigTooltip = undefined; - let imageSmall = ""; - const imageSmallTooltip = undefined; - let buttonOneText: string | undefined; - let buttonOneURL: string | undefined; - let buttonTwoText: string | undefined; - let buttonTwoURL: string | undefined; - - - const channelId = SelectedChannelStore.getChannelId(); - const guildId = SelectedGuildStore.getGuildId()!; - const voiceId = SelectedChannelStore.getVoiceChannelId(); - const currentUser = UserStore.getCurrentUser(); - if (userAvatarAsSmallImage) imageSmall = currentUser.getAvatarURL(undefined, undefined, true) || chino; - - const secretStuff = getSecretStuff(); - const isSecret = secretStuff.includes(channelId) || secretStuff.includes(guildId); - - if (isSecret) { - appName = "Hidden Channel/Guild"; - details = "#secret"; - state = "hehe"; - imageBig = chino; - } else if (!channelId) { - appName = "Friends List"; - details = `${onlineFriendCount()} online / ${totalFriendCount()} total`; - state = `${GuildStore.getGuildCount()} servers`; - imageBig = chino; - } else { - if (channelId === "@home" || channelId === "customize-community" || channelId === "channel-browser" || channelId === "onboarding") { - appName = channelId === "@home" ? "Server Guide" : channelId === "customize-community" ? "Channels & Roles" : channelId === "channel-browser" ? "Browse Channels" : "Onboarding"; - const guild = GuildStore.getGuild(guildId); - if (guild) { - details = guild.name; - state = memberCount(); - imageBig = IconUtils.getGuildIconURL({ id: guild.id, icon: guild.icon, canAnimate: true, size: 512 }) || chino; - if (guild.vanityURLCode) { - buttonOneText = `Join ${guild.name.slice(0, 26)}`; - buttonOneURL = `https://discord.gg/${guild.vanityURLCode}`; - } - } - } else { - - const channel = ChannelStore.getChannel(channelId); - - if (channel.isDM()) { - const recipient = UserStore.getUser(channel.recipients[0]); - appName = exposeDmsUsername ? `${recipient.username}'s DM` : "Direct Messages"; - details = `${onlineFriendCount()} online / ${totalFriendCount()} total`; - state = `${GuildStore.getGuildCount()} servers`; - imageBig = recipient.getAvatarURL(undefined, undefined, true) || chino; - } - - if (channel.isGroupDM()) { - appName = channel.name || "Group DM"; - details = `${channel.recipients.length + 1} members`; - imageBig = getChannelIconURL(channel); - } - - const guild = GuildStore.getGuild(guildId); - if (guild) { - appName = `#${channel.name}`; - details = guild.name; - state = memberCount(); - imageBig = IconUtils.getGuildIconURL({ id: guild.id, icon: guild.icon, canAnimate: true, size: 512 }) || chino; - if (guild.vanityURLCode) { - buttonOneText = `Join ${guild.name.slice(0, 31 - 5)}`; - buttonOneURL = `https://discord.gg/${guild.vanityURLCode}`; - } - } - } - } - - if (voiceId) { - const voiceChannel = ChannelStore.getChannel(voiceId); - const voiceGuild = GuildStore.getGuild(voiceChannel.guild_id); - const voiceMemberCount = Object.keys(VoiceStates.getVoiceStatesForChannel(voiceChannel.id)).length; - buttonTwoText = `🔊 ${voiceGuild.name.slice(0, 31 - 6 - voiceMemberCount.toString().length)} [${voiceMemberCount}]`; - buttonTwoURL = `https://discordapp.com/channels/${voiceGuild.id}/${voiceChannel.id}`; - - if (!buttonOneText) { - buttonOneText = buttonTwoText; - buttonOneURL = buttonTwoURL; - buttonTwoText = undefined; - buttonTwoURL = undefined; - } - } - - if ((new Date().getHours() === 7 || new Date().getHours() === 19) && new Date().getMinutes() === 27) { - type = ActivityType.PLAYING; - appName = "WHEN YOU SEE IT"; - imageBig = wysi; - details = "7:27 👈"; - state = ""; - } - - const activity: Activity = { - application_id: appID || "0", - name: appName, - state, - details, - type, - flags: 1 << 0, - }; - - if (type === ActivityType.STREAMING) activity.url = streamLink; - - switch (timestampMode) { - case TimestampMode.NOW: - activity.timestamps = { - start: Date.now() - }; - break; - case TimestampMode.TIME: - activity.timestamps = { - start: Date.now() - (new Date().getHours() * 3600 + new Date().getMinutes() * 60 + new Date().getSeconds()) * 1000 - }; - break; - case TimestampMode.CUSTOM: - if (startTime || endTime) { - activity.timestamps = {}; - if (startTime) activity.timestamps.start = startTime; - if (endTime) activity.timestamps.end = endTime; - } - break; - case TimestampMode.NONE: - default: - break; - } - - if (buttonOneText) { - activity.buttons = [ - buttonOneText, - buttonTwoText - ].filter(isTruthy); - - activity.metadata = { - button_urls: [ - buttonOneURL, - buttonTwoURL - ].filter(isTruthy) - }; - } - - if (imageBig) { - activity.assets = { - large_image: await getApplicationAsset(imageBig), - large_text: imageBigTooltip || undefined - }; - } - - if (imageSmall) { - activity.assets = { - ...activity.assets, - small_image: await getApplicationAsset(imageSmall), - small_text: imageSmallTooltip || undefined - }; - } - - - for (const k in activity) { - if (k === "type") continue; - const v = activity[k]; - if (!v || v.length === 0) - delete activity[k]; - } - - return activity; -} - -let timeout: NodeJS.Timeout | null = null; - -async function setRpc(disable?: boolean) { - const activities: any = presenceStore.getActivities(); - const activity: Activity | undefined = !activities.length || (activities.length === 1 && activities[0].application_id === settings.store.appID) ? await createActivity() : undefined; - - FluxDispatcher.dispatch({ - type: "LOCAL_ACTIVITY_UPDATE", - activity: !disable ? activity : null, - socketId: "CustomRPC", - }); - - if (!disable) { - timeout = setTimeout(() => setRpc(), 4000); - } else if (timeout) { - clearTimeout(timeout); - timeout = null; - } -} - -export default definePlugin({ - name: "VencordRPC", - description: "how to expose yourself", - authors: [Devs.AutumnVN], - start: setRpc, - stop: () => setRpc(true), - settings -});