Added telemetry

This commit is contained in:
SwingTheVine 2025-08-16 22:43:35 -04:00
parent 185cc91c56
commit 5368fbf3d8
10 changed files with 195 additions and 27 deletions

View file

@ -1 +1 @@
#bm-s{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;max-width:300px;width:auto;will-change:transform;backface-visibility:hidden;-webkit-backface-visibility:hidden;transform-style:preserve-3d;-webkit-transform-style:preserve-3d}#bm-8,#bm-s hr,#bm-7,#bm-3{transition:opacity .2s ease,height .2s ease}div#bm-s{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-r{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-r.dragging{cursor:grabbing}#bm-s:has(#bm-r.dragging){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}#bm-r.dragging{pointer-events:auto}#bm-c{margin-bottom:.5em}#bm-c[style*="text-align: center"]{display:flex;flex-direction:column;align-items:center;justify-content:center}#bm-s[style*="padding: 5px"]{width:auto!important;max-width:300px;min-width:200px}#bm-s img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle;transition:opacity .2s ease}#bm-c[style*="text-align: center"] img{display:block;margin:0 auto}#bm-r{transition:margin-bottom .2s ease}#bm-s h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-7 input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-7 label{margin-right:.5ch}.bm-v{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-i{vertical-align:middle}#bm-i svg{width:50%;margin:0 auto;fill:#111}div:has(>#bm-button-teleport){display:flex;gap:.5ch}#bm-button-favorite svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-d input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-d input[type=number]::-webkit-outer-spin-button,#bm-d input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-2{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-5)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-5,input[type=file][id*=template]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}#bm-g{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-3{display:flex;justify-content:space-between}#bm-s small{font-size:x-small;color:#d3d3d3}#bm-8,#bm-7,#bm-d,#bm-2,div:has(>#bm-5),#bm-g{margin-top:.5em}#bm-s button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-s button:hover,#bm-s button:focus-visible{background-color:#1061e5}#bm-s button:active,#bm-s button:disabled{background-color:#2e97ff}#bm-s button:disabled{text-decoration:line-through}
#bm-s,#bm-s-telemetry{position:fixed;background-color:#153063e6;color:#fff;padding:10px;border-radius:8px;z-index:9000;transition:all .3s ease,transform 0s;max-width:300px;width:auto;will-change:transform;backface-visibility:hidden;-webkit-backface-visibility:hidden;transform-style:preserve-3d;-webkit-transform-style:preserve-3d}#bm-8,#bm-s hr,#bm-s-telemetry hr,#bm-7,#bm-3{transition:opacity .2s ease,height .2s ease}div#bm-s,div#bm-s-telemetry{font-family:Roboto Mono,Courier New,Monaco,DejaVu Sans Mono,monospace,Arial;letter-spacing:.05em}#bm-r,#bm-r-telemetry{margin-bottom:.5em;background:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;cursor:grab;width:100%;height:1em}#bm-r.dragging,#bm-r-telemetry.dragging{cursor:grabbing}#bm-s:has(#bm-r.dragging),#bm-s-telemetry:has(#bm-r-telemetry.dragging){pointer-events:none;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}#bm-r.dragging,#bm-r-telemetry.dragging{pointer-events:auto}#bm-c,#bm-c-telemetry{margin-bottom:.5em}#bm-c[style*="text-align: center"],#bm-c-telemetry[style*="text-align: center"]{display:flex;flex-direction:column;align-items:center;justify-content:center}#bm-s[style*="padding: 5px"],#bm-s-telemetry[style*="padding: 5px"]{width:auto!important;max-width:300px;min-width:200px}#bm-s img{display:inline-block;height:2.5em;margin-right:1ch;vertical-align:middle;transition:opacity .2s ease}#bm-c[style*="text-align: center"] img{display:block;margin:0 auto}#bm-r,#bm-r-telemetry{transition:margin-bottom .2s ease}#bm-s h1,#bm-s-telemetry h1{display:inline-block;font-size:x-large;font-weight:700;vertical-align:middle}#bm-7 input[type=checkbox]{vertical-align:middle;margin-right:.5ch}#bm-7 label{margin-right:.5ch}.bm-v{border:white 1px solid;height:1.5em;width:1.5em;margin-top:2px;text-align:center;line-height:1em;padding:0!important}#bm-i{vertical-align:middle}#bm-i svg{width:50%;margin:0 auto;fill:#111}div:has(>#bm-button-teleport){display:flex;gap:.5ch}#bm-button-favorite svg,#bm-button-template svg{height:1em;margin:2px auto 0;text-align:center;line-height:1em;vertical-align:bottom}#bm-d input[type=number]{appearance:auto;-moz-appearance:textfield;width:5.5ch;margin-left:1ch;background-color:#0003;padding:0 .5ch;font-size:small}#bm-d input[type=number]::-webkit-outer-spin-button,#bm-d input[type=number]::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}#bm-2{display:flex;flex-direction:row;flex-wrap:wrap;align-content:center;justify-content:center;align-items:center;gap:1ch}div:has(>#bm-5)>button{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#bm-5,input[type=file][id*=template]{display:none!important;visibility:hidden!important;position:absolute!important;left:-9999px!important;top:-9999px!important;width:0!important;height:0!important;opacity:0!important;z-index:-9999!important;pointer-events:none!important}#bm-g{font-size:small;background-color:#0003;padding:0 .5ch;height:3.75em;width:100%}#bm-3{display:flex;justify-content:space-between}#bm-s small{font-size:x-small;color:#d3d3d3}#bm-8,#bm-7,#bm-d,#bm-2,div:has(>#bm-5),#bm-g{margin-top:.5em}#bm-s button,#bm-s-telemetry button{background-color:#144eb9;border-radius:1em;padding:0 .75ch}#bm-s button:hover,#bm-s button:focus-visible,#bm-s-telemetry button:hover,#bm-s-telemetry button:focus-visible{background-color:#1061e5}#bm-s button:active,#bm-s-telemetry button:active #bm-s button:disabled,#bm-s-telemetry button:disabled{background-color:#2e97ff}#bm-s button:disabled,#bm-s-telemetry button:disabled{text-decoration:line-through}

