This repository has been archived by the owner on Nov 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgcpd.ts
87 lines (69 loc) · 3.23 KB
/
gcpd.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// This module interfaces with GCPD. It recursively requests the GCPD pages (in
// normal use, they are asynchronously loaded when the user clicks a "Load more"
// button), and sending their content to the DOM parser, which extracts the
// match metadata from them.
import { EventName, GcpdError, GcpdTab, SyncStatus } from '../types/enums';
import { GcpdMatch, isParseSteamGcpdEventResponseBody, isSteamGcpdResponse } from '../types/interfaces';
import { LeetifyMatchUploader } from './leetify-match-uploader';
import { MatchSync } from './match-sync';
class Gcpd {
public async fetchAllMatches(tab: GcpdTab): Promise<GcpdMatch[] | GcpdError> {
const previouslyFoundMatchTimestamp = await LeetifyMatchUploader.getPreviouslyFoundMatchTimestamp(tab);
return this.fetchMatchesRecursively({ tab, previouslyFoundMatchTimestamp, matches: [] });
}
protected async fetchMatchesRecursively({
continueToken,
depth,
matches,
previouslyFoundMatchTimestamp,
tab,
}: {
continueToken?: string;
depth?: number;
matches: GcpdMatch[];
previouslyFoundMatchTimestamp: string | undefined;
tab: GcpdTab;
}): Promise<GcpdMatch[] | GcpdError> {
depth = depth === undefined ? 1 : depth;
await MatchSync.setStatus({ depth, status: SyncStatus.REQUESTING_GCPD_PAGE });
const url = new URL('https://steamcommunity.com/my/gcpd/730');
url.searchParams.set('ajax', '1');
url.searchParams.set('tab', tab);
if (continueToken) url.searchParams.set('continue_token', continueToken);
let res = await fetch(url);
// Steam sessions expire after about a day and need to be refreshed.
// We can do that by simply hitting the refresh endpoint, and asking it
// to redirect us back to our original target. If this fails, we'll
// error out and tell the user to log in to Steam.
if (res.url.startsWith('https://steamcommunity.com/login/home')) {
const refreshJwtUrl = new URL('https://login.steampowered.com/jwt/refresh');
refreshJwtUrl.searchParams.set('redir', url.toString());
res = await fetch(refreshJwtUrl);
}
if (!/^https:\/\/steamcommunity\.com\/(id\/[^/]+|profiles\/\d+)\/gcpd\/730/.test(res.url)) return GcpdError.STEAM_AUTH_FAILED;
const json = await res.json();
if (!isSteamGcpdResponse(json)) return GcpdError.INVALID_RESPONSE;
const parsed = await chrome.runtime.sendMessage({
event: EventName.REQUEST_PARSE_STEAM_GCPD,
data: { html: json.html, previouslyFoundMatchTimestamp },
});
if (!isParseSteamGcpdEventResponseBody(parsed)) return GcpdError.INVALID_RESPONSE;
matches.push(...parsed.matches);
const shouldEndRecursion = depth >= 16 // prevent this from going on too long
|| !json.continue_token // Steam won't let us request any more
|| parsed.cells > parsed.matches.length // either found no matches, or there were cells we couldn't parse
|| (json.continue_text.match(/^\d{4}-\d{2}-\d{2}$/) && +new Date(json.continue_text) < +new Date() - 30 * 24 * 60 * 60 * 1000); // we've looked back far enough, Valve doesn't keep demos for more than 30 days
if (shouldEndRecursion) return matches;
return this.fetchMatchesRecursively({
matches,
previouslyFoundMatchTimestamp,
tab,
continueToken: json.continue_token,
depth: depth + 1,
});
}
}
const singleton = new Gcpd();
export {
singleton as Gcpd,
};