// ==UserScript==
// @name [GC] Organize SW Results
// @namespace https://gf.qytechs.cn/en/users/1225524-kaitlin
// @match https://www.grundos.cafe/market/wizard*
// @match https://www.grundos.cafe/guilds/guild/*/members*
// @match https://www.grundos.cafe/block*
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js
// @require https://update.gf.qytechs.cn/scripts/487361/1389917/%5BGC%20%7C%20Library%5D%20-%20SSW%3A%20Savvy%20Shop%20Wiz.js
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.listValues
// @license MIT
// @version 2.0.1
// @author Cupkait
// @icon https://i.imgur.com/4Hm2e6z.png
// @description Blocked users can be synced but do not actually change anything in the SW results yet.
// ==/UserScript==
createSSWMenu();
addStyles();
let wizList = document.querySelector('.sw_results');
if (window.location.href.startsWith('https://www.grundos.cafe/market/wizard/') && wizList) {
generateNewGrid();
}
async function grabData() {
const resultsGrid = document.querySelector('.sw_results');
const resultsRaw = resultsGrid.querySelectorAll('.data');
const rawData = [];
for (var i = 0; i < resultsRaw.length;) {
var seller = resultsRaw[i].innerText;
console.log(seller)
var link = resultsRaw[i].innerHTML;
var stock = parseInt(resultsRaw[i + 2].innerText,10);
var price = parseInt(resultsRaw[i + 3].innerText.replace(/[^\d]/g, ''), 10);
var row = {
seller: seller,
link: link,
stock: stock,
price: price
};
rawData.push(row);
i = i + 4;
}
return rawData;
}
async function calculateValues() {
let rawData = await grabData();
let lowestPrice = Math.min(...rawData.map(item => item.price));
rawData.forEach(item => {
item.pricediff = item.price - lowestPrice;
});
return rawData;
}
async function getUsers() {
let friendList = await GM.getValue('friendList', '[]');
let guildsList = await GM.getValue('guildsList', '{}');
let blockList = await GM.getValue('blockList', '[]');
const guildMembers = Object.values(guildsList).flatMap(guild => guild.guildMembers);
if (typeof friendList === 'string') {
friendList = JSON.parse(friendList);
}
if (typeof guildMembers === 'string') {
guildMembers = JSON.parse(guildMembers);
}
if (typeof blockList === 'string') {
blockList = JSON.parse(blockList);
}
return { friendList, guildMembers, blockList };
}
async function createTableRow(item, friendList, guildMembers, blockList) {
var price = item.price.toLocaleString() + " NP";
let variance;
let friendstatus = friendList.includes(item.seller);
let guildstatus = guildMembers.includes(item.seller);
let blockstatus = blockList.includes(item.seller);
if (item.pricediff === 0) {
variance = '<span style="color:green">— LOWEST —</span>';
} else if (friendstatus || guildstatus) {
variance = `<span style="color:red">(+${item.pricediff.toLocaleString()})</span>`;
} else {
variance = '—';
}
let row = document.createElement('tr');
row.innerHTML = `
<td>${item.link}</td>
<td>${item.stock}</td>
<td><strong>${price}</strong></td>
<td><strong>${variance}</strong></td>
`;
if (friendstatus) {
row.classList.add('friend');
} else if (blockstatus) {
row.classList.add('blocked');
} else if (guildstatus) {
row.classList.add('guild');
}
return row;
}
async function generateNewGrid() {
const { friendList, guildMembers, blockList } = await getUsers();
const newGrid = document.createElement('table');
newGrid.innerHTML = `<thead><tr><th>Owner</th><th>Stock</th><th>Price</th><th>Variance</th></tr></thead>`;
let rawData = await calculateValues();
const newHeader = await updateHeader();
rawData.sort((a, b) => {
const aIsFriend = friendList.includes(a.seller);
const bIsFriend = friendList.includes(b.seller);
const aIsGuildMember = guildMembers.includes(a.seller);
const bIsGuildMember = guildMembers.includes(b.seller);
if ((aIsFriend || aIsGuildMember) && !(bIsFriend || bIsGuildMember)) return -1;
if (!(aIsFriend || aIsGuildMember) && (bIsFriend || bIsGuildMember)) return 1;
return 0;
});
const tbody = document.createElement('tbody');
for (const item of rawData) {
const row = await createTableRow(item, friendList, guildMembers, blockList);
tbody.appendChild(row);
}
newGrid.appendChild(tbody);
const oldGrid = document.querySelector('.sw_results');
oldGrid.insertAdjacentElement('beforebegin', newGrid);
oldGrid.style.display = 'none';
}
async function updateHeader() {
}
function addStyles() {
const newGridStyle = document.createElement('style');
newGridStyle.innerHTML = `
#sswmenu {
display: none;
border-radius: 15px 15px 15px 0px;
border-bottom: 3px solid;
position: absolute;
z-index: 999;
width: 250px;
height: 250px;
bottom: 0%;
left: 100%;
margin: -3px;
padding: 10px;
background-color: #d2d0cc;
box-shadow: 5px 0 5px rgba(0, 0, 0, 0.5);
}
#sswcontainer {
position: relative;
border-bottom: 3px solid;
padding: 5px 10px 5px 0px;
height: 30px;
width: 100%;
top: 00%;
left: 0px;
background-color: #d2d0cc;
box-shadow: 5px 0 5px rgba(0, 0, 0, 0.5);
}
#sswsettings {
position: relative;
font-size: 16px;
font-weight: bold;
font-family: courier;
border-radius: 5px;
padding: 5px;
width: 100%;
height: auto;
border: 1px solid rgb(204, 204, 204);
cursor: pointer;
background-color: lightgray;
}
#menubutton {
height: 35px;
width: 90%;
margin: 5px;
}
table {
position: relative;
min-width: 80%;
margin: 1em auto;
width: max-content;
max-width: 100%;
text-align: center;
border-collapse: collapse;
}
th,
td {
position: relative;
padding: 4px;
z-index: 3;
}
th {
background-color: var(--grid_head);
}
tr {
position: relative;
min-width: 100%;
margin: 1em auto;
width: max-content;
max-width: 100%;
text-align: center;
border-collapse: collapse;
}
tr:nth-child(even) {
background-color: var(--grid_even);
}
tr:nth-child(odd) {
background-color: var(--grid_odd);
}
.friend::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #00801a54;
z-index: 1;
}
.guild::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #2c008054;
z-index: 1;
}
.blocked {
opacity:10%
}
.blocked:hover {
opacity:85%
}
`;
document.head.appendChild(newGridStyle);
}
// SYNCING BELOW
function removeUsersManually() {
var userInput = prompt(
'Which username would you like to remove from your list? Enter one username then click OK.'
);
if (userInput !== null && userInput.trim() !== '') {
var index = friendList.findIndex(name => name
.toLowerCase() === userInput.trim().toLowerCase());
if (index !== -1) {
friendList.splice(index, 1);
GM.setValue('friendList', friendList);
console.log('Removed', userInput.trim(),
'from the friend list.');
} else {
console.log(userInput.trim(),
'not found in the friend list.');
}
if (window.location.href ===
'https://www.grundos.cafe/market/wizard/') {
if (confirm(
'Do you want to reload the page to see the updated results?'
)) {
location.reload();
}
}
} else {
console.log('No username entered. Operation canceled.');
}
}
$(ManualRemove).on('click', removeUsersManually);
async function syncFriendsList() {
try {
const response = await fetch(
'https://www.grundos.cafe/neofriends/');
const html = await response.text();
const doc = new DOMParser().parseFromString(html,
'text/html');
const uniqueUsernames = new Set();
doc.querySelectorAll(
'div.market_grid [href*="/userlookup/?user="]')
.forEach(function (element) {
uniqueUsernames.add(element.getAttribute(
'href').split('=')[1]);
});
console.log("Friends List Synced");
return Array.from(uniqueUsernames);
} catch (error) {
console.error('Error fetching and extracting usernames:',
error);
return [];
}
}
$(SyncFriends).on('click', async function () {
console.log("clicked")
const usernames = await syncFriendsList();
friendList = usernames;
GM.setValue('friendList', friendList);
console.log('Synced usernames:', usernames);
});
function syncBlockedList() {
if (window.location.href === 'https://www.grundos.cafe/block/') {
const blockList = $('.block_list').find('span').map(
function () {
return $(this).text();
}).get();
GM.setValue('blockList', blockList);
console.log('blockList stored:', blockList);
} else {
if (confirm(
'You can only do this from the Blocked Users page. Want to open it in a new tab?'
)) {
window.open('https://www.grundos.cafe/block/', '_blank');
} else {
console.log('User declined. No blocked sync occured.');
}
}
}
$(SyncBlocked).on('click', syncBlockedList);
function syncGuildMembers() {
if (/^https:\/\/www\.grundos\.cafe\/guilds\/guild\/.+\/members\//.test(window.location.href)) {
const userName = /user=(.*?)"/g.exec(document.body.innerHTML)[1];
const guildIdentifier = window.location.href.match(/guild\/([^\/-]+)/)?.[1];
const guildName = $('.guild-header strong').eq(0).text();
const guildID = "guildID_" + guildIdentifier;
const guildMembers = [...new Set($('div.member-grid [href*="/userlookup/?user="]')
.map(function() {
return $(this).attr('href').split('=')[1];
})
.filter(function(guildMember) {
return guildMember !== userName;
})
)];
GM.getValue('guildsList', {}).then(function(guildsList) {
guildsList[guildID] = {
'guildName': guildName,
'guildMembers': guildMembers
};
GM.setValue('guildsList', guildsList);
});
} else {
if (confirm('You can only do this from the Guild Members page. Want to open it in a new tab?')) {
window.open('https://www.grundos.cafe/guilds/', '_blank');
} else {
console.log('User declined. No action occurred.');
}
}
}
$(SyncGuild).on('click', syncGuildMembers);