File diff suppressed because one or more lines are too long

View file

@ -48,7 +48,7 @@
<a href="https://github.com/SwingTheVine/Wplace-BlueMarble/blob/main/LICENSE.txt" target="_blank" rel="noopener noreferrer"><img alt="Software License: MPL-2.0" src="https://img.shields.io/badge/Software_License-MPL--2.0-slateblue?style=flat"></a>
<a href="https://discord.gg/tpeBPy46hf" target="_blank" rel="noopener noreferrer"><img alt="Contact Me" src="https://img.shields.io/badge/Contact_Me-gray?style=flat&logo=Discord&logoColor=white&logoSize=auto&labelColor=cornflowerblue"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="WakaTime" src="https://img.shields.io/badge/Coding_Time-111hrs_12mins-blue?style=flat&logo=wakatime&logoColor=black&logoSize=auto&labelColor=white"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-494-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Patches" src="https://img.shields.io/badge/Total_Patches-531-black?style=flat"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Lines of Code" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=code"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Total Comments" src="https://tokei.rs/b1/github/SwingTheVine/Wplace-BlueMarble?category=comments"></a>
<a href="" target="_blank" rel="noopener noreferrer"><img alt="Compression" src="https://img.shields.io/badge/Compression-71.08%25-blue"></a>

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "wplace-bluemarble",
"version": "0.81.0",
"version": "0.81.37",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "wplace-bluemarble",
"version": "0.81.0",
"version": "0.81.37",
"devDependencies": {
"esbuild": "^0.25.0",
"jsdoc": "^4.0.4",

View file

@ -1,6 +1,6 @@
{
"name": "wplace-bluemarble",
"version": "0.81.0",
"version": "0.81.37",
"type": "module",
"scripts": {
"build": "node build/build.js",

View file

@ -1,7 +1,7 @@
// ==UserScript==
// @name Blue Marble
// @namespace https://github.com/SwingTheVine/
// @version 0.81.0
// @version 0.81.37
// @description A userscript to automate and/or enhance the user experience on Wplace.live. Make sure to comply with the site's Terms of Service, and rules! This script is not affiliated with Wplace.live in any way, use at your own risk. This script is not affiliated with TamperMonkey. The author of this userscript is not responsible for any damages, issues, loss of data, or punishment that may occur as a result of using this script. This script is provided "as is" under the MPL-2.0 license. The "Blue Marble" icon is licensed under CC0 1.0 Universal (CC0 1.0) Public Domain Dedication. The image is owned by NASA.
// @author SwingTheVine
// @license MPL-2.0
@ -16,6 +16,8 @@
// @grant GM_addStyle
// @grant GM.setValue
// @grant GM_getValue
// @grant GM_xmlhttpRequest
// @connect telemetry.thebluecorner.net
// @resource CSS-BM-File https://raw.githubusercontent.com/SwingTheVine/Wplace-BlueMarble/1b71f0f8403b459cec0e1e298b73823570ed6016/dist/BlueMarble.user.css
// ==/UserScript==

View file

@ -5,7 +5,7 @@
*/
import TemplateManager from "./templateManager.js";
import { escapeHTML, numberToEncoded, serverTPtoDisplayTP } from "./utils.js";
import { consoleError, escapeHTML, numberToEncoded, serverTPtoDisplayTP } from "./utils.js";
export default class ApiManager {
@ -141,4 +141,110 @@ export default class ApiManager {
}
});
}
// Sends a heartbeat to the telemetry server
async sendHeartbeat(version) {
console.log('Sending heartbeat to telemetry server...');
let userSettings = GM_getValue('bmUserSettings', '{}')
userSettings = JSON.parse(userSettings);
if (!userSettings || !userSettings.telemetry || !userSettings.uuid) {
console.log('Telemetry is disabled, not sending heartbeat.');
return; // If telemetry is disabled, do not send heartbeat
}
const ua = navigator.userAgent;
let browser = await this.#getBrowserFromUA(ua);
let os = this.#getOS(ua);
GM_xmlhttpRequest({
method: 'POST',
url: 'https://telemetry.thebluecorner.net/heartbeat',
headers: {
'Content-Type': 'application/json'
},
data: JSON.stringify({
uuid: userSettings.uuid,
version: version,
browser: browser,
os: os,
}),
onload: (response) => {
if (response.status !== 200) {
consoleError('Failed to send heartbeat:', response.statusText);
}
},
onerror: (error) => {
consoleError('Error sending heartbeat:', error);
}
});
}
async #getBrowserFromUA(ua = navigator.userAgent) {
ua = ua || "";
// Opera
if (ua.includes("OPR/") || ua.includes("Opera")) return "Opera";
// Edge (Chromium-based uses "Edg/")
if (ua.includes("Edg/")) return "Edge";
// Vivaldi
if (ua.includes("Vivaldi")) return "Vivaldi";
// Yandex
if (ua.includes("YaBrowser")) return "Yandex";
// Kiwi (not guaranteed, but typically shows "Kiwi")
if (ua.includes("Kiwi")) return "Kiwi";
// Brave (doesn't expose in UA by default; heuristic via Brave/ token in some versions)
if (ua.includes("Brave")) return "Brave";
// Firefox
if (ua.includes("Firefox/")) return "Firefox";
// Chrome (catch-all for Chromium browsers)
if (ua.includes("Chrome/")) return "Chrome";
// Safari (must be after Chrome check)
if (ua.includes("Safari/")) return "Safari";
// Brave special check
if (navigator.brave && typeof navigator.brave.isBrave === "function") {
if (await navigator.brave.isBrave()) return "Brave";
}
// Fallback
return 'Unknown';
}
#getOS(ua = navigator.userAgent) {
ua = ua || "";
if (/Windows NT 11/i.test(ua)) return "Windows 11";
if (/Windows NT 10/i.test(ua)) return "Windows 10";
if (/Windows NT 6\.3/i.test(ua)) return "Windows 8.1";
if (/Windows NT 6\.2/i.test(ua)) return "Windows 8";
if (/Windows NT 6\.1/i.test(ua)) return "Windows 7";
if (/Windows NT 6\.0/i.test(ua)) return "Windows Vista";
if (/Windows NT 5\.1|Windows XP/i.test(ua)) return "Windows XP";
if (/Mac OS X 10[_\.]15/i.test(ua)) return "macOS Catalina";
if (/Mac OS X 10[_\.]14/i.test(ua)) return "macOS Mojave";
if (/Mac OS X 10[_\.]13/i.test(ua)) return "macOS High Sierra";
if (/Mac OS X 10[_\.]12/i.test(ua)) return "macOS Sierra";
if (/Mac OS X 10[_\.]11/i.test(ua)) return "OS X El Capitan";
if (/Mac OS X 10[_\.]10/i.test(ua)) return "OS X Yosemite";
if (/Mac OS X 10[_\.]/i.test(ua)) return "macOS"; // Generic fallback
if (/Android/i.test(ua)) return "Android";
if (/iPhone|iPad|iPod/i.test(ua)) return "iOS";
if (/Linux/i.test(ua)) return "Linux";
return "Unknown";
}
}

