haxball.js 2.1.2 → 2.1.4
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/README.md +1 -43
- package/package.json +6 -5
- package/scripts/nodeify.js +135 -0
- package/src/index.js +5106 -5041
- package/test/build.js +20 -0
- package/test/room.js +7 -26
package/README.md
CHANGED
|
@@ -89,48 +89,6 @@ You may run the server with `ts-node room.ts` instead of `node room.js`. To make
|
|
|
89
89
|
//...
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
#### 💻 (Optional) Modularize Room Script
|
|
93
|
-
You can build full room scripts as NPM packages, that export `roomBuilder` function. These functions can be easily imported by another packages (launchers, remote orchestrators, etc.).
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
// super-futsal-room/index.ts
|
|
97
|
-
|
|
98
|
-
import { Headless } from "haxball.js"
|
|
99
|
-
|
|
100
|
-
// Every user installing the package will have to
|
|
101
|
-
// implement this interface in order to run it.
|
|
102
|
-
interface RoomArgs {
|
|
103
|
-
token: string
|
|
104
|
-
timeLimit: number
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const roomBuilder = (HBInit: Headless, args: RoomArgs) => {
|
|
108
|
-
let room = HBInit({
|
|
109
|
-
roomName: "Hello TypeScript!",
|
|
110
|
-
token: args.token
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
room.setTimeLimit(args.timeLimit)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export default roomBuilder;
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
```ts
|
|
120
|
-
// room-launcher/index.ts
|
|
121
|
-
|
|
122
|
-
// Local path as example, but may refer to installed npm package
|
|
123
|
-
import roomBuilder from "../super-futsal-room";
|
|
124
|
-
import HaxballJS from "haxball.js";
|
|
125
|
-
|
|
126
|
-
HaxballJS.then((HBInit => roomBuilder(HBInit, {
|
|
127
|
-
// Interface for config arguments is provided by the room script author
|
|
128
|
-
// Not defining them results in TypeScript error
|
|
129
|
-
token: "YOUR_TOKEN_HERE",
|
|
130
|
-
timeLimit: 3
|
|
131
|
-
})))
|
|
132
|
-
```
|
|
133
|
-
|
|
134
92
|
---
|
|
135
93
|
|
|
136
94
|
<h2 id="technologies">🚀 Technologies</h2>
|
|
@@ -202,7 +160,7 @@ HaxballJS.then((HBInit => roomBuilder(HBInit, {
|
|
|
202
160
|
|
|
203
161
|
<h2 id="license">🔏 License</h2>
|
|
204
162
|
|
|
205
|
-
Copyright ©
|
|
163
|
+
Copyright © 2023 [Mertushka <mertushka@proton.me>](https://github.com/mertushka)
|
|
206
164
|
|
|
207
165
|
This project is licensed by [MIT License](https://api.github.com/licenses/mit).
|
|
208
166
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "haxball.js",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.4",
|
|
4
4
|
"description": "A powerful library for interacting with the Haxball Headless API",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "types/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"test": "mocha --recursive --exit"
|
|
8
|
+
"test": "mocha --recursive --exit",
|
|
9
|
+
"build": "node scripts/nodeify.js"
|
|
9
10
|
},
|
|
10
11
|
"keywords": [
|
|
11
12
|
"haxball",
|
|
@@ -18,12 +19,12 @@
|
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"@koush/wrtc": "^0.5.3",
|
|
20
21
|
"@peculiar/webcrypto": "^1.3.3",
|
|
21
|
-
"@types/haxball-headless-browser": "
|
|
22
|
+
"@types/haxball-headless-browser": ">=0.3.0",
|
|
22
23
|
"http-proxy-agent": "^5.0.0",
|
|
23
24
|
"json5": "^2.2.1",
|
|
24
|
-
"node-fetch": "^2.6.6",
|
|
25
25
|
"pako": "^2.0.4",
|
|
26
|
-
"ws": "^8.5.0"
|
|
26
|
+
"ws": "^8.5.0",
|
|
27
|
+
"xhr2": "^0.2.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@mapbox/node-pre-gyp": "^1.0.9",
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
|
|
3
|
+
fetch("https://www.haxball.com/cache_hash.json")
|
|
4
|
+
.then((body) => body.text())
|
|
5
|
+
.then(async (data) => {
|
|
6
|
+
const hash = data.replaceAll('"', "");
|
|
7
|
+
|
|
8
|
+
let source = await (
|
|
9
|
+
await fetch(
|
|
10
|
+
`https://www.haxball.com/${hash}/__cache_static__/g/headless-min.js`
|
|
11
|
+
)
|
|
12
|
+
).text();
|
|
13
|
+
|
|
14
|
+
const target = fs.createWriteStream("./src/build.js");
|
|
15
|
+
|
|
16
|
+
// Remove <Window> references
|
|
17
|
+
source = source.replaceAll("window.", "");
|
|
18
|
+
source = source.replaceAll("parent.", "");
|
|
19
|
+
source = source.replaceAll("document.", "");
|
|
20
|
+
source = source.replaceAll(".innerHTML", "");
|
|
21
|
+
|
|
22
|
+
source = source.replaceAll('getElementById("roomlink")', "null");
|
|
23
|
+
source = source.replaceAll('getElementById("recaptcha")', "null");
|
|
24
|
+
|
|
25
|
+
source = source.replaceAll(
|
|
26
|
+
'"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this',
|
|
27
|
+
"global"
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// Fix HBInit Callback (Regex)
|
|
31
|
+
const HBInitRegex = /HBInit=.+?;/; // Match the pattern "HBInit=...;"
|
|
32
|
+
const HBInitMatch = source.match(HBInitRegex)[0];
|
|
33
|
+
|
|
34
|
+
if (HBInitMatch) {
|
|
35
|
+
const value = HBInitMatch.substring(7, HBInitMatch.length - 1); // Remove "HBInit=" and the trailing ";"
|
|
36
|
+
const updatedValue = `promiseResolve(${value});`;
|
|
37
|
+
|
|
38
|
+
source = source.replace(HBInitMatch, updatedValue);
|
|
39
|
+
} else {
|
|
40
|
+
throw new Error("No HBInit matches found!");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Fix Websocket (Regex)
|
|
44
|
+
|
|
45
|
+
const WebsocketRegex = /new WebSocket\([^)]+\);/; // Match the pattern "new WebSocket(f+"?token="+e);"
|
|
46
|
+
const WebsocketMatch = source.match(WebsocketRegex)[0];
|
|
47
|
+
|
|
48
|
+
if (WebsocketMatch) {
|
|
49
|
+
const updatedValue = WebsocketMatch.replace(
|
|
50
|
+
/new WebSocket\(([^)]+)\);/,
|
|
51
|
+
'new WebSocket($1, {headers:{origin: "https://html5.haxball.com"}, agent: proxyAgent});'
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
source = source.replace(WebsocketMatch, updatedValue);
|
|
55
|
+
} else {
|
|
56
|
+
throw new Error("No Websocket matches found!");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Remove recaptcha (Regex)
|
|
60
|
+
|
|
61
|
+
const RecaptchaRegex = /case "recaptcha":([a-zA-Z]+)\(([^)]+)\)/; // Match the pattern 'case "recaptcha":b(e)' method b and string e is random.
|
|
62
|
+
const RecaptchaMatch = source.match(RecaptchaRegex)[0];
|
|
63
|
+
|
|
64
|
+
if (RecaptchaMatch) {
|
|
65
|
+
const updatedValue = RecaptchaMatch.replace(
|
|
66
|
+
/case "recaptcha":([a-zA-Z]+)\(([^)]+)\)/,
|
|
67
|
+
'case "recaptcha":console.log(new Error("Invalid Token Provided!"))'
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
source = source.replace(RecaptchaMatch, updatedValue);
|
|
71
|
+
} else {
|
|
72
|
+
throw new Error("No Recaptcha matches found!");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Add proxy support & other things manually because too hard to string manipulate with minified code
|
|
76
|
+
// Also these things are not included in Headless Host Source originally so you can create ur own build without these things
|
|
77
|
+
/*
|
|
78
|
+
|
|
79
|
+
RoomConfigLookup: This is a example name for mimic minified function.
|
|
80
|
+
|
|
81
|
+
Example getting minified function: source.match(/(\w+)\("noPlayer",/)[1] -> this regex gets minified function's name from code
|
|
82
|
+
k("noPlayer", !1) -> in this example regex gets function's name as "k"
|
|
83
|
+
|
|
84
|
+
proxyAgent = RoomConfigLookup("proxy", null)
|
|
85
|
+
? new HttpsProxyAgent(url.parse(RoomConfigLookup("proxy", null)))
|
|
86
|
+
: null;
|
|
87
|
+
debug = RoomConfigLookup("debug", null) == true;
|
|
88
|
+
|
|
89
|
+
*/
|
|
90
|
+
// Websocket On Error Debug -> debug && console.error(e);
|
|
91
|
+
|
|
92
|
+
// Modules
|
|
93
|
+
target.write(`const wrtc = require("@koush/wrtc");
|
|
94
|
+
const XMLHttpRequest = require('xhr2');
|
|
95
|
+
const WebSocket = require("ws");
|
|
96
|
+
const url = require("url");
|
|
97
|
+
const HttpsProxyAgent = require("https-proxy-agent");
|
|
98
|
+
const JSON5 = require("json5");
|
|
99
|
+
const pako = require("pako");
|
|
100
|
+
const { Crypto } = require("@peculiar/webcrypto");
|
|
101
|
+
const { performance } = require("perf_hooks");
|
|
102
|
+
|
|
103
|
+
const {RTCPeerConnection,
|
|
104
|
+
RTCIceCandidate,
|
|
105
|
+
RTCSessionDescription} = wrtc
|
|
106
|
+
|
|
107
|
+
const crypto = new Crypto();
|
|
108
|
+
|
|
109
|
+
var promiseResolve;
|
|
110
|
+
var proxyAgent;
|
|
111
|
+
var debug = false;
|
|
112
|
+
|
|
113
|
+
const HBLoaded = new Promise(function (resolve, reject) {
|
|
114
|
+
promiseResolve = resolve;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const onHBLoaded = function (cb) {
|
|
118
|
+
return cb;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
Builded & Automated with Haxball.JS Nodeify Script
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
`);
|
|
126
|
+
|
|
127
|
+
// Update Source
|
|
128
|
+
target.write(source);
|
|
129
|
+
|
|
130
|
+
// Export HBInit Promise
|
|
131
|
+
target.write(`
|
|
132
|
+
module.exports = HBLoaded;`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
console.log("Done!");
|