DigDig.IO Server Selector

Server selector for digdig.io. Double click to copy, single click to download.

  1. // ==UserScript==
  2. // @name DigDig.IO Server Selector
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.3.3
  5. // @description Server selector for digdig.io. Double click to copy, single click to download.
  6. // @author Zertalious (Zert)
  7. // @match *://digdig.io/*
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @grant unsafeWindow
  10. // @grant GM_addStyle
  11. // ==/UserScript==
  12.  
  13. unsafeWindow.Image = new Proxy( unsafeWindow.Image, {
  14. construct( target, thisArgs, args ) {
  15.  
  16. const result = Reflect.construct( ...arguments );
  17.  
  18. result.crossOrigin = 'anonymous';
  19.  
  20. return result;
  21.  
  22. }
  23. } );
  24.  
  25. GM_addStyle( `
  26.  
  27. body {
  28. margin: 0;
  29. overflow: hidden;
  30. font-family: 'Ubuntu';
  31. text-shadow: 1px 0 #000, -1px 0 #000, 0 1px #000, 0 -1px #000, 1px 1px #000, -1px -1px #000;
  32. color: #fff;
  33. width: 100vw;
  34. height: 100vh;
  35. }
  36.  
  37. .pointer-lock canvas {
  38. pointer-events: none;
  39. }
  40.  
  41. .pointer-lock:after {
  42. content: 'Pointer is locked. Press [X] to unlock.';
  43. position: absolute;
  44. left: 50%;
  45. top: 10px;
  46. transform: translate(-50%, 0);
  47. background: rgb(233 30 30 / 60%);
  48. padding: 2px 5px;
  49. text-shadow: none;
  50. z-index: 999;
  51. }
  52.  
  53. .group {
  54. position: absolute;
  55. right: 15px;
  56. bottom: 15px;
  57. display: flex;
  58. flex-direction: column;
  59. }
  60.  
  61. .group > * {
  62. margin-bottom: 8px;
  63. }
  64.  
  65. .group > *:last-child {
  66. margin-bottom: 0;
  67. }
  68.  
  69. .btn {
  70. background: #aeaeae;
  71. font-size: 2.25em;
  72. text-align: center;
  73. padding: 0.2em;
  74. cursor: pointer;
  75. box-shadow: inset 0 0 0 0.1em rgba(0, 0, 0, 0.25);
  76. border-radius: 0.1em;
  77. position: relative;
  78. user-select: none;
  79. }
  80.  
  81. .btn:before {
  82. content: ' ';
  83. position: absolute;
  84. top: 0.1em;
  85. left: 0.1em;
  86. width: calc(100% - 0.2em);
  87. height: calc(100% - 0.2em);
  88. background: transparent;
  89. }
  90.  
  91. .btn:hover:before {
  92. background: hsla(0, 0%, 100%, 0.25);
  93. }
  94.  
  95. .btn:active:before {
  96. background: rgba(0, 0, 0, 0.1);
  97. }
  98.  
  99. .btn i {
  100. text-shadow: none;
  101. }
  102.  
  103. [tooltip] {
  104. position: relative;
  105. }
  106.  
  107. [tooltip]:after {
  108. content: attr(tooltip);
  109. font-size: 0.9rem;
  110. position: absolute;
  111. right: 100%;
  112. top: 50%;
  113. transform: translate(-10px, -50%);
  114. white-space: nowrap;
  115. pointer-events: none;
  116. background: rgba(0, 0, 0, 0.5);
  117. padding: 0.25em 0.4em;
  118. border-radius: 0.2em;
  119. opacity: 0;
  120. transition: 0.2s;
  121. z-index: 999;
  122. }
  123.  
  124. [tooltip]:not(.disabled):hover:after {
  125. opacity: 1;
  126. }
  127.  
  128. [tooltip]:is(.force-tooltip):after {
  129. opacity: 1 !important;
  130. }
  131.  
  132. .dialog {
  133. position: absolute;
  134. right: 85px;
  135. bottom: 15px;
  136. background: #aeaeae;
  137. padding: 0.75em;
  138. border-radius: 0.3em;
  139. box-shadow: inset 0 0 0 0.3em rgba(0, 0, 0, 0.25);
  140. width: 300px;
  141. transition: 0.2s;
  142. }
  143.  
  144. .dialog > * {
  145. margin-bottom: 5px;
  146. }
  147.  
  148. .dialog > *:last-child {
  149. margin-bottom: 0;
  150. }
  151.  
  152. .dialog.disabled {
  153. transform: translate(0, calc(100% + 15px));
  154. }
  155.  
  156. .dialog .btn {
  157. font-size: 1.25rem;
  158. background: #bb5555;
  159. }
  160.  
  161. .title {
  162. font-size: 1.5em;
  163. text-align: center;
  164. }
  165.  
  166. .spinner {
  167. margin: 10px auto;
  168. width: 60px;
  169. height: 60px;
  170. border: 10px solid transparent;
  171. border-top-color: rgba(0, 0, 0, 0.3);
  172. border-radius: 50%;
  173. animation: spin 0.5s infinite;
  174. }
  175.  
  176. .option {
  177. background: rgba(0, 0, 0, 0.1);
  178. padding: 0.5em 0.75em;
  179. border-radius: 0.25em;
  180. cursor: pointer;
  181. }
  182.  
  183. .option.active {
  184. box-shadow: inset 0 0 0 0.15em rgba(0, 0, 0, 0.2);
  185. }
  186.  
  187. @keyframes spin {
  188. from {
  189. transform: rotate(0);
  190. }
  191.  
  192. to {
  193. transform: rotate(360deg);
  194. }
  195. }
  196.  
  197. ` );
  198.  
  199. const temp = document.createElement( 'div' );
  200.  
  201. temp.innerHTML += `
  202.  
  203. <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
  204. <div class="group">
  205. <div class="btn leaderboard-btn" tooltip="Capture leaderboard [G]">
  206. <i class="fa fa-trophy"></i>
  207. </div>
  208. <div class="btn screenshot-btn" tooltip="Take screenshot [F]">
  209. <i class="fa fa-camera"></i>
  210. </div>
  211. <div class="btn servers-btn" tooltip="Servers">
  212. <i class="fa fa-globe"></i>
  213. </div>
  214. </div>
  215. <div class="dialog disabled">
  216. <div class="title">Servers</div>
  217. <div class="spinner"></div>
  218. <div class="btn refresh-btn">refresh</div>
  219. <div class="btn close-btn">close</div>
  220. </div>
  221. </div>
  222.  
  223. `;
  224.  
  225. while ( temp.children.length > 0 ) {
  226.  
  227. document.body.appendChild( temp.children[ 0 ] );
  228.  
  229. }
  230.  
  231. temp.innerHTML = '';
  232.  
  233. const spinner = document.querySelector( '.spinner' );
  234.  
  235. let selectedServerId = new URLSearchParams( document.location.search.substring( 1 ) ).get( 'server' );
  236.  
  237. if ( selectedServerId ) {
  238.  
  239. const idFromUrl = selectedServerId;
  240.  
  241. const interval = setInterval( function () {
  242.  
  243. if ( idFromUrl !== selectedServerId ) {
  244.  
  245. clearInterval( interval );
  246.  
  247. }
  248.  
  249. try {
  250.  
  251. connect( idFromUrl );
  252.  
  253. clearInterval( interval );
  254.  
  255. } catch ( e ) { }
  256.  
  257. }, 50 );
  258.  
  259. }
  260.  
  261. fetchAll();
  262.  
  263. async function fetchAll() {
  264.  
  265. spinner.style.display = '';
  266.  
  267. await fetchServers( 'ffa' );
  268. await fetchServers( 'teams' );
  269. await fetchServers( 'tag' );
  270. await fetchServers( 'br' );
  271. await fetchServers( 'maze' );
  272.  
  273. spinner.style.display = 'none';
  274.  
  275. }
  276.  
  277. const serverIds = {};
  278.  
  279. const refreshBtn = document.querySelector( '.refresh-btn' );
  280.  
  281. async function fetchServers( mode ) {
  282.  
  283. const response = await fetch( 'https://api.n.m28.io/endpoint/digdig-' + mode + '/findEach' );
  284. const json = await response.json();
  285.  
  286. for ( let key in json.servers ) {
  287.  
  288. const id = json.servers[ key ].id;
  289.  
  290. if ( ! serverIds[ id ] ) {
  291.  
  292. serverIds[ id ] = true;
  293.  
  294. const div = document.createElement( 'div' );
  295. div.classList.add( 'option' );
  296. div.innerHTML = mode + '_' + key.split( '-' )[ 1 ] + '_' + id;
  297. div.setAttribute( 'data-id', id );
  298.  
  299. dialog.insertBefore( div, refreshBtn );
  300.  
  301. div.onclick = function () {
  302.  
  303. connect( id );
  304.  
  305. }
  306.  
  307. if ( selectedServerId === id ) {
  308.  
  309. div.click();
  310.  
  311. }
  312.  
  313. }
  314.  
  315. }
  316.  
  317. }
  318.  
  319. function connect( id ) {
  320.  
  321. selectedServerId = id;
  322.  
  323. cp6.forceServerID( selectedServerId );
  324.  
  325. onServerChange();
  326.  
  327. }
  328.  
  329. unsafeWindow.console.log = new Proxy( unsafeWindow.console.log, {
  330. apply( target, thisArgs, [ text ] ) {
  331.  
  332. if ( typeof text === 'string' && text.startsWith( 'Connecting to ' ) ) {
  333.  
  334. const id = text.split('Connecting to ')[ 1 ].split( '.' )[ 0 ];
  335.  
  336. if ( id !== selectedServerId ) {
  337.  
  338. selectedServerId = id;
  339.  
  340. onServerChange();
  341.  
  342. }
  343.  
  344. }
  345.  
  346. return Reflect.apply( ...arguments );
  347.  
  348. }
  349. } );
  350.  
  351. function onServerChange() {
  352.  
  353. const url = new URL( window.location );
  354. url.searchParams.set( 'server', selectedServerId );
  355.  
  356. history.pushState( {}, document.title, url );
  357.  
  358. const active = document.querySelector( '.option.active' );
  359.  
  360. if ( active ) {
  361.  
  362. active.classList.remove( 'active' );
  363.  
  364. }
  365.  
  366. const option = document.querySelector( '[data-id="' + selectedServerId + '"]' );
  367.  
  368. if ( option ) {
  369.  
  370. option.classList.add( 'active' );
  371.  
  372. }
  373.  
  374. }
  375.  
  376. const dialog = document.querySelector( '.dialog' );
  377. const serversBtn = document.querySelector( '.servers-btn' );
  378.  
  379. serversBtn.onclick = function () {
  380.  
  381. if ( dialog.classList.contains( 'disabled' ) ) {
  382.  
  383. dialog.classList.remove( 'disabled' );
  384. this.classList.add( 'disabled' );
  385.  
  386. } else {
  387.  
  388. hideDialog();
  389.  
  390. }
  391.  
  392. }
  393.  
  394. document.body.onclick = function ( event ) {
  395.  
  396. if ( [ 'canvas', 'loading' ].indexOf( event.target.id ) > - 1 || event.target === this ) {
  397.  
  398. hideDialog();
  399.  
  400. }
  401.  
  402. }
  403.  
  404. document.querySelector( '.close-btn' ).onclick = hideDialog;
  405.  
  406. refreshBtn.onclick = fetchAll;
  407.  
  408. function hideDialog() {
  409.  
  410. dialog.classList.add( 'disabled' );
  411. serversBtn.classList.remove( 'disabled' );
  412.  
  413. }
  414.  
  415. function createClickListener( a, b ) {
  416.  
  417. let clicks = 0;
  418.  
  419. return function () {
  420.  
  421. clicks ++;
  422.  
  423. setTimeout( function () {
  424.  
  425. if ( clicks === 1 ) {
  426.  
  427. a();
  428.  
  429. }
  430.  
  431. clicks = 0;
  432.  
  433. }, 300 );
  434.  
  435. if ( clicks === 2 ) {
  436.  
  437. b();
  438.  
  439. }
  440.  
  441. }
  442.  
  443. }
  444.  
  445. const screenshotBtn = document.querySelector( '.screenshot-btn' );
  446. const leaderboardBtn = document.querySelector( '.leaderboard-btn' );
  447.  
  448. const displayCopiedScreenshot = createCopiedDisplayer( screenshotBtn );
  449. const displayCopiedLeaderboard = createCopiedDisplayer( leaderboardBtn );
  450.  
  451. screenshotBtn.onclick = createClickListener(
  452. function () {
  453.  
  454. downloadCanvas( document.querySelector( 'canvas' ), 'digdig' );
  455.  
  456. },
  457. copyScreenshot
  458. );
  459.  
  460. leaderboardBtn.onclick = createClickListener(
  461. function () {
  462.  
  463. if ( leaderboard ) {
  464.  
  465. downloadCanvas( getLeaderboardCanvas(), 'digdig_leaderboard' );
  466.  
  467. }
  468.  
  469. },
  470. copyLeaderboard
  471. );
  472.  
  473. function copyScreenshot() {
  474.  
  475. copyCanvasToClipboard( document.querySelector( 'canvas' ) );
  476.  
  477. displayCopiedScreenshot();
  478.  
  479. }
  480.  
  481. function copyLeaderboard() {
  482.  
  483. if ( leaderboard ) {
  484.  
  485. copyCanvasToClipboard( getLeaderboardCanvas() );
  486.  
  487. displayCopiedLeaderboard();
  488.  
  489. }
  490.  
  491. }
  492.  
  493. window.addEventListener( 'keyup', function ( event ) {
  494.  
  495. const key = String.fromCharCode( event.keyCode );
  496.  
  497. if ( key === 'F' ) {
  498.  
  499. copyScreenshot();
  500.  
  501. } else if ( key === 'G' ) {
  502.  
  503. copyLeaderboard();
  504.  
  505. } else if ( key === 'X' ) {
  506.  
  507. document.body.classList.toggle( 'pointer-lock' );
  508.  
  509. }
  510.  
  511. } );
  512.  
  513. function createCopiedDisplayer( element ) {
  514.  
  515. let old, timeout;
  516.  
  517. return function () {
  518.  
  519. if ( element.classList.contains( 'force-tooltip' ) ) {
  520.  
  521. clearTimeout( timeout );
  522.  
  523. } else {
  524.  
  525. old = element.getAttribute( 'tooltip' );
  526. element.setAttribute( 'tooltip', 'Copied!' );
  527. element.classList.add( 'force-tooltip' );
  528.  
  529. }
  530.  
  531. timeout = setTimeout( function () {
  532.  
  533. element.setAttribute( 'tooltip', old );
  534. element.classList.remove( 'force-tooltip' );
  535.  
  536. }, 1000 );
  537.  
  538. }
  539.  
  540. }
  541.  
  542.  
  543. function getLeaderboardCanvas() {
  544.  
  545. const offset = - 115 * 0.8 * 0;
  546.  
  547. const canvas = document.createElement( 'canvas' );
  548.  
  549. canvas.width = leaderboard.width;
  550. canvas.height = leaderboard.height + offset;
  551.  
  552. canvas.getContext( '2d' ).drawImage( leaderboard, 0, offset );
  553.  
  554. return canvas;
  555.  
  556. }
  557.  
  558. function copyCanvasToClipboard( canvas ) {
  559.  
  560. canvas.toBlob( function ( blob ) {
  561.  
  562. navigator.clipboard.write( [ new ClipboardItem( { 'image/png': blob } ) ] );
  563.  
  564. } );
  565.  
  566. }
  567.  
  568. function downloadCanvas( canvas, filename ) {
  569.  
  570. const a = document.createElement( 'a' );
  571.  
  572. a.href = canvas.toDataURL();
  573. a.download = filename + '_' + Date.now() + '.png';
  574.  
  575. a.click();
  576.  
  577. }
  578.  
  579. let leaderboard;
  580. let leaderboardPartial;
  581.  
  582. const Canvas = unsafeWindow.OffscreenCanvas ? unsafeWindow.OffscreenCanvas.prototype : unsafeWindow.HTMLCanvasElement.prototype;
  583.  
  584. Canvas.getContext = new Proxy( Canvas.getContext, {
  585. apply() {
  586.  
  587. const ctx = Reflect.apply( ...arguments );
  588.  
  589. ctx.fillText = new Proxy( ctx.fillText, {
  590. apply( target, thisArgs, args ) {
  591.  
  592. if ( args[ 0 ].indexOf( 'Diggers' ) > - 1 ) {
  593.  
  594. leaderboardPartial = ctx.canvas;
  595.  
  596. }
  597.  
  598. return Reflect.apply( ...arguments );
  599.  
  600. }
  601. } );
  602.  
  603. ctx.drawImage = new Proxy( ctx.drawImage, {
  604. apply( target, thisArgs, args ) {
  605.  
  606. if ( args[ 0 ] === leaderboardPartial ) {
  607.  
  608. leaderboard = ctx.canvas;
  609.  
  610. }
  611.  
  612. return Reflect.apply( ...arguments );
  613.  
  614. }
  615. } );
  616.  
  617. return ctx;
  618.  
  619. }
  620. } )

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址