; Tampermonkey - Herramienta Skip to main content
 

Tampermonkey - Herramienta

1
(Leído 53 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Tipster Oro
Maestro en apuestas
Enviado: 19 de Abril de 2026, 17:59
Os traigo algo que a mi me está siendo muy útil.

Se trata de añadir unos botones para expandir o plegar las ligas que aparecen en las webs flashscore y sofascore, en lugar de hacerlo una a una estos botones las expanden todas de formas que nos ahorra algo de tiempo al buscar los partidos.

Lo primero es añadir al navegador la extensión llamada Tampermonkey, (ir a google y buscar extensiones chrome) esta herramienta sirve para crear códigos que tu mismo puedes crear para simplificarte algunos trabajos como es este caso.

Bien, una vez instalado hay que abrir el panel de control de Tampermonkey, ir al sigo + para añadir el código que queremos, eliminamos todo lo que aparece y pegamos lo siguiente:

Para flashscore:
Código: [Seleccionar]
// ==UserScript==
// @name         Flashscore - Abrir y cerrar todas las ligas
// @namespace    flashscore-tools
// @version      2.0
// @description  Añade botones reales para abrir y cerrar todas las ligas sin redirecciones
// @match        https://www.flashscore.es/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    function stopEverything(e) {
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        return false;
    }

    function isVisible(el) {
        return !!(el && el.offsetParent !== null);
    }

    function getToggleButtons() {
        const icons = Array.from(
            document.querySelectorAll('[data-testid*="action-navigation-arrow"]')
        ).filter(isVisible);

        const toggles = icons
            .map(icon => {
                return (
                    icon.closest('button') ||
                    icon.closest('[role="button"]') ||
                    icon.closest('div')
                );
            })
            .filter(Boolean)
            .filter(isVisible);

        return [...new Set(toggles)];
    }

    function isExpanded(toggle) {
        const svg = toggle.querySelector('svg');
        if (!svg) return null;

        const path = svg.querySelector('path');
        if (!path) return null;

        const d = path.getAttribute('d') || '';

        // Flecha arriba = expandido
        if (d.includes('M.98 15 10 5.97 19.02 15')) return true;

        // Flecha abajo = contraído (por si cambia el icono)
        if (d.includes('M19.02 5')) return false;

        return null;
    }

    function expandAll() {
        getToggleButtons().forEach(toggle => {
            const state = isExpanded(toggle);
            if (state === false) {
                toggle.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
            }
        });
    }

    function collapseAll() {
        getToggleButtons().forEach(toggle => {
            const state = isExpanded(toggle);
            if (state === true) {
                toggle.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
            }
        });
    }

    function createButton(id, text, right, onClick) {
        if (document.getElementById(id)) return;

        const btn = document.createElement('button');
        btn.id = id;
        btn.type = 'button';
        btn.textContent = text;

        btn.style.cssText = `
    position: fixed !important;
    top: 25% !important;
    transform: translateY(-50%) !important;
    right: ${right} !important;
    z-index: 2147483647 !important;
    padding: 12px 16px !important;
    background: #d0021b !important;
    color: #fff !important;
    border: none !important;
    border-radius: 10px !important;
    font-size: 14px !important;
    font-weight: 700 !important;
    cursor: pointer !important;
    box-shadow: 0 2px 10px rgba(0,0,0,.35) !important;
    pointer-events: auto !important;
`;

        btn.addEventListener('click', function (e) {
            stopEverything(e);
            onClick();
        }, true);

        btn.addEventListener('mousedown', stopEverything, true);
        btn.addEventListener('mouseup', stopEverything, true);
        btn.addEventListener('pointerdown', stopEverything, true);
        btn.addEventListener('pointerup', stopEverything, true);
        btn.addEventListener('touchstart', stopEverything, true);
        btn.addEventListener('touchend', stopEverything, true);

        document.body.appendChild(btn);
    }

    function init() {
        if (!document.body) return;

        createButton('fs-expand-all', 'Abrir ligas', '850px', expandAll);
        createButton('fs-collapse-all', 'Cerrar ligas', '720px', collapseAll);
    }

    function boot() {
        init();

        const observer = new MutationObserver(() => {
            init();
        });

        observer.observe(document.documentElement, {
            childList: true,
            subtree: true
        });
    }

    window.addEventListener('load', () => {
        setTimeout(boot, 1500);
    });
})();
después vamos a archivo y guardar, listo, ahora en la pagina de flashscore nos aparece el botón abrir ligas y cerrar ligas.

