[GC] Organize SW Results

Blocked users can be synced but do not actually change anything in the SW results yet.

  1. // ==UserScript==
  2. // @name [GC] Organize SW Results
  3. // @namespace https://greasyfork.org/en/users/1225524-kaitlin
  4. // @match https://www.grundos.cafe/market/wizard*
  5. // @match https://www.grundos.cafe/guilds/guild/*/members*
  6. // @match https://www.grundos.cafe/block*
  7. // @require https://cdn.jsdelivr.net/npm/jquery@3.6.4/dist/jquery.min.js
  8. // @require https://update.greasyfork.org/scripts/487361/1389917/%5BGC%20%7C%20Library%5D%20-%20SSW%3A%20Savvy%20Shop%20Wiz.js
  9. // @grant GM.getValue
  10. // @grant GM.setValue
  11. // @grant GM.listValues
  12. // @license MIT
  13. // @version 2.0.1
  14. // @author Cupkait
  15. // @icon https://i.imgur.com/4Hm2e6z.png
  16. // @description Blocked users can be synced but do not actually change anything in the SW results yet.
  17.  
  18. // ==/UserScript==
  19.  
  20. createSSWMenu();
  21. addStyles();
  22.  
  23.  
  24. let wizList = document.querySelector('.sw_results');
  25. if (window.location.href.startsWith('https://www.grundos.cafe/market/wizard/') && wizList) {
  26. generateNewGrid();
  27. }
  28.  
  29.  
  30.  
  31. async function grabData() {
  32. const resultsGrid = document.querySelector('.sw_results');
  33. const resultsRaw = resultsGrid.querySelectorAll('.data');
  34. const rawData = [];
  35.  
  36. for (var i = 0; i < resultsRaw.length;) {
  37. var seller = resultsRaw[i].innerText;
  38. console.log(seller)
  39. var link = resultsRaw[i].innerHTML;
  40. var stock = parseInt(resultsRaw[i + 2].innerText,10);
  41. var price = parseInt(resultsRaw[i + 3].innerText.replace(/[^\d]/g, ''), 10);
  42.  
  43. var row = {
  44. seller: seller,
  45. link: link,
  46. stock: stock,
  47. price: price
  48. };
  49.  
  50. rawData.push(row);
  51. i = i + 4;
  52. }
  53. return rawData;
  54. }
  55.  
  56. async function calculateValues() {
  57. let rawData = await grabData();
  58.  
  59. let lowestPrice = Math.min(...rawData.map(item => item.price));
  60. rawData.forEach(item => {
  61. item.pricediff = item.price - lowestPrice;
  62. });
  63.  
  64. return rawData;
  65. }
  66.  
  67. async function getUsers() {
  68. let friendList = await GM.getValue('friendList', '[]');
  69. let guildsList = await GM.getValue('guildsList', '{}');
  70. let blockList = await GM.getValue('blockList', '[]');
  71. const guildMembers = Object.values(guildsList).flatMap(guild => guild.guildMembers);
  72.  
  73. if (typeof friendList === 'string') {
  74. friendList = JSON.parse(friendList);
  75. }
  76. if (typeof guildMembers === 'string') {
  77. guildMembers = JSON.parse(guildMembers);
  78. }
  79. if (typeof blockList === 'string') {
  80. blockList = JSON.parse(blockList);
  81. }
  82. return { friendList, guildMembers, blockList };
  83. }
  84.  
  85. async function createTableRow(item, friendList, guildMembers, blockList) {
  86. var price = item.price.toLocaleString() + " NP";
  87. let variance;
  88. let friendstatus = friendList.includes(item.seller);
  89. let guildstatus = guildMembers.includes(item.seller);
  90. let blockstatus = blockList.includes(item.seller);
  91.  
  92. if (item.pricediff === 0) {
  93. variance = '<span style="color:green">— LOWEST —</span>';
  94. } else if (friendstatus || guildstatus) {
  95. variance = `<span style="color:red">(+${item.pricediff.toLocaleString()})</span>`;
  96. } else {
  97. variance = '—';
  98. }
  99.  
  100. let row = document.createElement('tr');
  101. row.innerHTML = `
  102. <td>${item.link}</td>
  103. <td>${item.stock}</td>
  104. <td><strong>${price}</strong></td>
  105. <td><strong>${variance}</strong></td>
  106. `;
  107.  
  108. if (friendstatus) {
  109. row.classList.add('friend');
  110. } else if (blockstatus) {
  111. row.classList.add('blocked');
  112. } else if (guildstatus) {
  113. row.classList.add('guild');
  114. }
  115.  
  116. return row;
  117. }
  118.  
  119. async function generateNewGrid() {
  120. const { friendList, guildMembers, blockList } = await getUsers();
  121.  
  122. const newGrid = document.createElement('table');
  123. newGrid.innerHTML = `<thead><tr><th>Owner</th><th>Stock</th><th>Price</th><th>Variance</th></tr></thead>`;
  124.  
  125. let rawData = await calculateValues();
  126. const newHeader = await updateHeader();
  127.  
  128. rawData.sort((a, b) => {
  129. const aIsFriend = friendList.includes(a.seller);
  130. const bIsFriend = friendList.includes(b.seller);
  131. const aIsGuildMember = guildMembers.includes(a.seller);
  132. const bIsGuildMember = guildMembers.includes(b.seller);
  133.  
  134. if ((aIsFriend || aIsGuildMember) && !(bIsFriend || bIsGuildMember)) return -1;
  135. if (!(aIsFriend || aIsGuildMember) && (bIsFriend || bIsGuildMember)) return 1;
  136.  
  137.  
  138. return 0;
  139. });
  140.  
  141. const tbody = document.createElement('tbody');
  142.  
  143. for (const item of rawData) {
  144. const row = await createTableRow(item, friendList, guildMembers, blockList);
  145. tbody.appendChild(row);
  146. }
  147.  
  148. newGrid.appendChild(tbody);
  149.  
  150. const oldGrid = document.querySelector('.sw_results');
  151. oldGrid.insertAdjacentElement('beforebegin', newGrid);
  152. oldGrid.style.display = 'none';
  153. }
  154.  
  155. async function updateHeader() {
  156.  
  157.  
  158.  
  159. }
  160.  
  161.  
  162.  
  163.  
  164.  
  165.  
  166.  
  167.  
  168.  
  169.  
  170.  
  171. function addStyles() {
  172. const newGridStyle = document.createElement('style');
  173. newGridStyle.innerHTML = `
  174. #sswmenu {
  175. display: none;
  176. border-radius: 15px 15px 15px 0px;
  177. border-bottom: 3px solid;
  178. position: absolute;
  179. z-index: 999;
  180. width: 250px;
  181. height: 250px;
  182. bottom: 0%;
  183. left: 100%;
  184. margin: -3px;
  185. padding: 10px;
  186. background-color: #d2d0cc;
  187. box-shadow: 5px 0 5px rgba(0, 0, 0, 0.5);
  188. }
  189. #sswcontainer {
  190. position: relative;
  191. border-bottom: 3px solid;
  192. padding: 5px 10px 5px 0px;
  193. height: 30px;
  194. width: 100%;
  195. top: 00%;
  196. left: 0px;
  197. background-color: #d2d0cc;
  198. box-shadow: 5px 0 5px rgba(0, 0, 0, 0.5);
  199. }
  200. #sswsettings {
  201. position: relative;
  202. font-size: 16px;
  203. font-weight: bold;
  204. font-family: courier;
  205. border-radius: 5px;
  206. padding: 5px;
  207. width: 100%;
  208. height: auto;
  209. border: 1px solid rgb(204, 204, 204);
  210. cursor: pointer;
  211. background-color: lightgray;
  212. }
  213. #menubutton {
  214. height: 35px;
  215. width: 90%;
  216. margin: 5px;
  217. }
  218. table {
  219. position: relative;
  220. min-width: 80%;
  221. margin: 1em auto;
  222. width: max-content;
  223. max-width: 100%;
  224. text-align: center;
  225. border-collapse: collapse;
  226. }
  227. th,
  228. td {
  229. position: relative;
  230. padding: 4px;
  231. z-index: 3;
  232. }
  233. th {
  234. background-color: var(--grid_head);
  235. }
  236. tr {
  237. position: relative;
  238. min-width: 100%;
  239. margin: 1em auto;
  240. width: max-content;
  241. max-width: 100%;
  242. text-align: center;
  243. border-collapse: collapse;
  244. }
  245. tr:nth-child(even) {
  246. background-color: var(--grid_even);
  247. }
  248. tr:nth-child(odd) {
  249. background-color: var(--grid_odd);
  250. }
  251.  
  252. .friend::after {
  253. content: "";
  254. position: absolute;
  255. top: 0;
  256. left: 0;
  257. width: 100%;
  258. height: 100%;
  259. background-color: #00801a54;
  260. z-index: 1;
  261. }
  262. .guild::after {
  263. content: "";
  264. position: absolute;
  265. top: 0;
  266. left: 0;
  267. width: 100%;
  268. height: 100%;
  269. background-color: #2c008054;
  270. z-index: 1;
  271. }
  272. .blocked {
  273. opacity:10%
  274. }
  275. .blocked:hover {
  276. opacity:85%
  277. }
  278.  
  279. `;
  280. document.head.appendChild(newGridStyle);
  281. }
  282.  
  283.  
  284.  
  285.  
  286.  
  287.  
  288.  
  289.  
  290.  
  291.  
  292.  
  293.  
  294.  
  295.  
  296.  
  297.  
  298. // SYNCING BELOW
  299.  
  300.  
  301.  
  302.  
  303.  
  304.  
  305.  
  306. function removeUsersManually() {
  307. var userInput = prompt(
  308. 'Which username would you like to remove from your list? Enter one username then click OK.'
  309. );
  310.  
  311. if (userInput !== null && userInput.trim() !== '') {
  312. var index = friendList.findIndex(name => name
  313. .toLowerCase() === userInput.trim().toLowerCase());
  314. if (index !== -1) {
  315. friendList.splice(index, 1);
  316. GM.setValue('friendList', friendList);
  317. console.log('Removed', userInput.trim(),
  318. 'from the friend list.');
  319. } else {
  320. console.log(userInput.trim(),
  321. 'not found in the friend list.');
  322. }
  323.  
  324. if (window.location.href ===
  325. 'https://www.grundos.cafe/market/wizard/') {
  326.  
  327. if (confirm(
  328. 'Do you want to reload the page to see the updated results?'
  329. )) {
  330. location.reload();
  331. }
  332. }
  333.  
  334. } else {
  335. console.log('No username entered. Operation canceled.');
  336. }
  337. }
  338. $(ManualRemove).on('click', removeUsersManually);
  339. async function syncFriendsList() {
  340. try {
  341. const response = await fetch(
  342. 'https://www.grundos.cafe/neofriends/');
  343. const html = await response.text();
  344. const doc = new DOMParser().parseFromString(html,
  345. 'text/html');
  346. const uniqueUsernames = new Set();
  347.  
  348. doc.querySelectorAll(
  349. 'div.market_grid [href*="/userlookup/?user="]')
  350. .forEach(function (element) {
  351. uniqueUsernames.add(element.getAttribute(
  352. 'href').split('=')[1]);
  353. });
  354.  
  355. console.log("Friends List Synced");
  356. return Array.from(uniqueUsernames);
  357. } catch (error) {
  358. console.error('Error fetching and extracting usernames:',
  359. error);
  360. return [];
  361. }
  362.  
  363. }
  364. $(SyncFriends).on('click', async function () {
  365. console.log("clicked")
  366. const usernames = await syncFriendsList();
  367. friendList = usernames;
  368. GM.setValue('friendList', friendList);
  369. console.log('Synced usernames:', usernames);
  370. });
  371. function syncBlockedList() {
  372. if (window.location.href === 'https://www.grundos.cafe/block/') {
  373. const blockList = $('.block_list').find('span').map(
  374. function () {
  375. return $(this).text();
  376. }).get();
  377. GM.setValue('blockList', blockList);
  378. console.log('blockList stored:', blockList);
  379. } else {
  380. if (confirm(
  381. 'You can only do this from the Blocked Users page. Want to open it in a new tab?'
  382. )) {
  383. window.open('https://www.grundos.cafe/block/', '_blank');
  384. } else {
  385. console.log('User declined. No blocked sync occured.');
  386. }
  387. }
  388. }
  389. $(SyncBlocked).on('click', syncBlockedList);
  390. function syncGuildMembers() {
  391. if (/^https:\/\/www\.grundos\.cafe\/guilds\/guild\/.+\/members\//.test(window.location.href)) {
  392. const userName = /user=(.*?)"/g.exec(document.body.innerHTML)[1];
  393. const guildIdentifier = window.location.href.match(/guild\/([^\/-]+)/)?.[1];
  394. const guildName = $('.guild-header strong').eq(0).text();
  395. const guildID = "guildID_" + guildIdentifier;
  396. const guildMembers = [...new Set($('div.member-grid [href*="/userlookup/?user="]')
  397. .map(function() {
  398. return $(this).attr('href').split('=')[1];
  399. })
  400. .filter(function(guildMember) {
  401. return guildMember !== userName;
  402. })
  403. )];
  404.  
  405. GM.getValue('guildsList', {}).then(function(guildsList) {
  406. guildsList[guildID] = {
  407. 'guildName': guildName,
  408. 'guildMembers': guildMembers
  409. };
  410.  
  411. GM.setValue('guildsList', guildsList);
  412. });
  413. } else {
  414. if (confirm('You can only do this from the Guild Members page. Want to open it in a new tab?')) {
  415. window.open('https://www.grundos.cafe/guilds/', '_blank');
  416. } else {
  417. console.log('User declined. No action occurred.');
  418. }
  419. }
  420. }
  421. $(SyncGuild).on('click', syncGuildMembers);
  422.  
  423.  
  424.