Better bateworld.com

21/10/2025, 20:41:33

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

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

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @namespace   Circlejerk Scripts
// @name        Better bateworld.com
// @version     1.3.4
// @author      Thick Bro
// @match       https://bateworld.com//html5-chat/chat2/*
// @grant       GM_addStyle
// @grant       GM_getValue
// @grant       GM_getValues
// @grant       GM_setValue
// @grant       GM_setValues
// @grant       GM_listValues
// @grant       GM_addElement
// @require     https://unpkg.com/[email protected]/dist/preact.min.js#sha512-iMvQ2nmrBGovAh+dbYrh8gttIQ4Xa/aYwXhrgYdlhNHDXdyQA4tM0JyI81xJEm+laaTWnIhWGgawYgpEihg7AQ==
// @require     https://unpkg.com/[email protected]/hooks/dist/hooks.umd.js#sha512-lLsbsdkj5qskElcWFvsifiWNJH4y8GeUtEdI3a1KZoVF+TCDvk/UGCyf/2mKpJtwI1XKJh89hVCVI2J3rEytQQ==
// @require     https://unpkg.com/[email protected]/jsx-runtime/dist/jsxRuntime.umd.js#sha512-IRTpXYSw0jUJELE+zE319bPSVme0v6QV3LVh/SD5jljvKW4hb+LL6TRpMfkTEQvccsC5N7+6YusE6ol3yFuhLw==
// @description 21/10/2025, 20:41:33
// @license GPL-3.0-or-later
// ==/UserScript==

var __webpack_modules__ = ({
"./src/betterbw/styles/openPanels.css?inline": (function (module, __webpack_exports__, __webpack_require__) {
__webpack_require__.d(__webpack_exports__, {
  A: () => (__rspack_default_export)
});
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0);
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/api.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1);
// Imports

var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default()((_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, `[data-username="I_am_watching"] {
  #userList &.userItem .webcamBtn {
    background: #50ce85 !important;
  }

  #tabs &.userItem:before, #tabs & .watchCam:before {
    content: "";
    border: 1px solid #b2b2b2;
    border-top-width: 3px;
    border-radius: 2px;
    width: 12px;
    height: 11px;
    display: block;
    position: absolute;
    top: 50%;
    transform: translateY(-50%)translateX(-130%);
  }
}

.watchingMe, .user_watching_me {
  --gridGap: 4px;
  transition: box-shadow .2s ease-out;
  box-shadow: gold 0px 0px 1px var(--gridGap) !important;

  & .jsPanel-headerbar {
    background: linear-gradient(gold 0%, #0000 30% 70%, gold 85%);
  }

  & .jsPanel-title:before {
    content: "👁️";
    content: "";
    color: gold;
    z-index: 1;
    text-shadow: 1px 1px #daa520;
    font-family: "Font Awesome 5 Free";
    font-size: 14px;
    font-weight: 400;
    position: absolute;
    top: -5px;
    left: -2px;
  }
}
`, ""]);
// Exports
/* export default */ const __rspack_default_export = (___CSS_LOADER_EXPORT___.toString());

}),
"./src/betterbw/styles/static.css?inline": (function (module, __webpack_exports__, __webpack_require__) {
__webpack_require__.d(__webpack_exports__, {
  A: () => (__rspack_default_export)
});
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0);
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/api.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1);
// Imports

