Skip to content

Commit

Permalink
Skinport first launch support
Browse files Browse the repository at this point in the history
  • Loading branch information
GODrums committed Aug 29, 2023
1 parent 3658e8a commit 7fe032a
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 61 deletions.
1 change: 1 addition & 0 deletions css/skinport_styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@
.betterfloat-sale-tag {
font-size: 12px!important;
margin: 1px;
border-radius: 3px;
}
4 changes: 2 additions & 2 deletions html/skinport.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
<div class="GroupRow">
<p class="ElementText">Currency Conversion</p>
<select class="ElementDropdownMenu" id="SkinportCurrencyConversion" name="skinportRates">
<option value="real">Real</option>
<option value="skinport" selected>Skinport</option>
<option value="real" selected>Real</option>
<option value="skinport">Skinport</option>
</select>
</div>
<p class="ElementDescription">
Expand Down
242 changes: 183 additions & 59 deletions src/skinport/content_script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,7 @@ async function init() {
await loadMapping();
await loadBuffMapping();

// //check if url is in supported subpages
// if (url.endsWith('float.com/')) {
// await firstLaunch();
// } else {
// for (let i = 0; i < supportedSubPages.length; i++) {
// if (url.includes(supportedSubPages[i])) {
// await firstLaunch();
// }
// }
// }
await firstLaunch();

// mutation observer is only needed once
if (!isObserverActive) {
Expand All @@ -41,6 +32,27 @@ async function init() {
}
}

async function firstLaunch() {
let url = window.location.href;
console.log('[BetterFloat] First launch on Skinport, url: ', url);
if (url.includes('/market')) {
let catalogItems = document.querySelectorAll('.CatalogPage-item');
for (let item of catalogItems) {
await adjustItem(item);
}
} else if (url.includes('/cart')) {
let cartContainer = document.querySelector('.Cart-container');
if (cartContainer) {
await adjustCart(cartContainer);
}
} else if (url.includes('/item')) {
let itemPage = document.querySelectorAll('.ItemPage');
for (let item of itemPage) {
await adjustItemPage(item);
}
}
}

async function applyMutation() {
let observer = new MutationObserver(async (mutations) => {
if (extensionSettings.spBuffPrice) {
Expand All @@ -52,8 +64,14 @@ async function applyMutation() {

// console.log('[BetterFloat] Mutation observer triggered, added node:', addedNode);

if (addedNode.className && addedNode.className.toString().includes('CatalogPage-item')) {
await adjustItem(addedNode);
if (addedNode.className) {
if (addedNode.className.toString().includes('CatalogPage-item')) {
await adjustItem(addedNode);
} else if (addedNode.className.toString().includes('Cart-container')) {
await adjustCart(addedNode);
} else if (addedNode.className.toString() == 'ItemPage') {
await adjustItemPage(addedNode);
}
}
// item popout
// if (addedNode.tagName && addedNode.tagName.toLowerCase() == 'item-detail') {
Expand All @@ -69,8 +87,73 @@ async function applyMutation() {
observer.observe(document, { childList: true, subtree: true });
}

async function adjustItemPage(container: Element) {
console.log('[BetterFloat] Adjusting item page: ', container);
let itemRating = container.querySelector('.ItemPage-rating');
if (itemRating) {
itemRating.remove();
}

let btnGroup = container.querySelector('.ItemPage-btnGroup');
if (!btnGroup) return;
let newGroup = document.createElement('div');
newGroup.className = btnGroup.className ?? newGroup.className;
// if an item is sold, the original links are unclickable, hence we reproduce them
let links = container.querySelectorAll('.ItemPage-link');
let linkSteam = (Array.from(links).find((el) => el.innerHTML.includes('Steam')) as HTMLAnchorElement | null)?.href;
let linkInspect = (Array.from(links).find((el) => el.innerHTML.includes('Inspect')) as HTMLAnchorElement | null)?.href;
if (linkInspect) {
newGroup.insertAdjacentHTML('beforeend', `<button onclick="window.open('${linkInspect}');" type="button"><span>Inspect</span></button>`);
}
if (linkSteam) {
newGroup.insertAdjacentHTML('beforeend', `<button onclick="window.open('${linkSteam}');" type="button"><span>Steam</span></button>`);
}

let item = getFloatItem(container, itemSelectors.page);
console.log('[BetterFloat] Item: ', item);
if (!item) return;
let { buff_name: buff_name, priceListing, priceOrder } = await getBuffPrice(item);
let buffid = await getBuffMapping(buff_name);
let buffLink = buffid > 0 ? `https://buff.163.com/goods/${buffid}` : `https://buff.163.com/market/csgo#tab=selling&page_num=1&search=${encodeURIComponent(buff_name)}`;
newGroup.insertAdjacentHTML('beforeend', `<button onclick="window.open('${buffLink}','_blank');" type="button"><span>Buff</span></button>`);
btnGroup.after(newGroup);

let tooltipLink = <HTMLElement>container.querySelector('.ItemPage-value .Tooltip-link');
const currencySymbol = tooltipLink.textContent?.charAt(0);
let suggestedContainer = container.querySelector('.ItemPage-suggested');
if (suggestedContainer) {
generateBuffContainer(suggestedContainer as HTMLElement, priceListing, priceOrder, currencySymbol ?? '$', true);
}

const difference = item.price - (extensionSettings.spPriceReference == 0 ? priceOrder : priceListing);
let priceContainer = <HTMLElement>container.querySelector('.ItemPage-price');
if (priceContainer) {
let newContainer = document.createElement('div');
let saleTag = document.createElement('span');
newContainer.className = 'ItemPage-discount betterfloat-discount-container';
newContainer.style.background = `linear-gradient(135deg,#0073d5,${difference == 0 ? 'black' : difference < 0 ? 'green' : '#ce0000'})`;
newContainer.style.transform = 'skewX(-15deg)';
newContainer.style.borderRadius = '3px';
saleTag.style.margin = '5px';
saleTag.style.fontWeight = '700';
saleTag.textContent = difference == 0 ? `-${currencySymbol}0` : (difference > 0 ? '+' : '-') + currencySymbol + Math.abs(difference).toFixed(2);
newContainer.appendChild(saleTag);
priceContainer.appendChild(newContainer);
}
}

async function adjustCart(container: Element) {
if (extensionSettings.spCheckBoxes) {
let checkboxes = container.querySelectorAll('.Checkbox-input');
for (let checkbox of checkboxes) {
(checkbox as HTMLInputElement).click();
await new Promise((r) => setTimeout(r, 50)); // to avoid bot detection
}
}
}

async function adjustItem(container: Element) {
const item = getFloatItem(container);
const item = getFloatItem(container, itemSelectors.preview);
if (!item) return;
await addBuffPrice(item, container);
// if (extensionSettings.stickerPrices) {
Expand All @@ -81,14 +164,39 @@ async function adjustItem(container: Element) {
// }
}

function getFloatItem(container: Element): Skinport.Listing | null {
let name = container.querySelector('.ItemPreview-itemName')?.textContent ?? '';
const itemSelectors = {
preview: {
name: '.ItemPreview-itemName',
title: '.ItemPreview-itemTitle',
text: '.ItemPreview-itemText',
stickers: '.ItemPreview-stickers',
price: '.ItemPreview-price',
},
page: {
name: '.ItemPage-name',
title: '.ItemPage-title',
text: '.ItemPage-text',
stickers: '.ItemPage-stickers',
price: '.ItemPage-price',
},
} as const;

type ItemSelectors = (typeof itemSelectors)[keyof typeof itemSelectors];

function getFloatItem(container: Element, selector: ItemSelectors): Skinport.Listing | null {
let name = container.querySelector(selector.name)?.textContent ?? '';
if (name == '') {
return null;
}
let price = Number(container.querySelector('.ItemPreview-price .Tooltip-link')?.innerHTML.substring(1).replace(',', '')) ?? 0;
let type = container.querySelector('.ItemPreview-itemTitle')?.textContent ?? '';
let text = container.querySelector('.ItemPreview-itemText')?.innerHTML ?? '';
let price =
Number(
container
.querySelector(selector.price + ' .Tooltip-link')
?.innerHTML.substring(1)
.replace(',', '')
) ?? 0;
let type = container.querySelector(selector.title)?.textContent ?? '';
let text = container.querySelector(selector.text)?.innerHTML ?? '';

let style: ItemStyle = '';
if (name.includes('Doppler')) {
Expand All @@ -98,7 +206,7 @@ function getFloatItem(container: Element): Skinport.Listing | null {
}

let stickers: { name: string }[] = [];
let stickersDiv = container.querySelector('.ItemPreview-stickers');
let stickersDiv = container.querySelector(selector.stickers);
if (stickersDiv) {
for (let sticker of stickersDiv.children) {
let stickerName = sticker.children[0].getAttribute('alt');
Expand Down Expand Up @@ -142,18 +250,16 @@ function getFloatItem(container: Element): Skinport.Listing | null {
};
}

async function addBuffPrice(item: Skinport.Listing, container: Element): Promise<any> {
await loadMapping();
let buff_name = handleSpecialStickerNames(createBuffName(item));
async function getBuffPrice(item: Skinport.Listing): Promise<{ buff_name: string; priceListing: number; priceOrder: number }> {
let priceMapping = await getPriceMapping();
let buff_name = handleSpecialStickerNames(createBuffName(item));
let helperPrice: number | null = null;

if (!priceMapping[buff_name] || !priceMapping[buff_name]['buff163'] || !priceMapping[buff_name]['buff163']['starting_at'] || !priceMapping[buff_name]['buff163']['highest_order']) {
console.debug(`[BetterFloat] No price mapping found for ${buff_name}: `, container);
console.debug(`[BetterFloat] No price mapping found for ${buff_name}`);
helperPrice = await getInventoryHelperPrice(buff_name);
}

let buff_id = await getBuffMapping(buff_name);
// we cannot use the getItemPrice function here as it does not return the correct price for doppler skins
let priceListing = 0;
let priceOrder = 0;
Expand Down Expand Up @@ -188,6 +294,58 @@ async function addBuffPrice(item: Skinport.Listing, container: Element): Promise
priceOrder = priceOrder * currencyRate;
}

return { buff_name, priceListing, priceOrder };
}

async function generateBuffContainer(container: HTMLElement, priceListing: number, priceOrder: number, currencySymbol: string, isItemPage: boolean = false) {
container.className += ' betterfloat-buffprice';
let buffContainer = document.createElement('div');
buffContainer.style.display = 'flex';
buffContainer.style.marginTop = '5px';
buffContainer.style.alignItems = 'center';
if (!isItemPage) {
buffContainer.style.justifyContent = 'center';
}
let buffImage = document.createElement('img');
buffImage.setAttribute('src', runtimePublicURL + '/buff_favicon.png');
buffImage.setAttribute('style', `height: 20px; margin-right: 5px; ${isItemPage ? 'margin-bottom: 1px;' : ''}`);
buffContainer.appendChild(buffImage);
let buffPrice = document.createElement('div');
buffPrice.setAttribute('class', 'suggested-price betterfloat-buffprice');
if (isItemPage) {
buffPrice.style.fontSize = '18px';
}
let tooltipSpan = document.createElement('span');
tooltipSpan.setAttribute('class', 'betterfloat-buff-tooltip');
tooltipSpan.textContent = 'Bid: Highest buy order price; Ask: Lowest listing price';
buffPrice.appendChild(tooltipSpan);
let buffPriceBid = document.createElement('span');
buffPriceBid.setAttribute('style', 'color: orange;');
buffPriceBid.textContent = `Bid ${currencySymbol}${priceOrder.toFixed(2)}`;
buffPrice.appendChild(buffPriceBid);
let buffPriceDivider = document.createElement('span');
buffPriceDivider.setAttribute('style', 'color: gray;margin: 0 3px 0 3px;');
buffPriceDivider.textContent = '|';
buffPrice.appendChild(buffPriceDivider);
let buffPriceAsk = document.createElement('span');
buffPriceAsk.setAttribute('style', 'color: greenyellow;');
buffPriceAsk.textContent = `Ask ${currencySymbol}${priceListing.toFixed(2)}`;
buffPrice.appendChild(buffPriceAsk);
buffContainer.appendChild(buffPrice);
if (extensionSettings.spSteamPrice || isItemPage) {
let divider = document.createElement('div');
container.after(buffContainer);
container.after(divider);
} else {
container.replaceWith(buffContainer);
}
}

async function addBuffPrice(item: Skinport.Listing, container: Element): Promise<any> {
await loadMapping();
let { buff_name, priceListing, priceOrder } = await getBuffPrice(item);
let buff_id = await getBuffMapping(buff_name);

const presentationDiv = container.querySelector('.ItemPreview-mainAction');
if (presentationDiv) {
let buffLink = document.createElement('a');
Expand All @@ -209,41 +367,7 @@ async function addBuffPrice(item: Skinport.Listing, container: Element): Promise
const currencySymbol = tooltipLink.textContent?.charAt(0);
let priceDiv = container.querySelector('.ItemPreview-oldPrice');
if (priceDiv && !container.querySelector('.betterfloat-buffprice')) {
priceDiv.className += ' betterfloat-buffprice';
let buffContainer = document.createElement('div');
buffContainer.style.display = 'flex';
buffContainer.style.marginTop = '5px';
buffContainer.style.justifyContent = 'center';
let buffImage = document.createElement('img');
buffImage.setAttribute('src', runtimePublicURL + '/buff_favicon.png');
buffImage.setAttribute('style', 'height: 20px; margin-right: 5px');
buffContainer.appendChild(buffImage);
let buffPrice = document.createElement('div');
buffPrice.setAttribute('class', 'suggested-price betterfloat-buffprice');
let tooltipSpan = document.createElement('span');
tooltipSpan.setAttribute('class', 'betterfloat-buff-tooltip');
tooltipSpan.textContent = 'Bid: Highest buy order price; Ask: Lowest listing price';
buffPrice.appendChild(tooltipSpan);
let buffPriceBid = document.createElement('span');
buffPriceBid.setAttribute('style', 'color: orange;');
buffPriceBid.textContent = `Bid ${currencySymbol}${priceOrder.toFixed(2)}`;
buffPrice.appendChild(buffPriceBid);
let buffPriceDivider = document.createElement('span');
buffPriceDivider.setAttribute('style', 'color: gray;margin: 0 3px 0 3px;');
buffPriceDivider.textContent = '|';
buffPrice.appendChild(buffPriceDivider);
let buffPriceAsk = document.createElement('span');
buffPriceAsk.setAttribute('style', 'color: greenyellow;');
buffPriceAsk.textContent = `Ask ${currencySymbol}${priceListing.toFixed(2)}`;
buffPrice.appendChild(buffPriceAsk);
buffContainer.appendChild(buffPrice);
if (extensionSettings.spSteamPrice) {
let divider = document.createElement('div');
priceDiv.after(buffContainer);
priceDiv.after(divider);
} else {
priceDiv.replaceWith(buffContainer);
}
generateBuffContainer(priceDiv as HTMLElement, priceListing, priceOrder, currencySymbol ?? '$');
}

if (extensionSettings.spBuffDifference) {
Expand Down

0 comments on commit 7fe032a

Please sign in to comment.