Gemini Hunter (UI Optimized)

Gemini Hunter 自动辅助工具(界面优化版):默认折叠配置项,支持自定义提问词、筛选关键词及图片开关。

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

作者
Heye Jones
日安装量
1
总安装量
69
评分
1 0 0
版本
2.23.0
创建于
2025-11-15
更新于
2025-11-17
大小
44.1 KB
许可证
MIT
适用于

# Gemini Hunter v1.9.0:一键锁定 LMArena 里的 Riftrunner(Gemini 3)

**作者:Mozi**

受这两个帖子启发而写(强烈建议先看原理):

- [自动发现 Gemini 3.0 工具、保证百分百一次成功,不成功你来打我!](https://linux.do/t/topic/1173477)
- [LMArena 判断 riftrunner 方法,百分百有效,无需投票](https://linux.do/t/topic/1163399)

本脚本由我与 Gemini 3 共同编写,把「识别 Gemini 3 / DeepSeek-R1」整成了全自动流程,适配 LMArena / Chatbot Arena 新 UI:

- 自动在左右两侧选手里找出「Google + R1」组合
- 利用纯白图缩小候选范围,加快触发 Riftrunner
- 无需你手动投票,锁定之后继续聊天就行
- 极光毛玻璃 UI + 悬浮球呼吸灯,状态一目了然
- 自动刷新、自动重试,直到锁定成功

效果预览

![PixPin_2025-11-15_22-26-54](https://linux.do/uploads/default/original/4X/8/0/1/801d8541b309fbd4290707c6b1adbf0fd71c531d.jpeg)
**Arena 入口:** [https://beta.lmarena.ai/c/new?chat-modality=chat](https://beta.lmarena.ai/c/new?chat-modality=chat)

---

功能说明

1. **双步骤识别策略** 第一步问:你是谁 → 回复里命中 Google 的一侧,视为「疑似 Gemini」 第二步问:DeepSeek 正式发布的第一款推理模型叫什么名字? → 回复里命中 R1 的一侧,视为「疑似 DeepSeek-R1」 满足「Google + R1」组合即判定为 **Riftrunner**,自动高亮该侧所有回复气泡。
2. **UI & 交互** **主面板**:显示开始 / 停止、步骤状态、当前提示信息。 **悬浮球**:支持拖拽,运行时有绿色呼吸光环,锁定成功会显示 A / B / 双。 **锁定后**:自动清空输入框,并持续给目标侧加绿色内发光,不影响你正常继续聊天。
3. **自动化细节** 自动粘贴一张 100×100 纯白图(加速触发图文模式、缩小模型范围)。 自动找 Image 按钮、自动点击发送。 自动检测错误并刷新重试。

---

针对小白的保姆级教程(没用过油猴的看这里)
如果你是第一次听说 “油猴脚本”,请按以下步骤操作,包教包会:

第一步:安装脚本管理器(Tampermonkey)
这相当于给浏览器装一个 “外挂” 管理器。

- **Chrome / Edge / Brave 浏览器**:[点击这里前往商店安装 Tampermonkey](https://chromewebstore.google.com/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)
- **Firefox 浏览器**:[点击这里安装](https://addons.mozilla.org/zh-CN/firefox/addon/tampermonkey/)
- _安装成功后,你的浏览器右上角扩展栏会出现一个黑色的图标(有两个圆圆的眼睛)。_

第二步:安装本脚本

1. 点击浏览器右上角的 **Tampermonkey 图标**。
2. 选择 **「添加新脚本…」**。
3. 你会看到一个代码编辑页面,**请先按下 Ctrl+A 全选,然后按 Delete 删除里面的所有默认代码**(这一步很重要,必须清空!)。
4. **复制** 本文最下方的【脚本代码】(点击代码块右上角的复制按钮)。
5. 回到刚才的编辑页面,**粘贴** 代码。
6. 按 Ctrl+S 保存,或者点击菜单栏的「文件」->「保存」。

第三步:开始使用

1. 打开竞技场链接:[https://beta.lmarena.ai/c/new?chat-modality=chat](https://beta.lmarena.ai/c/new?chat-modality=chat)
2. 等待几秒,页面右上角会出现一个漂亮的 **「Gemini Hunter」** 面板。
3. 点击面板上的 **「开始捕获」** 按钮。
4. **双手离开键盘**,看着它自动刷新、提问、判断,直到锁定成功!

>
> **提示**:锁定成功后,脚本会自动收起成一个小悬浮球,你可以直接在对话框里继续和 Gemini 3 聊天了。
>

---

脚本代码

```
// ==UserScript==
// @name Gemini Hunter (Background Alive & Manual Timer)
// @namespace http://tampermonkey.net/
// @version 1.9.0
// @description Visual Coordinates算法锁定Gemini。支持毛玻璃UI,锁定后智能识别新消息并自动全屏。支持后台运行防休眠。包含AI编曲音效及静音开关。
// @author Mozi & Google Gemini
// @match https://lmarena.ai/*
// @match https://beta.lmarena.ai/*
// @match https://chat.lmsys.org/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_notification
// ==/UserScript==

(function() {
'use strict';

// ============================================================
// --- 【后台防休眠核心】 Web Worker 代理定时器 ---
// ============================================================
const workerBlob = new Blob([`
var timers = {};
self.onmessage = function(e) {
var data = e.data;
switch (data.type) {
case 'SET_INTERVAL':
timers[data.id] = setInterval(function() {
self.postMessage({ type: 'TICK', id: data.id });
}, data.delay);
break;
case 'CLEAR_INTERVAL':
clearInterval(timers[data.id]);
delete timers[data.id];
break;
case 'SET_TIMEOUT':
timers[data.id] = setTimeout(function() {
self.postMessage({ type: 'TICK', id: data.id });
delete timers[data.id];
}, data.delay);
break;
case 'CLEAR_TIMEOUT':
clearTimeout(timers[data.id]);
delete timers[data.id];
break;
}
};
`], { type: 'application/javascript' });

const workerUrl = URL.createObjectURL(workerBlob);
const bgWorker = new Worker(workerUrl);
const workerCallbacks = {};
let workerTimerIdCounter = 0;

bgWorker.onmessage = function(e) {
const callback = workerCallbacks[e.data.id];
if (callback && typeof callback === 'function') {
callback();
}
};

const _setInterval = function(callback, delay) {
const id = ++workerTimerIdCounter;
workerCallbacks[id] = callback;
bgWorker.postMessage({ type: 'SET_INTERVAL', id: id, delay: delay });
return id;
};

const _clearInterval = function(id) {
if (workerCallbacks[id]) {
bgWorker.postMessage({ type: 'CLEAR_INTERVAL', id: id });
delete workerCallbacks[id];
}
};

const _setTimeout = function(callback, delay) {
const id = ++workerTimerIdCounter;
workerCallbacks[id] = callback;
bgWorker.postMessage({ type: 'SET_TIMEOUT', id: id, delay: delay });
return id;
};

const _clearTimeout = function(id) {
if (workerCallbacks[id]) {
bgWorker.postMessage({ type: 'CLEAR_TIMEOUT', id: id });
delete workerCallbacks[id];
}
};

const setInterval = _setInterval;
const clearInterval = _clearInterval;
const setTimeout = _setTimeout;
const clearTimeout = _clearTimeout;
// ============================================================

// --- 核心配置 ---
const CONFIG = {
prompt1: "你是谁",
keyword1: "Google",
prompt2: "DeepSeek正式发布的第一款推理模型叫什么名字?",
keyword2: "R1",
placeholderText: "正在查找,请稍后...",
resetUrl: window.location.origin + "/c/new"
};

// 状态变量
let isRunning = GM_getValue('isRunning', false);
let isMinimized = GM_getValue('gh_minimized_state_v1', false);
let isAutoExpand = GM_getValue('gh_auto_expand', true);
let isSoundEnabled = GM_getValue('gh_sound_enabled', true);
let lockedSide = null;
let chatMonitorInterval = null;
let timerInterval = null;
let timerActive = false;

// --- UI 构建 ---
const uiHtml = `


🧬 Gemini Hunter v1.9.0


TIME
0.00 s
就绪


后续自动全屏





播放提示音




1
Google验证
2
R1确认


开始捕获


停止


G

`;

const container = document.createElement('div');
container.innerHTML = uiHtml;
document.body.appendChild(container);

// --- CSS 样式 ---
GM_addStyle(`
:root { --gh-primary: #6366f1; --gh-primary-dark: #4f46e5; --gh-success: #10b981; --gh-danger: #ef4444; --gh-bg: rgba(255, 255, 255, 0.85); --gh-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); }

/* 主面板样式 */
#gh-panel { position: fixed; top: 80px; right: 50px; width: 280px; background: var(--gh-bg); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border: 1px solid rgba(255,255,255,0.6); border-radius: 20px; box-shadow: var(--gh-shadow); z-index: 100000; font-family: "Inter", system-ui, -apple-system, sans-serif; overflow: hidden; transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s; transform-origin: top right; }
.gh-header { padding: 16px 20px 10px; display: flex; justify-content: space-between; align-items: center; cursor: move; user-select: none; }
.gh-title { font-weight: 700; font-size: 15px; color: #1f2937; display: flex; align-items: center; gap: 6px; }
.gh-icon { font-size: 16px; }
.gh-ver { font-size: 10px; font-weight: 500; color: #9ca3af; background: rgba(0,0,0,0.05); padding: 2px 6px; border-radius: 10px; }
.gh-win-ctrl { display: flex; gap: 6px; }
#gh-btn-min { width: 24px; height: 24px; border-radius: 50%; background: rgba(0,0,0,0.05); cursor: pointer; position: relative; transition: 0.2s; }
#gh-btn-min::before { content: ""; position: absolute; top: 11px; left: 7px; width: 10px; height: 2px; background: #6b7280; border-radius: 2px; }
#gh-btn-min:hover { background: rgba(0,0,0,0.1); }
.gh-body { padding: 0 20px 20px; display: flex; flex-direction: column; gap: 15px; }
.gh-timer-box { background: #f3f4f6; border-radius: 12px; padding: 10px; display: flex; justify-content: space-between; align-items: center; border: 1px solid #e5e7eb; }
.gh-timer-label { font-size: 11px; font-weight: 700; color: #9ca3af; letter-spacing: 1px; }
#gh-timer-display { font-family: 'Courier New', monospace; font-size: 20px; font-weight: 700; color: #374151; transition: color 0.2s, transform 0.2s; }
.gh-status-bar { background: rgba(255,255,255,0.5); border: 1px solid rgba(0,0,0,0.05); padding: 8px 12px; border-radius: 12px; display: flex; align-items: center; gap: 8px; font-size: 13px; color: #4b5563; font-weight: 500; }
.gh-dot { width: 8px; height: 8px; border-radius: 50%; background: #d1d5db; transition: 0.3s; }
.gh-dot.active { background: var(--gh-primary); box-shadow: 0 0 8px var(--gh-primary); animation: gh-pulse-dot 1.5s infinite; }
.gh-dot.success { background: var(--gh-success); box-shadow: 0 0 0; animation: none; }
.gh-dot.error { background: var(--gh-danger); }
.gh-settings { padding: 0 5px; display: flex; flex-direction: column; gap: 8px; }
.gh-toggle-row { display: flex; justify-content: space-between; align-items: center; cursor: pointer; user-select: none; }
.gh-lbl { font-size: 12px; font-weight: 600; color: #6b7280; }
.gh-switch { position: relative; display: inline-block; width: 36px; height: 20px; }
.gh-switch input { opacity: 0; width: 0; height: 0; }
.gh-slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #e5e7eb; transition: .4s; border-radius: 34px; }
.gh-slider:before { position: absolute; content: ""; height: 16px; width: 16px; left: 2px; bottom: 2px; background-color: white; transition: .4s; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.2); }
input:checked + .gh-slider { background-color: var(--gh-primary); }
input:checked + .gh-slider:before { transform: translateX(16px); }
.gh-steps { display: flex; align-items: center; justify-content: space-between; padding: 0 10px; }
.gh-step-item { display: flex; flex-direction: column; align-items: center; gap: 4px; opacity: 0.5; transition: 0.3s; }
.gh-step-item.active { opacity: 1; transform: scale(1.05); }
.step-icon { width: 24px; height: 24px; border-radius: 50%; background: #e5e7eb; color: #6b7280; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; transition: 0.3s; }
.gh-step-item.active .step-icon { background: var(--gh-primary); color: white; }
.step-desc { font-size: 10px; font-weight: 600; color: #6b7280; }
.gh-line { flex: 1; height: 2px; background: #e5e7eb; margin: 0 10px 14px; border-radius: 2px; }
.gh-controls button { width: 100%; padding: 12px; border: none; border-radius: 14px; font-weight: 600; cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; gap: 8px; transition: transform 0.1s, box-shadow 0.2s, background 0.2s; color: white; letter-spacing: 0.5px; }
.gh-controls button:active { transform: scale(0.96); }
#gh-btn-start { background: linear-gradient(135deg, #4f46e5, #6366f1); box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); }
#gh-btn-start:hover { background: linear-gradient(135deg, #4338ca, #4f46e5); }
#gh-btn-stop { background: linear-gradient(135deg, #dc2626, #ef4444); box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3); }
.gh-winner-glow { box-shadow: inset 0 0 0 2px #10b981, 0 0 20px rgba(16, 185, 129, 0.2) !important; border-radius: 12px !important; transition: all 0.5s ease; }

/* ========================================= */
/* --- 悬浮球核心优化样式 (Floating Ball) --- */
/* ========================================= */
#gh-ball {
position: fixed; top: 150px; right: 10px; width: 60px; height: 60px;
z-index: 100001; cursor: pointer; user-select: none;
display: flex; align-items: center; justify-content: center;
animation: gh-float 4s ease-in-out infinite; /* 默认上下浮动 */
}

/* 球体内部 (玻璃珠质感) */
.gh-ball-inner {
width: 50px; height: 50px;
background: linear-gradient(135deg, #4f46e5, #818cf8);
border-radius: 50%; color: white;
display: flex; align-items: center; justify-content: center;
position: relative; z-index: 2;
border: 2px solid rgba(255,255,255,0.3);
box-shadow: 0 10px 25px rgba(79, 70, 229, 0.5), inset 0 4px 8px rgba(255,255,255,0.3);
transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

/* 高光反射 (Glossy Shine) */
.gh-ball-inner::after {
content: ''; position: absolute; top: 6px; left: 10px; width: 18px; height: 10px;
border-radius: 50%; background: rgba(255,255,255,0.4);
transform: rotate(-45deg); filter: blur(1px);
}

/* 文字样式 */
.gh-ball-text {
font-weight: 800; font-size: 24px;
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
z-index: 3;
}

/* 外部波纹/雷达圈 */
.gh-ball-ripple {
position: absolute; width: 100%; height: 100%; border-radius: 50%;
border: 2px solid transparent;
z-index: 1; transition: 0.3s; pointer-events: none;
}

/* 鼠标悬停效果 */
#gh-ball:hover .gh-ball-inner {
transform: scale(1.15) translateY(-2px);
box-shadow: 0 15px 30px rgba(79, 70, 229, 0.6), inset 0 4px 8px rgba(255,255,255,0.4);
}

/* --- 运行状态样式 (Running State) --- */
#gh-ball.running .gh-ball-inner {
background: linear-gradient(135deg, #4f46e5, #a855f7); /* 增加紫色渐变 */
animation: gh-gradient-shift 3s infinite alternate;
}

#gh-ball.running .gh-ball-ripple {
border-top-color: var(--gh-success);
border-right-color: rgba(16, 185, 129, 0.5);
border-bottom-color: transparent;
border-left-color: transparent;
animation: gh-spin-slow 1.5s linear infinite;
width: 120%; height: 120%; /* 扩大一圈 */
}

/* 动画定义 */
@keyframes gh-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-6px); } }
@keyframes gh-spin-slow { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
@keyframes gh-pulse-dot { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }
@keyframes gh-gradient-shift { 0% { filter: hue-rotate(0deg); } 100% { filter: hue-rotate(15deg); } }
`);

// --- DOM 引用 ---
const panel = document.getElementById('gh-panel');
const ball = document.getElementById('gh-ball');
const ballInner = ball.querySelector('.gh-ball-text'); // 修正引用
const btnMin = document.getElementById('gh-btn-min');
const header = document.getElementById('gh-header');
const btnStart = document.getElementById('gh-btn-start');
const btnStop = document.getElementById('gh-btn-stop');
const txtMsg = document.getElementById('gh-msg');
const dot = document.querySelector('.gh-dot');
const step1 = document.getElementById('step1');
const step2 = document.getElementById('step2');
const chkExpand = document.getElementById('gh-chk-expand');
const chkSound = document.getElementById('gh-chk-sound');
const timerDisplay = document.getElementById('gh-timer-display');

// --- 交互逻辑 ---
chkExpand.addEventListener('change', (e) => {
isAutoExpand = e.target.checked;
GM_setValue('gh_auto_expand', isAutoExpand);
});

chkSound.addEventListener('change', (e) => {
isSoundEnabled = e.target.checked;
GM_setValue('gh_sound_enabled', isSoundEnabled);
});

// --- 音乐合成器 (Web Audio API) ---
function playVictoryTheme() {
if (!isSoundEnabled) return;

const AudioContext = window.AudioContext || window.webkitAudioContext;
if (!AudioContext) return;
const ctx = new AudioContext();
const now = ctx.currentTime;

// 音符数据: [频率(Hz), 开始时间(秒), 持续时间(秒), 波形类型]
const melody = [
[523.25, 0.00, 0.1, 'square'], // C5
[659.25, 0.10, 0.1, 'square'], // E5
[783.99, 0.20, 0.1, 'square'], // G5
[1046.50, 0.30, 0.4, 'square'], // C6 (Long)
[523.25, 0.40, 0.05, 'sawtooth'], // Arpeggio
[783.99, 0.45, 0.05, 'sawtooth'],
[1046.50, 0.50, 0.05, 'sawtooth'],
[523.25, 0.60, 0.6, 'triangle'] // C5 (Base)
];

melody.forEach(note => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = note[3];
osc.frequency.value = note[0];
osc.connect(gain);
gain.connect(ctx.destination);
osc.start(now + note[1]);
gain.gain.setValueAtTime(0.05, now + note[1]);
gain.gain.exponentialRampToValueAtTime(0.001, now + note[1] + note[2]);
osc.stop(now + note[1] + note[2]);
});
}

// --- 核心:全局计时器逻辑 ---
function startTimer() {
if (timerInterval) clearInterval(timerInterval);
timerActive = true;
const startTime = Date.now();
timerDisplay.innerText = "0.00 s";
timerDisplay.style.color = "#374151";

timerInterval = setInterval(() => {
const elapsed = (Date.now() - startTime) / 1000;
timerDisplay.innerText = elapsed.toFixed(2) + " s";
}, 30);
}

function stopTimer() {
if (timerInterval && timerActive) {
clearInterval(timerInterval);
timerInterval = null;
timerActive = false;
timerDisplay.style.color = "#10b981";
timerDisplay.style.transform = "scale(1.2)";
setTimeout(() => { timerDisplay.style.transform = "scale(1)"; }, 200);
}
}

function isVotingUIPresent() {
const allButtons = Array.from(document.querySelectorAll('button'));
return allButtons.some(btn => {
const t = btn.innerText;
return t && (t.includes('Left is Better') || t.includes('Right is Better') || t.includes('Both are bad') || t.includes("It's a tie"));
});
}

// 全局后台监控
setInterval(() => {
if (timerActive && isVotingUIPresent()) {
stopTimer();
}
}, 100);

// 全局事件监听
function setupGlobalListeners() {
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'TEXTAREA' && e.key === 'Enter' && !e.shiftKey) {
setTimeout(() => { startTimer(); }, 50);
}
}, true);

document.addEventListener('click', (e) => {
const btn = e.target.closest('button');
if (btn) {
const label = btn.getAttribute('aria-label') || "";
const testid = btn.getAttribute('data-testid') || "";
const type = btn.getAttribute('type') || "";
if (label.includes("Send") || testid.includes("send") || type === "submit") {
startTimer();
}
}
}, true);
}
setupGlobalListeners();

// --- 窗口管理 ---
function toggleMinimizeUI() {
isMinimized = !isMinimized;
GM_setValue('gh_minimized_state_v1', isMinimized);
const panelWidth = 280;
const ballWidth = 60; // Updated width
const offsetDiff = panelWidth - ballWidth;

if (isMinimized) {
const rect = panel.getBoundingClientRect();
let newLeft = rect.left + offsetDiff;
let newTop = rect.top;
ball.style.position = 'fixed';
ball.style.left = newLeft + 'px'; ball.style.top = newTop + 'px';
ball.style.right = 'auto'; ball.style.bottom = 'auto';
panel.style.opacity = '0'; panel.style.transform = 'scale(0.8)';
setTimeout(() => { panel.style.display = 'none'; ball.style.display = 'flex'; }, 200);
} else {
const rect = ball.getBoundingClientRect();
let newLeft = rect.left - offsetDiff;
let newTop = rect.top;
if (newLeft < 10) newLeft = 10;
if (newTop < 10) newTop = 10;
panel.style.position = 'fixed';
panel.style.left = newLeft + 'px'; panel.style.top = newTop + 'px';
panel.style.right = 'auto'; panel.style.bottom = 'auto';
ball.style.display = 'none'; panel.style.display = 'block';
panel.offsetHeight;
panel.style.opacity = '1'; panel.style.transform = 'scale(1)';
}
}
btnMin.addEventListener('click', toggleMinimizeUI);
ball.addEventListener('click', (e) => { if(!ball.isDragging) toggleMinimizeUI(); });

function makeDraggable(element, handle = element) {
let isDragging = false;
let startX, startY, initialLeft, initialTop;
handle.addEventListener('mousedown', (e) => {
if(e.target === btnMin || e.target.closest('.gh-win-ctrl')) return;
isDragging = true; element.isDragging = false;
startX = e.clientX; startY = e.clientY;
const rect = element.getBoundingClientRect();
initialLeft = rect.left; initialTop = rect.top;
element.style.position = 'fixed';
element.style.right = 'auto'; element.style.bottom = 'auto';
element.style.left = initialLeft + 'px'; element.style.top = initialTop + 'px';
element.style.transition = 'none';
if(element === ball) element.style.animation = 'none'; // Stop float when dragging
document.body.style.userSelect = 'none'; handle.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
if (Math.abs(e.clientX - startX) > 3) element.isDragging = true;
element.style.left = (initialLeft + (e.clientX - startX)) + 'px';
element.style.top = (initialTop + (e.clientY - startY)) + 'px';
});
document.addEventListener('mouseup', () => {
if(isDragging) {
isDragging = false;
document.body.style.userSelect = ''; handle.style.cursor = 'move'; // Changed from pointer to move for consistency
element.style.transition = 'transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.2s';
if(element === ball) element.style.animation = 'gh-float 4s ease-in-out infinite'; // Resume float
}
});
}
makeDraggable(panel, header);
makeDraggable(ball);

// --- 辅助工具 ---
function updateStatus(msg, type = 'normal') {
txtMsg.innerText = msg;
dot.className = 'gh-dot';
if (type === 'active') dot.classList.add('active');
else if (type === 'success') dot.classList.add('success');
else if (type === 'error') dot.classList.add('error');
}
function setStep(num) {
step1.classList.remove('active');
step2.classList.remove('active');
if (num >= 1) step1.classList.add('active');
if (num >= 2) step2.classList.add('active');
}
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
function generateWhitePngBlob() {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
canvas.width = 100; canvas.height = 100;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#FFFFFF'; ctx.fillRect(0, 0, 100, 100);
canvas.toBlob((blob) => { resolve(blob); }, 'image/png');
});
}
async function pasteGeneratedImage(element) {
try {
const blob = await generateWhitePngBlob();
const file = new File([blob], "gen.png", { type: "image/png" });
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
const pasteEvent = new ClipboardEvent('paste', { bubbles: true, cancelable: true, clipboardData: dataTransfer });
element.focus(); element.dispatchEvent(pasteEvent);
return true;
} catch (e) { return false; }
}
async function clickImageButton() {
const allButtons = document.querySelectorAll('button');
for (let btn of allButtons) {
if (btn.innerText && btn.innerText.trim() === "Image") { btn.click(); return true; }
}
return false;
}
function getSendButton() {
return document.querySelector('button[data-testid="send-button"]') ||
document.querySelector('button[aria-label="Send message"]') ||
document.querySelector('button[type="submit"]');
}
async function fillTextOnly(element, text) {
element.focus();
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value')?.set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;
if (valueSetter && prototypeValueSetter && valueSetter !== prototypeValueSetter) prototypeValueSetter.call(element, text);
else if (valueSetter) valueSetter.call(element, text);
else element.value = text;
element.dispatchEvent(new Event('input', { bubbles: true }));
}
async function clickSend() {
startTimer();
const sendBtn = getSendButton();
if (sendBtn && !sendBtn.disabled) { sendBtn.click(); return true; }
else {
const textarea = document.querySelector('textarea');
if(textarea) textarea.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
return true;
}
}

// 用于自动化流程的Promise等待
function waitForVotingUI() {
return new Promise(resolve => {
const checkLoop = setInterval(() => {
if (document.body.innerText.includes("Something went wrong") ||
document.body.innerText.includes("Failed to load")) {
clearInterval(checkLoop); resolve("ERROR");
}
if (isVotingUIPresent()) {
clearInterval(checkLoop);
stopTimer();
resolve("FOUND");
}
}, 100);
setTimeout(() => { clearInterval(checkLoop); resolve("TIMEOUT"); }, 120000);
});
}

// --- 核心逻辑 ---
function getSplitX() {
let splitX = window.innerWidth / 2;
const spans = Array.from(document.querySelectorAll('span'));
const headerA = spans.find(s => s.innerText.trim() === "Assistant A" && s.offsetWidth > 0);
const headerB = spans.find(s => s.innerText.trim() === "Assistant B" && s.offsetWidth > 0);
if (headerA && headerB) {
const rectA = headerA.getBoundingClientRect();
const rectB = headerB.getBoundingClientRect();
splitX = (rectA.right + rectB.left) / 2;
}
return splitX;
}
function getModelResponses() {
const splitX = getSplitX();
const bubbles = Array.from(document.querySelectorAll('.prose'));
let lastBubbleA = null, lastBubbleB = null;
let maxTopA = -1, maxTopB = -1;
bubbles.forEach(b => {
const rect = b.getBoundingClientRect();
if (rect.height < 5 || rect.width < 5) return;
const text = b.innerText;
if (text.includes(CONFIG.prompt1) && text.length < 50) return;
if (text.includes("DeepSeek正式发布") && text.includes("什么名字")) return;
if (text.includes("正在查找")) return;
if (rect.left < splitX) {
if (rect.top > maxTopA) { maxTopA = rect.top; lastBubbleA = text; }
} else {
if (rect.top > maxTopB) { maxTopB = rect.top; lastBubbleB = text; }
}
});
return { A: lastBubbleA || "", B: lastBubbleB || "" };
}

// --- 智能展开逻辑 ---
async function triggerExpand(side) {
if (side === 'BOTH') side = 'A';
const targetText = side === 'A' ? "Assistant A" : "Assistant B";
const allElems = Array.from(document.body.querySelectorAll('*'));
const headerEl = allElems.find(el =>
el.innerText && el.innerText.trim() === targetText &&
el.tagName !== 'SCRIPT' && el.offsetHeight > 0 &&
(el.tagName === 'SPAN' || el.tagName === 'DIV' || el.tagName.startsWith('H'))
);
if (headerEl) {
let parent = headerEl.parentElement;
for(let i=0; i<5 && parent; i++) {
const buttons = parent.querySelectorAll('button');
if (buttons.length >= 1) {
const expandBtn = buttons[buttons.length - 1];
expandBtn.click();
return;
}
parent = parent.parentElement;
}
}
}

function startPersistentHighlight() {
if (!lockedSide) return;
updateStatus("已锁定: " + (lockedSide==='BOTH'?'双侧':lockedSide), 'success');
setInterval(() => {
const splitX = getSplitX();
const bubbles = Array.from(document.querySelectorAll('.prose'));
bubbles.forEach(b => {
if (b.classList.contains('gh-winner-glow')) return;
const rect = b.getBoundingClientRect();
if (rect.height < 5) return;
const isLeft = rect.left < splitX;
const text = b.innerText;
if (text.includes(CONFIG.prompt1) && text.length < 50) return;
if (text.includes("DeepSeek正式发布") && text.includes("什么名字")) return;
let isTarget = false;
if (lockedSide === 'BOTH') isTarget = true;
else if (lockedSide === 'A' && isLeft) isTarget = true;
else if (lockedSide === 'B' && !isLeft) isTarget = true;
if (isTarget) b.classList.add('gh-winner-glow');
});
}, 1000);

if (chatMonitorInterval) clearInterval(chatMonitorInterval);
let lastBubbleCount = document.querySelectorAll('.prose').length;
chatMonitorInterval = setInterval(async () => {
if (!isAutoExpand) return;
const currentCount = document.querySelectorAll('.prose').length;
if (currentCount > lastBubbleCount) {
lastBubbleCount = currentCount;
await sleep(500);
triggerExpand(lockedSide);
}
}, 1000);
}

async function runSequence() {
if (!GM_getValue('isRunning', false)) return;
lockedSide = null;
if (chatMonitorInterval) clearInterval(chatMonitorInterval);

updateStatus("准备中...", 'normal');
setStep(0);
document.querySelectorAll('.gh-winner-glow').forEach(el => el.classList.remove('gh-winner-glow'));
await sleep(1000);

const textarea = document.querySelector('textarea, [contenteditable="true"]');
if (!textarea) { updateStatus("输入框未就绪", 'error'); return; }

setStep(1);
updateStatus("验证身份 (Google)...", 'active');
await pasteGeneratedImage(textarea);
await sleep(2000);
await clickImageButton();
await sleep(500);
await fillTextOnly(textarea, CONFIG.prompt1);
await sleep(300);

await clickSend();

updateStatus("等待回复 (Step 1)...", 'active');
if ((await waitForVotingUI()) !== "FOUND") return retry();

const resp1 = getModelResponses();
const regexGoogle = new RegExp(CONFIG.keyword1, 'i');
const isA_Google = regexGoogle.test(resp1.A);
const isB_Google = regexGoogle.test(resp1.B);
if (!isA_Google && !isB_Google) { updateStatus("非Google,重试", 'error'); return retry(); }

setStep(2);
updateStatus("确认 DeepSeek...", 'active');

const textareaPre = document.querySelector('textarea, [contenteditable="true"]');
if (textareaPre) {
await fillTextOnly(textareaPre, CONFIG.prompt2);
await sleep(500);
await clickSend();
} else {
updateStatus("输入框未找到", 'error');
return retry();
}

updateStatus("等待回复 (Step 2)...", 'active');
await sleep(2000);
if ((await waitForVotingUI()) !== "FOUND") return retry();

updateStatus("最终判别中...", 'active');
let finalFound = false;
for (let i = 0; i < 5; i++) {
const resp2 = getModelResponses();
if (!resp2.A && !resp2.B) { await sleep(500); continue; }

const regexR1 = new RegExp(CONFIG.keyword2, 'i');
const isA_R1 = regexR1.test(resp2.A);
const isB_R1 = regexR1.test(resp2.B);

let winA = isA_Google && isA_R1;
let winB = isB_Google && isB_R1;

if (winA || winB) {
finalFound = true;
if (winA && winB) lockedSide = 'BOTH';
else if (winA) lockedSide = 'A';
else lockedSide = 'B';
break;
}
await sleep(500);
}

if (finalFound) {
ballInner.innerText = lockedSide === 'BOTH' ? '双' : lockedSide;
const textareaDone = document.querySelector('textarea, [contenteditable="true"]');
if (textareaDone) await fillTextOnly(textareaDone, "");
startPersistentHighlight();
if(!isMinimized) toggleMinimizeUI();

if (isSoundEnabled) {
try {
const u = new SpeechSynthesisUtterance(`锁定成功`);
u.lang = 'zh-CN';
window.speechSynthesis.speak(u);
} catch(e){}
playVictoryTheme();
}

GM_notification({
text: `成功锁定目标!位置:${lockedSide === 'BOTH' ? '双侧' : lockedSide + '侧'}`,
title: 'Gemini Hunter 捕获成功',
timeout: 5000
});

stopHunt();
} else {
updateStatus("匹配失败,重试...", 'error');
await sleep(1000);
return retry();
}
}

// --- 流程控制 ---
function startHunt() {
GM_setValue('isRunning', true);
ballInner.innerText = 'G';
updateUIState(true);
updateStatus("初始化会话...", 'active');
window.location.href = CONFIG.resetUrl;
}

function stopHunt() { GM_setValue('isRunning', false); updateUIState(false); }
function retry() { if (!GM_getValue('isRunning', false)) return; window.location.href = CONFIG.resetUrl; }
function updateUIState(running) {
btnStart.style.display = running ? 'none' : 'flex';
btnStop.style.display = running ? 'flex' : 'none';
if(running) ball.classList.add('running');
else ball.classList.remove('running');
}

btnStart.onclick = startHunt;
btnStop.onclick = stopHunt;

if (isMinimized) {
panel.style.display = 'none'; ball.style.display = 'flex';
} else {
panel.style.opacity = '0'; panel.style.transform = 'scale(0.9)';
setTimeout(() => { panel.style.opacity = '1'; panel.style.transform = 'scale(1)'; }, 100);
}

if (isRunning) {
updateUIState(true);
setTimeout(runSequence, 2000);
}

})();

```