您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
POE2 trade data processor with clipboard and shared data support, and Coze.cn support
// ==UserScript== // @name 流放之路2集市AI助手插件 // @namespace http://tampermonkey.net/ // @version 2025-07-25 // @description POE2 trade data processor with clipboard and shared data support, and Coze.cn support // @author 三鲜大饺子滋滋滋 // @match https://www.pathofexile.com/* // @match https://www.coze.cn/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_getValue // @grant GM_setValue // @grant GM_openInTab // @license AGPL License // ==/UserScript== (function() { 'use strict'; // ======================================== // 配置常量 (Configuration Constants) // ======================================== const CONFIG = { API_BASE_URL: 'https://www.pathofexile.com/api/trade2/search', TRADE_BASE_URL: 'https://www.pathofexile.com/trade2/search/poe2', REQUEST_TIMEOUT: 10000, // 10秒超时 CHECK_INTERVAL: 500, // 检查间隔500ms USER_AGENT: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', // 数据前缀常量 CLIPBOARD_DATA_PREFIX: 'POE2AITRADEQPOS0_', SHARED_DATA_PREFIX: 'POE2AITRADEQPOS1_', STORAGE_KEY: 'poe2_trade_data', PROCESSED_MESSAGE: '已处理' }; // ======================================== // 全局状态管理 (Global State Management) // ======================================== const AppState = { checkInterval: null, isCozeWebsite: false, isPathOfExileWebsite: false, /** * 初始化应用状态 */ init() { this.isCozeWebsite = window.location.hostname.includes('coze.cn'); this.isPathOfExileWebsite = window.location.hostname.includes('pathofexile.com'); Logger.info(`当前域名: ${window.location.hostname}`); }, /** * 清理定时器 */ clearInterval() { if (this.checkInterval) { clearInterval(this.checkInterval); this.checkInterval = null; } } }; // ======================================== // 日志工具 (Logging Utilities) // ======================================== const Logger = { /** * 输出信息日志 * @param {string} message - 日志消息 * @param {any} data - 可选的数据对象 */ info(message, data = null) { console.log(`[POE2 Trade Helper] ${message}`, data || ''); }, /** * 输出警告日志 * @param {string} message - 警告消息 * @param {Error} error - 可选的错误对象 */ warn(message, error = null) { console.warn(`[POE2 Trade Helper] ${message}`, error || ''); }, /** * 输出错误日志 * @param {string} message - 错误消息 * @param {Error} error - 错误对象 */ error(message, error = null) { console.error(`[POE2 Trade Helper] ${message}`, error || ''); } }; // ======================================== // 数据处理工具 (Data Processing Utilities) // ======================================== const DataProcessor = { /** * 解析JSON数据 * @param {string} jsonString - JSON字符串 * @returns {Object|null} 解析后的对象或null */ parseJSON(jsonString) { try { return JSON.parse(jsonString); } catch (error) { Logger.error('JSON解析失败', error); return null; } }, /** * 验证数据前缀 * @param {string} data - 数据字符串 * @param {string} prefix - 前缀 * @returns {boolean} 是否匹配前缀 */ hasPrefix(data, prefix) { return data && data.trim().startsWith(prefix); }, /** * 提取前缀后的数据 * @param {string} data - 完整数据 * @param {string} prefix - 前缀 * @returns {string} 提取的数据 */ extractData(data, prefix) { return data.substring(prefix.length); }, /** * 转换数据前缀 * @param {string} data - 原始数据 * @param {string} fromPrefix - 源前缀 * @param {string} toPrefix - 目标前缀 * @returns {string} 转换后的数据 */ convertPrefix(data, fromPrefix, toPrefix) { return data.replace(fromPrefix, toPrefix); } }; // ======================================== // API交互模块 (API Interaction Module) // ======================================== const TradeAPI = { /** * 执行交易搜索 * @param {Object} queryInfo - 查询信息 * @returns {Promise<Object>} 搜索结果 */ async executeTradeSearch(queryInfo) { try { Logger.info('开始执行交易搜索', queryInfo); const { server_name: serverName, trade_req: tradeReq } = queryInfo; if (!serverName || !tradeReq) { throw new Error('缺少必要的查询参数: server_name 或 trade_req'); } const requestBody = this._prepareRequestBody(tradeReq); const response = await this._sendAPIRequest(serverName, requestBody); const result = await this._processAPIResponse(response, serverName); Logger.info('交易搜索完成', result); return result; } catch (error) { Logger.error('交易搜索失败', error); throw error; } }, /** * 准备请求体 * @param {any} tradeReq - 交易请求数据 * @returns {Object} 格式化的请求体 */ _prepareRequestBody(tradeReq) { try { return tradeReq; } catch { // 如果解析失败,创建基本搜索结构 return { query: { status: { option: "online" }, name: tradeReq, type: "", stats: [{ type: "and", filters: [] }] }, sort: { price: "asc" } }; } }, /** * 发送API请求 * @param {string} serverName - 服务器名称 * @param {Object} requestBody - 请求体 * @returns {Promise<Response>} API响应 */ async _sendAPIRequest(serverName, requestBody) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), CONFIG.REQUEST_TIMEOUT); try { const apiUrl = `${CONFIG.API_BASE_URL}/${serverName}`; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'User-Agent': CONFIG.USER_AGENT, 'Accept': 'application/json', 'Cache-Control': 'no-cache' }, body: JSON.stringify(requestBody), signal: controller.signal }); clearTimeout(timeoutId); return response; } catch (fetchError) { clearTimeout(timeoutId); if (fetchError.name === 'AbortError') { throw new Error(`请求超时 (${CONFIG.REQUEST_TIMEOUT / 1000}秒)`); } throw fetchError; } }, /** * 处理API响应 * @param {Response} response - API响应 * @param {string} serverName - 服务器名称 * @returns {Promise<Object>} 处理结果 */ async _processAPIResponse(response, serverName) { if (!response.ok) { const errorText = await response.text().catch(() => '未知错误'); throw new Error(`HTTP ${response.status}: ${response.statusText}. ${errorText}`); } const data = await response.json(); if (!data || typeof data !== 'object' || !data.id) { throw new Error('API响应格式无效或缺少必要字段'); } const tradeUrl = `${CONFIG.TRADE_BASE_URL}/${serverName}/${data.id}`; this._openTradeURL(tradeUrl); return { id: data.id, url: tradeUrl, timestamp: new Date().toISOString() }; }, /** * 打开交易URL * @param {string} tradeUrl - 交易URL */ _openTradeURL(tradeUrl) { const currentUrl = window.location.href; if (currentUrl.includes('pathofexile.com/trade2/')) { window.location.href = tradeUrl; } else { window.open(tradeUrl, '_blank'); } Logger.info('已打开交易URL', tradeUrl); } }; // ======================================== // Coze DOM监控模块 (Coze DOM Monitoring Module) // ======================================== const CozeDOMMonitor = { /** * 检查DOM元素内容变化 */ checkChanges() { try { const targetDiv = document.getElementById('Div17'); if (!targetDiv) { Logger.info('CozeDOMMonitor: checkChanges - 未找到ID为 Div17 的元素'); return; // Div not found, do nothing } // 获取所有符合条件的textDiv元素 const textDivs = targetDiv.querySelectorAll('.ui-builder-text > div'); if (!textDivs || textDivs.length === 0) { Logger.info('CozeDOMMonitor: checkChanges - 在 Div2 内未找到 .ui-builder-text > div 元素'); return; // Text divs not found } const prefix = 'POE2AITRADEQPOS0_'; const suffix = '_POE2AITRADEQPOS0'; let processedCount = 0; // 按顺序处理每个textDiv元素 textDivs.forEach((textDiv, index) => { try { const rawData = textDiv.textContent; const hasPrefix = rawData && rawData.startsWith(prefix); const hasSuffix = rawData && rawData.endsWith(suffix); const isNotProcessed = rawData && !rawData.startsWith('finish.'); // Check for prefix/suffix and that it hasn't been processed yet if (hasPrefix && hasSuffix && isNotProcessed) { Logger.info(`检测到第${index + 1}个Coze DOM数据,准备处理`, rawData.substring(0, 80) + '...'); // Mark as processed immediately to avoid loops textDiv.textContent = 'finish.' + rawData; Logger.info(`已更新第${index + 1}个DOM元素内容,防止重复处理`); // Extract the core data const data = rawData.substring(prefix.length, rawData.length - suffix.length); // Prepend the original prefix for processData function const fullData = CONFIG.CLIPBOARD_DATA_PREFIX + data; this.processData(fullData); processedCount++; } } catch (error) { Logger.warn(`处理第${index + 1}个textDiv时出错`, error); } }); if (processedCount > 0) { Logger.info(`本次检查共处理了${processedCount}个DOM元素`); } } catch (error) { Logger.warn('检查Coze DOM失败', error); } }, /** * 处理数据 * @param {string} data - The full data string with prefix */ processData(data) { Logger.info('开始处理Coze DOM数据'); const jsonData = DataProcessor.extractData(data, CONFIG.CLIPBOARD_DATA_PREFIX); const parsedData = DataProcessor.parseJSON(jsonData); if (!parsedData) { Logger.error('Coze DOM数据JSON解析失败'); return; } const { server_name: serverName } = parsedData; if (!serverName) { Logger.warn('Coze DOM数据中未找到server_name'); return; } Logger.info('提取的服务器名称', serverName); // 转换数据前缀并保存到共享存储 const convertedData = DataProcessor.convertPrefix( data, CONFIG.CLIPBOARD_DATA_PREFIX, CONFIG.SHARED_DATA_PREFIX ); SharedDataManager.save(convertedData); // 打开交易页面 this._openTradePage(serverName); }, /** * 打开交易页面 * @param {string} serverName - 服务器名称 */ _openTradePage(serverName) { const tradeUrl = `${CONFIG.TRADE_BASE_URL}/${encodeURIComponent(serverName)}`; Logger.info('打开POE2交易页面', tradeUrl); GM_openInTab(tradeUrl, { active: true }); } }; // ======================================== // 共享数据管理模块 (Shared Data Management Module) // ======================================== const SharedDataManager = { /** * 保存数据到共享存储 * @param {string} data - 要保存的数据 */ save(data) { try { GM_setValue(CONFIG.STORAGE_KEY, data); Logger.info('共享数据保存成功'); } catch (error) { Logger.error('共享数据保存失败', error); } }, /** * 从共享存储读取数据 * @returns {string} 共享数据 */ load() { try { return GM_getValue(CONFIG.STORAGE_KEY, ''); } catch (error) { Logger.error('共享数据读取失败', error); return ''; } }, /** * 清除共享数据 */ clear() { try { GM_setValue(CONFIG.STORAGE_KEY, 'handle_finish'); Logger.info('共享数据已清除'); } catch (error) { Logger.error('共享数据清除失败', error); } }, /** * 检查共享数据变化 */ checkChanges() { try { const sharedData = this.load(); Logger.info('当前共享数据', sharedData); if (sharedData && sharedData.trim() !== '') { const trimmedData = sharedData.trim(); if (DataProcessor.hasPrefix(trimmedData, CONFIG.SHARED_DATA_PREFIX)) { Logger.info('检测到共享数据', trimmedData.substring(0, 50) + '...'); this.processData(trimmedData); } } } catch (error) { Logger.error('共享数据检查失败', error); } }, /** * 处理共享数据 * @param {string} data - 共享数据 */ async processData(data) { Logger.info('开始处理共享数据'); const jsonData = DataProcessor.extractData(data, CONFIG.SHARED_DATA_PREFIX); const parsedData = DataProcessor.parseJSON(jsonData); if (!parsedData) { Logger.error('共享数据JSON解析失败'); return; } try { await TradeAPI.executeTradeSearch(parsedData); this.clear(); } catch (error) { Logger.error('交易搜索执行失败', error); } } }; // ======================================== // 应用监控管理模块 (Application Monitoring Manager) // ======================================== const MonitoringManager = { /** * 启动监控 */ start() { Logger.info('开始启动监控服务'); // 清理现有定时器 AppState.clearInterval(); // 初始化应用状态 AppState.init(); // 根据域名启动相应的监控 if (AppState.isCozeWebsite) { this._startCozeDOMMonitoring(); } else if (AppState.isPathOfExileWebsite) { this._startSharedDataMonitoring(); } else { Logger.warn('当前域名不支持监控功能'); } }, /** * 停止监控 */ stop() { AppState.clearInterval(); Logger.info('监控服务已停止'); }, /** * 启动Coze DOM监控 (仅在coze.cn) */ _startCozeDOMMonitoring() { Logger.info('启动Coze DOM监控服务 (coze.cn)'); AppState.checkInterval = setInterval(() => { CozeDOMMonitor.checkChanges(); }, CONFIG.CHECK_INTERVAL); }, /** * 启动共享数据监控 (仅在pathofexile.com) */ _startSharedDataMonitoring() { Logger.info('准备启动共享数据监控服务 (pathofexile.com)'); if (document.readyState === 'complete') { Logger.info('DOM已完全加载,立即执行共享数据检查'); SharedDataManager.checkChanges(); } else { Logger.info('等待DOM完全加载...'); window.addEventListener('load', () => { Logger.info('DOM加载完成,执行共享数据检查'); SharedDataManager.checkChanges(); }); } } }; // ======================================== // 应用初始化 (Application Initialization) // ======================================== /** * 应用程序入口点 */ function initializeApp() { Logger.info('POE2 Trade Helper 正在初始化...'); try { // 注册(不可用)事件监听器 window.addEventListener('beforeunload', () => { MonitoringManager.stop(); }); // 启动监控服务 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { MonitoringManager.start(); }); } else { MonitoringManager.start(); } Logger.info('POE2 Trade Helper 初始化完成'); } catch (error) { Logger.error('应用初始化失败', error); } } // ======================================== // 启动应用 (Start Application) // ======================================== // 立即执行应用初始化 initializeApp(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址