Skip to content

Commit bc01dd5

Browse files
authored
Merge pull request #234 from csfloat/feature/trade-cancel-pings
Implements Cancel Trade Pings
2 parents 87dce81 + 9967ea1 commit bc01dd5

6 files changed

Lines changed: 125 additions & 19 deletions

File tree

src/lib/alarms/csfloat_trade_pings.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Trade} from '../types/float_market';
22
import {FetchPendingTrades} from '../bridge/handlers/fetch_pending_trades';
33
import {pingTradeHistory} from './trade_history';
4-
import {pingSentTradeOffers} from './trade_offer';
4+
import {pingCancelTrades, pingSentTradeOffers} from './trade_offer';
55
import {HasPermissions} from '../bridge/handlers/has_permissions';
66
import {PingExtensionStatus} from '../bridge/handlers/ping_extension_status';
77

@@ -53,4 +53,10 @@ export async function pingTradeStatus() {
5353
} catch (e) {
5454
console.error('failed to ping sent trade offer state', e);
5555
}
56+
57+
try {
58+
await pingCancelTrades(pendingTrades);
59+
} catch (e) {
60+
console.error('failed to ping cancel ping trade offers', e);
61+
}
5662
}

src/lib/alarms/trade_offer.ts

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {TradeOfferState} from '../types/steam_constants';
2-
import {Trade} from '../types/float_market';
2+
import {Trade, TradeState} from '../types/float_market';
33
import {TradeOfferStatus, TradeOffersType} from '../bridge/handlers/trade_offer_status';
44
import {clearAccessTokenFromStorage, getAccessToken} from './access_token';
55
import {AnnotateOffer} from '../bridge/handlers/annotate_offer';
6+
import {PingCancelTrade} from '../bridge/handlers/ping_cancel_trade';
67

78
interface OfferStatus {
89
offer_id: string;
@@ -64,6 +65,44 @@ export async function pingSentTradeOffers(pendingTrades: Trade[]) {
6465
}
6566
}
6667