var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default()((_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, `body > div:first-child {
  height: calc(100% - 4px);
}

body:not([data-algo]) button[data-sort-order=""], body[data-algo=""] button[data-sort-order=""], body[data-algo="new"] button[data-sort-order="new"], body[data-algo="top"] button[data-sort-order="top"] {
  color: #fff;
  background-color: #50c180;
}

input.numberOfCams {
  width: 4em;
}

.panel-action {
  position: absolute;
  left: 3px;
}

.panel-action-btn {
  cursor: pointer;
  color: inherit;
  float: right;
  opacity: 1;
  background: #fffc;
  border: 1px solid #ddd;
  height: 15px;
  margin-left: 8px;
  font-size: 14px;
  line-height: 0;
  transition: opacity .2s ease-out;
  position: relative;
  top: 2px;
}

.panel-action-btn:where([data-status-value="--"], [data-status-value="-"], [data-status-value="+"], [data-status-value="++"]) {
  margin-left: 0;
}

.jsPanel[data-grid-index][data-actions-attached]:not(.ui-draggable-dragging) {
  transition: top .15s linear, right .15s linear, bottom .15s linear, left .15s linear, box-shadow .2s ease-out;
}

.jsPanel:not(:hover) .panel-action-btn {
  opacity: 0;
}

.jsPanel .speaks {
  width: 3px;
  top: 0;
  bottom: 0;
  left: 0;
  rotate: 180deg;

  & .volume {
    background: #ff0;
    box-shadow: 1px 1px 1px #000, 2px 2px 1px #fff;
  }
}

.jsPanel .jsPanel-content {
  background: #111;
}

:where(#userMenu, .jsPanel)[data-status="--"] [data-status-value="--"] {
  color: #fff;
  background: #cd0000;
}

:where(#userMenu, .jsPanel)[data-status="-"] [data-status-value="-"] {
  color: #fff;
  background: #ed9200;
}

:where(#userMenu, .jsPanel)[data-status="+"] [data-status-value="+"] {
  color: #fff;
  background: #00a7e4;
}

:where(#userMenu, .jsPanel)[data-status="++"] [data-status-value="++"] {
  color: #fff;
  background: #9532ff;
}

:where(#userMenu, .jsPanel)[data-is-cooldown="true"] [data-status-value="⏱"] {
  color: #fff;
  background: #323232;
}

#userMenu .menuUserItem[data-action="whisper"] {
  display: block !important;
}

#userMenu[data-private-cam="true"] [data-action="viewWebcam"] {
  color: red;

  & .fa.fa-video-camera:after {
    color: red;
    content: " ";
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
  }
}

#tabs .addPrivateMessage {
  & .mention, & .userLabelBBW.receiver {
    pointer-events: none;
  }

  & .name-time {
    flex-wrap: wrap;

    & .content {
      flex-basis: 100%;
    }
  }

  & .whisper:after {
    width: initial;
    opacity: .75;
    background: linear-gradient(90deg, #0000 0%, red 25% 75%, #0000 100%);
    height: 10px;
    padding-inline: 25px;
    font-size: 8px;
    left: 0;
    right: auto;
  }
}

#tabs .serverMessage[data-private-cam="true"]:after, #userMenu[data-private-cam="true"] [data-action="viewWebcam"].fa:after {
  content: " ";
  color: red;
  font-family: "Font Awesome 5 Free";
  font-weight: 900;
}

#tabs .serverMessage[data-private-cam="true"]:after {
  margin-left: 20px;
}

#tabs .webcamOpened .watchCam {
  position: relative;
}

.jsPanel .userAvatar {
  cursor: pointer;
}

.jsPanel[data-status="undefined"] .jsPanel-title {
  text-decoration: underline 2px orange;
}

.jsPanel[data-status="undefined"] button:where([data-status-value="++"]), .jsPanel[data-status="--"] button:where([data-status-value="++"]), .jsPanel[data-status="-"] button:where([data-status-value="++"]), .jsPanel[data-status="+"] button:where([data-status-value="--"]), .jsPanel[data-status="++"] button:where([data-status-value="--"]) {
  display: none;
}

.jsPanel:where([data-rotation=""], [data-rotation="0"]) video {
  rotate: none;
}

.jsPanel[data-rotation="90"] video {
  rotate: 90deg;
}

.jsPanel[data-rotation="180"] video {
  rotate: 180deg;
}

.jsPanel[data-rotation="270"] video {
  rotate: 270deg;
}

.jsPanel[data-rotation="90"] video, .jsPanel[data-rotation="270"] video {
  margin-left: 7.5%;
  width: 85% !important;
}

.slide_block {
  width: 14px;
}

#usersContainer {
  width: 240px;

  &.leftLayout #slide_block {
    z-index: 101;
    height: 37px;
    top: 50px;
    left: 31px;
  }
}

#myWebcamContainer .btn {
  text-wrap: inherit;
  line-height: 1 !important;
}

#userList .userItem {
  border-bottom: none;
  margin-left: 1px;
}

#userList .userLabel {
  border-top-left-radius: 9px;
  border-bottom-left-radius: 9px;
  margin-left: 2px;
  padding-left: 8px;
  top: 0;

  & .userSince {
    left: unset;
  }
}

.webcamBtn {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  margin: 0;
  padding-inline: 1rem;
}

.webcamBtn i.lock {
  &.fa-unlock {
    display: none;
  }

  &.fa-lock {
    text-shadow: -2px 2px #fff;
    top: 1px;
    right: -25px;
  }

  font-size: 16px;
  position: absolute;
  top: 5px;
  left: -11px;
}

.webcamBtn i.fa-volume-down {
  padding-inline: 4px;
}

.eye-icon .fa-eye.isWatching {
  text-shadow: -1px -1px 1px #daa520;
  position: absolute;
  bottom: 5px;
  right: 42px;
  color: gold !important;
}

#userList .userItem:has(.webcamBtn.visible i.lock.fa-lock) {
  --lock-color: #cd000054;
  --v-padding: 0px;

  & .userLabel {
    box-shadow: inset 3px 0 1px 2px var(--lock-color);
  }

  & .webcamBtn.visible {
    box-shadow: inset -3px 0 1px 2px var(--lock-color);
    border: none;
  }
}

.jsPanel {
  box-shadow: none;
  border-color: #888 !important;
}

#roomsBtn {
  line-height: 1 !important;
}

#header {
  height: 30px !important;
}

#tabsAndFooter, #footer {
  width: min(50%, 100% - 1107px);
}

#tabsAndFooter {
  resize: horizontal;
  overflow: hidden;
}

#footer {
  width: 100%;
  position: relative;
}

video.mobile.mobile {
  max-width: 100% !important;
  max-height: 100% !important;
}

.jsPanel-headerbar {
  min-height: 18px;
}

.jsPanel-titlebar {
  min-height: 16px;
}

.jsPanel-titlebar h3 {
  margin-block: 1px;
}
`, ""]);
// Exports
/* export default */ const __rspack_default_export = (___CSS_LOADER_EXPORT___.toString());

}),
"./src/betterbw/styles/tiers.css?inline": (function (module, __webpack_exports__, __webpack_require__) {
__webpack_require__.d(__webpack_exports__, {
  A: () => (__rspack_default_export)
});
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0);
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1 = __webpack_require__("./node_modules/@rsbuild/core/compiled/css-loader/api.js");
/* import */ var _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default = /*#__PURE__*/__webpack_require__.n(_node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1);
// Imports

var ___CSS_LOADER_EXPORT___ = _node_modules_rsbuild_core_compiled_css_loader_api_js__rspack_import_1_default()((_node_modules_rsbuild_core_compiled_css_loader_noSourceMaps_js__rspack_import_0_default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, `#userList .userItem:where(.group_minus_minus) {
  opacity: .3 !important;
}

#tabs .userLabelBBW:where(.group_minus_minus) {
  text-decoration: line-through 1.8rem #cd000026;
}

#tabs .userLabelBBW:where(.group_minus) {
  text-decoration: line-through 1.8rem #ed920033;
}

#userList .userItem:where(.group_minus) .userLabel {
  background: #ed920033;
}

#tabs .userLabelBBW:where(.group_plus) {
  text-decoration: line-through 1.8rem #0064ff33;
}

#userList .userItem:where(.group_plus) .userLabel {
  background: #0064ff33;
}

#tabs .userLabelBBW:where(.group_plus_plus) {
  text-decoration: line-through 1.8rem #9532ff33;
}

#userList .userItem:where(.group_plus_plus) .userLabel {
  background: #9532ff33;
}
`, ""]);
// Exports
/* export default */ const __rspack_default_export = (___CSS_LOADER_EXPORT___.toString());

}),
"./node_modules/@rsbuild/core/compiled/css-loader/api.js": (function (module) {

/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
module.exports = function (cssWithMappingToString) {
  var list = [];

  // return the list of modules as css string
  list.toString = function toString() {
    return this.map(function (item) {
      var content = "";
      var needLayer = typeof item[5] !== "undefined";
      if (item[4]) {
        content += "@supports (".concat(item[4], ") {");
      }
      if (item[2]) {
        content += "@media ".concat(item[2], " {");
      }
      if (needLayer) {
        content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
      }
      content += cssWithMappingToString(item);
      if (needLayer) {
        content += "}";
      }
      if (item[2]) {
        content += "}";
      }
      if (item[4]) {
        content += "}";
      }
      return content;
    }).join("");
  };

  // import a list of modules into the list
  list.i = function i(modules, media, dedupe, supports, layer) {
    if (typeof modules === "string") {
      modules = [[null, modules, undefined]];
    }
    var alreadyImportedModules = {};
    if (dedupe) {
      for (var k = 0; k < this.length; k++) {
        var id = this[k][0];
        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }
    for (var _k = 0; _k < modules.length; _k++) {
      var item = [].concat(modules[_k]);
      if (dedupe && alreadyImportedModules[item[0]]) {
        continue;
      }
      if (typeof layer !== "undefined") {
        if (typeof item[5] === "undefined") {
          item[5] = layer;
        } else {
          item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
          item[5] = layer;
        }
      }
      if (media) {
        if (!item[2]) {
          item[2] = media;
        } else {
          item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
          item[2] = media;
        }
      }
      if (supports) {
        if (!item[4]) {
          item[4] = "".concat(supports);
        } else {
          item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
          item[4] = supports;
        }
      }
      list.push(item);
    }
  };
  return list;
};

}),
"./node_modules/@rsbuild/core/compiled/css-loader/noSourceMaps.js": (function (module) {

module.exports = function (i) {
  return i[1];
};

}),

});
// The module cache
var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {

// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
id: moduleId,
exports: {}
});
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

