/* * Vencord, a Discord client mod * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType, PluginNative } from "@utils/types"; import { ApplicationAssetUtils, FluxDispatcher, UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; export async function getApplicationAsset(key: string): Promise<string> { if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); return (await ApplicationAssetUtils.fetchAssetIds("0", [key]))[0]; } enum StatsDisplay { messagesSentToday, messagesSentAllTime, mostListenedAlbum } const settings = definePluginSettings( { assetURL: { type: OptionType.STRING, description: "The image to use for your rpc. Your profile picture is used if left blank", default: "", restartNeeded: false, onChange: () => { updateData(); } }, RPCTitle: { type: OptionType.STRING, description: "The title for the rpc", default: "RPCStats", restartNeeded: false, onChange: () => { updateData(); } }, statDisplay: { type: OptionType.SELECT, description: "What should the rpc display? (you can only have one line i'm pretty sure)", options: [ { value: StatsDisplay.messagesSentToday, label: "The amount of messages sent today", default: true }, { value: StatsDisplay.messagesSentAllTime, label: "The amount of messages sent all time" }, { value: StatsDisplay.mostListenedAlbum, label: "Your most listened album for the week" } ], restartNeeded: false, onChange: () => { updateData(); } }, lastFMApiKey: { type: OptionType.STRING, description: "Your last.fm API key", default: "", restartNeeded: false, onChange: () => { updateData(); } }, lastFMUsername: { type: OptionType.STRING, description: "Your last.fm username", default: "", restartNeeded: false, onChange: () => { updateData(); } }, albumCoverImage: { type: OptionType.BOOLEAN, description: "Should the album cover image be used as the rpc image? (if you have the last fm display chosen)", default: true, restartNeeded: false, onChange: () => { updateData(); } }, lastFMStatFormat: { type: OptionType.STRING, description: "How should the last fm stat be formatted? $album is replaced with the album name, and $artist is replaced with the artist name", default: "Top album this week: \"$album - $artist\"", restartNeeded: false, onChange: () => { updateData(); } } }); async function setRpc(disable?: boolean, details?: string, imageURL?: string) { if (!disable) { if (!settings.store.lastFMApiKey.length && settings.store.statDisplay === StatsDisplay.mostListenedAlbum) { FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: null, socketId: "RPCStats", }); } } const activity = { "application_id": "0", "name": settings.store.RPCTitle, "details": details || "No info right now :(", "type": 0, "flags": 1, "assets": { // i love insanely long statements "large_image": (imageURL == null || !settings.store.albumCoverImage) ? await getApplicationAsset(settings.store.assetURL.length ? settings.store.assetURL : UserStore.getCurrentUser().getAvatarURL()) : await getApplicationAsset(imageURL) } }; FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", activity: !disable ? activity : null, socketId: "RPCStats", }); } function getCurrentDate(): string { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } interface IMessageCreate { type: "MESSAGE_CREATE"; optimistic: boolean; isPushNotification: boolean; channelId: string; message: Message; } const Native = VencordNative.pluginHelpers.RPCStats as PluginNative<typeof import("./native")>; async function updateData() { switch (settings.store.statDisplay) { case StatsDisplay.messagesSentToday: let messagesSent; if (await DataStore.get("RPCStatsDate") === getCurrentDate()) { messagesSent = await DataStore.get("RPCStatsMessages"); } else { await DataStore.set("RPCStatsDate", getCurrentDate()); await DataStore.set("RPCStatsMessages", 0); messagesSent = 0; } setRpc(false, `Messages sent today: ${messagesSent}\n`); break; case StatsDisplay.messagesSentAllTime: let messagesAllTime = await DataStore.get("RPCStatsAllTimeMessages"); if (!messagesAllTime) { DataStore.set("RPCStatsAllTimeMessages", 0); messagesAllTime = 0; } setRpc(false, `Messages sent all time: ${messagesAllTime}\n`); break; // slightly cursed case StatsDisplay.mostListenedAlbum: const lastFMDataJson = await Native.fetchTopAlbum( { apiKey: settings.store.lastFMApiKey, user: settings.store.lastFMUsername, period: "7day" }); if (lastFMDataJson == null) return; const lastFMData = JSON.parse(lastFMDataJson); console.log(lastFMData); setRpc(false, settings.store.lastFMStatFormat.replace("$album", lastFMData.albumName).replace("$artist", lastFMData.artistName), lastFMData?.albumCoverUrl); break; } } export default definePlugin({ name: "RPCStats", description: "Displays stats about your activity as an rpc", authors: [Devs.Samwich], async start() { updateData(); setInterval(() => { checkForNewDay(); updateData(); }, 1000); }, settings, stop() { setRpc(true); }, flux: { async MESSAGE_CREATE({ optimistic, type, message }: IMessageCreate) { if (optimistic || type !== "MESSAGE_CREATE") return; if (message.state === "SENDING") return; if (message.author.id !== UserStore.getCurrentUser().id) return; await DataStore.set("RPCStatsMessages", await DataStore.get("RPCStatsMessages") + 1); await DataStore.set("RPCStatsAllTimeMessages", await DataStore.get("RPCStatsAllTimeMessages") + 1); updateData(); }, } }); let lastCheckedDate: string = getCurrentDate(); function checkForNewDay(): void { const currentDate = getCurrentDate(); if (currentDate !== lastCheckedDate) { lastCheckedDate = currentDate; } }