Skip to content

Commit f38f7be

Browse files
authored
Merge pull request #198 from csfloat/feature/annotate-offers-csfloat
Implements Ability to Annotate Offers on CSFloat
2 parents 262a725 + 81307fc commit f38f7be

File tree

5 files changed

+114
-0
lines changed

5 files changed

+114
-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: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ 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';
610

711
init('src/lib/page_scripts/trade_offer.js', main);
812

913
async function main() {
14+
injectAnnotateOffer();
1015
injectInventoryFallback();
1116
}
1217

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

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)