// Return the exports of the module
return module.exports;

}

// webpack/runtime/compat_get_default_export
(() => {
// getDefaultExport function for compatibility with non-ESM modules
__webpack_require__.n = (module) => {
	var getter = module && module.__esModule ?
		() => (module['default']) :
		() => (module);
	__webpack_require__.d(getter, { a: getter });
	return getter;
};

})();
// webpack/runtime/define_property_getters
(() => {
__webpack_require__.d = (exports, definition) => {
	for(var key in definition) {
        if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
            Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
        }
    }
};
})();
// webpack/runtime/has_own_property
(() => {
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
var __webpack_exports__ = {};

;// file://./src/betterbw/utils/sortFunctions.ts
const topRandom = (a, b) => b.bias - a.bias || Math.random() - 0.5;

// EXTERNAL MODULE: ./src/betterbw/styles/static.css?inline
var staticinline = __webpack_require__("./src/betterbw/styles/static.css?inline");
;// file://./src/betterbw/features/cooldown.ts
function cooldownIt({ username, minutes = 15, panel }) {
  if (!username) return;
  const expiry = Date.now() + 60000 * minutes;
  setCooldown(username, expiry);
  console.log(
    `User ${username} put on cooldown until`,
    new Date(expiry).toISOString(),
  );
  if (panel) jsPanel.activePanels.getPanel(panel.id)?.close();
}
function setCooldown(id, expiryMs) {
  GM_setValue(`${id}_cooldown`, expiryMs);
}
function getCooldownExpiry(id) {
  const val = GM_getValue(`${id}_cooldown`);
  return val ? Number(val) : null;
}
function isOnCooldown(id) {
  const expiry = getCooldownExpiry(id);
  return expiry && Date.now() < expiry;
}

;// file://./src/betterbw/utils/organizePanels.ts

const base = { my: "right-top", at: "right-top" };
const panelRatio = 318 / 365;
const gridconf = {
  MARGIN_TOP: 30,
  MARGIN_RIGHT: 5,
  GAP_X: 4,
  GAP_Y: 4,
  WIDTH: GM_getValue("config.webcamWidth", 365),
  HEIGHT: (318 * GM_getValue("config.webcamWidth", 365)) / 365,
};
const gridPlace = (c, r, offset = 0) => ({
  ...base,
  offsetX: -(gridconf.MARGIN_RIGHT + c * (gridconf.WIDTH + gridconf.GAP_X)),
  offsetY:
    gridconf.MARGIN_TOP + r * (gridconf.HEIGHT + gridconf.GAP_Y) + offset,
});
const organizePanels_offset = 65 - gridconf.MARGIN_TOP;
const positions3x3plus1 = () => [
  gridPlace(0, 0),
  gridPlace(0, 1),
  gridPlace(1, 0),
  gridPlace(1, 1),
  gridPlace(2, 0),
  gridPlace(2, 1),
  gridPlace(0, 2),
  gridPlace(1, 2),
  gridPlace(2, 2),
  gridPlace(3, 0, organizePanels_offset),
  gridPlace(3, 1, organizePanels_offset),
  gridPlace(3, 2, organizePanels_offset),
  gridPlace(4, 0, organizePanels_offset),
  gridPlace(4, 1, organizePanels_offset),
  gridPlace(4, 2, organizePanels_offset),
];
function organizePanels(getPositions = positions3x3plus1) {
  const opened = queryPanels();
  if (!opened.length) return;
  let lastIndex = 0;
  const groups = { prePositioned: [], newlyCreated: [] };
  for (const panel of opened) {
    const gridIndex = panel.dataset.gridIndex;
    if (gridIndex) {
      groups.prePositioned.push(panel);
      const gridIndexNumber = +gridIndex;
      if (gridIndexNumber > lastIndex) lastIndex = gridIndexNumber;
    } else groups.newlyCreated.push(panel);
  }
  const grid = Array(
    Math.max(+chatHTML5.roles.user.webcamMax, opened.length, lastIndex),
  ).fill(null);
  for (const panel of groups.prePositioned)
    if (null != panel.dataset.gridIndex && "" !== panel.dataset.gridIndex)
      if (grid[+panel.dataset.gridIndex]) {
        panel.dataset.gridIndex = "";
        groups.newlyCreated.unshift(panel);
      } else grid[+panel.dataset.gridIndex] = panel;
  for (let i = grid.length - 1; i >= 9; i--) {
    if (!grid[i]) continue;
    const nextAvailableSlot = grid.indexOf(null);
    if (!(nextAvailableSlot < 0) && !(nextAvailableSlot >= i)) {
      grid[i].dataset.gridIndex = nextAvailableSlot.toString();
      grid[nextAvailableSlot] = grid[i];
      grid[i] = null;
    }
  }
  for (const panel of groups.newlyCreated) {
    const nextAvailableSlot = grid.indexOf(null);
    if (nextAvailableSlot < 0) {
      panel.dataset.gridIndex = grid.length.toString();
      grid.push(panel);
    } else {
      panel.dataset.gridIndex = nextAvailableSlot.toString();
      grid[nextAvailableSlot] = panel;
    }
  }
  const positions = getPositions();
  let idx = -1;
  for (const place of grid) {
    idx++;
    const position = positions[idx];
    if (!place || !position) continue;
    const panel = jsPanel.activePanels.getPanel(place.id);
    if (panel)
      panel
        .resize({ width: gridconf.WIDTH, height: gridconf.HEIGHT })
        .reposition(position);
  }
}

;// file://./src/betterbw/utils/scrappers.ts
function scrappers_getUsername(panel) {
  const id = panel.id.split("_")[2] || $("[data-id]", panel)[0]?.dataset.id;
  return id && getUserById(id)?.username;
}
function getUserObjectById(userId) {
  return chatHTML5.users[userId];
}
function getUserById(userId) {
  const user = getUserObjectById(userId);
  if (!user) return;
  return { obj: user, username: user.username.split("_")[0] };
}

;// file://./src/betterbw/utils/openPanel.ts

const PANEL_SELECTOR = ".jsPanel.jsPanel-theme-default";
const queryPanels = () => document.querySelectorAll(PANEL_SELECTOR);
const queryPanel = target => target.querySelector(PANEL_SELECTOR);
function tryToOpenPanel(candidate) {
  $(".webcamBtn", candidate.item).trigger("click");
}
const getCandidates = (compareFn = topRandom, _biases = {}) => {
  const biases = { "-": 1, undefined: 2, "+": 3, "++": 4, ..._biases };
  const userItems = document.querySelectorAll(
    '#userList [data-status="online"][data-webcam="true"]:not(:has(:is(.fa.fa-lock, .fa.fa-eye-slash)))',
  );
  const entries = [];
  for (const item of userItems.values()) {
    const id = item.dataset.username?.split("_")[0];
    if (!id) continue;
    const status = GM_getValue(`${id}_status`);
    if ("--" !== status) {
      if (!isOnCooldown(id))
        entries.push({
          item,
          id,
          status,
          bias: biases[status],
          onlineSince: parseInt(
            item.querySelector(".userLabel [data-date]")?.dataset.date || "0",
          ),
        });
    }
  }
  entries.sort(compareFn);
  return entries;
};
function openCandidates(candidates) {
  const opened = document.querySelectorAll(PANEL_SELECTOR);
  let openedLength = opened.length || 0;
  const maxToOpen = +chatHTML5.roles.user.webcamMax;
  if (openedLength >= maxToOpen) {
    organizePanels();
    return console.log(`Max number of panels (${maxToOpen}) already open`);
  }
  const openedIds = new Set(
    Array.from(opened).map(panel => scrappers_getUsername(panel)),
  );
  while (openedLength < maxToOpen && candidates.length > 0) {
    const candidate = candidates.shift();
    if (openedIds.has(candidate.id)) continue;
    tryToOpenPanel(candidate);
    openedLength++;
  }
  organizePanels();
}

;// file://./src/betterbw/utils/waitToBe.ts
function waitToBe(
  selector,
  attributeFilter = ["aria-hidden"],
  predicate = el => "false" !== el.getAttribute("aria-hidden"),
) {
  return new Promise(resolve => {
    let attrObserver = null;
    let domObserver = null;
    function cleanup() {
      if (attrObserver) {
        attrObserver.disconnect();
        attrObserver = null;
      }
      if (domObserver) {
        domObserver.disconnect();
        domObserver = null;
      }
    }
    function attachAttrObserver(el) {
      if (!el) return false;
      if (predicate(el)) {
        cleanup();
        resolve(el);
        return true;
      }
      (attrObserver = new MutationObserver(muts => {
        for (const m of muts)
          if (
            "attributes" === m.type
            && m.attributeName
            && attributeFilter.includes(m.attributeName)
          ) {
            if (predicate(el)) {
              cleanup();
              resolve(el);
              return;
            }
          }
      })).observe(el, { attributes: true, attributeFilter });
      return false;
    }
    const existing = document.querySelector(selector);
    if (attachAttrObserver(existing)) return;
    (domObserver = new MutationObserver(muts => {
      for (const m of muts)
        for (const node of m.addedNodes) {
          if (!(node instanceof HTMLElement)) continue;
          const found = node.matches(selector)
            ? node
            : node.querySelector(selector);
          if (found) {
            if (attachAttrObserver(found)) {
              if (domObserver) {
                domObserver.disconnect();
                domObserver = null;
              }
              return;
            }
          }
        }
    })).observe(document.body, { childList: true, subtree: true });
  });
}

;// CONCATENATED MODULE: external "jsxRuntime"
const external_jsxRuntime_namespaceObject = window.jsxRuntime;
;// CONCATENATED MODULE: external "preact"
const external_preact_namespaceObject = window.preact;
;// file://./src/betterbw/utils/formatters.ts
const dataUsername = id =>
  `[data-username="${id}"],[data-username^="${id}_"]`;

// EXTERNAL MODULE: ./src/betterbw/styles/tiers.css?inline
var tiersinline = __webpack_require__("./src/betterbw/styles/tiers.css?inline");
;// file://./src/betterbw/dynamicStyle.ts

const dataUserItems = group => group.map(dataUsername).join(",");
async function getCSS() {
  const values = GM_getValues(
    GM_listValues().filter(key => key.match(/\.*?_status/)),
  );
  const groups = { "--": [], "-": [], "+": [], "++": [] };
  for (const [key, value] of Object.entries(values)) {
    const id = key.split("_")[0];
    groups[value].push(id);
  }
  const selectors = {
    group_minus_minus: dataUserItems(groups["--"]),
    group_minus: dataUserItems(groups["-"]),
    group_plus: dataUserItems(groups["+"]),
    group_plus_plus: dataUserItems(groups["++"]),
  };
  return tiersinline/* ["default"].replace */.A.replace(
    /\.(group_\w+)/gm,
    (_match, key, openStyle) => selectors[key] + openStyle,
  );
}
const dynamicStyle = GM_addStyle("");
const refreshDynamicStyle = async () =>
  requestAnimationFrame(async () => (dynamicStyle.innerHTML = await getCSS()));
refreshDynamicStyle();

;// file://./src/betterbw/features/rotateCam.tsx
function getRotation(element) {
  if (!element || !element.dataset.rotation) return 0;
  return parseInt(element.dataset.rotation, 10) || 0;
}
function rotateCam({ id, panel }) {
  const newRotation = (getRotation(panel) + 90) % 360;
  panel.dataset.rotation = newRotation;
  GM_setValue(`${id}_rotation`, newRotation);
}

;// file://./src/betterbw/features/panelActions.tsx

const BtnClassify = ({ getUsername, panel, value, title, onClick }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-status-value": value,
    title: title,
    className: "panel-action-btn",
    onClick: e => {
      const username = getUsername();
      if (username) {
        GM_setValue(`${username}_status`, value);
        refreshDynamicStyle();
      }
      if (panel) panel.dataset.status = value;
      onClick?.(e);
    },
    children: value,
  });
const BtnMinus2 = ({ getUsername, panel }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    getUsername: getUsername,
    panel: panel,
    value: "--",
    title: "Nope\nDo not suggest this person.",
    onClick: () => {
      if (panel) jsPanel.activePanels.getPanel(panel.id)?.close();
    },
  });
