Skip to content

Commit

Permalink
CSF currency and agent support
Browse files Browse the repository at this point in the history
  • Loading branch information
GODrums committed Dec 7, 2023
1 parent 2ff04f9 commit b23d04e
Show file tree
Hide file tree
Showing 12 changed files with 583 additions and 227 deletions.
23 changes: 23 additions & 0 deletions html/changelog.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
<div class="Group">
<div class="VersionHeader">1.13.4</div>
<div class="VersionBox">
<p class="VersionSubHeader">
What's new?
</p>
<ul class="VersionText">
<li><b>FEATURE:</b> CSFloat - Support for all newly introduced currencies on CSFloat. The extension uses the same exchange rates as CSFloat. Remember that your account balance will always be USD and CSFloat's exchange rates may not represent real world rates.</li>
</ul>
</div>
</div>
<div class="Group">
<div class="VersionHeader">1.13.3</div>
<div class="VersionBox">
<p class="VersionSubHeader">
What's new?
</p>
<ul class="VersionText">
<li><b>FEATURE:</b> CSFloat - Items without a screenshot now have a button that allows you to generate screenshots via Swap.gg.</li>
<li><b>FEATURE:</b> CSFloat - Support for CSFloat's newly introduced agents.</li>
</ul>
</div>
</div>
<div class="Group">
<div class="VersionHeader">1.13.0</div>
<div class="VersionBox">
Expand Down
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "BetterFloat",
"author": "Rums",
"version": "1.13.2",
"version_name": "1.13.2",
"version": "1.13.4",
"version_name": "1.13.4",
"description": "Enhance your experience on CSFloat.com, Skinport.com & Skinbid.com!",
"manifest_version": 3,
"host_permissions": ["*://prices.csgotrader.app/*", "*://*.csfloat.com/*", "*://*.skinport.com/*", "*://*.skinbid.com/*"],
Expand Down
2 changes: 1 addition & 1 deletion manifest_firefox.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"name": "Rums",
"url": "https://github.com/GODrums"
},
"version": "1.13.2",
"version": "1.13.4",
"description": "Enhance your experience on CSFloat.com, Skinport.com & Skinbid.com!",
"manifest_version": 3,
"host_permissions": ["*://prices.csgotrader.app/*", "*://*.csfloat.com/*", "*://*.skinport.com/*", "*://*.skinbid.com/*"],
Expand Down
1 change: 1 addition & 0 deletions public/camera-add-solid.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/@typings/ExtensionTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ export namespace Extension {
skinbid: boolean;
};

export type CustomPriceMapping = {
[name: string]: {
bid: number; // 105
ask: number; // 167
avg30: number; // 175
liquidity: number; // 78.14
};
}
export type ApiBuffResponse = {
data: CustomPriceMapping,
time: number;
cached: boolean;
};

/**
* Mapping corresponding to the response from https://prices.csgotrader.app/latest/buff163.json
*/
Expand Down
17 changes: 17 additions & 0 deletions src/@typings/FloatTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export namespace CSFloat {
float: number;
price: number;
bargain: false | number;
currency: string;
};

export type ListingData = {
Expand Down Expand Up @@ -101,6 +102,22 @@ export namespace CSFloat {
wear: number;
};

// https://csfloat.com/api/v1/meta/location
export type Location = {
inferred_location: {
currency: string;
long: string; // country
short: string; // country code
}
}

// https://csfloat.com/api/v1/meta/exchange-rates
export type ExchangeRates = {
data: {
[key: string]: number;
}
};

export type HistoryGraphData = {
avg_price: number;
count: number;
Expand Down
97 changes: 80 additions & 17 deletions src/csfloat/content_script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BlueGem, Extension, FadePercentage } from '../@typings/ExtensionTypes';
import { activateHandler } from '../eventhandler';
import {
getBuffMapping,
getCSFCurrencyRate,
getCSFPopupItem,
getCrimsonWebMapping,
getFirstCSFItem,
Expand Down Expand Up @@ -566,6 +567,8 @@ function applyMutation() {
} else if (location.pathname == '/profile/offers' && addedNode.className.toString().includes('mat-list-item')) {
// offer list in offers page
offerItemClickListener(addedNode);
} else if (addedNode.tagName.toLowerCase() == 'app-markdown-dialog') {
adjustCurrencyChangeNotice(addedNode);
}
}
}
Expand All @@ -581,6 +584,33 @@ function applyMutation() {
observer.observe(document, { childList: true, subtree: true });
}