68+
export async function pingCancelTrades(pendingTrades: Trade[]) {
69+
const hasWaitForCancelPing = pendingTrades.find((e) => e.state === TradeState.PENDING && e.wait_for_cancel_ping);
70+
if (!hasWaitForCancelPing) {
71+
// Nothing to process/ping, exit
72+
return;
73+
}
74+
75+
const tradeOffers = await getSentAndReceivedTradeOffersFromAPI();
76+
77+
const allTradeOffers = [...(tradeOffers.sent || []), ...(tradeOffers.received || [])];
78+
79+
for (const trade of pendingTrades) {
80+
if (trade.state !== TradeState.PENDING) {
81+
continue;
82+
}
83+
84+
if (!trade.wait_for_cancel_ping) {
85+
continue;
86+
}
87+
88+
const tradeOffer = allTradeOffers.find((e) => e.offer_id === trade.steam_offer.id);
89+
if (
90+
tradeOffer &&
91+
(tradeOffer.state === TradeOfferState.Active ||
92+
tradeOffer.state === TradeOfferState.Accepted ||
93+
tradeOffer.state === TradeOfferState.CreatedNeedsConfirmation)
94+
) {
95+
// We don't want to send a cancel ping if the offer is active or valid
96+
continue;
97+
}
98+
99+
try {
100+
await PingCancelTrade.handleRequest({trade_id: trade.id}, {});
101+
} catch (e) {
102+
console.error(`failed to send cancel ping for trade ${trade.id}`, e);
103+
}
104+
}
105+
}
67106
async function getEnglishSentTradeOffersHTML(): Promise<string> {
68107
const resp = await fetch(`https://steamcommunity.com/id/me/tradeoffers/sent`, {
69108
credentials: 'include',
@@ -90,7 +129,7 @@ async function getEnglishSentTradeOffersHTML(): Promise<string> {
90129

91130
async function getSentTradeOffers(): Promise<{offers: OfferStatus[]; type: TradeOffersType}> {
92131
try {
93-
const offers = await getTradeOffersFromAPI();
132+
const offers = await getSentTradeOffersFromAPI();
94133
if (offers.length > 0) {
95134
// Hedge in case this endpoint gets killed, only return if there are results, fallback to HTML parser
96135
return {offers, type: TradeOffersType.API};
@@ -109,19 +148,31 @@ interface TradeOfferItem {
109148
assetid: string;
110149
}
111150

151+
interface TradeOffersAPIOffer {
152+
tradeofferid: string;
153+
accountid_other: string;
154+
trade_offer_state: TradeOfferState;
155+
items_to_give?: TradeOfferItem[];
156+
items_to_receive?: TradeOfferItem[];
157+
}
158+
112159
interface TradeOffersAPIResponse {
113160
response: {
114-
trade_offers_sent: {
115-
tradeofferid: string;
116-
accountid_other: string;
117-
trade_offer_state: TradeOfferState;
118-
items_to_give?: TradeOfferItem[];
119-
items_to_receive?: TradeOfferItem[];
120-
}[];
161+
trade_offers_sent: TradeOffersAPIOffer[];
162+
trade_offers_received: TradeOffersAPIOffer[];
121163
};
122164
}
123165

124-
async function getTradeOffersFromAPI(): Promise<OfferStatus[]> {
166+
function offerStateMapper(e: TradeOffersAPIOffer): OfferStatus {
167+
return {
168+
offer_id: e.tradeofferid,
169+
state: e.trade_offer_state,
170+
given_asset_ids: (e.items_to_give || []).map((e) => e.assetid),
171+
received_asset_ids: (e.items_to_receive || []).map((e) => e.assetid),
172+
} as OfferStatus;
173+
}
174+
175+
async function getSentTradeOffersFromAPI(): Promise<OfferStatus[]> {
125176
const accessToken = await getAccessToken();
126177

127178
const resp = await fetch(
@@ -136,14 +187,28 @@ async function getTradeOffersFromAPI(): Promise<OfferStatus[]> {
136187
}
137188

138189
const data = (await resp.json()) as TradeOffersAPIResponse;
139-
return data.response.trade_offers_sent.map((e) => {
140-
return {
141-
offer_id: e.tradeofferid,
142-
state: e.trade_offer_state,
143-
given_asset_ids: (e.items_to_give || []).map((e) => e.assetid),
144-
received_asset_ids: (e.items_to_receive || []).map((e) => e.assetid),
145-
} as OfferStatus;
146-
});
190+
return data.response.trade_offers_sent.map(offerStateMapper);
191+
}
192+
193+
async function getSentAndReceivedTradeOffersFromAPI(): Promise<{received: OfferStatus[]; sent: OfferStatus[]}> {
194+
const accessToken = await getAccessToken();
195+
196+
const resp = await fetch(
197+
`https://api.steampowered.com/IEconService/GetTradeOffers/v1/?access_token=${accessToken}&get_received_offers=true&get_sent_offers=true`,
198+
{
199+
credentials: 'include',
200+
}
201+
);
202+
203+
if (resp.status !== 200) {
204+
throw new Error('invalid status');
205+
}
206+
207+
const data = (await resp.json()) as TradeOffersAPIResponse;
208+
return {
209+
received: data.response.trade_offers_received.map(offerStateMapper),
210+
sent: data.response.trade_offers_sent.map(offerStateMapper),
211+
};
147212
}
148213

149214
const BANNER_TO_STATE: {[banner: string]: TradeOfferState} = {

src/lib/bridge/handlers/handlers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {TradeOfferStatus} from './trade_offer_status';
1616
import {HasPermissions} from './has_permissions';
1717
import {PingSetupExtension} from './ping_setup_extension';
1818
import {PingExtensionStatus} from './ping_extension_status';
19+
import {PingCancelTrade} from './ping_cancel_trade';
1920

2021
export const HANDLERS_MAP: {[key in RequestType]: RequestHandler<any, any>} = {
2122
[RequestType.EXECUTE_SCRIPT_ON_PAGE]: ExecuteScriptOnPage,
@@ -34,4 +35,5 @@ export const HANDLERS_MAP: {[key in RequestType]: RequestHandler<any, any>} = {
3435
[RequestType.HAS_PERMISSIONS]: HasPermissions,
3536
[RequestType.PING_SETUP_EXTENSION]: PingSetupExtension,
3637
[RequestType.PING_EXTENSION_STATUS]: PingExtensionStatus,
38+
[RequestType.PING_CANCEL_TRADE]: PingCancelTrade,
3739
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import {SimpleHandler} from './main';
2+
import {RequestType} from './types';
3+
import {environment} from '../../../environment';
4+
import {Trade} from '../../types/float_market';
5+
6+
export interface PingCancelTradeRequest {
7+
trade_id: string;
8+
}
9+
10+
export interface PingCancelTradeResponse {
11+
trade: Trade;
12+
}
13+
14+
export const PingCancelTrade = new SimpleHandler<PingCancelTradeRequest, PingCancelTradeResponse>(
15+
RequestType.PING_CANCEL_TRADE,
16+
async (req) => {
17+
const resp = await fetch(`${environment.csfloat_base_api_url}/v1/trades/${req.trade_id}/cancel-ping`, {
18+
credentials: 'include',
19+
method: 'POST',
20+
});
21+
22+
if (resp.status !== 200) {
23+
throw new Error('invalid status');
24+
}
25+
26+
const trade = (await resp.json()) as Trade;
27+
return {
28+
trade,
29+
};
30+
}
31+
);

src/lib/bridge/handlers/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ export enum RequestType {
1515
HAS_PERMISSIONS,
1616
PING_SETUP_EXTENSION,
1717
PING_EXTENSION_STATUS,
18+
PING_CANCEL_TRADE,
1819
}

src/lib/types/float_market.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,5 @@ export interface Trade {
102102
state: TradeState;
103103
trade_url: string;
104104
steam_offer: SteamOffer;
105+
wait_for_cancel_ping?: boolean;
105106
}

0 commit comments

Comments
 (0)