From 53af4025ae9e1d92a70b30934bebf4099f392310 Mon Sep 17 00:00:00 2001 From: Jethro Date: Wed, 22 Apr 2026 22:43:28 +0200 Subject: [PATCH 1/3] feat(RetroAchievements): add activity --- websites/R/RetroAchievements/metadata.json | 29 +++++++ websites/R/RetroAchievements/presence.ts | 96 ++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 websites/R/RetroAchievements/metadata.json create mode 100644 websites/R/RetroAchievements/presence.ts diff --git a/websites/R/RetroAchievements/metadata.json b/websites/R/RetroAchievements/metadata.json new file mode 100644 index 000000000000..d449ff81377f --- /dev/null +++ b/websites/R/RetroAchievements/metadata.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schemas.premid.app/metadata/1.16", + "apiVersion": 1, + "author": { + "id": "961292362208919582", + "name": "jethi_" + }, + "service": "RetroAchievements", + "description": { + "en": "RetroAchievements is a platform for tracking and earning achievements in retro video games." + }, + "url": "retroachievements.org", + "regExp": "^https?[:][/][/]([a-z0-9-]+[.])*retroachievements[.]org[/]", + "version": "1.0.0", + "logo": "https://i.imgur.com/dFegUxW.png", + "thumbnail": "https://i.imgur.com/xuvJ8sD.png", + "color": "#1065df", + "category": "games", + "tags": [ + "ra", + "achievements", + "games", + "gaming", + "leaderboards", + "retro", + "retroachievements", + "tracker" + ] +} diff --git a/websites/R/RetroAchievements/presence.ts b/websites/R/RetroAchievements/presence.ts new file mode 100644 index 000000000000..1ac9c25b7048 --- /dev/null +++ b/websites/R/RetroAchievements/presence.ts @@ -0,0 +1,96 @@ +import { Assets } from 'premid' + +const presence = new Presence({ + clientId: '1496589220515680326', +}) + +const browsingTimestamp = Math.floor(Date.now() / 1000) + +presence.on('UpdateData', async () => { + const pathname = document.location.pathname + const strings = await presence.getStrings({ + buttonViewProfile: 'general.buttonViewProfile', + buttonViewGame: 'general.buttonViewGame', + }) + + const presenceData: PresenceData = { + largeImageKey: 'https://i.imgur.com/dFegUxW.png', + startTimestamp: browsingTimestamp, + } + + // Home + if (pathname === '/' || pathname === '') { + presenceData.details = 'Homepage' + } + // Achievement page + else if (pathname.includes('/achievement/')) { + const achievementTitle = document.querySelector('p[class*="pb-[3px]"]')?.textContent?.trim() || + document.querySelector('h1')?.textContent?.trim() || + document.querySelector('.pagetitle')?.textContent?.trim() || + document.title.split(' - ')[0]?.trim() + presenceData.details = 'Viewing Achievement' + presenceData.state = achievementTitle || 'Achievement Details' + presenceData.buttons = [{ label: 'View Achievement', url: document.location.href }] + } + // Game page + else if (pathname.includes('/game/')) { + const gameTitle = document.querySelector('h1')?.textContent?.trim() || + document.querySelector('.pagetitle')?.textContent?.trim() + presenceData.details = 'Viewing Game' + presenceData.state = gameTitle || 'Game Page' + presenceData.buttons = [{ label: strings.buttonViewGame, url: document.location.href }] + } + // User profile + else if (pathname.includes('/user/')) { + const usernameFromUrl = pathname.split('/user/')[1]?.split('/')[0] + const usernameFromPage = document.querySelector('h1')?.textContent?.trim() || + document.querySelector('.username')?.textContent?.trim() + const username = usernameFromPage || usernameFromUrl + presenceData.details = 'Viewing User Profile' + presenceData.state = username ? decodeURIComponent(username) : 'User Profile' + presenceData.buttons = [{ label: strings.buttonViewProfile, url: document.location.href }] + } + // Games list / System games + else if (pathname.startsWith('/games') || pathname.includes('/system/') && pathname.includes('/games')) { + const searchQuery = new URL(document.location.href).searchParams.get('filter[title]') + if (searchQuery) { + presenceData.details = 'Searching for Games' + presenceData.state = searchQuery + presenceData.smallImageKey = Assets.Search + } else { + presenceData.details = 'Browsing Games' + } + } + // Forums + else if (pathname === '/forum.php' || pathname.startsWith('/viewforum.php')) { + presenceData.details = 'In Forums' + } + // Settings + else if (pathname.includes('/settings')) { + presenceData.details = 'Managing Account' + } + // Downloads + else if (pathname.includes('/downloads')) { + presenceData.details = 'Viewing Emulators' + } + // Hubs + else if (pathname === '/hubs' || pathname.match(/\/hub\/\d+/)) { + if (pathname === '/hubs') { + presenceData.details = 'Viewing Hubs' + } else { + const hubTitle = document.querySelector('div.flex.items-center.gap-2')?.textContent?.trim() + presenceData.details = 'Viewing a Hub' + // TODO: - Add button to view hub + // - state should be the hub title + } + } + // Default fallback + else { + presenceData.details = 'Browsing RetroAchievements' + } + + if (presenceData.details) + presence.setActivity(presenceData) + else + presence.clearActivity() +}) From 4de4100714f66982559a0cd9b678d469b1b21514 Mon Sep 17 00:00:00 2001 From: Jethro Date: Wed, 22 Apr 2026 23:00:47 +0200 Subject: [PATCH 2/3] chore(RetroAchievements): removed "retroachievements" tag --- websites/R/RetroAchievements/metadata.json | 1 - 1 file changed, 1 deletion(-) diff --git a/websites/R/RetroAchievements/metadata.json b/websites/R/RetroAchievements/metadata.json index d449ff81377f..003c3ee4e5a4 100644 --- a/websites/R/RetroAchievements/metadata.json +++ b/websites/R/RetroAchievements/metadata.json @@ -23,7 +23,6 @@ "gaming", "leaderboards", "retro", - "retroachievements", "tracker" ] } From 24f37dd22941e25b3f353d20fb373dfca1d203ea Mon Sep 17 00:00:00 2001 From: Jethro Date: Wed, 22 Apr 2026 23:11:08 +0200 Subject: [PATCH 3/3] e --- websites/R/RetroAchievements/presence.ts | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/websites/R/RetroAchievements/presence.ts b/websites/R/RetroAchievements/presence.ts index 1ac9c25b7048..79d19047e8af 100644 --- a/websites/R/RetroAchievements/presence.ts +++ b/websites/R/RetroAchievements/presence.ts @@ -24,18 +24,18 @@ presence.on('UpdateData', async () => { } // Achievement page else if (pathname.includes('/achievement/')) { - const achievementTitle = document.querySelector('p[class*="pb-[3px]"]')?.textContent?.trim() || - document.querySelector('h1')?.textContent?.trim() || - document.querySelector('.pagetitle')?.textContent?.trim() || - document.title.split(' - ')[0]?.trim() + const achievementTitle = document.querySelector('p[class*="pb-[3px]"]')?.textContent?.trim() + || document.querySelector('h1')?.textContent?.trim() + || document.querySelector('.pagetitle')?.textContent?.trim() + || document.title.split(' - ')[0]?.trim() presenceData.details = 'Viewing Achievement' presenceData.state = achievementTitle || 'Achievement Details' presenceData.buttons = [{ label: 'View Achievement', url: document.location.href }] } // Game page else if (pathname.includes('/game/')) { - const gameTitle = document.querySelector('h1')?.textContent?.trim() || - document.querySelector('.pagetitle')?.textContent?.trim() + const gameTitle = document.querySelector('h1')?.textContent?.trim() + || document.querySelector('.pagetitle')?.textContent?.trim() presenceData.details = 'Viewing Game' presenceData.state = gameTitle || 'Game Page' presenceData.buttons = [{ label: strings.buttonViewGame, url: document.location.href }] @@ -43,21 +43,23 @@ presence.on('UpdateData', async () => { // User profile else if (pathname.includes('/user/')) { const usernameFromUrl = pathname.split('/user/')[1]?.split('/')[0] - const usernameFromPage = document.querySelector('h1')?.textContent?.trim() || - document.querySelector('.username')?.textContent?.trim() + const usernameFromPage = document.querySelector('h1')?.textContent?.trim() + || document.querySelector('.username')?.textContent?.trim() const username = usernameFromPage || usernameFromUrl presenceData.details = 'Viewing User Profile' presenceData.state = username ? decodeURIComponent(username) : 'User Profile' presenceData.buttons = [{ label: strings.buttonViewProfile, url: document.location.href }] } // Games list / System games - else if (pathname.startsWith('/games') || pathname.includes('/system/') && pathname.includes('/games')) { + else if (pathname.startsWith('/games') + || (pathname.includes('/system/') && pathname.includes('/games'))) { const searchQuery = new URL(document.location.href).searchParams.get('filter[title]') if (searchQuery) { presenceData.details = 'Searching for Games' presenceData.state = searchQuery presenceData.smallImageKey = Assets.Search - } else { + } + else { presenceData.details = 'Browsing Games' } } @@ -77,11 +79,11 @@ presence.on('UpdateData', async () => { else if (pathname === '/hubs' || pathname.match(/\/hub\/\d+/)) { if (pathname === '/hubs') { presenceData.details = 'Viewing Hubs' - } else { - const hubTitle = document.querySelector('div.flex.items-center.gap-2')?.textContent?.trim() + } + else { presenceData.details = 'Viewing a Hub' - // TODO: - Add button to view hub - // - state should be the hub title + // TODO: - Add button to view hub + // - state should be the hub title } } // Default fallback