GC - Virtupets API library

A library for virtupets.net APIs.

Ce script ne devrait pas être installé directement. C'est une librairie créée pour d'autres scripts. Elle doit être inclus avec la commande // @require https://update.greatest.deepsurf.us/scripts/512407/1582200/GC%20-%20Virtupets%20API%20library.js

  1. const url = "https://virtupets.net";
  2.  
  3. async function vpApiSetupClientID() {
  4. let clientID;
  5. try {
  6. clientID = await GM.getValue('ClientID');
  7. if (!clientID) {
  8. const id = crypto.randomUUID();
  9. await GM.setValue('ClientID', crypto.randomUUID());
  10. clientID = id;
  11. }
  12. } catch (error) {
  13. console.error(error, "Failed to setup client ID.", "setupClientID");
  14. clientID = "";
  15. }
  16. return clientID;
  17. }
  18.  
  19. async function vpApiCreateGetRequest(apiVersion) {
  20. const clientID = await vpApiSetupClientID();
  21. return {
  22. method: "GET",
  23. headers: {
  24. "Version": apiVersion,
  25. "ClientID": clientID
  26. }
  27. }
  28. }
  29.  
  30. async function vpApiCreatePostRequest(apiVersion, body) {
  31. const clientID = await vpApiSetupClientID();
  32. return {
  33. method: "POST",
  34. headers: {
  35. "Content-Type": "application/json",
  36. "Version": apiVersion,
  37. "ClientID": clientID,
  38. "ClientVersion": "0.10a",
  39. },
  40. body: JSON.stringify(body),
  41. }
  42. }
  43.  
  44. async function getItemDetails(itemName) {
  45. const apiVersion = "0.1";
  46. const request = await vpApiCreateGetRequest(apiVersion);
  47. return fetch(`${url}/items/details?q=${encodeURIComponent(itemName)}`, request);
  48. }
  49.  
  50. /* Expects items to be an array of item name strings. */
  51. async function bulkShopWizardPrices(items) {
  52. const apiVersion = "0.1";
  53. const request = await vpApiCreatePostRequest(apiVersion, items);
  54. return fetch(`${url}/shop-prices/bulk`, request);
  55. }
  56.  
  57. /* Expects to receive the shop wizard page document page */
  58. async function sendShopWizardPrices(doc) {
  59. try {
  60. const tokens = doc?.querySelector('.mt-1 strong')?.textContent?.split(" ... ");
  61. let body;
  62. const itemName = tokens?.length >= 2 ? tokens[1]?.trim() : undefined;
  63. if (!vpApiValidateSearchRange(doc) || !itemName) {
  64. return;
  65. }
  66. else if (vpApiValidateTable(doc)) {
  67. const dataElements = doc.querySelectorAll('.market_grid .data');
  68. const i32Max = 2147483647;
  69. let lowestPrice = i32Max;
  70. let totalStock = 0;
  71. let totalShops = 0;
  72. let includesOwnUnpricedItem = false;
  73.  
  74. for (let i = 0; i < dataElements.length; i += 4) {
  75. const stock = parseInt(dataElements[i + 2].querySelector('span').textContent);
  76. const price = parseInt(dataElements[i + 3].querySelector('strong').textContent.replace(/[^0-9]/g, ''));
  77.  
  78. if (price > 0) {
  79. lowestPrice = Math.min(price, lowestPrice);
  80. totalStock += stock;
  81. totalShops += 1;
  82. } else {
  83. includesOwnUnpricedItem = true;
  84. }
  85. }
  86. if (lowestPrice < i32Max && totalStock > 0 && dataElements.length > 0) {
  87. body = {
  88. item_name: itemName,
  89. price: lowestPrice,
  90. total_stock: totalStock,
  91. total_shops: totalShops
  92. };
  93. } else if (includesOwnUnpricedItem && totalStock == 0 && dataElements.length == 4) {
  94. body = {
  95. item_name: itemName,
  96. total_stock: 0,
  97. total_shops: 0
  98. };
  99. }
  100. }
  101. else if (vpApiValidateUnbuyable(doc)) {
  102. body = {
  103. item_name: itemName,
  104. total_stock: 0,
  105. total_shops: 0
  106. };
  107. }
  108.  
  109. if (body && !doc.querySelector('#vp_api_data_sent')) {
  110. const grid = doc.querySelector('.market_grid.sw_results');
  111. const sentFlag = doc.createElement('div');
  112. sentFlag.style.display = 'none';
  113. sentFlag.id = 'vp_api_data_sent';
  114. grid.appendChild(sentFlag);
  115.  
  116. const apiVersion = "0.11";
  117. const options = await vpApiCreatePostRequest(apiVersion, body);
  118. console.log(`Data uploaded to ${url}`);
  119. return await fetch(`${url}/shop-prices`, options);
  120. }
  121.  
  122. } catch (error) {
  123. vpApiLogError(error, "Failed to send shop prices to the API.", "sendShopPrices");
  124. }
  125. }
  126.  
  127. function vpApiValidateSearchRange(doc) {
  128. if (doc.querySelector('main .center .mt-1 span')?.textContent?.toLowerCase() == '(searching between 1 and 99,999 np)') {
  129. return true;
  130. }
  131. return false;
  132. }
  133. function vpApiValidateTable(doc) {
  134. const header = doc.querySelectorAll('.market_grid .header');
  135. const check = ['owner', 'item', 'stock', 'price'];
  136. if (check.length != header.length) return false;
  137. for (let i = 0; i < header.length; i += 1) {
  138. const title = header[i].querySelector('strong').textContent.toLowerCase();
  139. if (check[i] != title) {
  140. const message = `Unknown header named "${title}" in position ${i + 1}, expected "${check[i]}".`;
  141. const error = new Error(message);
  142. logError(error, "Validation Error.", "validateTable");
  143. throw error;
  144. }
  145. }
  146. return true;
  147. }
  148.  
  149. function vpApiValidateUnbuyable(doc) {
  150. const notFoundMsg = "i did not find anything. :( please try again, and i will search elsewhere!";
  151. const wrongHeaders = doc.querySelectorAll('.market_grid .header').length > 0;
  152. const wrongMessage = doc.querySelector('main p.center').textContent.toLowerCase() != notFoundMsg;
  153. if (wrongHeaders || wrongMessage) {
  154. return false;
  155. }
  156. return true;
  157. }
  158.  
  159. async function vpApiLogError(error, message, operation, statusCode = undefined) {
  160. try {
  161. console.log(message);
  162. const errorBody = {
  163. message: `${message}. Error: ${error.message.replace(/^Error:\s*/, '')}`,
  164. status_code: statusCode,
  165. route: `virtupets_api::${operation}`
  166. };
  167. const options = await vpApiCreatePostRequest("0.1", errorBody);
  168. fetch(`${url}/errors`, options);
  169. } catch (error) {
  170. console.error('Error sending error report:', error);
  171. }
  172. }