const BtnMinus1 = props =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    value: "-",
    title: "So so\nIt depends on the day, on the mood...",
    ...props,
  });
const BtnPlus1 = props =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    value: "+",
    title: "Yeah\nI liked you, buddy",
    ...props,
  });
const BtnPlus2 = props =>
  (0,external_jsxRuntime_namespaceObject.jsx)(BtnClassify, {
    value: "++",
    title: "Ohhh Yeah!\nI liked you a lot, buddy!",
    ...props,
  });
const BtnRotate = ({ panel, getUsername }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    title: "Rotate",
    className: "panel-action-btn",
    onClick: () => {
      if (panel) rotateCam({ id: getUsername(), panel });
    },
    children: "⟳",
  });
const BtnCooldown = ({ panel, getUsername }) =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    title: "Cooldown 15m\nDo not suggest this person for the next 15 minutes",
    className: "panel-action-btn",
    onClick: () => cooldownIt({ username: getUsername(), panel }),
    children: "⏱",
  });
const PanelActions = ({ panel, username }) => {
  const props = { panel, getUsername: () => username };
  return (0,external_jsxRuntime_namespaceObject.jsxs)(external_jsxRuntime_namespaceObject.Fragment, {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnCooldown, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnRotate, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus2, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus2, { ...props }),
    ],
  });
};
const MenuActions = () => {
  const props = { getUsername: getLatestUser };
  return (0,external_jsxRuntime_namespaceObject.jsxs)("div", {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnCooldown, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus2, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnPlus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus1, { ...props }),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnMinus2, { ...props }),
    ],
  });
};
function getLatestUser() {
  try {
    return chatHTML5.myUser.id.split("_")[0];
  } catch (e) {
    console.error("Oooops...");
    const muteItem = $('#userMenu [data-action="mute"]')[0];
    return muteItem?.textContent.split(" ").at(-1)?.split("_").at(0);
  }
}
function resizePanel(panelJS) {
  panelJS.resize({ width: gridconf.WIDTH, height: gridconf.HEIGHT });
}
function monitorVideoReadiness(video, panel, panelJS) {
  let timeoutId;
  const cleanup = () => {
    clearTimeout(timeoutId);
    video.removeEventListener("loadeddata", onVideoReady);
  };
  const onVideoReady = () => {
    cleanup();
  };
  const originalClose = panelJS.close.bind(panelJS);
  panelJS.close = function () {
    cleanup();
    return originalClose();
  };
  timeoutId = setTimeout(() => {
    cooldownIt({ username: scrappers_getUsername(panel), minutes: 5 });
    panelJS.close();
  }, 25000);
  video.addEventListener("loadeddata", onVideoReady);
}
function attachPanelActions(panel) {
  if ("1" === panel.dataset.actionsAttached) return;
  const panelJS = jsPanel.activePanels.getPanel(panel.id);
  if (!panelJS) return console.warn("Panel not found for", panel.id);
  resizePanel(panelJS);
  const header = $(".jsPanel-hdr .jsPanel-title", panel)[0];
  if (!header) return;
  const username = scrappers_getUsername(panel);
  if (!username) return;
  panel.dataset.username = username;
  panel.dataset.status = GM_getValue(`${username}_status`);
  const actions = document.createElement("div");
  actions.className = "panel-action";
  header.appendChild(actions);
  (0,external_preact_namespaceObject.render)((0,external_jsxRuntime_namespaceObject.jsx)(PanelActions, { username: username, panel: panel }), actions);
  panel.dataset.actionsAttached = "1";
  panel.dataset.rotation = GM_getValue(`${username}_rotation`);
  $(".jsPanel-btn.jsPanel-btn-close", panel).on("click", () => {
    cooldownIt({ username: username, minutes: 1 });
  });
  $(".userAvatar", panel).on("click", event => {
    event.stopPropagation();
    if (!("pageX" in event && "pageY" in event)) return;
    if ($("#userMenu").is(":visible")) return void $("#userMenu").hide();
    $(
      `#userList .userItem[data-id=${JSON.stringify(panel.id.split("_")[2])}]`,
    ).trigger(
      new jQuery.Event("click", { pageX: event.pageX + 3, pageY: event.pageY }),
    );
  });
  const video = panel.querySelector("video");
  if (video) {
    video.volume = 0.08;
    monitorVideoReadiness(video, panel, panelJS);
  }
}
function cleanupPanel(panel) {}