View file

@ -185,6 +185,26 @@ const storageTemplates = JSON.parse(GM_getValue('bmTemplates', '{}'));
console.log(storageTemplates);
templateManager.importJSON(storageTemplates); // Loads the templates
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}')); // Loads the user settings
console.log(userSettings);
console.log(Object.keys(userSettings).length);
if (Object.keys(userSettings).length == 0) {
const uuid = crypto.randomUUID(); // Generates a random UUID
console.log(uuid);
GM.setValue('bmUserSettings', JSON.stringify({
'uuid': uuid
}));
}
setInterval(() => apiManager.sendHeartbeat(version), 1000 * 60 * 30); // Sends a heartbeat every 30 minutes
console.log(`Telemetry is ${userSettings?.telemetry == undefined}`);
if ((userSettings?.telemetry == undefined) || (userSettings?.telemetry > 1)) { // Increment 1 to retrigger telemetry notice
const telemetryOverlay = new Overlay(name, version);
telemetryOverlay.setApiManager(apiManager); // Sets the API manager for the telemetry overlay
buildTelemetryOverlay(telemetryOverlay); // Notifies the user about telemetry
telemetryOverlay.handleDrag('#bm-overlay-telemetry', '#bm-bar-drag-telemetry'); // Creates dragging capability on the drag bar for dragging the overlay
}
buildOverlayMain(); // Builds the main overlay
overlayMain.handleDrag('#bm-overlay', '#bm-bar-drag'); // Creates dragging capability on the drag bar for dragging the overlay
@ -697,6 +717,44 @@ function buildOverlayMain() {
}, 0);
}
function buildTelemetryOverlay(overlay) {
overlay.addDiv({'id': 'bm-overlay-telemetry', style: 'top: 20%'})
.addDiv({'id': 'bm-contain-header-telemetry'})
.addDiv({'id': 'bm-bar-drag-telemetry'}).buildElement()
.addHeader(1, {'textContent': `${name} Telemetry`}).buildElement()
.buildElement()
.addHr().buildElement()
.addDiv({'id': 'bm-contain-telemetry'})
.addP({'textContent': 'We collect anonymous telemetry data such as your browser, OS, and script version to make the experience better for everyone. The data is never shared personally. The data is never sold. You can turn this off anytime by pressing the \'Disable\' button, but keeping it on helps us improve features and reliability faster. Thank you for supporting the Blue Marble!'}).buildElement()
.addP({'textContent': 'You can disable telemetry by pressing the "Disable" button below.'}).buildElement()
.addButton({'id': 'bm-button-telemetry-enable', 'textContent': 'Enable Telemetry'}, (instance, button) => {
button.onclick = () => {
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}'));
userSettings.telemetry = 1;
GM.setValue('bmUserSettings', JSON.stringify(userSettings));
const element = document.getElementById('bm-overlay-telemetry');
if (element) {
element.style.display = 'none';
}
}
}).buildElement()
.addButton({'id': 'bm-button-telemetry-disable', 'textContent': 'Disable Telemetry'}, (instance, button) => {
button.onclick = () => {
const userSettings = JSON.parse(GM_getValue('bmUserSettings', '{}'));
userSettings.telemetry = 0;
GM.setValue('bmUserSettings', JSON.stringify(userSettings));
const element = document.getElementById('bm-overlay-telemetry');
if (element) {
element.style.display = 'none';
}
}
}).buildElement()
.buildElement()
.buildOverlay(document.body);
}
function buildOverlayTabTemplate() {
overlayTabTemplate.addDiv({'id': 'bm-tab-template', 'style': 'top: 20%; left: 10%;'})
.addDiv()

View file

@ -1,7 +1,7 @@
/* @since 0.5.1 */
/* The entire overlay */
#bm-overlay {
#bm-overlay, #bm-overlay-telemetry {
position: fixed;
background-color: rgba(21, 48, 99, 0.9);
color: white;
@ -21,14 +21,14 @@
/* Smooth transitions for minimize/maximize functionality */
#bm-contain-userinfo,
#bm-overlay hr,
#bm-overlay hr, #bm-overlay-telemetry hr,
#bm-contain-automation,
#bm-contain-buttons-action {
transition: opacity 0.2s ease, height 0.2s ease;
}
/* The entire overlay BUT it is cascading */
div#bm-overlay {
div#bm-overlay, div#bm-overlay-telemetry {
/* Font stack is as follows:
* Highest Priority (Roboto Mono)
* Windows fallback (Courier New)
@ -41,7 +41,7 @@ div#bm-overlay {
}
/* The drag bar */
#bm-bar-drag {
#bm-bar-drag, #bm-bar-drag-telemetry {
margin-bottom: 0.5em;
/* For background circles, width & height should be odd, cx & cy should be half of width & height, and r should be less than or equal to cx & cy */
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="5" height="5"><circle cx="3" cy="3" r="1.5" fill="CornflowerBlue" /></svg>') repeat;
@ -51,12 +51,12 @@ div#bm-overlay {
}
/* When the overlay is being dragged */
#bm-bar-drag.dragging {
#bm-bar-drag.dragging, #bm-bar-drag-telemetry.dragging {
cursor: grabbing;
}
/* Disable interactions during drag for better performance */
#bm-overlay:has(#bm-bar-drag.dragging) {
#bm-overlay:has(#bm-bar-drag.dragging), #bm-overlay-telemetry:has(#bm-bar-drag-telemetry.dragging) {
pointer-events: none;
user-select: none;
-webkit-user-select: none;
@ -65,17 +65,17 @@ div#bm-overlay {
}
/* Keep drag bar interactive when dragging */
#bm-bar-drag.dragging {
#bm-bar-drag.dragging, #bm-bar-drag-telemetry.dragging {
pointer-events: auto;
}
/* The container for the overlay header */
#bm-contain-header {
#bm-contain-header, #bm-contain-header-telemetry {
margin-bottom: 0.5em;
}
/* When minimized, adjust header container */
#bm-contain-header[style*="text-align: center"] {
#bm-contain-header[style*="text-align: center"], #bm-contain-header-telemetry[style*="text-align: center"] {
display: flex;
flex-direction: column;
align-items: center;
@ -83,7 +83,7 @@ div#bm-overlay {
}
/* Ensure overlay maintains consistent width when minimized */
#bm-overlay[style*="padding: 5px"] {
#bm-overlay[style*="padding: 5px"], #bm-overlay-telemetry[style*="padding: 5px"] {
width: auto !important;
max-width: 300px;
min-width: 200px;
@ -107,12 +107,12 @@ div#bm-overlay {
}
/* Ensure drag bar remains functional when minimized */
#bm-bar-drag {
#bm-bar-drag, #bm-bar-drag-telemetry {
transition: margin-bottom 0.2s ease;
}
/* The Blue Marble header */
#bm-overlay h1 {
#bm-overlay h1, #bm-overlay-telemetry h1 {
display: inline-block;
font-size: x-large;
font-weight: bold;
@ -255,24 +255,24 @@ div:has(> #bm-input-file-template),
}
/* All overlay buttons */
#bm-overlay button {
#bm-overlay button, #bm-overlay-telemetry button {
background-color: #144eb9;
border-radius: 1em;
padding: 0 0.75ch;
}
/* All overlay buttons when hovered/focused */
#bm-overlay button:hover, #bm-overlay button:focus-visible {
#bm-overlay button:hover, #bm-overlay button:focus-visible, #bm-overlay-telemetry button:hover, #bm-overlay-telemetry button:focus-visible {
background-color: #1061e5;
}
/* All overlay buttons when pressed (plus disabled color) */
#bm-overlay button:active,
#bm-overlay button:disabled {
#bm-overlay button:active, #bm-overlay-telemetry button:active
#bm-overlay button:disabled, #bm-overlay-telemetry button:disabled {
background-color: #2e97ff;
}
/* All overlay buttons when disabled */
#bm-overlay button:disabled {
#bm-overlay button:disabled, #bm-overlay-telemetry button:disabled {
text-decoration: line-through;
}

View file

@ -459,7 +459,7 @@ export default class TemplateManager {
const paintedStr = new Intl.NumberFormat().format(aggPainted);
const requiredStr = new Intl.NumberFormat().format(totalRequired);
const wrongStr = new Intl.NumberFormat().format(aggWrong);
const wrongStr = new Intl.NumberFormat().format(totalRequired - aggPainted); // Used to be aggWrong, but that is bugged
this.overlay.handleDisplayStatus(
`Displaying ${templateCount} template${templateCount == 1 ? '' : 's'}.\nPainted ${paintedStr} / ${requiredStr} • Wrong ${wrongStr}`