Ahora para sofascore creamos otro script pulsando en +, eliminamos todo lo que aparece y pegamos lo siguiente:
Código: [Seleccionar]
// ==UserScript==
// @name         Sofascore ES - Abrir/Cerrar ligas limpio
// @namespace    https://www.sofascore.com/
// @version      1.0
// @description  Abre o cierra ligas de la columna izquierda en Sofascore
// @match        https://www.sofascore.com/es*
// @match        https://www.sofascore.com/es/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  const WRAP_ID = 'tm-sofascore-controls';

  const visible = el => {
    const s = getComputedStyle(el);
    const r = el.getBoundingClientRect();
    return s.display !== 'none' && s.visibility !== 'hidden' && r.width && r.height;
  };

  const getDirection = svg => {
    const d = svg.querySelector('path')?.getAttribute('d') || '';
    if (/M11\.99\s+18/.test(d)) return 'down'; // cerrada
    if (/M11\.99\s+6/.test(d)) return 'up';   // abierta
    return null;
  };

  const getRows = () => {
    const selectors = [
      'div.pb_sm > div.d_flex.ai_center.ps_xl.cursor_pointer',
      'div.pb_0 > div.d_flex.ai_center.ps_xl.cursor_pointer'
    ];

    return [...document.querySelectorAll(selectors.join(','))]
      .filter(visible)
      .filter(el => el.getBoundingClientRect().left < window.innerWidth * 0.45)
      .map(header => {
        const toggle = header.querySelector(':scope > div.d_flex.ai_center.me_md:last-child');
        const svg = toggle?.querySelector('svg');
        if (!toggle || !svg) return null;

        return {
          toggle,
          dir: getDirection(svg)
        };
      })
      .filter(Boolean);
  };

  const click = el => {
    const r = el.getBoundingClientRect();
    const x = r.left + r.width / 2;
    const y = r.top + r.height / 2;

    ['pointerdown', 'mousedown', 'pointerup', 'mouseup', 'click'].forEach(type => {
      el.dispatchEvent(new MouseEvent(type, {
        bubbles: true,
        cancelable: true,
        clientX: x,
        clientY: y
      }));
    });
  };

  const act = async mode => {
    const rows = getRows();
    const targets = rows.filter(r => r.dir === (mode === 'open' ? 'down' : 'up'));

    for (const r of targets) {
      click(r.toggle);
      await new Promise(res => setTimeout(res, 120));
    }
  };

  const makeBtn = (text, fn) => {
    const b = document.createElement('button');
    b.textContent = text;
    Object.assign(b.style, {
      padding: '10px 14px',
      borderRadius: '10px',
      border: '1px solid #999',
      background: '#fff',
      cursor: 'pointer',
      fontWeight: '600',
      boxShadow: '0 2px 10px rgba(0,0,0,0.2)'
    });
    b.onclick = fn;
    return b;
  };

  const init = () => {
    if (document.getElementById(WRAP_ID)) return;

    const wrap = document.createElement('div');
    wrap.id = WRAP_ID;

    Object.assign(wrap.style, {
      position: 'fixed',
      right: '16px',
      bottom: '16px',
      zIndex: '999999',
      display: 'flex',
      flexDirection: 'column',
      gap: '8px'
    });

    wrap.appendChild(makeBtn('Abrir ligas', () => act('open')));
    wrap.appendChild(makeBtn('Cerrar ligas', () => act('close')));

    document.body.appendChild(wrap);
  };

  window.addEventListener('load', () => setTimeout(init, 1200));
})();

ahora ya tenemos los botones abrir ligas y cerrar ligas, estos botones aparecen en la parte inferior derecha de la pagina web.

Estos códigos los he ido probando a través de chatgpt hasta que han funcionado correctamente ya que no funcionan a la primera porque chatgpt a veces no es el mejor programador.

Espero que os sirva, si tenéis dudas o usáis algún otro código mas que sea interesante podemos compartirlo por aquí.

Saludos
Tipster Oro
Maestro en apuestas
Enviado: 19 de Abril de 2026, 18:35
Os dejo otros botones interesantes para sofascore.

Estos hacen que puedas quitar de la lista de partidos todos los que no tienen cuotas, y eso es porque hay muchos partidos de ligas como sub17 o sub15 que sofascore los muestra pero no son útiles para nosotros porque no aparecen en ninguna casa de apuestas y al quitarlos de la lista hace nuestro trabajo mas rápido.

El primer botón "Ocultar sin cuotas" quita de la lista todas las ligas que no tengan al menos 1 partido con cuota, no quita los partidos sin cuotas, quita las ligas que tengan todos sus partidos sin cuotas.

El segundo botón "Cargar y ocultar" muestra todas las ligas (puede tardar un poco, sobre todo en fin de semana por la cantidad de partidos) una vez termina elimina todas las ligas que no tengan al menos un partido con cuotas, , si al terminar aparece alguna liga sin cuotas simplemente vamos de nuevo al botón "Ocultar sin cuotas" y listo.