;// file://./src/betterbw/utils/spyOn.ts
function spyOn(obj, watchers) {
  return Proxy.revocable(obj, {
    set(target, prop, value) {
      if (prop in watchers && target[prop] !== value) watchers[prop](value);
      target[prop] = value;
      return true;
    },
  });
}

// EXTERNAL MODULE: ./src/betterbw/styles/openPanels.css?inline
var openPanelsinline = __webpack_require__("./src/betterbw/styles/openPanels.css?inline");
;// file://./src/betterbw/dynamicOpenedStyle.ts

const dynamicOpenedStyle = GM_addStyle("");
function updateCssForOpenedPanels() {
  const opened = queryPanels();
  if (!opened?.length) return;
  const usernames = Array.from(opened)
    .map(panel => scrappers_getUsername(panel))
    .filter(v => null != v);
  const selectorsIamWatching = usernames.map(dataUsername).join(",");
  const selectorsWatchingMe = usernames
    .map(
      id =>
        `body:has(#userList .userItem:where(${dataUsername(id)}) .eye-icon .isWatching) .jsPanel[data-username="${id}"]`,
    )
    .join(", ");
  dynamicOpenedStyle.innerHTML = openPanelsinline/* ["default"].replace */.A.replace(/\[data-username="I_am_watching"\]/g, selectorsIamWatching)
    .replace(/\.user_watching_me/g, selectorsWatchingMe);
}

