您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Summarizes strategy sections using AI and shows cat recommendations
// ==UserScript== // @name Battle Cats Wiki - Strategy Summarizer // @namespace https://battlecats.miraheze.org/ // @version 1.0 // @description Summarizes strategy sections using AI and shows cat recommendations // @author ProfessionalScriptKiddie // @license MIT // @match https://battlecats.miraheze.org/wiki/* // @icon https://files.catbox.moe/7srjk6.png // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @run-at document-idle // ==/UserScript== function waitForStrategySection(callback) { const check = () => { const el = document.querySelector('.citizen-section#citizen-section-2'); if (el) { callback(); return true; } return false; }; if (check()) return; const observer = new MutationObserver(() => { if (check()) observer.disconnect(); }); observer.observe(document.body, { childList: true, subtree: true }); setTimeout(() => { if (!check()) observer.disconnect(); }, 5000); } function renderMarkdown(text) { return text .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') .replace(/^[•\-\*] (.*?)$/gm, '<li style="margin: 4px 0; color: #fff; list-style-type: none; position: relative; padding-left: 20px;">$1</li>') .replace(/\*(.*?)\*/g, '<em>$1</em>') .replace(/^### (.*?)$/gm, '<h3 style="color: #4a9eff; margin: 12px 0 8px 0; font-size: 16px;">$1</h3>') .replace(/^## (.*?)$/gm, '<h2 style="color: #4a9eff; margin: 16px 0 12px 0; font-size: 18px;">$1</h2>') .replace(/^# (.*?)$/gm, '<h1 style="color: #4a9eff; margin: 20px 0 16px 0; font-size: 20px;">$1</h1>') .replace(/(<li.*?<\/li>(?:\s*<li.*?<\/li>)*)/gs, '<ul style="margin: 8px 0; padding-left: 0; list-style: none;">$1</ul>') .replace(/^\d+\. (.*?)$/gm, '<li style="margin: 4px 0; color: #fff;">$1</li>') .replace(/(<li.*?<\/li>(?:\s*<li.*?<\/li>)*)/gs, '<ol style="margin: 8px 0; padding-left: 20px;">$1</ol>') .replace(/\n\n/g, '<br><br>') .replace(/\n/g, '<br>') .replace(/<li style="margin: 4px 0; color: #fff; list-style-type: none; position: relative; padding-left: 20px;">/g, '<li style="margin: 4px 0; color: #fff; list-style-type: none; position: relative; padding-left: 20px;">' + '<span style="position: absolute; left: 0; color: #4a9eff; font-weight: bold;">•</span>'); } async function summarizeWithGemini(text, apiKey) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'POST', url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${apiKey}`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ contents: [{ parts: [{ text: `Please summarize this Battle Cats strategy guide. Focus on: 1. What cats/units to bring (be specific about names) 2. Key tactics and timing 3. Important warnings or things to avoid 4. Overall difficulty level Keep it concise but actionable. Use bullet points for clarity. Strategy text: ${text}` }] }], generationConfig: { temperature: 0.3, maxOutputTokens: 500 } }), onload: function(response) { try { if (response.status === 200) { const data = JSON.parse(response.responseText); if (data.candidates && data.candidates[0] && data.candidates[0].content) { resolve(data.candidates[0].content.parts[0].text); } else { reject(new Error('Invalid response format from Gemini API')); } } else { const error = JSON.parse(response.responseText); reject(new Error(error.error?.message || `API Error: ${response.status}`)); } } catch (e) { reject(new Error(`Failed to parse response: ${e.message}`)); } }, onerror: function(error) { reject(new Error(`Request failed: ${error.error || 'Network error'}`)); }, ontimeout: function() { reject(new Error('Request timed out')); }, timeout: 30000 }); }); } function createApiKeyModal() { const modal = document.createElement('div'); modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); z-index: 10000; display: flex; align-items: center; justify-content: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; `; const content = document.createElement('div'); content.style.cssText = ` background: #2a2a2a; padding: 24px; border-radius: 12px; max-width: 500px; width: 90%; color: #fff; border: 2px solid #555; `; content.innerHTML = ` <h2 style="margin: 0 0 16px 0; color: #fff;">🤖 Setup AI Strategy Summarizer</h2> <p style="margin-bottom: 16px; color: #ccc; line-height: 1.5;"> To get AI-powered strategy summaries, you'll need a free Google Gemini API key: </p> <ol style="color: #ccc; line-height: 1.6; margin-bottom: 20px; padding-left: 20px;"> <li>Go to <a href="https://aistudio.google.com/app/apikey" target="_blank" style="color: #4a9eff;">aistudio.google.com/app/apikey</a></li> <li>Sign in with your Google account</li> <li>Click "Create API Key" (free tier included)</li> <li>Copy the key and paste it below</li> </ol> <p style="margin-bottom: 12px; color: #ffeb3b; font-size: 14px;"> ⚠️ Your API key is stored locally and only used for Gemini requests </p> <input type="password" id="api-key-input" placeholder="AIza..." style=" width: 100%; padding: 12px; margin-bottom: 16px; background: #333; border: 1px solid #555; border-radius: 6px; color: #fff; font-family: monospace; box-sizing: border-box; "> <div style="display: flex; gap: 12px; justify-content: flex-end;"> <button id="cancel-btn" style=" padding: 8px 16px; background: #555; border: none; border-radius: 6px; color: #fff; cursor: pointer; transition: background 0.2s; ">Skip (use basic mode)</button> <button id="save-btn" style=" padding: 8px 16px; background: #4a9eff; border: none; border-radius: 6px; color: #fff; cursor: pointer; transition: background 0.2s; ">Save & Continue</button> </div> `; modal.appendChild(content); document.body.appendChild(modal); return new Promise((resolve) => { const input = content.querySelector('#api-key-input'); const saveBtn = content.querySelector('#save-btn'); const cancelBtn = content.querySelector('#cancel-btn'); saveBtn.addEventListener('mouseenter', () => saveBtn.style.background = '#357abd'); saveBtn.addEventListener('mouseleave', () => saveBtn.style.background = '#4a9eff'); cancelBtn.addEventListener('mouseenter', () => cancelBtn.style.background = '#666'); cancelBtn.addEventListener('mouseleave', () => cancelBtn.style.background = '#555'); saveBtn.addEventListener('click', () => { const key = input.value.trim(); if (key && key.startsWith('AIza')) { GM_setValue('gemini_api_key', key); modal.remove(); resolve(key); } else { input.style.borderColor = '#ff4444'; input.placeholder = 'Please enter a valid Gemini API key (starts with AIza)'; } }); cancelBtn.addEventListener('click', () => { modal.remove(); resolve(null); }); input.addEventListener('input', () => { input.style.borderColor = '#555'; }); }); } waitForStrategySection(async () => { const strategySection = document.querySelector('.citizen-section#citizen-section-2'); if (!strategySection) return; let apiKey = GM_getValue('gemini_api_key', ''); if (!apiKey) { apiKey = await createApiKeyModal(); } const strategyText = strategySection.innerText.trim(); const hasStrategyContent = strategyText.length > 50; const catLinks = Array.from(strategySection.querySelectorAll('a')) .filter(link => { const href = link.getAttribute('href'); const text = link.textContent.trim(); return (href && href.includes('Cat')) || text.endsWith('Cat'); }) .map(link => ({ text: link.textContent.trim(), href: link.href })) .filter((link, index, self) => index === self.findIndex(l => l.text === link.text) ); const popup = document.createElement('div'); popup.id = 'strategy-popup'; popup.style.cssText = ` position: fixed; top: 20px; right: 20px; width: 350px; min-width: 300px; max-height: 500px; min-height: 200px; background: #2a2a2a; border: 2px solid #555; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); z-index: 9999; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; overflow: hidden; transition: opacity 0.3s ease; resize: both; `; const header = document.createElement('div'); header.style.cssText = ` background: #444; padding: 12px 16px; border-bottom: 1px solid #555; display: flex; justify-content: space-between; align-items: center; `; const title = document.createElement('h3'); title.innerHTML = apiKey ? '🤖 AI Strategy Summary' : '🐱 What to bring?'; title.style.cssText = ` margin: 0; color: #fff; font-size: 16px; font-weight: 600; `; const headerButtons = document.createElement('div'); headerButtons.style.cssText = 'display: flex; gap: 8px; align-items: center;'; const settingsBtn = document.createElement('button'); settingsBtn.innerHTML = '⚙️'; settingsBtn.title = 'Change API Key'; settingsBtn.style.cssText = ` background: none; border: none; color: #ccc; font-size: 16px; cursor: pointer; padding: 4px; border-radius: 4px; transition: background 0.2s; `; settingsBtn.addEventListener('click', async () => { const newKey = await createApiKeyModal(); if (newKey !== null) { location.reload(); } }); const closeBtn = document.createElement('button'); closeBtn.textContent = '×'; closeBtn.style.cssText = ` background: none; border: none; color: #ccc; font-size: 20px; cursor: pointer; padding: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: background 0.2s, color 0.2s; `; [settingsBtn, closeBtn].forEach(btn => { btn.addEventListener('mouseenter', () => { btn.style.background = '#555'; btn.style.color = '#fff'; }); btn.addEventListener('mouseleave', () => { btn.style.background = 'none'; btn.style.color = '#ccc'; }); }); closeBtn.addEventListener('click', () => { popup.style.opacity = '0'; setTimeout(() => popup.remove(), 300); }); headerButtons.appendChild(settingsBtn); headerButtons.appendChild(closeBtn); header.appendChild(title); header.appendChild(headerButtons); const content = document.createElement('div'); content.style.cssText = ` max-height: calc(100% - 50px); overflow-y: auto; padding: 16px; color: #fff; line-height: 1.5; `; if (apiKey && hasStrategyContent && strategyText) { content.innerHTML = '<div style="text-align: center; padding: 20px; color: #ccc;">🤖 Generating AI summary...</div>'; try { const summary = await summarizeWithGemini(strategyText, apiKey); const renderedSummary = renderMarkdown(summary); content.innerHTML = `<div>${renderedSummary}</div>`; if (catLinks.length > 0) { const linksSection = document.createElement('div'); linksSection.style.cssText = 'margin-top: 20px; padding-top: 16px; border-top: 1px solid #555;'; linksSection.innerHTML = '<h4 style="margin: 0 0 8px 0; color: #4a9eff;">🔗 Quick Cat Links:</h4>'; catLinks.forEach(catLink => { const linkElement = document.createElement('a'); linkElement.href = catLink.href; linkElement.textContent = catLink.text; linkElement.style.cssText = ` display: inline-block; margin: 4px 8px 4px 0; padding: 4px 8px; background: #333; color: #fff; text-decoration: none; border-radius: 4px; font-size: 12px; border: 1px solid #555; transition: all 0.2s ease; `; linkElement.addEventListener('mouseenter', () => { linkElement.style.background = '#444'; linkElement.style.borderColor = '#666'; }); linkElement.addEventListener('mouseleave', () => { linkElement.style.background = '#333'; linkElement.style.borderColor = '#555'; }); linksSection.appendChild(linkElement); }); content.appendChild(linksSection); } } catch (error) { content.innerHTML = ` <div style="color: #ff6b6b; margin-bottom: 12px;">❌ AI Summary failed: ${error.message}</div> <div style="color: #ccc; margin-bottom: 16px;">Falling back to basic mode...</div> `; if (catLinks.length > 0) { catLinks.forEach(catLink => { const linkElement = document.createElement('a'); linkElement.href = catLink.href; linkElement.textContent = catLink.text; linkElement.style.cssText = ` display: block; padding: 8px 12px; margin: 4px 0; background: #333; color: #fff; text-decoration: none; border-radius: 4px; border: 1px solid #555; transition: all 0.2s ease; font-size: 14px; `; linkElement.addEventListener('mouseenter', () => { linkElement.style.background = '#444'; linkElement.style.transform = 'translateX(2px)'; }); linkElement.addEventListener('mouseleave', () => { linkElement.style.background = '#333'; linkElement.style.transform = 'translateX(0)'; }); content.appendChild(linkElement); }); } else { content.innerHTML += '<div style="color: #999; text-align: center; padding: 20px; font-style: italic;">📝 There is no strategy guide for this page yet.</div>'; } } } else { if (catLinks.length > 0) { catLinks.forEach(catLink => { const linkElement = document.createElement('a'); linkElement.href = catLink.href; linkElement.textContent = catLink.text; linkElement.style.cssText = ` display: block; padding: 8px 12px; margin: 4px 0; background: #333; color: #fff; text-decoration: none; border-radius: 4px; border: 1px solid #555; transition: all 0.2s ease; font-size: 14px; `; linkElement.addEventListener('mouseenter', () => { linkElement.style.background = '#444'; linkElement.style.transform = 'translateX(2px)'; }); linkElement.addEventListener('mouseleave', () => { linkElement.style.background = '#333'; linkElement.style.transform = 'translateX(0)'; }); content.appendChild(linkElement); }); } else { content.innerHTML = '<div style="color: #999; text-align: center; padding: 20px; font-style: italic;">📝 There is no strategy guide for this page yet.</div>'; } } popup.appendChild(header); popup.appendChild(content); document.body.appendChild(popup); popup.style.opacity = '0'; setTimeout(() => popup.style.opacity = '1', 100); let isDragging = false; let dragOffset = { x: 0, y: 0 }; header.addEventListener('mousedown', (e) => { if (e.target === settingsBtn || e.target === closeBtn) return; isDragging = true; dragOffset.x = e.clientX - popup.offsetLeft; dragOffset.y = e.clientY - popup.offsetTop; header.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', (e) => { if (isDragging) { popup.style.left = (e.clientX - dragOffset.x) + 'px'; popup.style.top = (e.clientY - dragOffset.y) + 'px'; popup.style.right = 'auto'; } }); document.addEventListener('mouseup', () => { isDragging = false; header.style.cursor = 'grab'; }); header.style.cursor = 'grab'; });
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址