Auto next on takeout for Autodarts

Userscript for Autodarts to reset board and switch to next player if takeout stucks

// ==UserScript==
// @id           auto-next-on-takeout-for-autodarts@https://github.com/sebudde/auto-next-on-takeout-for-autodarts
// @name         Auto next on takeout for Autodarts
// @namespace    https://github.com/sebudde/auto-next-on-takeout-for-autodarts
// @version      0.1
// @description  Userscript for Autodarts to reset board and switch to next player if takeout stucks
// @author       sebudde
// @match        https://play.autodarts.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=autodarts.io
// @license      MIT
// @grant        GM.getValue
// @grant        GM.setValue
// ==/UserScript==

(async function() {
  'use strict';

  const observeDOM = (function() {
    const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    return function(obj, config, callback) {
      if (!obj || obj.nodeType !== 1) return;
      const mutationObserver = new MutationObserver(callback);
      const mutConfig = {
        ...{
          attributes: true,
          childList: true,
          subtree: true
        }, ...config
      };
      mutationObserver.observe(obj, mutConfig);
      return mutationObserver;
    };
  })();

  const onDOMready = async () => {

    setTimeout(async () => {

      console.log('match ready!');

      const matchMenuEl = document.getElementById('ad-ext-game-settings-extra');

      let matchMenuContainer;

      if (matchMenuEl.children[0].classList.contains('adp_match-menu-row')) {
        // Autodarts Plus is active
        matchMenuContainer = matchMenuEl.children[0];

      } else {
        // const matchMenuRow = document.createElement('div');
        // matchMenuRow.classList.add('css-k008qs');
        // matchMenuRow.style.marginTop = 'calc(var(--chakra-space-2) * -1 - 4px)';
        // matchMenuContainer = document.createElement('div');
        // matchMenuContainer.classList.add('css-a6m3v9');
        // matchMenuRow.appendChild(matchMenuContainer);
        // document.querySelector('.css-k008qs').after(matchMenuRow);
      }

      const turnContainerEl = document.getElementById('ad-ext-turn');

      let nextPlayerAfterTakeoutSec = (await GM.getValue('nextPlayerAfterTakeoutSec')) || 'OFF';

      const onSelectChange = (event) => {
        (async () => {
          eval(event.target.id + ' = event.target.value');
          await GM.setValue(event.target.id, event.target.value);
        })();
      };

      const nextPlayerAfterTakeoutSecArr = [
        {
          value: 'OFF'
        }, {
          value: '10'
        }, {
          value: '15'
        }, {
          value: '20'
        }, {
          value: '30'
        }];

      const nextPlayerAfterTakeoutSecSelect = document.createElement('select');
      nextPlayerAfterTakeoutSecSelect.id = 'nextPlayerAfterTakeoutSec';
      nextPlayerAfterTakeoutSecSelect.classList.add('css-1xbroe7');
      nextPlayerAfterTakeoutSecSelect.style.padding = '0 5px';
      nextPlayerAfterTakeoutSecSelect.onchange = onSelectChange;

      matchMenuContainer.appendChild(nextPlayerAfterTakeoutSecSelect);

      nextPlayerAfterTakeoutSecArr.forEach((sec) => {
        const optionEl = document.createElement('option');
        optionEl.value = sec.value;
        optionEl.text = `Next Player after Takeout for ${sec.value}${sec.value === 'OFF' ? '' : ' sec'}`;
        optionEl.style.backgroundColor = '#353d47';
        if (nextPlayerAfterTakeoutSec === sec.value) optionEl.setAttribute('selected', 'selected');
        nextPlayerAfterTakeoutSecSelect.appendChild(optionEl);
      });

      const boardStatusContainer = document.querySelector('.css-1uodvt1 a');

      const config = {
        characterData: true,
        attributes: false,
        childList: false
      };

      let countdown;

      const playgroundEl = document.getElementById('ad-ext-turn').nextElementSibling;

      const nextPlayerBtn = [...playgroundEl.querySelectorAll('button')].filter(el => el.textContent.includes('Next'))[0];
      const boardMenuEl = document.getElementById('ad-ext-game-settings-extra').previousSibling;
      const resetBoardBtn = [...boardMenuEl.querySelectorAll('button')].filter(el => el.textContent.includes('Reset'))[0];
      const timerEl = document.createElement('span');

      const resetCountdown = () => {
        clearInterval(countdown);
        timerEl?.remove();
      };

      nextPlayerBtn.addEventListener('click', (event) => {
        resetCountdown();
      }, false);

      [...turnContainerEl.querySelectorAll('.ad-ext-turn-throw')].forEach((counterBtn) => {
        counterBtn.addEventListener('click', (event) => {
          resetCountdown();
        }, false);
      });

      observeDOM(boardStatusContainer, config, function(m) {
        m.forEach((record) => {
          if (record.target.textContent !== '✊') {
            resetCountdown();
          } else {
            if (nextPlayerAfterTakeoutSec !== 'OFF' && nextPlayerBtn && resetBoardBtn) {
              timerEl.style.paddingLeft = '15px';
              timerEl.textContent = nextPlayerAfterTakeoutSec;
              nextPlayerBtn.append(timerEl);
              countdown = setInterval(() => {
                const time = parseInt(timerEl.textContent);
                timerEl.textContent = (time - 1).toString();
                if (time === 1) {
                  resetBoardBtn.click();
                  nextPlayerBtn.click();
                }
              }, 1000);

            }
          }
        });
      });

    }, 500);
  };

  observeDOM(document.getElementById('root'), {}, function(mutationrecords) {
    mutationrecords.forEach((record) => {
      if (record.addedNodes.length && record.addedNodes[0].classList?.length) {
        const elemetClassList = [...record.addedNodes[0].classList];
        return elemetClassList.forEach((className) => {
          if (className === 'css-ul22ge') {
            onDOMready();
          }
        });
      }
    });
  });
})();