El tercer botón "Mostrar todas" vuelve a mostrar todos los partidos eliminados.

Todos las ligas que elimina vuelven a aparecer al refrescar la pagina, por lo que no es algo definitivo.

Código a usar en Tampermonkey:

Código: [Seleccionar]
// ==UserScript==
// @name         Sofascore ES - Ocultar ligas sin cuotas
// @namespace    https://www.sofascore.com/
// @version      1.0
// @description  Oculta en la columna izquierda las ligas que no tienen ningún partido con cuotas
// @match        https://www.sofascore.com/es*
// @match        https://www.sofascore.com/es/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  const PANEL_ID = 'tm-sofascore-odds-filter';

  const sleep = ms => new Promise(r => setTimeout(r, ms));

  function visible(el) {
    if (!el || !(el instanceof Element)) return false;
    const s = getComputedStyle(el);
    const r = el.getBoundingClientRect();
    return s.display !== 'none' && s.visibility !== 'hidden' && r.width > 0 && r.height > 0;
  }

  function getLeagueBlocks() {
    return [
      ...document.querySelectorAll('div.pb_sm'),
      ...document.querySelectorAll('div.pb_0')
    ].filter(block => {
      const header = block.querySelector(':scope > div.d_flex.ai_center.ps_xl.cursor_pointer');
      if (!header || !visible(header)) return false;

      const rect = header.getBoundingClientRect();
      return rect.left < window.innerWidth * 0.45;
    });
  }

  function hasOdds(block) {
    return block.querySelectorAll('a.z_button').length > 0;
  }

  function hideNoOddsLeagues() {
    const blocks = getLeagueBlocks();
    let hidden = 0;

    for (const block of blocks) {
      if (hasOdds(block)) {
        block.style.display = '';
        block.dataset.tmOddsHidden = '0';
      } else {
        block.style.display = 'none';
        block.dataset.tmOddsHidden = '1';
        hidden++;
      }
    }

    alert(`Ocultadas ${hidden} ligas sin cuotas.`);
  }

  function showAllLeagues() {
    const blocks = getLeagueBlocks();

    for (const block of blocks) {
      block.style.display = '';
      block.dataset.tmOddsHidden = '0';
    }
  }

  async function loadAllContent() {
    const scroller = document.scrollingElement || document.documentElement || document.body;

    let stableRounds = 0;
    let lastHeight = 0;

    while (stableRounds < 4) {
      const before = scroller.scrollHeight;

      window.scrollTo(0, before);
      await sleep(1200);

      const after = scroller.scrollHeight;

      if (after <= lastHeight || after === before) {
        stableRounds++;
      } else {
        stableRounds = 0;
      }

      lastHeight = after;
    }

    window.scrollTo(0, 0);
    await sleep(300);
  }

  async function loadAndHideNoOdds() {
    await loadAllContent();
    hideNoOddsLeagues();
  }

  function makeBtn(label, fn) {
    const btn = document.createElement('button');
    btn.textContent = label;

    Object.assign(btn.style, {
      padding: '10px 14px',
      borderRadius: '10px',
      border: '1px solid #999',
      background: '#fff',
      color: '#111',
      fontSize: '14px',
      fontWeight: '600',
      cursor: 'pointer',
      boxShadow: '0 2px 10px rgba(0,0,0,0.20)'
    });

    btn.addEventListener('click', fn);
    return btn;
  }

  function initPanel() {
    if (document.getElementById(PANEL_ID)) return;

    const panel = document.createElement('div');
    panel.id = PANEL_ID;

    Object.assign(panel.style, {
      position: 'fixed',
      right: '16px',
      bottom: '120px',
      zIndex: '999999',
      display: 'flex',
      flexDirection: 'column',
      gap: '8px'
    });

    panel.appendChild(makeBtn('Ocultar sin cuotas', hideNoOddsLeagues));
    panel.appendChild(makeBtn('Cargar y ocultar', loadAndHideNoOdds));
    panel.appendChild(makeBtn('Mostrar todas', showAllLeagues));

    document.body.appendChild(panel);
  }

  window.addEventListener('load', () => {
    setTimeout(initPanel, 1200);
  });

  setTimeout(initPanel, 2500);
})();

Saludos.

 


*Las cuotas mostradas en la web son meramente orientativas. Nuestro equipo trabaja muy duro para poder ofrecerte las cuotas más actualizadas posibles. Estas cuotas están sujetas a posibles cambios por parte de los operadores. No olvides comprobar la cuota actualizada en la casa de apuestas antes de realizar cualquier apuesta.