;// file://./src/betterbw/features/tabFocus.ts
const tabFocused = () => "visible" === document.visibilityState;
const whenTabFocused = callback => {
  document.addEventListener(
    "visibilitychange",
    () => {
      "visible" === document.visibilityState && callback();
    },
    { once: true, passive: true },
  );
};

;// file://./src/betterbw/utils/observeIt.ts
function iterate(nodes, selector, fn) {
  let found = false;
  for (const node of nodes) {
    if (!(node instanceof HTMLElement)) continue;
    const panels = node.matches(selector)
      ? [node]
      : node.querySelectorAll(selector);
    if (panels.length) {
      if (fn) panels.forEach(fn);
      found = true;
    }
  }
  return found;
}
function observeIt({
  target,
  selector,
  forEachAddedNode,
  forEachRemovedNode,
  cleanup,
}) {
  const observerPanels = new MutationObserver(mutations => {
    let nodesAdded = false;
    let nodesRemoved = false;
    for (const mutation of mutations) {
      if (iterate(mutation.addedNodes, selector, forEachAddedNode))
        nodesAdded = true;
      if (iterate(mutation.removedNodes, selector, forEachRemovedNode))
        nodesRemoved = true;
    }
    cleanup?.({ nodesAdded, nodesRemoved });
  });
  observerPanels.observe(target, { childList: true, subtree: false });
  return {
    teardown: () => {
      observerPanels.disconnect();
    },
  };
}

;// CONCATENATED MODULE: external "preactHooks"
const external_preactHooks_namespaceObject = window.preactHooks;
;// file://./src/betterbw/algo.ts
let algo_algo = "";
const getAlgo = () => algo_algo;
const setAlgo = val => {
  document.body.dataset.algo = algo_algo = val;
};

;// file://./src/betterbw/utils/debounce.ts
function debounce(func, wait) {
  let timeout;
  return function (...args) {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(context, args), wait);
  };
}

;// file://./src/betterbw/features/globalActions.tsx

const btnDisableClick = () => {
  organizePanels();
  setAlgo("");
};
const BtnDisable = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-sort-order": "",
    title: "Organize open panels and stop the algorithm",
    onClick: btnDisableClick,
    children: "\xf8",
  });
const btnNewClick = () => {
  setAlgo("new");
  openCandidates(getCandidates(topRandom, { undefined: 9, "+": 8 }));
};
const BtnNew = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-sort-order": "new",
    title: "Prioritize users you haven't liked/disliked before",
    onClick: btnNewClick,
    children: "New",
  });
const btnTopClick = () => {
  setAlgo("top");
  openCandidates(getCandidates(topRandom));
};
const BtnTop = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)("button", {
    "data-sort-order": "top",
    title: "Prioritize users you liked more",
    onClick: btnTopClick,
    children: "Top",
  });