function adjustCurrencyChangeNotice(container: Element) {
const warningDiv = document.createElement('div');
warningDiv.setAttribute('style', 'display: flex; align-items: center; background-color: hsl(0deg 100% 27.25% / 50%); border-radius: 18px;');
const warningSymbol = document.createElement('img');
warningSymbol.setAttribute('src', extensionSettings.runtimePublicURL + '/triangle-exclamation-solid.svg');
warningSymbol.style.height = '30px';
warningSymbol.style.margin = '0 10px';
warningSymbol.style.filter = 'brightness(0) saturate(100%) invert(87%) sepia(66%) saturate(5511%) hue-rotate(102deg) brightness(103%) contrast(104%)';
const warningText = document.createElement('p');
warningText.textContent = 'Please note that BetterFloat requires a page refresh after changing the currency.';
warningDiv.appendChild(warningSymbol);
warningDiv.appendChild(warningText);

const refreshContainer = document.createElement('div');
refreshContainer.setAttribute('style', 'display: flex; align-items: center; justify-content: center; margin-top: 15px;');
const refreshButton = document.createElement('button');
refreshButton.className = 'mat-raised-button mat-warn';
refreshButton.textContent = 'Refresh';
refreshButton.onclick = () => {
location.reload();
};
refreshContainer.appendChild(refreshButton);

container.children[0].appendChild(warningDiv);
container.children[0].appendChild(refreshContainer);
}

