@ekmanss/5eplay 20260716.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/async_queue.d.ts +6 -0
- package/dist/async_queue.js +44 -0
- package/dist/capture.d.ts +11 -0
- package/dist/capture.js +199 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +41 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +4 -0
- package/dist/errors.d.ts +20 -0
- package/dist/errors.js +42 -0
- package/dist/http.d.ts +23 -0
- package/dist/http.js +75 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +7 -0
- package/dist/input.d.ts +3 -0
- package/dist/input.js +45 -0
- package/dist/live_matches.d.ts +2 -0
- package/dist/live_matches.js +209 -0
- package/dist/log.d.ts +4 -0
- package/dist/log.js +153 -0
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +508 -0
- package/dist/mqtt.d.ts +29 -0
- package/dist/mqtt.js +285 -0
- package/dist/session.d.ts +2 -0
- package/dist/session.js +169 -0
- package/dist/transform.d.ts +21 -0
- package/dist/transform.js +466 -0
- package/dist/types.d.ts +504 -0
- package/dist/types.js +2 -0
- package/dist/value.d.ts +13 -0
- package/dist/value.js +76 -0
- package/dist/write_markdown.d.ts +11 -0
- package/dist/write_markdown.js +34 -0
- package/docs/data-contracts.md +113 -0
- package/docs/recipes.md +129 -0
- package/package.json +55 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Data contracts
|
|
2
|
+
|
|
3
|
+
## Match Detail 1.0.0
|
|
4
|
+
|
|
5
|
+
`getFiveEPlayMatch()` returns `{ data, diagnostics }`. `data` has `schemaVersion: '1.0.0'`,
|
|
6
|
+
`sport: 'cs2'`, a canonical 5EPlay source URL, and the following stable sections.
|
|
7
|
+
|
|
8
|
+
### Identity and series state
|
|
9
|
+
|
|
10
|
+
- `match` contains the `csgo_mc_*` identity, numeric projection, live status, CS2 version,
|
|
11
|
+
best-of, schedule, stage, and per-team series score.
|
|
12
|
+
- `tournament` contains the event identity, logo, grade, location, prize, dates, and source status.
|
|
13
|
+
- `teams` contains exactly the source teams with 5E/Valve ranks, logos, series scores, odds, and
|
|
14
|
+
displayed odds percentages.
|
|
15
|
+
- `veto` preserves source order and normalizes `ban`, `pick`, and `left` actions.
|
|
16
|
+
|
|
17
|
+
When an in-progress API response temporarily omits an unplayed decider from `bouts_state`, the
|
|
18
|
+
collector reconstructs that `upcoming` map from the authoritative veto. It does not invent scores
|
|
19
|
+
or player data.
|
|
20
|
+
|
|
21
|
+
### Maps
|
|
22
|
+
|
|
23
|
+
Every map contains:
|
|
24
|
+
|
|
25
|
+
- status, pick ownership, winner, score, current sides, first/second-half and overtime results;
|
|
26
|
+
- live round number/stage, round start, timer, bomb state, player health, money, armor, kit, weapon;
|
|
27
|
+
- `playerStats[].overall`, `.ct`, and `.t`, covering Rating, K-D-A, K/D, KD difference, KAST, ADR,
|
|
28
|
+
Round Swing, KPR, DPR, impact, multi-kill rating, headshots, first kills, flash assists, trades,
|
|
29
|
+
clutches, MVPs, and multi-kills;
|
|
30
|
+
- `playerDuels`, the exact Player Comparison matrix represented as typed player/opponent kill rows;
|
|
31
|
+
- public highlights and milestones;
|
|
32
|
+
- `eventLog.events` in chronological order.
|
|
33
|
+
|
|
34
|
+
An HTTP log page with `not_more: '1'` is marked `complete: true`. Each event retains the provider's
|
|
35
|
+
monotonic `updateVersion` and typed round, kill, assist, flash, bomb, join/leave, suicide, restart,
|
|
36
|
+
weapon, special-kill flag, and coordinate fields. Unknown future event types remain present as
|
|
37
|
+
`kind: 'unknown'` instead of being discarded.
|
|
38
|
+
|
|
39
|
+
`current` is the same live map object projected at the top level, or `null` when no map is live.
|
|
40
|
+
|
|
41
|
+
### Analysis and community ratings
|
|
42
|
+
|
|
43
|
+
`analysis` covers the visible pre-match team metrics, player metrics, map history, player-power
|
|
44
|
+
records, recent matches, and head-to-head matches. Provider-specific power records remain JSON
|
|
45
|
+
objects so new metrics are preserved without a schema bump.
|
|
46
|
+
|
|
47
|
+
`communityRatings` contains every public tab and returned card, including player, coach, and
|
|
48
|
+
big-event cards. Account-specific `my_*` voting state, write actions, and chat messages are not
|
|
49
|
+
returned.
|
|
50
|
+
|
|
51
|
+
### Missing values
|
|
52
|
+
|
|
53
|
+
Missing numeric/string scalars are `null`; collections are empty arrays. Source percentage strings
|
|
54
|
+
are returned as numbers without `%`. Provider IDs stay strings because team, player, tournament,
|
|
55
|
+
and match namespaces are distinct.
|
|
56
|
+
|
|
57
|
+
## Realtime updates
|
|
58
|
+
|
|
59
|
+
A `FiveEPlayMatchSession` is an async iterable of:
|
|
60
|
+
|
|
61
|
+
- `snapshot`: the initial complete HTTP result;
|
|
62
|
+
- `state`: a merged MQTT scoreboard/state update;
|
|
63
|
+
- `log`: one deduplicated MQTT event-log update.
|
|
64
|
+
|
|
65
|
+
Every update includes a complete immutable snapshot. MQTT messages can be partial: map states are
|
|
66
|
+
merged by `bout_num`, while match/tournament/global sections are shallow-merged over the verified
|
|
67
|
+
HTTP baseline. Log events are merged by `updateVersion` and remain chronological.
|
|
68
|
+
|
|
69
|
+
The credential endpoint's `client_id`, `username`, and `password` are transport-only. They are not
|
|
70
|
+
part of either contract.
|
|
71
|
+
|
|
72
|
+
## Live Matches 1.0.0
|
|
73
|
+
|
|
74
|
+
`getFiveEPlayLiveMatches()` returns a lightweight `{ data, diagnostics }` result intended for
|
|
75
|
+
frequent polling. `data.hasLiveMatches` is exactly `data.matches.length > 0`. Every match contains
|
|
76
|
+
its canonical ID/URL, BO format, schedule/stage, tournament identity, two teams and series scores,
|
|
77
|
+
available map summaries, and `currentMap`.
|
|
78
|
+
|
|
79
|
+
The schedule endpoint groups both live and upcoming rows. A row is included only when its series
|
|
80
|
+
state is live or it contains a live map. A series remains live between maps even though
|
|
81
|
+
`currentMap` is then `null`. Normal polling uses the source's first 20 ordered rows and one HTTP
|
|
82
|
+
request; another page is requested only when all 20 rows are live. No detail, analysis, event-log,
|
|
83
|
+
community-rating, Markdown, or MQTT credential request is made.
|
|
84
|
+
|
|
85
|
+
## Diagnostics
|
|
86
|
+
|
|
87
|
+
Diagnostics include operation timing, canonical input where applicable, warnings, and one sanitized
|
|
88
|
+
entry per HTTP request with only request kind, HTTP status, duration, byte size, and optional
|
|
89
|
+
map/tab/page identity.
|
|
90
|
+
URLs with credentials, request/response bodies, MQTT credentials, cookies, and chat state are never
|
|
91
|
+
included.
|
|
92
|
+
|
|
93
|
+
## Markdown Report 1.0.0
|
|
94
|
+
|
|
95
|
+
`writeFiveEPlayMatchMarkdown(input, target)` fetches a snapshot, renders a reader-focused report,
|
|
96
|
+
creates missing parent directories, and atomically replaces the destination. A target ending in
|
|
97
|
+
`.md` is the exact file; any other target is treated as a directory and receives
|
|
98
|
+
`<match-id>.md`. `renderFiveEPlayMatchMarkdown(result)` returns the same content without writing.
|
|
99
|
+
|
|
100
|
+
The report includes:
|
|
101
|
+
|
|
102
|
+
- a compact match/team overview and the complete veto;
|
|
103
|
+
- completed/live map scores, half splits, readable round sequences, player overview/side tables,
|
|
104
|
+
player duels, concise highlights/milestones, and readable event logs;
|
|
105
|
+
- a single-line placeholder for each upcoming map;
|
|
106
|
+
- compact team/player/map analysis, head-to-head scores, recent results, and seven player-power
|
|
107
|
+
category scores;
|
|
108
|
+
- sanitized capture diagnostics.
|
|
109
|
+
|
|
110
|
+
The report intentionally omits community ratings, odds, logos, schedule/event metadata, transport
|
|
111
|
+
versions, coordinates, raw round/milestone/power JSON, and provider-only codes. Those values remain
|
|
112
|
+
available in the typed Match Detail contract. The Markdown writer also disables the community
|
|
113
|
+
rating HTTP requests, while all other report data is fetched normally.
|
package/docs/recipes.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Manual recipes
|
|
2
|
+
|
|
3
|
+
Run these commands from the repository root after `pnpm build`.
|
|
4
|
+
|
|
5
|
+
## Poll for newly started matches
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
9
|
+
import { getFiveEPlayLiveMatches } from '@ekmanss/5eplay';
|
|
10
|
+
|
|
11
|
+
const known = new Set<string>();
|
|
12
|
+
|
|
13
|
+
while (true) {
|
|
14
|
+
const { data } = await getFiveEPlayLiveMatches({ timeoutMs: 5_000 });
|
|
15
|
+
for (const match of data.matches) {
|
|
16
|
+
if (!known.has(match.id)) {
|
|
17
|
+
console.log('started', match.id, match.url);
|
|
18
|
+
known.add(match.id);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
for (const id of [...known]) {
|
|
22
|
+
if (!data.matches.some((match) => match.id === id)) known.delete(id);
|
|
23
|
+
}
|
|
24
|
+
await delay(5_000);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Calls are serialized, so a slow request never overlaps the next poll. A normal call uses one list
|
|
29
|
+
request and performs no match-detail, log, analysis, community, Markdown, browser, or MQTT work.
|
|
30
|
+
|
|
31
|
+
## Generate the human-readable Markdown report
|
|
32
|
+
|
|
33
|
+
Pass an exact Markdown filename:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm 5eplay:md -- \
|
|
37
|
+
'https://event.5eplay.com/csgo/matches/csgo_mc_2395709' \
|
|
38
|
+
'./outputs/5eplay/csgo_mc_2395709.md'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or pass a directory and let the writer use `<match-id>.md`:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pnpm 5eplay:md -- 'csgo_mc_2395709' './outputs/5eplay'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The writer creates missing parent directories and atomically replaces an existing destination.
|
|
48
|
+
Community ratings are not requested because they are not part of the Markdown report.
|
|
49
|
+
|
|
50
|
+
From another Node.js program:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { writeFiveEPlayMatchMarkdown } from '@ekmanss/5eplay';
|
|
54
|
+
|
|
55
|
+
const written = await writeFiveEPlayMatchMarkdown(
|
|
56
|
+
'csgo_mc_2395709',
|
|
57
|
+
'/absolute/path/current-match.md',
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
console.log(written.outputPath, written.bytes);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Save a complete snapshot
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
FIVEEPLAY_MATCH_URL='https://event.5eplay.com/csgo/matches/csgo_mc_2395709' \
|
|
67
|
+
node --input-type=module <<'EOF' > match.json
|
|
68
|
+
import { getFiveEPlayMatch } from './packages/5eplay/dist/index.js';
|
|
69
|
+
|
|
70
|
+
const result = await getFiveEPlayMatch(process.env.FIVEEPLAY_MATCH_URL);
|
|
71
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
72
|
+
EOF
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Inspect maps and live players
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
jq '{
|
|
79
|
+
match: .data.match,
|
|
80
|
+
maps: [.data.maps[] | {number, name, status, teams}],
|
|
81
|
+
livePlayers: [.data.current.playerStats[]?.overall[]? | {
|
|
82
|
+
name,
|
|
83
|
+
health: .equipment.health,
|
|
84
|
+
money: .equipment.money,
|
|
85
|
+
weapon: .equipment.weapon,
|
|
86
|
+
kills: .metrics.kills,
|
|
87
|
+
deaths: .metrics.deaths,
|
|
88
|
+
assists: .metrics.assists,
|
|
89
|
+
adr: .metrics.adr
|
|
90
|
+
}]
|
|
91
|
+
}' match.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Stream realtime changes as NDJSON
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
FIVEEPLAY_MATCH_URL='csgo_mc_2395709' \
|
|
98
|
+
node --input-type=module <<'EOF'
|
|
99
|
+
import { createFiveEPlayMatchSession } from './packages/5eplay/dist/index.js';
|
|
100
|
+
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
process.once('SIGINT', () => controller.abort());
|
|
103
|
+
|
|
104
|
+
await using session = await createFiveEPlayMatchSession(
|
|
105
|
+
process.env.FIVEEPLAY_MATCH_URL,
|
|
106
|
+
{ signal: controller.signal },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
for await (const update of session) {
|
|
110
|
+
const compact = update.type === 'log'
|
|
111
|
+
? { type: update.type, capturedAt: update.capturedAt, event: update.event }
|
|
112
|
+
: { type: update.type, capturedAt: update.capturedAt, current: update.snapshot.current };
|
|
113
|
+
process.stdout.write(JSON.stringify(compact) + '\n');
|
|
114
|
+
}
|
|
115
|
+
EOF
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Compact scoreboard-only snapshot
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const result = await getFiveEPlayMatch(match, {
|
|
122
|
+
includeAnalysis: false,
|
|
123
|
+
includeCommunityRatings: false,
|
|
124
|
+
includeLogs: false,
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
This mode makes only the match-data request and is appropriate for high-frequency read-only
|
|
129
|
+
scoreboard refreshes when a persistent MQTT session is not desired.
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ekmanss/5eplay",
|
|
3
|
+
"version": "20260716.0.0",
|
|
4
|
+
"description": "Typed 5EPlay CS2 live-match discovery, match-detail snapshots, and MQTT updates without a browser.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"5eplay-match-md": "dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"docs",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=22"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"check": "tsc --noEmit",
|
|
28
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
|
|
29
|
+
"match:md": "tsx src/cli.ts",
|
|
30
|
+
"test": "tsx --test tests/match.test.ts",
|
|
31
|
+
"test:live": "tsx tests/live.ts",
|
|
32
|
+
"test:live:list": "tsx tests/live_list.ts",
|
|
33
|
+
"verify": "pnpm check && pnpm test && pnpm build",
|
|
34
|
+
"prepack": "pnpm verify"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "24.13.3",
|
|
38
|
+
"tsx": "4.23.0",
|
|
39
|
+
"typescript": "5.9.3"
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/ekmanss/sports-data-collectors.git",
|
|
44
|
+
"directory": "packages/5eplay"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/ekmanss/sports-data-collectors/tree/main/packages/5eplay#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/ekmanss/sports-data-collectors/issues"
|
|
49
|
+
},
|
|
50
|
+
"author": "ekmanss",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
}
|
|
55
|
+
}
|