const clickOnCurrentAlgoButton = debounce(() => {
  const algo = getAlgo();
  if ("new" === algo) btnNewClick();
  else if ("top" === algo) btnTopClick();
  else if ("" === algo) organizePanels();
}, 200);
const NumberOfCams = () => {
  const [value, setValue] = (0,external_preactHooks_namespaceObject.useState)(() => GM_getValue("user.webcamMax", 10));
  (0,external_preactHooks_namespaceObject.useEffect)(() => {
    const maxWebcamreached = chatHTML5.maxWebcamreached;
    chatHTML5.maxWebcamreached = function () {
      if (!maxWebcamreached()) return false;
      chatHTML5.roles.user.webcamMax = +chatHTML5.roles.user.webcamMax + 1;
      return maxWebcamreached();
    };
    const { proxy, revoke } = spyOn(chatHTML5.roles.user, {
      webcamMax: n => {
        setValue(n);
        GM_setValue("user.webcamMax", n);
      },
    });
    chatHTML5.roles.user = proxy;
    return () => {
      chatHTML5.maxWebcamreached = maxWebcamreached;
      revoke();
    };
  }, []);
  (0,external_preactHooks_namespaceObject.useEffect)(() => {
    clickOnCurrentAlgoButton();
  }, [value]);
  return (0,external_jsxRuntime_namespaceObject.jsxs)("label", {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)("input", {
        title: "# of open cams",
        type: "number",
        className: "numberOfCams",
        value: value,
        onChange: e => {
          chatHTML5.roles.user.webcamMax = +e.currentTarget.value;
        },
      }),
      " cams",
    ],
  });
};
const globalActions_onChangeEffect = value => {
  gridconf.WIDTH = +value;
  gridconf.HEIGHT = value * panelRatio;
  clickOnCurrentAlgoButton();
};
const PanelSizeInput = () =>
  (0,external_jsxRuntime_namespaceObject.jsx)(MemoInput, {
    storageKey: "config.webcamWidth",
    title: "Size of the panel",
    type: "number",
    className: "numberOfCams",
    suffix: "px",
    min: "150",
    step: "5",
    defaultValue: gridconf.WIDTH,
    onChangeEffect: globalActions_onChangeEffect,
  });
const MemoInput = ({
  storageKey,
  prefix,
  suffix,
  defaultValue,
  onChangeEffect,
  ...props
}) => {
  const [value, setValue] = (0,external_preactHooks_namespaceObject.useState)(() =>
    GM_getValue(storageKey, defaultValue),
  );
  (0,external_preactHooks_namespaceObject.useEffect)(() => {
    onChangeEffect(value);
  }, [value]);
  return (0,external_jsxRuntime_namespaceObject.jsxs)("label", {
    children: [
      prefix,
      (0,external_jsxRuntime_namespaceObject.jsx)("input", {
        ...props,
        value: value,
        onChange: e => {
          const value = e.currentTarget.value;
          setValue(value);
          GM_setValue(storageKey, value);
        },
      }),
      suffix,
    ],
  });
};
const AlgoControls = () =>
  (0,external_jsxRuntime_namespaceObject.jsxs)(external_jsxRuntime_namespaceObject.Fragment, {
    children: [
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnTop, {}),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnNew, {}),
      (0,external_jsxRuntime_namespaceObject.jsx)(BtnDisable, {}),
      (0,external_jsxRuntime_namespaceObject.jsx)(NumberOfCams, {}),
      " @ ",
      (0,external_jsxRuntime_namespaceObject.jsx)(PanelSizeInput, {}),
    ],
  });

;// file://./src/betterbw/features/observeOpenPanels.ts

let observeOpenPanels_nextUser = null;
const setNextCam = val => (observeOpenPanels_nextUser = val);
let clickRequested = false;
function observePanels() {
  return observeIt({
    target: document.body,
    selector: PANEL_SELECTOR,
    forEachAddedNode: attachPanelActions,
    forEachRemovedNode: cleanupPanel,
    cleanup: ({ nodesAdded, nodesRemoved }) => {
      if (!nodesAdded && !nodesRemoved) return;
      organizePanels();
      if (nodesRemoved) {
        if (observeOpenPanels_nextUser) {
          $(".webcamBtn.visible", observeOpenPanels_nextUser).trigger("click");
          observeOpenPanels_nextUser = null;
        } else if (tabFocused()) clickOnCurrentAlgoButton();
        else if (!clickRequested) {
          clickRequested = true;
          whenTabFocused(() => {
            clickRequested = false;
            clickOnCurrentAlgoButton();
          });
        }
      }
      updateCssForOpenedPanels();
    },
  });
}

;// file://./src/betterbw/features/spyOnConfig.ts

function spyOnConfig(watchers = {}) {
  const { proxy, revoke } = spyOn(chatHTML5.config, watchers);
  chatHTML5.config = proxy;
  return { proxy, revoke, watchers };
}

;// file://./src/betterbw/setupTools.tsx