async function adjustItemBubble(container: Element) {
const buffData: { buff_name: string; priceFromReference: number } = JSON.parse(document.querySelector('.betterfloat-buffprice')?.getAttribute('data-betterfloat') ?? '{}');
const bargainPrice = Number(container.querySelector('b')?.textContent?.replace('$', ''));
Expand Down Expand Up @@ -656,6 +686,7 @@ async function adjustSalesTableRow(container: Element) {

async function adjustItem(container: Element, isPopout = false) {
const item = getFloatItem(container);
// console.log('[BetterFloat] Adjusting item:', item);
if (Number.isNaN(item.price)) return;
const priceResult = await addBuffPrice(item, container, isPopout);
const cachedItem = getFirstCSFItem();
Expand All @@ -681,6 +712,7 @@ async function adjustItem(container: Element, isPopout = false) {
removeImageElements(container);
}
await patternDetections(container, cachedItem, false);
addScreenshotReplacement(container, cachedItem);
} else if (isPopout) {
// need timeout as request is only sent after popout is loaded
setTimeout(async () => {
Expand All @@ -698,6 +730,7 @@ async function adjustItem(container: Element, isPopout = false) {
await patternDetections(container, apiItem, true);
await addFloatColoring(container, apiItem);
addQuickLinks(container, apiItem);
addScreenshotReplacement(container, apiItem);
}

// last as it has to wait for history api data
Expand All @@ -706,6 +739,13 @@ async function adjustItem(container: Element, isPopout = false) {
}
}

function addScreenshotReplacement(container: Element, listing: CSFloat.ListingData) {
const detailButtons = container.querySelector('.detail-buttons');
if (detailButtons && container.querySelectorAll('.detail-buttons > button').length == 0 && !detailButtons.querySelector('.bf-tooltip') && listing.item.inspect_link && listing.item.type == 'skin') {
CSFloatHelpers.addReplacementScreenshotButton(detailButtons, '#06dedf', `https://swap.gg/screenshot?inspectLink=${listing.item.inspect_link}`, extensionSettings.runtimePublicURL);
}
}

function addQuickLinks(container: Element, listing: CSFloat.ListingData) {
const actionsContainer = document.querySelector('.item-actions');
if (!actionsContainer) return;
Expand Down Expand Up @@ -815,7 +855,7 @@ async function patternDetections(container: Element, listing: CSFloat.ListingDat
await badgeCKimono(container, item);
} else if (item.item_name.includes('Phoenix Blacklight')) {
await badgePhoenix(container, item);
} else if (item.item_name.includes('Gamma Doppler') && item.phase == 'Phase 3') {
} else if (item.item_name.includes('Karambit | Gamma Doppler') && item.phase == 'Phase 3') {
await badgeCyanbit(container, item);
} else if (item.item_name.includes('Overprint')) {
await badgeOverprint(container, item);
Expand Down Expand Up @@ -1394,12 +1434,25 @@ function getFloatItem(container: Element): CSFloat.FloatItem {
const header_details = <Element>nameContainer?.childNodes[1];

const name = nameContainer?.querySelector('.item-name')?.textContent?.replace('\n', '').trim();
const priceText = priceContainer?.textContent?.trim().split(' ') ?? [];
let priceText = priceContainer?.textContent?.trim().split(/\s/) ?? [];
let price: string;
let currency = '$';
if (location.pathname === '/sell') {
price = priceText[1].split('Price')[1];
} else if (priceText.includes('Bids')) {
price = '0';
} else {
price = priceText.includes('Bids') ? '0' : priceText[0];
let pricingText = priceText[0];
if (pricingText.split(/\s/).length > 1) {
let parts = pricingText.replace(',', '').replace('.', '').split(/\s/);
price = String(Number(parts.filter((x) => !isNaN(+x)).join('')) / 100);
currency = parts.filter((x) => isNaN(+x))[0];
} else {
const firstDigit = Array.from(pricingText).findIndex((x) => !isNaN(Number(x)));
currency = pricingText.substring(0, firstDigit);
price = String(Number(pricingText.substring(firstDigit).replace(',', '').replace('.', '')) / 100);
}
console.log(currency, price);
}
let condition: ItemCondition = '';
let quality = '';
Expand All @@ -1410,7 +1463,7 @@ function getFloatItem(container: Element): CSFloat.FloatItem {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
const text = node.textContent?.trim();
if (text && (text.includes('StatTrak') || text.includes('Souvenir') || text.includes('Container') || text.includes('Sticker'))) {
if (text && (text.includes('StatTrak') || text.includes('Souvenir') || text.includes('Container') || text.includes('Sticker') || text.includes('Agent'))) {
// TODO: integrate the ItemQuality type
// https://stackoverflow.com/questions/51528780/typescript-check-typeof-against-custom-type
quality = text;
Expand All @@ -1436,8 +1489,9 @@ function getFloatItem(container: Element): CSFloat.FloatItem {
style: style,
condition: condition,
float: Number(floatContainer?.querySelector('.ng-star-inserted')?.textContent ?? 0),
price: price?.includes('Bids') ? 0 : Number(price?.split(' ver')[0].split(' ')[0].trim().replace('$', '').replace(',', '')),
price: price?.includes('Bids') ? 0 : parseInt(price),
bargain: false,
currency: currency,
};
}

Expand All @@ -1447,14 +1501,21 @@ async function getBuffItem(item: CSFloat.FloatItem) {

const { priceListing, priceOrder } = await getBuffPrice(buff_name, item.style);

const userCurrency = document.querySelector('mat-select-trigger')?.textContent?.trim() ?? 'USD';
let currencyRate = await getCSFCurrencyRate(userCurrency);
if (!currencyRate) {
console.log('[BetterFloat] Could not get currency rate for ' + userCurrency);
currencyRate = 1;
}

const priceFromReference = extensionSettings.priceReference == 1 ? priceListing : priceOrder;
return {
buff_name: buff_name,
buff_id: buff_id,
priceListing: priceListing,
priceOrder: priceOrder,
priceFromReference: priceFromReference,
difference: item.price - priceFromReference,
priceListing: priceListing * currencyRate,
priceOrder: priceOrder * currencyRate,
priceFromReference: priceFromReference * currencyRate,
difference: item.price - (priceFromReference * currencyRate),
};
}

Expand All @@ -1469,6 +1530,8 @@ async function addBuffPrice(

let suggestedContainer = container.querySelector('.reference-container');
const showBoth = extensionSettings.showSteamPrice || isPopout;
const userCurrency = document.querySelector('mat-select-trigger')?.textContent?.trim() ?? 'USD';
const CurrencyFormatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: userCurrency, minimumFractionDigits: 0, maximumFractionDigits: 2 });

if (!suggestedContainer && location.pathname === '/sell') {
suggestedContainer = document.createElement('div');
Expand Down Expand Up @@ -1503,15 +1566,15 @@ async function addBuffPrice(
buffPrice.appendChild(tooltipSpan);
const buffPriceBid = document.createElement('span');
buffPriceBid.setAttribute('style', 'color: orange;');
buffPriceBid.textContent = `Bid ${USDollar.format(priceOrder)}`;
buffPriceBid.textContent = `Bid ${CurrencyFormatter.format(priceOrder)}`;
buffPrice.appendChild(buffPriceBid);
const buffPriceDivider = document.createElement('span');
buffPriceDivider.setAttribute('style', 'color: gray;margin: 0 3px 0 3px;');
buffPriceDivider.textContent = '|';
buffPrice.appendChild(buffPriceDivider);
const buffPriceAsk = document.createElement('span');
buffPriceAsk.setAttribute('style', 'color: greenyellow;');
buffPriceAsk.textContent = `Ask ${USDollar.format(priceListing)}`;
buffPriceAsk.textContent = `Ask ${CurrencyFormatter.format(priceListing)}`;
buffPrice.appendChild(buffPriceAsk);
buffContainer.appendChild(buffPrice);

Expand Down Expand Up @@ -1557,18 +1620,18 @@ async function addBuffPrice(
let differenceSymbol;
if (difference < 0) {
backgroundColor = extensionSettings.colors.csfloat.profit;
differenceSymbol = '-$';
differenceSymbol = '-';
} else if (difference > 0) {
backgroundColor = extensionSettings.colors.csfloat.loss;
differenceSymbol = '+$';
differenceSymbol = '+';
} else {
backgroundColor = extensionSettings.colors.csfloat.neutral;
differenceSymbol = '-$';
differenceSymbol = '-';
}

const buffPriceHTML = `<span class="sale-tag betterfloat-sale-tag" style="background-color: ${backgroundColor};" data-betterfloat="${difference}">${differenceSymbol}${Math.abs(
const buffPriceHTML = `<span class="sale-tag betterfloat-sale-tag" style="background-color: ${backgroundColor};" data-betterfloat="${difference}">${differenceSymbol}${CurrencyFormatter.format(Math.abs(
difference
).toFixed(2)} ${extensionSettings.showBuffPercentageDifference ? ' (' + ((item.price / priceFromReference) * 100).toFixed(2) + '%)' : ''}</span>`;
))} ${extensionSettings.showBuffPercentageDifference ? ' (' + ((item.price / priceFromReference) * 100).toFixed(2) + '%)' : ''}</span>`;
if (item.price > 1999 && extensionSettings.showBuffPercentageDifference) parseHTMLString('<br>', priceContainer);

parseHTMLString(buffPriceHTML, priceContainer);
Expand All @@ -1583,7 +1646,7 @@ function createBuffName(item: CSFloat.FloatItem): string {
let full_name = `${item.name}`;
if (item.quality.includes('Sticker')) {
full_name = 'Sticker | ' + full_name;
} else if (!item.quality.includes('Container')) {
} else if (!item.quality.includes('Container') && !item.quality.includes('Agent')) {
if (item.quality.includes('StatTrak') || item.quality.includes('Souvenir')) {
full_name = full_name.includes('★') ? `★ StatTrak™ ${full_name.split('★ ')[1]}` : `${item.quality} ${full_name}`;
}
Expand Down
23 changes: 17 additions & 6 deletions src/csfloat/csfloat_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export namespace CSFloatHelpers {
badgeContainer.appendChild(badge);
}

export function addReplacementScreenshotButton(detailButtons: Element, color: string, href: string) {
export function addReplacementScreenshotButton(detailButtons: Element, color: string, href: string, runtimePublicURL?: string) {
detailButtons.setAttribute('style', 'display: flex;');
const outerContainer = document.createElement('div');
outerContainer.className = 'bf-tooltip';
Expand All @@ -73,16 +73,27 @@ export namespace CSFloatHelpers {
iconButton.setAttribute('style', `color: ${buttonColor};`);
const iconSpan = document.createElement('span');
iconSpan.className = 'mat-button-wrapper';
const icon = document.createElement('i');
icon.className = 'material-icons';
icon.textContent = 'camera_alt';
iconSpan.appendChild(icon);
if (runtimePublicURL) {
const icon = document.createElement('img');
icon.setAttribute('style', 'width: 24px; height: 24px;');
icon.setAttribute('src', runtimePublicURL + '/camera-add-solid.svg');
iconSpan.appendChild(icon);
} else {
const icon = document.createElement('i');
icon.className = 'material-icons';
icon.textContent = 'camera_alt';
iconSpan.appendChild(icon);
}
iconButton.appendChild(iconSpan);
screenshotButton.appendChild(iconButton);
let tooltip = document.createElement('div');
tooltip.className = 'bf-tooltip-inner';
let tooltipSpan = document.createElement('span');
tooltipSpan.textContent = 'Show pattern screenshot';
if (runtimePublicURL) {
tooltipSpan.textContent = 'Generate Swap.gg screenshot';
} else {
tooltipSpan.textContent = 'Show pattern screenshot';
}
tooltip.appendChild(tooltipSpan);
outerContainer.appendChild(screenshotButton);
outerContainer.appendChild(tooltip);
Expand Down
6 changes: 6 additions & 0 deletions src/eventhandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
cacheSkinbidUserCurrency,
cacheSpItems,
cacheSpPopupItem,
cacheCSFLocation,
cacheCSFExchangeRates,
} from './mappinghandler';
import { handleListed, handleSold } from './skinport/websockethandler';

Expand Down Expand Up @@ -162,6 +164,10 @@ function processCSFloatEvent(eventData: EventData<unknown>) {
// item table - last sales
cacheCSFHistorySales(eventData.data as CSFloat.HistorySalesData[]);
}
} else if (eventData.url.includes('v1/meta/location')) {
cacheCSFLocation(eventData.data as CSFloat.Location);
} else if (eventData.url.includes('v1/meta/exchange-rates')) {
cacheCSFExchangeRates(eventData.data as CSFloat.ExchangeRates);
} else if (eventData.url.includes('v1/me')) {
// user data, repeats often
} else if (eventData.url.includes('v1/listings/') && eventData.url.split('/').length == 7) {
Expand Down
Loading

0 comments on commit b23d04e

Please sign in to comment.