diff --git a/classes/Database.php b/classes/Database.php index 8d33cb6..147d09f 100644 --- a/classes/Database.php +++ b/classes/Database.php @@ -6,7 +6,7 @@ "host" => "", "username" => "", "password" => "", - "database" => "stadia_avatars" + "database" => "stadia_avatars" // 'setup-db.sql' contains the db structure ]; protected static $instance; @@ -44,6 +44,7 @@ } + // DB interface for manipulating Stadia Avatar class StadiaAvatarDB extends DBConnector { public function __construct() { @@ -64,6 +65,7 @@ return $this->sql->exec_query($SQL); } + // Return the value of column 'avatar' public function get_avatar($user_id) { $SQL = "SELECT `userid`, `avatar` FROM `avatars` WHERE `userid` = '${user_id}'"; $query = $this->sql->exec_query($SQL); @@ -73,6 +75,7 @@ } } + // Insert or update user avatar public function set_avatar($user_id,$avatar) { if(!$this->get_avatar($user_id)) { http_response_code("201"); diff --git a/userscript-bot/bot.js b/userscript-bot.js similarity index 83% rename from userscript-bot/bot.js rename to userscript-bot.js index 79e26dc..8886ce8 100644 --- a/userscript-bot/bot.js +++ b/userscript-bot.js @@ -1,10 +1,21 @@ +// ==UserScript== +// @name Stadia Avatars - Bot +// @namespace https://victorwesterlund.com/ +// @version 1.0 +// @description https://github.com/VictorWesterlund/stadia-avatar +// @author VictorWesterlund +// @match https://stadia.google.com/* +// @grant none +// @noframes +// ==/UserScript== + (function() { 'use strict'; /* The Stadia Service Worker "mgsw.js" acts like a proxy and rejects requests to the StadiaAvatar endpoint. Enable the "Bypass for network" toggle in DevTools->Application->Service Workers to circumvent this issue. */ - const endpoint = ""; + const endpoint = "https://api.victorwesterlund.com/stadia-avatar/update"; const sharedSecret = ""; // Key to update the database const className = { @@ -67,7 +78,7 @@ console.log("StadiaAvatar bot started."); }, stop: () => { - interval = null + interval = null; console.log("StadiaAvatar bot stopped."); } } diff --git a/userscript-client.js b/userscript-client.js new file mode 100644 index 0000000..0e6e0d4 --- /dev/null +++ b/userscript-client.js @@ -0,0 +1,118 @@ +// ==UserScript== +// @name Stadia Avatars +// @namespace https://victorwesterlund.com/ +// @version 1.0 +// @description victorWesterlund/stadia-avatar +// @author VictorWesterlund +// @match https://stadia.google.com/* +// @grant none +// @noframes +// ==/UserScript== + +(function() { + 'use strict'; + + const stadiaAvatar = new URL("https://api.victorwesterlund.com/stadia-avatar/get"); + const gravatar = new URL("https://www.gravatar.com/"); + + /* + G: Suitable for display on all websites with any audience type. + PG: May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence. + R: May contain such things as harsh profanity, intense violence, nudity, or hard drug use. + X: May contain hardcore sexual imagery or extremely disturbing violence. + */ + gravatar.searchParams.set("rating","G"); + + // Stylesheet for Stadia Avatars + class StadiaAvatarCSS { + + constructor() { + this.sheet = null; + this.createStylesheet(); + } + + createStylesheet() { + const style = document.createElement("style"); + style.setAttribute("data-stadia-avatars",""); + style.setAttribute("data-late-css",""); + + document.head.appendChild(style); + this.sheet = style.sheet; + } + + // Serialized group of selectors based on context + selectors(group,id = false) { + switch(group) { + case "me": return ".ksZYgc, .rybUIf"; + case "friends": return `.Y1rZWd[data-playerid="${id}"] .Fnd1Pd, .w2Sl7c[data-playerid="${id}"] .drvCDc`; + } + } + + add(selectors,avatar) { + this.sheet.insertRule(`${selectors} { background-image: url(${avatar}) !important; }`); + console.log(`${selectors} { background-image: url(${avatar}) !important; }`); + } + + } + + const avatars = new StadiaAvatarCSS(); + + // ---- + + // Return the player ID attribute of an element + const getID = (target) => { + const id = target.getAttribute("data-player-id") ?? target.getAttribute("data-playerid"); + return id; + } + + async function getStadiaAvatar(playerID) { + stadiaAvatar.searchParams.set("userID",playerID); + + const response = await fetch(stadiaAvatar); + return response.json(); + } + + // Fetch avatar and append to stylesheet + function replaceWithGravatar(group,playerID) { + getStadiaAvatar(playerID).then(response => { + gravatar.pathname = "/avatar/" + response.avatar; // Append Gravatar hash + avatars.add(avatars.selectors(group,playerID),gravatar); // Add style override by group + }); + } + + // ---- + + replaceWithGravatar("me",getID(document.querySelector("[jsname='HiaYvf']"))); + + function updateFamily(group,wrapper) { + for(const element of wrapper) { + const id = getID(element); + if(!id) { + continue; + } + + replaceWithGravatar(group,id); + } + } + + let timeout = null; + + const friendsList = (mutation,observer) => { + clearTimeout(timeout); + + timeout = setTimeout(() => { + const friends = document.querySelector("[jsname='FhFdCc']").children; + const messages = document.querySelector("[jsname='FhFdCc']").children; + + updateFamily("friends",messages); + },700); + } + + const friendsMenu = document.querySelector("[jsname='TpfyL']"); + const friends = new MutationObserver(friendsList); + friends.observe(friendsMenu,{ + childList: true, + subtree: true + }); + +})(); \ No newline at end of file diff --git a/userscript-client/UserScript.js b/userscript-client/UserScript.js deleted file mode 100644 index 437bb8e..0000000 --- a/userscript-client/UserScript.js +++ /dev/null @@ -1,11 +0,0 @@ -// ==UserScript== -// @name Stadia Avatars -// @namespace https://victorwesterlund.com/ -// @version 0.1 -// @description try to take over the world! -// @author VictorWesterlund -// @match https://stadia.google.com/* -// @grant none -// @require file://C:\path\to\userscript.user.js -// @noframes -// ==/UserScript== \ No newline at end of file diff --git a/userscript-client/script.js b/userscript-client/script.js deleted file mode 100644 index 278b13a..0000000 --- a/userscript-client/script.js +++ /dev/null @@ -1,32 +0,0 @@ -(function() { - 'use strict'; - - function getElementByJSname(name) { - return document.querySelector(`[jsname="${name}"]`); - } - - const myID = () => { - let element = getElementByJSname("HiaYvf"); - return element.getAttribute("data-player-id"); - } - - function locationChanged() { - switch(window.location.pathname) { - case "/home": console.log("home"); break; - case "/settings": console.log("settings"); break; - default: break; - } - } - - let currentLocation = window.location.href; - - // Poll window.location.href for changes - let poll = setInterval(() => { - if(window.location.href != currentLocation) { - currentLocation = window.location.href; - locationChanged(); - } - },500); - - locationChanged(); -})(); \ No newline at end of file