新球体育网欧盘分析

新球体育网(球探)手机端网页,比赛的分析页面里加入关键时间点的欧盘分析表格,注意提示里面的让球转换只是粗略值。

// ==UserScript==
// @name         新球体育网欧盘分析
// @namespace    http://dol.freevar.com/
// @version      0.6
// @description  新球体育网(球探)手机端网页,比赛的分析页面里加入关键时间点的欧盘分析表格,注意提示里面的让球转换只是粗略值。
// @author       Dolphin
// @match        https://m.titan007.com/analy/Analysis/*
// @match        https://m.titan007.com/Analy/Analysis/*
// @run-at       document-idle
// @grant        GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 全局变量存储解析后的赔率数据
    let oddsData = {}; // 存储格式: {公司编号: {时间戳: {win, draw, lose}, ...}, ...}
    let allTimestamps = []; // 所有可用的时间戳
    let tableData = []; // 表格数据

    // 初始化函数
    function init() {
        // 使用页面自带的scheduleId变量请求数据
        const url = `https://txt.titan007.com/1x2/${scheduleId}.js`;

        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: function(response) {
                if (response.status === 200) {
                    parseOddsData(response.responseText);
                    initializeTableData();
                    createUI();
                } else {
                    console.error("获取赔率数据失败:", response.statusText);
                }
            },
            onerror: function(error) {
                console.error("请求赔率数据时发生错误:", error);
            }
        });
    }

    // 解析赔率数据
    function parseOddsData(data) {
        // 提取gameDetail数组
        const gameDetailMatch = data.match(/var gameDetail=Array\((.*?)\);/);
        if (!gameDetailMatch) {
            console.error("无法解析gameDetail数据");
            return;
        }

        const gameDetailStr = gameDetailMatch[1];
        // 分割各个公司的数据
        const companyEntries = gameDetailStr.split('","').map(item => {
            // 去除首尾引号
            return item.replace(/^"/, '').replace(/"$/, '');
        });

        // 解析每个公司的数据
        companyEntries.forEach(entry => {
            const [companyId, oddsInfo] = entry.split('^');
            if (!oddsInfo) return;

            // 分割不同时间点的赔率
            const oddsEntries = oddsInfo.split(';').filter(item => item.trim() !== '');

            oddsData[companyId] = {};

            oddsEntries.forEach(oddsEntry => {
                // 解析赔率条目
                const parts = oddsEntry.split('|');
                if (parts.length < 4) return;

                const [winOdds, drawOdds, loseOdds, timeStr] = parts;
                // 解析时间格式:月-日 时:分
                const [datePart, timePart] = timeStr.split(' ');
                const [month, day] = datePart.split('-');
                const [hour, minute] = timePart.split(':');

                // 从数据中提取年份
                const year = parts[7] || new Date().getFullYear();

                // 创建时间戳
                const timestamp = new Date(`${year}-${month}-${day}T${hour}:${minute}:00`).getTime();

                // 存储赔率信息
                oddsData[companyId][timestamp] = {
                    win: parseFloat(winOdds),
                    draw: parseFloat(drawOdds),
                    lose: parseFloat(loseOdds)
                };

                // 收集所有时间戳
                if (!allTimestamps.includes(timestamp)) {
                    allTimestamps.push(timestamp);
                }
            });
        });

        // 按时间戳排序
        allTimestamps.sort((a, b) => a - b);
    }

    // 初始化表格数据 - 添加最早和最晚的时间点
    function initializeTableData() {
        if (allTimestamps.length > 0) {
            // 添加最晚的时间点
            addTimePointToTable(allTimestamps[allTimestamps.length - 1]);
            // 添加主客最近一场比赛开始和结束的时间点
            addTimePointToTable(parseInt(jsonData.nearMatches.homeMatches.matches[0].matchTime) * 1000);
            addTimePointToTable(parseInt(jsonData.nearMatches.homeMatches.matches[0].matchTime) * 1000 + 3 * 3600000);
            addTimePointToTable(parseInt(jsonData.nearMatches.awayMatches.matches[0].matchTime) * 1000);
            addTimePointToTable(parseInt(jsonData.nearMatches.awayMatches.matches[0].matchTime) * 1000 + 3 * 3600000);
            // 添加最早的时间点
            addTimePointToTable(allTimestamps[0]);
        }
    }

    // 添加时间点到表格数据
    function addTimePointToTable(timestamp) {
        let totalWin = 0;
        let totalDraw = 0;
        let totalLose = 0;
        let companyCount = 0;

        // 计算该时间点前的赔率平均值
        Object.values(oddsData).forEach(companyOdds => {
            // 找到该时间点或之前的最新赔率
            const companyTimestamps = Object.keys(companyOdds).map(Number).sort((a, b) => b - a);
            const validTimestamp = companyTimestamps.find(ts => ts <= timestamp);

            if (validTimestamp) {
                const odds = companyOdds[validTimestamp];
                totalWin += odds.win;
                totalDraw += odds.draw;
                totalLose += odds.lose;
                companyCount++;
            }
        });

        if (companyCount > 0) {
            const avgWin = (totalWin / companyCount).toFixed(4);
            const avgDraw = (totalDraw / companyCount).toFixed(4);
            const avgLose = (totalLose / companyCount).toFixed(4);

            // 格式化时间显示
            const date = new Date(timestamp);
            const formattedTime = `${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;

            // 检查该时间点是否已存在
            const existingIndex = tableData.findIndex(item => item.timestamp === timestamp);
            if (existingIndex === -1) {
                // 添加到表格数据
                tableData.push({
                    timestamp,
                    formattedTime,
                    companyCount,
                    avgWin,
                    avgDraw,
                    avgLose
                });

                // 按时间戳倒序排序(最新的在上面)
                tableData.sort((a, b) => b.timestamp - a.timestamp);

                // 更新表格显示
                updateTable();
            }
        }
    }

    // 添加用户自定义时间点
    function addCustomTimePoint(timeStr) {
        // 解析用户输入的时间格式:YYYYMMDDHHmm
        if (timeStr.length !== 12) {
            alert("请输入正确的时间格式:YYYYMMDDHHmm");
            return;
        }

        const year = timeStr.substr(0, 4);
        const month = timeStr.substr(4, 2);
        const day = timeStr.substr(6, 2);
        const hour = timeStr.substr(8, 2);
        const minute = timeStr.substr(10, 2);

        try {
            const timestamp = new Date(`${year}-${month}-${day}T${hour}:${minute}:00`).getTime();
            if (isNaN(timestamp)) {
                alert("无效的时间格式");
                return;
            }
            addTimePointToTable(timestamp);
        } catch (e) {
            alert("无效的时间格式");
            console.error("时间解析错误:", e);
        }
    }

    // 创建用户界面
    function createUI() {
        // 创建容器
        const container = document.createElement('div');
        container.style.border = '1px solid #888';
        container.style.borderRadius = '5px';
        container.style.textAlign = 'center';
        container.style.fontSize = '16px';

        // 创建输入框和按钮
        const timeInput = document.createElement('input');
        timeInput.type = 'number';
        timeInput.style.fontSize = '16px';
        timeInput.placeholder = '输入时间(YYYYMMDDHHmm)';
        timeInput.style.padding = '4px';
        timeInput.style.margin = '8px';
        timeInput.style.border = '1px solid #888';
        timeInput.style.borderRadius = '4px';

        const addButton = document.createElement('button');
        addButton.style.fontSize = '16px';
        addButton.textContent = '添加时间';
        addButton.style.padding = '6px 12px';
        addButton.style.backgroundColor = '#48c';
        addButton.style.color = 'white';
        addButton.style.border = 'none';
        addButton.style.borderRadius = '4px';
        addButton.style.cursor = 'pointer';
        addButton.addEventListener('click', () => {
            addCustomTimePoint(timeInput.value);
            timeInput.value = '';
        });

        container.appendChild(timeInput);
        container.appendChild(addButton);

        // 创建标题
        const title = document.createElement('div');
        title.style.background = '#eee';
        title.textContent = '热门比赛欧亚转换(非精确):0.5球1.9⚽0.75球1.7⚽1球1.5⚽1.25球1.4⚽1.5球1.28⚽1.75球1.22⚽2球1.16';
        container.appendChild(title);

        // 创建表格
        const table = document.createElement('table');
        table.id = 'oddsTable';
        table.style.borderCollapse = 'collapse';
        table.style.margin = '8px auto';

        // 创建表头
        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');
        headerRow.style.backgroundColor = '#48c';
        headerRow.style.color = 'white';

        const headers = ['公司数', '平均胜赔', '平均平赔', '平均负赔', '时间'];
        headers.forEach(headerText => {
            const th = document.createElement('th');
            th.textContent = headerText;
            th.style.border = '1px solid #888';
            headerRow.appendChild(th);
        });

        thead.appendChild(headerRow);
        table.appendChild(thead);

        // 创建表体
        const tbody = document.createElement('tbody');
        tbody.id = 'oddsTableBody';
        table.appendChild(tbody);

        container.appendChild(table);

        // 插入到页面中
        const contentDiv = document.querySelector('div#content');
        contentDiv.insertBefore(container, contentDiv.firstChild);

        // 初始更新表格内容
        updateTable();
    }

    // 更新表格内容
    function updateTable() {
        const tbody = document.getElementById('oddsTableBody');
        if (!tbody) return;

        // 清空表格
        tbody.innerHTML = '';

        // 添加数据行
        tableData.forEach((rowData) => {
            const row = document.createElement('tr');

            // 公司数量
            const countCell = document.createElement('td');
            countCell.textContent = rowData.companyCount;
            countCell.style.border = '1px solid #888';
            row.appendChild(countCell);

            // 平均胜赔
            const winCell = document.createElement('td');
            winCell.textContent = rowData.avgWin;
            winCell.style.border = '1px solid #888';
            row.appendChild(winCell);

            // 平均平赔
            const drawCell = document.createElement('td');
            drawCell.textContent = rowData.avgDraw;
            drawCell.style.border = '1px solid #888';
            row.appendChild(drawCell);

            // 平均负赔
            const loseCell = document.createElement('td');
            loseCell.textContent = rowData.avgLose;
            loseCell.style.border = '1px solid #888';
            row.appendChild(loseCell);

            // 时间
            const timeCell = document.createElement('td');
            timeCell.textContent = rowData.formattedTime;
            timeCell.style.border = '1px solid #888';
            row.appendChild(timeCell);

            tbody.appendChild(row);
        });

        // 设置颜色标注
        setOddsColorCoding();
    }

    // 设置赔率颜色标注 - 比较上下行赔率
    function setOddsColorCoding() {
        const rows = document.querySelectorAll('#oddsTableBody tr');
        if (rows.length < 2) return;

        // 从第二行开始,与上一行比较
        for (let i = 1; i < rows.length; i++) {
            const currentRow = rows[i];
            const prevRow = rows[i - 1];

            // 获取当前行和上一行的赔率单元格
            const currentWin = parseFloat(currentRow.cells[1].textContent);
            const currentDraw = parseFloat(currentRow.cells[2].textContent);
            const currentLose = parseFloat(currentRow.cells[3].textContent);

            const prevWin = parseFloat(prevRow.cells[1].textContent);
            const prevDraw = parseFloat(prevRow.cells[2].textContent);
            const prevLose = parseFloat(prevRow.cells[3].textContent);

            // 比较并设置颜色
            // 上一行相比当前行(上一行是更新的时间点)
            if (prevWin > currentWin) {
                prevRow.cells[1].style.backgroundColor = '#fbb'; // 红色背景表示上涨
            } else if (prevWin < currentWin) {
                prevRow.cells[1].style.backgroundColor = '#bfb'; // 绿色背景表示下跌
            } else {
                prevRow.cells[1].style.backgroundColor = '';
            }

            if (prevDraw > currentDraw) {
                prevRow.cells[2].style.backgroundColor = '#fbb';
            } else if (prevDraw < currentDraw) {
                prevRow.cells[2].style.backgroundColor = '#bfb';
            } else {
                prevRow.cells[2].style.backgroundColor = '';
            }

            if (prevLose > currentLose) {
                prevRow.cells[3].style.backgroundColor = '#fbb';
            } else if (prevLose < currentLose) {
                prevRow.cells[3].style.backgroundColor = '#bfb';
            } else {
                prevRow.cells[3].style.backgroundColor = '';
            }
        }
    }

    // 启动脚本
    init();
})();