// ==UserScript== // @name DigDig.IO Server Selector // @namespace http://tampermonkey.net/ // @version 0.2.0 // @description Server selector for digdig.io. Double click to copy, single click to download. // @author Zertalious (Zert) // @match *://digdig.io/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant unsafeWindow // @grant GM_addStyle // @downloadURL none // ==/UserScript== const sockets = []; let initialServerId; unsafeWindow.console.log = new Proxy( unsafeWindow.console.log, { apply( target, thisArgs, args ) { const x = args[ 0 ]; if ( x.startsWith( 'Connecting to ' ) ) { initialServerId = x.slice( 0, x.indexOf( '.' ) ).split( ' ' )[ 2 ]; } return Reflect.apply( ...arguments ); } } ) unsafeWindow.WebSocket = new Proxy( unsafeWindow.WebSocket, { construct( target, args ) { if ( selectedServerId ) { const url = new URL( args[ 0 ] ); const items = url.hostname.split( '.' ); if ( serverIds[ items[ 0 ] ] || initialServerId === items[ 0 ] ) { items[ 0 ] = selectedServerId; url.hostname = items.join( '.' ); console.log( '[SERVER SELECTOR] Overriding...', { from: args[ 0 ], to: url.toString(), selectedServerId } ); } args[ 0 ] = url.toString(); } const socket = Reflect.construct( ...arguments ); sockets.push( socket ); return socket; } } ); function reconnect() { while ( sockets.length > 0 ) { try { sockets.shift().close(); } catch ( err ) {} } } GM_addStyle( ` body { margin: 0; overflow: hidden; background: #744100; font-family: 'Ubuntu'; } .group { position: absolute; right: 15px; bottom: 15px; display: flex; flex-direction: column; } .group > * { margin-bottom: 8px; } .group > *:last-child { margin-bottom: 0; } .btn { color: #fff; background: #aeaeae; font-size: 2.25em; text-align: center; padding: 0.2em; cursor: pointer; box-shadow: inset 0 0 0 0.1em rgba(0, 0, 0, 0.25); border-radius: 0.1em; position: relative; user-select: none; } .btn:before { content: ' '; position: absolute; top: 0.1em; left: 0.1em; width: calc(100% - 0.2em); height: calc(100% - 0.2em); background: transparent; } .btn:hover:before { background: hsla(0, 0%, 100%, 0.25); } .btn:active:before { background: rgba(0, 0, 0, 0.1); } [tooltip] { position: relative; } [tooltip]:after { content: attr(tooltip); font-size: 1rem; position: absolute; right: 100%; top: 50%; transform: translate(-10px, -50%); white-space: nowrap; pointer-events: none; background: rgba(0, 0, 0, 0.5); padding: 0.25em 0.4em; border-radius: 0.2em; opacity: 0; transition: 0.2s; } [tooltip]:not(.disabled):hover:after { opacity: 1; } [tooltip]:is(.force-tooltip):after { opacity: 1 !important; } .dialog { position: absolute; right: 85px; bottom: 15px; color: #fff; background: #aeaeae; padding: 0.75em; border-radius: 0.3em; box-shadow: inset 0 0 0 0.3em rgba(0, 0, 0, 0.25); text-shadow: 1px 0 #000, -1px 0 #000, 0 1px #000, 0 -1px #000, 1px 1px #000, -1px -1px #000; width: 300px; transition: 0.2s; } .dialog > * { margin-bottom: 5px; } .dialog > *:last-child { margin-bottom: 0; } .dialog.disabled { transform: translate(0, calc(100% + 15px)); } .dialog .btn { font-size: 1.25rem; background: #bb5555; } .title { font-size: 1.5em; text-align: center; } .spinner { margin: 10px auto; width: 60px; height: 60px; border: 10px solid transparent; border-top-color: rgba(0, 0, 0, 0.3); border-radius: 50%; animation: spin 0.5s infinite; } .option { background: rgba(0, 0, 0, 0.1); padding: 0.5em 0.75em; border-radius: 0.25em; cursor: pointer; } .option.active { box-shadow: inset 0 0 0 0.15em rgba(0, 0, 0, 0.2); } @keyframes spin { from { transform: rotate(0); } to { transform: rotate(360deg); } } ` ); document.body.innerHTML += `
Servers
close
refresh
`; const spinner = document.querySelector( '.spinner' ); ( async function () { await fetchServers( 'ffa' ); await fetchServers( 'teams' ); spinner.style.display = 'none'; } )(); const serverIds = {}; let selectedServerId = new URLSearchParams( document.location.search.substring( 1 ) ).get( 'server' ); if ( selectedServerId ) { reconnect(); } async function fetchServers( mode ) { const response = await fetch( 'https://api.n.m28.io/endpoint/digdig-' + mode + '/findEach' ); const json = await response.json(); for ( let key in json.servers ) { const id = json.servers[ key ].id; if ( ! serverIds[ id ] ) { serverIds[ id ] = true; const div = document.createElement( 'div' ); div.classList.add( 'option' ); div.innerHTML = mode + '_' + key.split( '-' )[ 1 ] + '_' + id; dialog.appendChild( div ); div.onclick = function () { const active = document.querySelector( '.option.active' ); if ( active ) { active.classList.remove( 'active' ); } this.classList.add( 'active' ); selectedServerId = id; const url = new URL( window.location ); url.searchParams.set( 'server', selectedServerId ); history.pushState( {}, document.title, url ); reconnect(); } if ( ! selectedServerId ) { div.click(); } else if ( selectedServerId === id ) { div.click(); } } } } const dialog = document.querySelector( '.dialog' ); const serversBtn = document.querySelector( '.servers-btn' ); serversBtn.onclick = function () { if ( dialog.classList.contains( 'disabled' ) ) { dialog.classList.remove( 'disabled' ); this.classList.add( 'disabled' ); } else { hideDialog(); } } document.getElementById( 'canvas' ).onclick = hideDialog; document.querySelector( '.close-btn' ).onclick = hideDialog; document.querySelector( '.refresh-btn' ).onclick = async function () { spinner.style.display = ''; await fetchServers( 'ffa' ); await fetchServers( 'teams' ); spinner.style.display = 'none'; }; function hideDialog() { dialog.classList.add( 'disabled' ); serversBtn.classList.remove( 'disabled' ); } function createClickListener( a, b ) { let clicks = 0; return function () { clicks ++; setTimeout( function () { if ( clicks === 1 ) { a(); } clicks = 0; }, 300 ); if ( clicks === 2 ) { b(); } } } const screenshotBtn = document.querySelector( '.screenshot-btn' ); const leaderboardBtn = document.querySelector( '.leaderboard-btn' ); const displayCopiedScreenshot = createCopiedDisplayer( screenshotBtn ); const displayCopiedLeaderboard = createCopiedDisplayer( leaderboardBtn ); screenshotBtn.onclick = createClickListener( function () { downloadCanvas( document.querySelector( 'canvas' ), 'digdig' ); }, copyScreenshot ); leaderboardBtn.onclick = createClickListener( function () { if ( leaderboard ) { downloadCanvas( getLeaderboardCanvas(), 'digdig_leaderboard' ); } }, copyLeaderboard ); function copyScreenshot() { copyCanvasToClipboard( document.querySelector( 'canvas' ) ); displayCopiedScreenshot(); } function copyLeaderboard() { if ( leaderboard ) { copyCanvasToClipboard( getLeaderboardCanvas() ); displayCopiedLeaderboard(); } } window.addEventListener( 'keyup', function ( event ) { const key = String.fromCharCode( event.keyCode ); if ( key === 'F' ) { copyScreenshot(); } else if ( key === 'G' ) { copyLeaderboard(); } } ); function createCopiedDisplayer( element ) { let old, timeout; return function () { if ( element.classList.contains( 'force-tooltip' ) ) { clearTimeout( timeout ); } else { old = element.getAttribute( 'tooltip' ); element.setAttribute( 'tooltip', 'Copied!' ); element.classList.add( 'force-tooltip' ); } timeout = setTimeout( function () { element.setAttribute( 'tooltip', old ); element.classList.remove( 'force-tooltip' ); }, 1000 ); } } function getLeaderboardCanvas() { const offset = - 115; const canvas = document.createElement( 'canvas' ); canvas.width = leaderboard.width; canvas.height = leaderboard.height + offset; canvas.getContext( '2d' ).drawImage( leaderboard, 0, offset ); return canvas; } function copyCanvasToClipboard( canvas ) { canvas.toBlob( function ( blob ) { navigator.clipboard.write( [ new ClipboardItem( { 'image/png': blob } ) ] ); } ); } function downloadCanvas( canvas, filename ) { const a = document.createElement( 'a' ); a.href = canvas.toDataURL(); a.download = filename + '_' + Date.now() + '.png'; a.click(); } let leaderboard; let leaderboardPartial; const Canvas = unsafeWindow.OffscreenCanvas ? unsafeWindow.OffscreenCanvas.prototype : unsafeWindow.HTMLCanvasElement.prototype; Canvas.getContext = new Proxy( Canvas.getContext, { apply() { const ctx = Reflect.apply( ...arguments ); ctx.fillText = new Proxy( ctx.fillText, { apply( target, thisArgs, args ) { if ( args[ 0 ].indexOf( 'Diggers' ) > - 1 ) { leaderboardPartial = ctx.canvas; } return Reflect.apply( ...arguments ); } } ); ctx.drawImage = new Proxy( ctx.drawImage, { apply( target, thisArgs, args ) { if ( args[ 0 ] === leaderboardPartial ) { leaderboard = ctx.canvas; } return Reflect.apply( ...arguments ); } } ); return ctx; } } )