PokerHistory

Records all Poker history.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         PokerHistory
// @namespace    http://www.torn.com/
// @version      1.2
// @description  Records all Poker history.
// @author       bot_7420 [2937420]
// @match        https://www.torn.com/page.php?sid=holdem*
// @run-at       document-body
// @grant        GM_addStyle
// ==/UserScript==

(function () {
  "use strict";

  let db = null;
  let messageBoxObserver = null;

  initIndexDB();

  window.onload = function () {
    initCSS();
    initControlPanel();
    initPokerObserver();
  };

  function initIndexDB() {
    const openRequest = indexedDB.open("scriptPokerHistoryDB", 1);
    openRequest.onupgradeneeded = function (e) {
      console.log("PokerHistory: initIndexDB open onupgradeneeded");
      db = e.target.result;
      if (!db.objectStoreNames.contains("messageStore")) {
        console.log("PokerHistory: initIndexDB open onupgradeneeded create store");
        const objectStore = db.createObjectStore("messageStore", { keyPath: "autoId", autoIncrement: true });
      }
    };
    openRequest.onsuccess = function (e) {
      console.log("PokerHistory: initIndexDB open onsuccess");
      db = e.target.result;
    };
    openRequest.onerror = function (e) {
      console.error("PokerHistory: initIndexDB open onerror");
      console.dir(e);
    };
  }

  function dbWrite(message) {
    if (!db || !message) {
      return;
    }

    const transaction = db.transaction(["messageStore"], "readwrite");
    transaction.oncomplete = (event) => {
      //console.log("PokerHistory: dbWrite transaction oncomplete [" + message.text + "]");
    };
    transaction.onerror = (event) => {
      console.error("PokerHistory: dbWrite transaction onerror [" + message.text + "]");
    };

    const store = transaction.objectStore("messageStore");
    const request = store.put(message);
    request.onsuccess = (event) => {};
  }

  function dbReadAll() {
    if (!db) {
      console.error("PokerHistory: dbReadAll db is null");
    }

    const transaction = db.transaction(["messageStore"], "readonly");
    transaction.oncomplete = (event) => {
      console.log("PokerHistory: dbReadAll transaction oncomplete");
    };
    transaction.onerror = (event) => {
      console.error("PokerHistory: dbReadAll transaction onerror");
    };

    const store = transaction.objectStore("messageStore");
    return new Promise((resolve, reject) => {
      const resultList = [];
      store.openCursor().onerror = (event) => {
        resolve(resultList);
      };
      store.openCursor().onsuccess = (event) => {
        const cursor = event.target.result;
        if (cursor) {
          resultList.push(cursor.value);
          cursor.continue();
        } else {
          resolve(resultList);
        }
      };
    });
  }

  function initPokerObserver() {
    const $poker = $("div.holdemWrapper___D71Gy");
    const observerConfig = { attributes: false, childList: true, subtree: false };
    const observer = new MutationObserver(() => {
      reObserveMessageBox();
    });
    if ($poker.length === 1) {
      console.log("PokerHistory: observe poker page");
      observer.observe($poker[0], observerConfig);
      reObserveMessageBox();
    }
  }

  function reObserveMessageBox() {
    console.log("PokerHistory: reObserveMessageBox");
    if (!messageBoxObserver) {
      messageBoxObserver = new MutationObserver((mutated) => {
        handleMessageBoxChange(mutated);
      });
    }
    messageBoxObserver.disconnect();
    const $messageWrap = $("div.holdemWrapper___D71Gy div.messagesWrap___tBx9u");
    const observerConfig = { attributes: true, childList: true, subtree: false };
    messageBoxObserver.observe($messageWrap[0], observerConfig);
  }

  function handleMessageBoxChange(mutated) {
    if (mutated.length >= 40) {
      console.log("PokerHistory: handlePokerBoxChange disregarded " + mutated.length);
      return;
    }

    for (const mutation of mutated) {
      for (const node of mutation.addedNodes) {
        if (node.classList.contains("message___RlFXd")) {
          let message = {
            timestamp: new Date().getTime() / 1000,
            text: node.innerText,
          };
          dbWrite(message);
        }
      }
    }
  }

  function initCSS() {
    const isDarkmode = $("body").hasClass("dark-mode");
    GM_addStyle(`.poker-control-panel-popup {
                        position: fixed;
                        top: 10%;
                        left: 15%;
                        border-radius: 10px;
                        padding: 10px;
                        background: ${isDarkmode ? "#282828" : "#F0F0F0"};
                        z-index: 1000;
                        display: none;
                      }

                      .poker-control-panel-results {
                        padding: 10px;
                      }

                      .poker-control-player {
                        margin: 4px 4px 4px 4px !important;
                        display: inline-block !important;
                      }

                      .poker-control-panel-overlay {
                        position: fixed;
                        top: 0;
                        left: 0;
                        background: ${isDarkmode ? "#404040" : "#B0B0B0"};
                        width: 100%;
                        height: 100%;
                        opacity: 0.7;
                        z-index: 900;
                        display: none;
                      }

                      .poker-control-panel-item {
                        display: inline-block;
                        margin: 2px 2px 2px 2px;
                      }`);
  }

  function initControlPanel() {
    const $title = $("div#top-page-links-list");
    if ($title.length === 0) {
      console.log("PokerHistory: nowhere to put control panel button");
    }
    const $controlBtn = $(`<a id="pokerHistoryControl" class="t-clear h c-pointer right last">
                                  <span class="icon-wrap svg-icon-wrap">
                                    <span class="link-icon-svg">
                                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 10.33"><defs><style>.cls-1{opacity:0.35;}.cls-2{fill:#fff;}.cls-3{fill:#777;}</style></defs><g id="Слой_2" data-name="Слой 2"><g id="icons"><g class="cls-1"><path class="cls-2" d="M10,5.67a2,2,0,0,1-4,0,1.61,1.61,0,0,1,0-.39A1.24,1.24,0,0,0,7.64,3.7a2.19,2.19,0,0,1,.36,0A2,2,0,0,1,10,5.67ZM8,1C3,1,0,5.37,0,5.37s3.22,5,8,5c5.16,0,8-5,8-5S13.14,1,8,1ZM8,9a3.34,3.34,0,1,1,3.33-3.33A3.33,3.33,0,0,1,8,9Z"></path></g><path class="cls-3" d="M10,4.67a2,2,0,0,1-4,0,1.61,1.61,0,0,1,0-.39A1.24,1.24,0,0,0,7.64,2.7a2.19,2.19,0,0,1,.36,0A2,2,0,0,1,10,4.67ZM8,0C3,0,0,4.37,0,4.37s3.22,5,8,5c5.16,0,8-5,8-5S13.14,0,8,0ZM8,8a3.34,3.34,0,1,1,3.33-3.33A3.33,3.33,0,0,1,8,8Z"></path></g></g></svg>
                                    </span>
                                  </span>
                                  <span>PokerHistory</span>
                                </a>`);
    $title.append($controlBtn);

    const $controlPanelDiv = $(`<div id="pokerControlPanel" class="poker-control-panel-popup">control</div>`);
    const $controlPanelOverlayDiv = $(`<div id="pokerControlOverlayPanel" class="poker-control-panel-overlay"></div>`);
    $controlPanelDiv.html(`
        <textarea readonly id="poker-results" cols="120" rows="30"></textarea>
        `);

    $title.append($controlPanelDiv);
    $title.append($controlPanelOverlayDiv);

    $controlBtn.click(function () {
      dbReadAll().then((result) => {
        let text = "";
        for (const message of result) {
          const timeStr = formatDateString(new Date(message.timestamp * 1000));
          text += timeStr + " " + message.text + "\n";
        }
        text += "Found " + result.length + " records\n";
        const $textarea = $("textarea#poker-results");
        $textarea.val(text);
        $textarea.scrollTop($textarea[0].scrollHeight);
      });

      $controlPanelDiv.fadeToggle(200);
      $controlPanelOverlayDiv.fadeToggle(200);
    });

    $controlPanelOverlayDiv.click(function () {
      $controlPanelDiv.fadeOut(200);
      $controlPanelOverlayDiv.fadeOut(200);
    });
  }

  function formatDateString(date) {
    const pad = (v) => {
      return v < 10 ? "0" + v : v;
    };
    let year = date.getFullYear();
    let month = pad(date.getMonth() + 1);
    let day = pad(date.getDate());
    let hour = pad(date.getHours());
    let min = pad(date.getMinutes());
    let sec = pad(date.getSeconds());
    return year + "/" + month + "/" + day + " " + hour + ":" + min + ":" + sec;
  }
})();