Skip to content

Commit 5ff4355

Browse files
committed
Implements Ability to Annotate Offers on CSFloat
1 parent 262a725 commit 5ff4355

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {SimpleHandler} from './main';
2+
import {RequestType} from './types';
3+
4+
export interface AnnotateOfferRequest {
5+
assets_to_send: string[];
6+
assets_to_receive: string[];
7+
offer_id: string;
8+
}
9+
10+
export interface AnnotateOfferResponse {}
11+
12+
export const AnnotateOffer = new SimpleHandler<AnnotateOfferRequest, AnnotateOfferResponse>(
13+
RequestType.ANNOTATE_OFFER,
14+
async (req) => {
15+
const resp = await fetch(`https://csfloat.com/api/v1/trades/annotate-offer`, {
16+
credentials: 'include',
17+
method: 'POST',
18+
headers: {
19+
'Content-Type': 'application/json',
20+
},
21+
body: JSON.stringify(req),
22+
});
23+
24+
if (resp.status !== 200) {
25+
throw new Error('invalid status');
26+
}
27+
28+
return resp.json() as Promise<AnnotateOfferResponse>;
29+
}
30+
);

src/lib/bridge/handlers/handlers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {FetchSkinModel} from './fetch_skin_model';
1010
import {StorageRemove} from './storage_remove';
1111
import {RequestType} from './types';
1212
import {FetchExtensionFile} from './fetch_extension_file';
13+
import {AnnotateOffer} from './annotate_offer';
1314

1415
export const HANDLERS_MAP: {[key in RequestType]: RequestHandler<any, any>} = {
1516
[RequestType.EXECUTE_SCRIPT_ON_PAGE]: ExecuteScriptOnPage,
@@ -22,4 +23,5 @@ export const HANDLERS_MAP: {[key in RequestType]: RequestHandler<any, any>} = {
2223
[RequestType.FETCH_PENDING_TRADES]: FetchPendingTrades,
2324
[RequestType.FETCH_SKIN_MODEL]: FetchSkinModel,
2425
[RequestType.FETCH_EXTENSION_FILE]: FetchExtensionFile,
26+
[RequestType.ANNOTATE_OFFER]: AnnotateOffer,
2527
};

src/lib/bridge/handlers/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export enum RequestType {
99
FETCH_PENDING_TRADES,
1010
FETCH_SKIN_MODEL,
1111
FETCH_EXTENSION_FILE,
12+
ANNOTATE_OFFER,
1213
}

src/lib/page_scripts/trade_offer.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ import '../components/trade_offer/trade_item_holder_metadata';
33
import '../components/trade_offer/auto_fill';
44
import {ClassIdAndInstanceId, rgDescription, rgInventoryAsset, TradeInventory} from '../types/steam';
55
import {fetchRegisteredSteamAPIKey} from '../utils/key';
6+
import {deserializeForm} from '../utils/browser';
7+
import {AppId} from '../types/steam_constants';
8+
import {ClientSend} from '../bridge/client';
9+
import {AnnotateOffer} from '../bridge/handlers/annotate_offer';
10+
import {of} from 'rxjs';
611

712
init('src/lib/page_scripts/trade_offer.js', main);
813

914
async function main() {
15+
injectAnnotateOffer();
1016
injectInventoryFallback();
1117
}
1218

@@ -117,3 +123,59 @@ function injectInventoryFallback() {
117123
);
118124
};
119125
}
126+
127+
interface JsonTradeofferAsset {
128+
appid: number;
129+
contextid: string;
130+
amount: number;
131+
assetid: string;
132+
}
133+
134+
interface JsonTradeoffer {
135+
me: {
136+
assets: JsonTradeofferAsset[];
137+
};
138+
them: {
139+
assets: JsonTradeofferAsset[];
140+
};
141+
version: number;
142+
}
143+
144+
function injectAnnotateOffer() {
145+
// Annotate offers for use in CSFloat Market, if the user isn't logged into CSFloat this does nothing
146+
// Similarly if they don't have an active sale, it does nothing
147+
$J(document).on('ajaxComplete', async (event, request, settings) => {
148+
if (!settings.url.includes('tradeoffer/new/send')) {
149+
// Ignore requests that aren't a new trade offer
150+
return;
151+
}
152+
153+
const offer_id = request?.responseJSON?.tradeofferid;
154+
155+
if (!offer_id) {
156+
// Something wrong with the format
157+
return;
158+
}
159+
160+
let assets_to_send: string[] = [];
161+
let assets_to_receive: string[] = [];
162+
const deserialized = deserializeForm(settings.data) as {json_tradeoffer?: string};
163+
164+
if (deserialized && deserialized.json_tradeoffer) {
165+
try {
166+
const parsed = JSON.parse(deserialized.json_tradeoffer) as JsonTradeoffer;
167+
assets_to_send = parsed.me.assets.filter((e) => e.appid === AppId.CSGO).map((e) => e.assetid);
168+
assets_to_receive = parsed.them.assets.filter((e) => e.appid === AppId.CSGO).map((e) => e.assetid);
169+
} catch (e) {
170+
console.error('failed to parse json tradeoffer', e, deserialized.json_tradeoffer);
171+
// Still proceed with annotating the offer id on a best-effort
172+
}
173+
}
174+
175+
await ClientSend(AnnotateOffer, {
176+
assets_to_send,
177+
assets_to_receive,
178+
offer_id: offer_id,
179+
});
180+
});
181+
}

src/lib/utils/browser.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,23 @@ export function getQueryParameter(param: string): string | null {
1010
export function hasQueryParameter(param: string): boolean {
1111
return !!getQueryParameter(param);
1212
}
13+
14+
export function deserializeForm(serialized: string): any {
15+
if (serialized.slice(0, 1) === '?') {
16+
serialized = serialized.slice(1);
17+
}
18+
19+
if (!serialized) {
20+
return {};
21+
}
22+
23+
return serialized.split('&').reduce((acc: any, e) => {
24+
const pair = e.split('=');
25+
if (pair.length < 2) {
26+
return acc;
27+
}
28+
29+
acc[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
30+
return acc;
31+
}, {});
32+
}

0 commit comments

Comments
 (0)