function setupHeader() {
  const header = $("#header .header-custom-btns")[0];
  if (!header) return;
  const controls = document.createElement("div");
  controls.id = "bbw_controls";
  header.prepend(controls);
  (0,external_preact_namespaceObject.render)((0,external_jsxRuntime_namespaceObject.jsx)(AlgoControls, {}), controls);
}
function setupUserMenu() {
  const userMenu = $("#userMenu")[0];
  if (!userMenu) return;
  const controls = document.createElement("div");
  controls.id = "bbw_controls";
  userMenu.appendChild(controls);
  (0,external_preact_namespaceObject.render)((0,external_jsxRuntime_namespaceObject.jsx)(MenuActions, {}), controls);
  chatHTML5.myUser = spyOn(chatHTML5.myUser, {
    selectedUserid: selectedUserid => {
      const nuser = getUserById(selectedUserid);
      if (!nuser) return;
      userMenu.dataset.username = nuser.username;
      userMenu.dataset.status = GM_getValue(`${nuser.username}_status`);
      userMenu.dataset.isCooldown = Boolean(
        isOnCooldown(nuser.username),
      ).toString();
      userMenu.dataset.privateCam = Boolean(!nuser.obj.webcamPublic).toString();
    },
  }).proxy;
}
function setupSidebar() {
  const userList = document.getElementById("userList");
  if (!userList) return;
  $(userList).on("click", ".webcamBtn", function (event) {
    const opened = queryPanels();
    const webcamMax = +chatHTML5.roles.user.webcamMax;
    if (opened.length < webcamMax) return;
    const nextUser = $(this).closest(".userItem")[0];
    if (nextUser) {
      event.stopPropagation();
      setNextCam(nextUser);
      const panel = $(`[data-grid-index="${webcamMax - 1}"]`)[0];
      if (panel) jsPanel.activePanels.getPanel(panel.id)?.close();
    }
  });
  $(userList).on("click", ".fa-eye.fa-2x", function (e) {
    if (e.shiftKey) return;
    e.preventDefault();
    e.stopPropagation();
    e.stopImmediatePropagation();
  });
}
const ConfigWatchers = {};
function setupTools() {
  chatHTML5.config.timeBeforeWatchingCamAgain = "1000";
  chatHTML5.config.checkOwnStream = "1";
  chatHTML5.config.showCountryFlag = "1";
  chatHTML5.roles.user.webcamMax = GM_getValue("user.webcamMax", 10);
  const { revoke } = spyOnConfig(ConfigWatchers);
  chatHTML5.amIMuted = () => chatHTML5.myUser.mutedUntil > Date.now();
  setupHeader();
  setupUserMenu();
  setupSidebar();
  $("#sortWebcamtBtn").trigger("click");
  return revoke;
}

;// file://./src/betterbw/features/observeChat.tsx

function handleWebcamOpened(message) {
  const span = $("[data-id]", message)[0];
  if (!span) return;
  const user = getUserById(span.dataset.id);
  if (!user) return;
  message.dataset.username = user.username;
  const privateCam = Boolean(!user.obj.webcamPublic);
  message.dataset.privateCam = privateCam.toString();
  span.innerHTML = span.innerHTML.replace(
    /(User )(.*?)( has opened his webcam)/gi,
    `$1<span class="userLabelBBW" data-username=${JSON.stringify(user.username)}>$2</span>$3`,
  );
  if (!privateCam) clickOnCurrentAlgoButton();
}
function handleRegularMessage(message) {
  const msgHeader = $("[data-ip]", message)[0];
  if (!msgHeader) return;
  msgHeader.classList.add("userLabelBBW", "sender");
}
function handleWhisperOrPrivateMessage(message) {
  const msgHeader = $("[data-ip]", message)[0];
  if (!msgHeader || !msgHeader.childNodes.length) return;
  {
    const firstChild = msgHeader.childNodes[0];
    const span = document.createElement("span");
    span.dataset.username = msgHeader.dataset.username;
    span.classList.add("userLabelBBW", "sender");
    msgHeader.insertBefore(span, firstChild);
    span.appendChild(firstChild);
  }
  {
    const thirdChild = msgHeader.childNodes[2];
    const span = document.createElement("span");
    span.dataset.username = thirdChild.textContent?.trim();
    span.classList.add("userLabelBBW", "receiver");
    msgHeader.insertBefore(span, thirdChild);
    span.appendChild(thirdChild);
  }
}
function handlePrivateRequested(message) {
  const textNode = Array.from(message.childNodes).find(
    n => 3 === n.nodeType && n.textContent?.trim(),
  );
  if (!textNode || !textNode.textContent) return;
  const textContent = textNode.textContent;
  const match = textContent.match(
    /\s*(\S+?)\s+has invited you to watch his cam/,
  );
  if (!match) return;
  const username = match[1];
  if (!username) return;
  const span = document.createElement("span");
  span.className = "userLabelBBW";
  span.dataset.username = username;
  span.textContent = username;
  message.insertBefore(span, textNode);
  textNode.textContent = textContent.replace(/(^\s*\S+?)(\s+.*$)/, "$2");
}
function handleWebcamRequest(message) {}
function handleChatMessage(message) {
  if (message.matches(".serverMessage.webcamOpened"))
    handleWebcamOpened(message);
  else if (message.matches(".message.msg-box:not(.whisper)"))
    handleRegularMessage(message);
  else if (
    message.matches(".message.addPrivateMessage,.message.msg-box.whisper")
  )
    handleWhisperOrPrivateMessage(message);
  else if (message.matches(".serverMessage.privateRequested"))
    handlePrivateRequested(message);
  else if (message.matches(".serverMessage.webcamRequest"))
    handleWebcamRequest(message);
}
function observeChat(room) {
  return observeIt({
    target: room,
    selector: ".message,.serverMessage",
    forEachAddedNode: handleChatMessage,
    forEachRemovedNode: message => {
      console.log("Chat message removed in", room.id, message);
    },
  });
}
function observeChatNav() {
  const tabs = {};
  return observeIt({
    target: $("#tabs .tab-content")[0],
    selector: ".tab-pane",
    forEachAddedNode: room => {
      tabs[room.id] = observeChat(room);
    },
    forEachRemovedNode: room => {
      if (room.id in tabs) {
        tabs[room.id]?.teardown?.();
        delete tabs[room.id];
      }
    },
  });
}

;// file://./src/betterbw/betterbw.user.ts

async function main() {
  GM_addStyle(staticinline/* ["default"] */.A);
  observeChatNav();
  observePanels();
  await waitToBe(
    "#roomsModal",
    ["aria-hidden"],
    el => "false" === el.getAttribute("aria-hidden"),
  );
  await waitToBe(
    "#roomsModal",
    ["aria-hidden"],
    el => "false" !== el.getAttribute("aria-hidden"),
  );
  setupTools();
  openCandidates(getCandidates(topRandom));
}

;// file://./src/betterbw/index.ts

chatHTML5.myUser.mutedUsers = chatHTML5.myUser.mutedUsers || "";
main();