NotDave123456 / BetterSham

// ==UserScript==
// @name         BetterSham
// @namespace    https://openuserjs.org/users/NotDave123456
// @version      0.1.2
// @description  Adds some extra things to sham.chat such as auto reconnect and fake leave
// @copyright    2019-2021, NotDave123456 (https://openuserjs.org/users/NotDave123456)
// @license      MIT
// @author       NotDave123456
// @updateURL    https://openuserjs.org/meta/NotDave123456/BetterSham.meta.js
// @downloadURL  https://openuserjs.org/install/NotDave123456/BetterSham.user.js
// @match        https://sham.chat/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// ==/UserScript==

// Chnagelog
// 0.1.2
// Fix: BetterSham settings modal now adhere site theme

(function () {
  'use strict'
  // Mutation observer watch for DOM change
  let observer = new MutationObserver(mutations => {
    for (let mutation of mutations) {
      // examine new nodes, is there anything to highlight
      for (let node of mutation.addedNodes) {
        // we track only elements, skip other nodes (e.g. text nodes)
        if (!(node instanceof HTMLElement)) continue
        if (node.innerHTML === 'More' && GM_getValue('reconnect')) {
          let timeLeft = 10
          let countdownDom = document.querySelector('.countdownDom')
          let countdownTime = setInterval(() => {
            timeLeft--
            countdownDom.textContent = timeLeft
            if (timeLeft <= 0) {
              clearInterval(countdownTime)
              countdownDom.textContent = GM_getValue('reconnectInv', 10)
              node.click()
            }
          }, parseInt(GM_getValue('reconnectInv', 10)) * 100)
        }
      }
    }
  })

  // Start observer
  let demoElem = document.querySelector('body')
  observer.observe(demoElem, { childList: true, subtree: true })

  // HTML for settings
  let settingModal = `
  <div class="betterShamSettings modal fade in" id="betterShamSettings" role="dialog">
    <div class="modal-dialog">
      <div class="betterShamSettingsContent modal-content">
        <div class="betterShamSettingsTitle modal-header">
          <button type="button" class="close" data-dismiss="modal">×</button>
          <h4 class="modal-title">BetterSham Settings</h4>
        </div>
        <div class="betterShamSettingsBody modal-body">
          <label>Auto Reconnect Interval <input class="form-control settings text number" id="reconnectInv" type="number" value="${GM_getValue('reconnectInv', 10)}"/></label>
          <label>Socket Reconnect Interval <input class="form-control settings text number" id="socketInv" type="number" value="${GM_getValue('socketInv', 3)}"/></label>
        </div>
        <div class="betterShamSettingsFooter modal-footer">
          <button type="button" class="btn btn-default settings button close" data-dismiss="modal" data-baction="saveSettings">Save &amp; Quit</button>
        </div>
      </div>
    </div>
  </div>
  `

  // HTML for the header
  let headerHTML = `
  <div class="betterSham">
    <p class="betterShamStatus">Socket connected: <span>${socket.connected}</span></p>
    <button class="btn btn-info betterShamButton betterShamButtonConnect" data-baction="connect">${socket.connected ? 'Disconnect' : 'Connect'}</button>
    <p class="betterShamTitle"><a data-baction="home">BetterSham</a></p>
    <button class="btn btn-info betterShamButton" type="button" data-toggle="modal" data-target="#betterShamSettings" data-baction="updateSettings">Settings</button>
    <div>
      <p class="onoff">
        <input class="settigs checkbox" type="checkbox" name="reconnectCheck" id="reconnectCheck" ${GM_getValue('reconnect', false) ? 'checked' : ''}>
        <label for="reconnectCheck"></label>
      </p>
      Auto Reconnect: <span class="countdownDom">${GM_getValue('reconnectInv', 10)}</span>
    </div>
  </div>
  `

  // Helper funaction to generate useble HTML
  let parseHTML = (html) => {
    let t = document.createElement('template')
    t.innerHTML = html
    return t.content.cloneNode(true)
  }

  // Setup page
  document.querySelector('.desktop').append(parseHTML(headerHTML))
  document.querySelector('body').append(parseHTML(settingModal))
  document.querySelector('#hats').remove()
  document.querySelector('#logo').remove()

  // Event listnener for button clicks
  document.addEventListener('click', (e) => {
    if (e.target.dataset.baction === 'home') go_home()

    if (e.target.dataset.baction === `connect`) {
      setTimeout(() => {
        document.querySelector('.betterShamButtonConnect').innerHTML = socket.connected ? 'Disconnect' : 'Connect'
        document.querySelector('.betterShamStatus>span').innerHTML = socket.connected
      }, parseInt(GM_getValue('reconnectInv', 10)) * 100)
      if (socket.connected) {
        socket.disconnect()
      } else {
        socket.connect()
      }
    }

    if (e.target.dataset.baction === 'updateSettings') {
      document.querySelector('#reconnectInv').value = GM_getValue('reconnectInv', 10)
      document.querySelector('#socketInv').value = GM_getValue('socketInv', 3)
    }

    if (e.target.dataset.baction === `saveSettings`) {
      GM_setValue('reconnectInv', document.querySelector('#reconnectInv').value)
      GM_setValue('socketInv', document.querySelector('#socketInv').value)
    }

    if (e.target.id === 'reconnectCheck') GM_setValue('reconnect', e.target.checked)
  })

  // Update Socket UI
  setTimeout(() => {
    document.querySelector('.betterShamStatus>span').innerHTML = socket.connected
    document.querySelector('.betterShamButtonConnect').innerHTML = socket.connected ? 'Disconnect' : 'Connect'
  }, parseInt(GM_getValue('reconnectInv', 10)) * 100)

  toggle_night(meta.dark)

  // CSS inject
  GM_addStyle(`
  .betterSham {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-evenly;
  }

  .betterShamSettings {display:none}

  .betterShamTitle > a {cursor: pointer;}

  .list-group {height:calc(100vh - 11em)}
  `)
})()