mirror of
https://codeberg.org/vlw/stadia-avatar.git
synced 2025-09-13 15:23:40 +02:00
Added DB interface and get/update endpoints.
The bot userscript is now fully operational, the only thing left to do is to make it send the actual payload we need for the endpoint. Added setup for MySQL database. Added DB interface with a concept-stage set/update endpoint.
This commit is contained in:
parent
51a70545b0
commit
ef58aa530e
6 changed files with 231 additions and 17 deletions
77
classes/Database.php
Normal file
77
classes/Database.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
class DBConnector extends mysqli {
|
||||
|
||||
public static $config = [
|
||||
"host" => "",
|
||||
"username" => "",
|
||||
"password" => "",
|
||||
"database" => "stadia_avatars"
|
||||
];
|
||||
|
||||
public function __construct() {
|
||||
parent::init();
|
||||
|
||||
if(!parent::options(MYSQLI_INIT_COMMAND,"SET AUTOCOMMIT = 0")) {
|
||||
die("Setting MYSQLI_INIT_COMMAND failed");
|
||||
}
|
||||
|
||||
if(!parent::options(MYSQLI_OPT_CONNECT_TIMEOUT,5)) {
|
||||
die("Setting MYSQLI_OPT_CONNECT_TIMEOUT failed");
|
||||
}
|
||||
|
||||
if(!parent::real_connect(DBConnector::$config["host"],DBConnector::$config["username"],DBConnector::$config["password"],DBConnector::$config["database"])) {
|
||||
die("Connect Error (".mysqli_connect_errno().") ".mysqli_connect_error());
|
||||
}
|
||||
}
|
||||
|
||||
private function check_connection() {
|
||||
if(parent::connect_errno) {
|
||||
die("Invalid connection");
|
||||
}
|
||||
}
|
||||
|
||||
private function insert_avatar($user_id,$value) {
|
||||
$time = time();
|
||||
$query = "INSERT INTO avatars (userid, avatar, modified) VALUES ('${user_id}', '${value}', '${time}');";
|
||||
|
||||
if($result = parent::query($query) === true) {
|
||||
http_response_code("206");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function update_avatar($user_id,$value) {
|
||||
$time = time();
|
||||
$query = "UPDATE avatars SET avatar = '${value}', modified = '${time}' WHERE avatars.userid = '${user_id}';";
|
||||
|
||||
if($result = parent::query($query) === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----
|
||||
|
||||
public function get_avatar($user_id) {
|
||||
$query = "SELECT userid, avatar FROM avatars WHERE userid = '${user_id}';";
|
||||
|
||||
if($result = parent::query($query)) {
|
||||
return $result->fetch_array(MYSQLI_NUM)[1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function set_avatar($user_id,$value) {
|
||||
if($this->get_avatar($user_id)) {
|
||||
return $this->update_avatar($user_id,$value);
|
||||
}
|
||||
|
||||
return $this->insert_avatar($user_id,$value);
|
||||
}
|
||||
|
||||
}
|
8
classes/Message.php
Normal file
8
classes/Message.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
function error($code,$message) {
|
||||
http_response_code($code);
|
||||
die("{\"status\":\"error\",\"code\":\"${code}\",\"message\":\"${message}\"}");
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
require "../classes/Message.php";
|
||||
require "../classes/Database.php";
|
||||
|
||||
$user_id = $_GET["userID"] ?? error("400","No userID provided");
|
||||
|
||||
$db = new DBConnector();
|
||||
$avatar = $db->get_avatar($user_id);
|
||||
|
||||
if($avatar) {
|
||||
echo "{\"status\":\"OK\",\"avatar\":\"${avatar}\"}";
|
||||
return;
|
||||
}
|
||||
|
||||
error("404","No avatar was found for the supplied userID");
|
18
endpoint/update.php
Normal file
18
endpoint/update.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
require "../classes/Message.php";
|
||||
require "../classes/Database.php";
|
||||
|
||||
$sharedSecret = ""; // Key to update the database
|
||||
|
||||
if($sharedSecret !== "") {
|
||||
error("403","Invalid shared secret.");
|
||||
}
|
||||
|
||||
$db = new DBConnector();
|
||||
|
||||
if(!$db->set_avatar("foo","bario")) {
|
||||
error("500","Something went wrong.");
|
||||
}
|
||||
|
||||
echo "{\"status\":\"OK\"}";
|
50
setup-db.sql
50
setup-db.sql
|
@ -0,0 +1,50 @@
|
|||
-- phpMyAdmin SQL Dump
|
||||
-- version 4.9.5
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- Host: victorwesterlund.com
|
||||
-- Generation Time: Feb 04, 2021 at 11:15 AM
|
||||
-- Server version: 10.3.27-MariaDB-0+deb10u1
|
||||
-- PHP Version: 7.3.19-1~deb10u1
|
||||
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
SET AUTOCOMMIT = 0;
|
||||
START TRANSACTION;
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
--
|
||||
-- Database: `stadia_avatars`
|
||||
--
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Table structure for table `avatars`
|
||||
--
|
||||
|
||||
CREATE TABLE `avatars` (
|
||||
`userid` varchar(255) NOT NULL,
|
||||
`avatar` tinytext NOT NULL,
|
||||
`modified` int(255) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
--
|
||||
-- Indexes for dumped tables
|
||||
--
|
||||
|
||||
--
|
||||
-- Indexes for table `avatars`
|
||||
--
|
||||
ALTER TABLE `avatars`
|
||||
ADD PRIMARY KEY (`userid`);
|
||||
COMMIT;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
|
@ -1,29 +1,74 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
const targetJSName = "FhFdCc";
|
||||
let observer = null;
|
||||
/* 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 receivedMessage = (mutations,observer) => {
|
||||
const mutation = mutations[3].addedNodes[0];
|
||||
console.log(mutations);
|
||||
const endpoint = "";
|
||||
const sharedSecret = ""; // Key to update the database
|
||||
|
||||
const className = {
|
||||
newMessage: "daVSod",
|
||||
messageContent: "dgrMg",
|
||||
messageWindow: "P9pxvf",
|
||||
backButton: "rkvT7c"
|
||||
}
|
||||
|
||||
function attachObserver() {
|
||||
const target = document.querySelector(`[jsname="${targetJSName}"]`);
|
||||
let interval = null;
|
||||
|
||||
observer = new MutationObserver(receivedMessage);
|
||||
observer.observe(target,{
|
||||
subtree: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
characterData: true
|
||||
async function updateAvatar(userID,payload) {
|
||||
const response = await fetch(endpoint,{
|
||||
method: "POST",
|
||||
mode: "no-cors",
|
||||
headers: {
|
||||
"Content-Type": "text/plain"
|
||||
},
|
||||
body: `{"sharedSecret":"${sharedSecret}","userID":"${userID}","payload":"${payload}"}`
|
||||
});
|
||||
return response.text();
|
||||
}
|
||||
|
||||
// Start the StadiaAvatar bot with 'window._StadiaAvatar()'
|
||||
window._StadiaAvatar = () => {
|
||||
attachObserver();
|
||||
const backToMessageList = (mutations,observer) => {
|
||||
observer.disconnect();
|
||||
// Click the back button once the animation has ended
|
||||
setTimeout(() => document.getElementsByClassName(className.backButton)[0].click(),1000);
|
||||
}
|
||||
|
||||
function pollNewMessages() {
|
||||
const messages = document.getElementsByClassName(className.newMessage);
|
||||
if(!messages) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const message of messages) {
|
||||
const userID = message.getAttribute("data-playerid");
|
||||
const userMessage = message.getElementsByClassName(className.messageContent)[0].innerText;
|
||||
|
||||
message.click(); // Send the AckMessage request natively by simulating a click
|
||||
|
||||
// Wait for the message window to open
|
||||
const messageWindow = document.getElementsByClassName(className.messageWindow)[0];
|
||||
|
||||
const observer = new MutationObserver(backToMessageList);
|
||||
observer.observe(messageWindow,{
|
||||
attributes: true,
|
||||
subtree: true
|
||||
});
|
||||
|
||||
updateAvatar(userID,userMessage)
|
||||
.catch(err => console.error("Failed to update avatar",err));
|
||||
}
|
||||
}
|
||||
|
||||
window._StadiaAvatar = {
|
||||
// Start the StadiaAvatar bot with 'window._StadiaAvatar.start()'
|
||||
start: (delay = 500) => {
|
||||
interval = setInterval(() => pollNewMessages(),delay);
|
||||
console.log("StadiaAvatar bot started.");
|
||||
},
|
||||
stop: () => {
|
||||
interval = null
|
||||
console.log("StadiaAvatar bot stopped.");
|
||||
}
|
||||
}
|
||||
})();
|
Loading…
Add table
Reference in a new issue