您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A library for virtupets.net APIs.
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/512407/1582200/GC%20-%20Virtupets%20API%20library.js
const url = "https://virtupets.net"; async function vpApiSetupClientID() { let clientID; try { clientID = await GM.getValue('ClientID'); if (!clientID) { const id = crypto.randomUUID(); await GM.setValue('ClientID', crypto.randomUUID()); clientID = id; } } catch (error) { console.error(error, "Failed to setup client ID.", "setupClientID"); clientID = ""; } return clientID; } async function vpApiCreateGetRequest(apiVersion) { const clientID = await vpApiSetupClientID(); return { method: "GET", headers: { "Version": apiVersion, "ClientID": clientID } } } async function vpApiCreatePostRequest(apiVersion, body) { const clientID = await vpApiSetupClientID(); return { method: "POST", headers: { "Content-Type": "application/json", "Version": apiVersion, "ClientID": clientID, "ClientVersion": "0.10a", }, body: JSON.stringify(body), } } async function getItemDetails(itemName) { const apiVersion = "0.1"; const request = await vpApiCreateGetRequest(apiVersion); return fetch(`${url}/items/details?q=${encodeURIComponent(itemName)}`, request); } /* Expects items to be an array of item name strings. */ async function bulkShopWizardPrices(items) { const apiVersion = "0.1"; const request = await vpApiCreatePostRequest(apiVersion, items); return fetch(`${url}/shop-prices/bulk`, request); } /* Expects to receive the shop wizard page document page */ async function sendShopWizardPrices(doc) { try { const tokens = doc?.querySelector('.mt-1 strong')?.textContent?.split(" ... "); let body; const itemName = tokens?.length >= 2 ? tokens[1]?.trim() : undefined; if (!vpApiValidateSearchRange(doc) || !itemName) { return; } else if (vpApiValidateTable(doc)) { const dataElements = doc.querySelectorAll('.market_grid .data'); const i32Max = 2147483647; let lowestPrice = i32Max; let totalStock = 0; let totalShops = 0; let includesOwnUnpricedItem = false; for (let i = 0; i < dataElements.length; i += 4) { const stock = parseInt(dataElements[i + 2].querySelector('span').textContent); const price = parseInt(dataElements[i + 3].querySelector('strong').textContent.replace(/[^0-9]/g, '')); if (price > 0) { lowestPrice = Math.min(price, lowestPrice); totalStock += stock; totalShops += 1; } else { includesOwnUnpricedItem = true; } } if (lowestPrice < i32Max && totalStock > 0 && dataElements.length > 0) { body = { item_name: itemName, price: lowestPrice, total_stock: totalStock, total_shops: totalShops }; } else if (includesOwnUnpricedItem && totalStock == 0 && dataElements.length == 4) { body = { item_name: itemName, total_stock: 0, total_shops: 0 }; } } else if (vpApiValidateUnbuyable(doc)) { body = { item_name: itemName, total_stock: 0, total_shops: 0 }; } if (body && !doc.querySelector('#vp_api_data_sent')) { const grid = doc.querySelector('.market_grid.sw_results'); const sentFlag = doc.createElement('div'); sentFlag.style.display = 'none'; sentFlag.id = 'vp_api_data_sent'; grid.appendChild(sentFlag); const apiVersion = "0.11"; const options = await vpApiCreatePostRequest(apiVersion, body); console.log(`Data uploaded to ${url}`); return await fetch(`${url}/shop-prices`, options); } } catch (error) { vpApiLogError(error, "Failed to send shop prices to the API.", "sendShopPrices"); } } function vpApiValidateSearchRange(doc) { if (doc.querySelector('main .center .mt-1 span')?.textContent?.toLowerCase() == '(searching between 1 and 99,999 np)') { return true; } return false; } function vpApiValidateTable(doc) { const header = doc.querySelectorAll('.market_grid .header'); const check = ['owner', 'item', 'stock', 'price']; if (check.length != header.length) return false; for (let i = 0; i < header.length; i += 1) { const title = header[i].querySelector('strong').textContent.toLowerCase(); if (check[i] != title) { const message = `Unknown header named "${title}" in position ${i + 1}, expected "${check[i]}".`; const error = new Error(message); logError(error, "Validation Error.", "validateTable"); throw error; } } return true; } function vpApiValidateUnbuyable(doc) { const notFoundMsg = "i did not find anything. :( please try again, and i will search elsewhere!"; const wrongHeaders = doc.querySelectorAll('.market_grid .header').length > 0; const wrongMessage = doc.querySelector('main p.center').textContent.toLowerCase() != notFoundMsg; if (wrongHeaders || wrongMessage) { return false; } return true; } async function vpApiLogError(error, message, operation, statusCode = undefined) { try { console.log(message); const errorBody = { message: `${message}. Error: ${error.message.replace(/^Error:\s*/, '')}`, status_code: statusCode, route: `virtupets_api::${operation}` }; const options = await vpApiCreatePostRequest("0.1", errorBody); fetch(`${url}/errors`, options); } catch (error) { console.error('Error sending error report:', error); } }