gjrequest.js 1.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/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # gjrequest.js
2
+
3
+ > A lightweight Node.js SDK for interacting with the **Geometry Dash API** (GDPlatform-powered).
4
+ > Handles login, reading/sending messages, and fetching level scores using `gjp2`.
5
+
6
+ ---
7
+
8
+ ## Features
9
+
10
+ * Login with `accountID` + password (generates `gjp2`)
11
+ * Read inbox or sent messages
12
+ * Send messages (Base64-encoded)
13
+ * Fetch level scores
14
+ * Generic endpoint requests via `requestEndpoint()`
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install gjrequest.js
22
+ ```
23
+
24
+ > Or copy the module locally if not published.
25
+
26
+ ---
27
+
28
+ ## Usage
29
+
30
+ ```js
31
+ require("dotenv").config();
32
+ const { Client } = require("gjrequest.js");
33
+
34
+ (async () => {
35
+ const client = new Client();
36
+
37
+ // Login securely via environment variables
38
+ await client.login({
39
+ accountID: process.env.GD_ACCOUNT_ID,
40
+ password: process.env.GD_PASSWORD
41
+ });
42
+
43
+ // Read inbox messages
44
+ const inbox = await client.readMessages();
45
+ console.log(inbox);
46
+
47
+ // Send a message
48
+ const sent = await client.sendMessage(
49
+ 29294657,
50
+ "Hello World",
51
+ "This is a test message!"
52
+ );
53
+ console.log(sent);
54
+
55
+ // Get level scores
56
+ const scores = await client.getLevelScores(1234567);
57
+ console.log(scores);
58
+
59
+ // Generic endpoint request
60
+ const response = await client.requestEndpoint("uploadGJMessage20.php", {
61
+ toAccountID: 29294657,
62
+ subject: Buffer.from("Test").toString("base64"),
63
+ body: Buffer.from("Hello!").toString("base64")
64
+ });
65
+ console.log(response);
66
+ })();
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Security Notes
72
+
73
+ * **Do NOT hardcode your GD password** in public repositories.
74
+ * Use environment variables (`.env`) or other secure storage.
75
+ * `gjp2` is generated locally; no real login is sent except the hashed payload.
76
+
77
+ ---
78
+
79
+ ## References
80
+
81
+ * [GD Docs by Wyliemaster](https://wyliemaster.github.io/gddocs/)
82
+ * [Geometry Dash API Endpoints](https://www.boomlings.com/database/)
83
+
84
+ ---
85
+
86
+ ## License
87
+
88
+ MIT © SkunkPlatform
package/index.js ADDED
@@ -0,0 +1,93 @@
1
+ const { generateGjp2 } = require("./modules/gjp");
2
+ const { messages, read, upload } = require("./modules/message");
3
+ const { getScores } = require("./modules/scores"); // new module we defined
4
+ const { doPost } = require("./modules/endpoint-httprequest");
5
+
6
+ class Client {
7
+ constructor() {
8
+ this.accountID = null;
9
+ this.gjp2 = null;
10
+ }
11
+
12
+ /**
13
+ * "Login" by providing your account ID and password
14
+ * for APIs that accept gjp2 (no actual login call needed)
15
+ */
16
+ async login({ accountID, password }) {
17
+ if (!accountID || !password) {
18
+ throw new Error("AccountID and password are required.");
19
+ }
20
+
21
+ this.accountID = accountID;
22
+ this.gjp2 = generateGjp2(password);
23
+
24
+ return {
25
+ accountID: this.accountID,
26
+ gjp2: this.gjp2
27
+ };
28
+ }
29
+
30
+ async getLevelScores(levelID) {
31
+ this._requireLogin();
32
+ return getScores({
33
+ accountID: this.accountID,
34
+ gjp2: this.gjp2,
35
+ levelID
36
+ });
37
+ }
38
+
39
+ async sendMessage(toAccountID, subject = "untitled", body = "") {
40
+ this._requireLogin();
41
+
42
+ // Helper to match the Python base64.b64encode(b"...").decode() logic
43
+ const encode = (str) => Buffer.from(str).toString('base64');
44
+
45
+ return upload({
46
+ accountID: this.accountID,
47
+ gjp2: this.gjp2,
48
+ toAccountID: toAccountID,
49
+ subject: encode(subject), // Encodes "You're dumb lol"
50
+ body: encode(body) // Encodes "Mhm yep..."
51
+ });
52
+ }
53
+
54
+ async readMessage(messageID) {
55
+ this._requireLogin();
56
+
57
+ return read({
58
+ accountID: this.accountID,
59
+ gjp: this.gjp2,
60
+ messageID
61
+ });
62
+ }
63
+
64
+ async readMessages({ page = 0, sent = false } = {}) {
65
+ this._requireLogin();
66
+
67
+ return messages({
68
+ accountID: this.accountID,
69
+ gjp2: this.gjp2,
70
+ page,
71
+ sent
72
+ });
73
+ }
74
+
75
+ async requestEndpoint(endpoint, params = {}, secret = "Wmfd2893gb7") {
76
+ this._requireLogin();
77
+
78
+ // Always include accountID and gjp2
79
+ params.accountID = this.accountID;
80
+ params.gjp2 = this.gjp2;
81
+ params.secret = secret;
82
+
83
+ return doPost(`/database/${endpoint}`, params);
84
+ }
85
+
86
+ _requireLogin() {
87
+ if (!this.accountID || !this.gjp2) {
88
+ throw new Error("Client not logged in. Call login() first.");
89
+ }
90
+ }
91
+ }
92
+
93
+ module.exports = { Client };
@@ -0,0 +1,54 @@
1
+ const https = require("https");
2
+ const querystring = require("querystring");
3
+
4
+ const GD_SERVER = "www.boomlings.com";
5
+ const SECRET = "Wmfd2893gb7"; // standard for user/score APIs
6
+
7
+ function doPost(path, params) {
8
+ return new Promise((resolve, reject) => {
9
+ const postData = querystring.stringify(params);
10
+
11
+ const options = {
12
+ hostname: GD_SERVER,
13
+ path,
14
+ method: "POST",
15
+ headers: {
16
+ "Content-Type": "application/x-www-form-urlencoded",
17
+ "Content-Length": Buffer.byteLength(postData),
18
+ "User-Agent": "GeometryDash/2.11"
19
+ }
20
+ };
21
+
22
+ const req = https.request(options, (res) => {
23
+ let data = "";
24
+ res.on("data", chunk => data += chunk);
25
+ res.on("end", () => resolve(data));
26
+ });
27
+
28
+ req.on("error", reject);
29
+ req.write(postData);
30
+ req.end();
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Get scores for a level
36
+ * @param {Object} opts
37
+ * Required fields:
38
+ * - accountID
39
+ * - gjp2 (encrypted password)
40
+ * - levelID
41
+ * - secret (optional, defaults to GD_SERVER secret)
42
+ */
43
+ async function getScores({ accountID, gjp2, levelID }) {
44
+ const params = {
45
+ accountID,
46
+ gjp2,
47
+ levelID,
48
+ secret: SECRET
49
+ };
50
+
51
+ return doPost("/database/getGJScores20.php", params);
52
+ }
53
+
54
+ module.exports = { getScores };
@@ -0,0 +1,36 @@
1
+ const https = require("https");
2
+ const querystring = require("querystring");
3
+
4
+ const GD_SERVER = "www.boomlings.com";
5
+
6
+ /**
7
+ * Generic POST request helper for Geometry Dash endpoints
8
+ */
9
+ function doPost(path, params, userAgent = "GeometryDash/2.11") {
10
+ return new Promise((resolve, reject) => {
11
+ const postData = querystring.stringify(params);
12
+
13
+ const options = {
14
+ hostname: GD_SERVER,
15
+ path,
16
+ method: "POST",
17
+ headers: {
18
+ "Content-Type": "application/x-www-form-urlencoded",
19
+ "Content-Length": Buffer.byteLength(postData),
20
+ "User-Agent": userAgent
21
+ }
22
+ };
23
+
24
+ const req = https.request(options, (res) => {
25
+ let data = "";
26
+ res.on("data", chunk => data += chunk);
27
+ res.on("end", () => resolve(data));
28
+ });
29
+
30
+ req.on("error", reject);
31
+ req.write(postData);
32
+ req.end();
33
+ });
34
+ }
35
+
36
+ module.exports = { doPost };
package/modules/gjp.js ADDED
@@ -0,0 +1,60 @@
1
+ const crypto = require("crypto");
2
+
3
+ const XOR_KEY = "37526";
4
+ const SALT = "mI29fmAnxgTs";
5
+
6
+ // XOR cipher
7
+ function xorCipher(input, key = XOR_KEY) {
8
+ const inputBuffer = Buffer.from(input, "utf8");
9
+ const keyBuffer = Buffer.from(key, "utf8");
10
+
11
+ const output = Buffer.alloc(inputBuffer.length);
12
+
13
+ for (let i = 0; i < inputBuffer.length; i++) {
14
+ output[i] = inputBuffer[i] ^ keyBuffer[i % keyBuffer.length];
15
+ }
16
+
17
+ return output.toString("utf8");
18
+ }
19
+
20
+ // SHA1(password + salt)
21
+ function generateGjp2(password = "", salt = SALT) {
22
+ return crypto
23
+ .createHash("sha1")
24
+ .update(password + salt)
25
+ .digest("hex");
26
+ }
27
+
28
+ // XOR + Base64 (URL-safe)
29
+ function encodeGjp(password) {
30
+ const xored = xorCipher(password);
31
+
32
+ return Buffer.from(xored, "utf8")
33
+ .toString("base64")
34
+ .replace(/\+/g, "-")
35
+ .replace(/\//g, "_")
36
+ .replace(/=+$/, ""); // remove padding (GD-safe)
37
+ }
38
+
39
+ // Reverse URL-safe Base64 + XOR
40
+ function decodeGjp(gjp) {
41
+ // Restore URL-safe chars
42
+ let normalized = gjp
43
+ .replace(/-/g, "+")
44
+ .replace(/_/g, "/");
45
+
46
+ // Restore padding
47
+ while (normalized.length % 4) {
48
+ normalized += "=";
49
+ }
50
+
51
+ const decoded = Buffer.from(normalized, "base64").toString("utf8");
52
+
53
+ return xorCipher(decoded);
54
+ }
55
+
56
+ module.exports = {
57
+ generateGjp2,
58
+ encodeGjp,
59
+ decodeGjp
60
+ };
@@ -0,0 +1,94 @@
1
+ const https = require("https");
2
+ const querystring = require("querystring");
3
+
4
+ const GD_SERVER = "www.boomlings.com"; // primary Geometry Dash API server
5
+ const SECRET = "Wmfd2893gb7"; // the secret param required for most GD API requests
6
+
7
+ function doPost(path, params) {
8
+ return new Promise((resolve, reject) => {
9
+ const postData = querystring.stringify(params);
10
+
11
+ const options = {
12
+ hostname: GD_SERVER,
13
+ path,
14
+ method: "POST",
15
+ headers: {
16
+ "User-Agent": "",
17
+ "Content-Type": "application/x-www-form-urlencoded",
18
+ "Content-Length": Buffer.byteLength(postData)
19
+ }
20
+ };
21
+
22
+ const req = https.request(options, (res) => {
23
+ let data = "";
24
+ res.on("data", (chunk) => {
25
+ data += chunk;
26
+ });
27
+ res.on("end", () => {
28
+ resolve(data);
29
+ });
30
+ });
31
+
32
+ req.on("error", reject);
33
+ req.write(postData);
34
+ req.end();
35
+ });
36
+ }
37
+
38
+ /**
39
+ * Upload (send) a private message
40
+ * @param {Object} opts
41
+ * Required fields typically include:
42
+ * - accountID
43
+ * - gjp (GJP2 hash of password)
44
+ * - message (text)
45
+ * - toAccountID (the user you’re messaging)
46
+ */
47
+ async function upload(opts) {
48
+ const params = {
49
+ secret: SECRET,
50
+ accountID: opts.accountID,
51
+ gjp2: opts.gjp2, // Use gjp2 for modern 2.2+ auth
52
+ toAccountID: opts.toAccountID,
53
+ subject: opts.subject, // API expects 'subject'
54
+ body: opts.body, // API expects 'body'
55
+ gameVersion: 22,
56
+ binaryVersion: 42,
57
+ gdw: 0
58
+ };
59
+
60
+ return doPost("/database/uploadGJMessage20.php", params);
61
+ }
62
+
63
+ /**
64
+ * Read (download) a private message
65
+ * @param {Object} opts
66
+ * Required fields typically include:
67
+ * - accountID
68
+ * - gjp
69
+ * - messageID (the ID of the message to download)
70
+ */
71
+ async function read(opts) {
72
+ const params = {
73
+ secret: SECRET,
74
+ accountID: opts.accountID,
75
+ gjp: opts.gjp,
76
+ messageID: opts.messageID,
77
+ };
78
+
79
+ return doPost("/database/downloadGJMessage20.php", params);
80
+ }
81
+
82
+ async function messages(opts) {
83
+ const params = {
84
+ secret: SECRET,
85
+ accountID: opts.accountID,
86
+ gjp2: opts.gjp2, // SHA1(password + "mI29fmAnxgTs")
87
+ page: opts.page || 0, // optional (default 0)
88
+ getSent: opts.sent ? 1 : 0 // optional (0 = inbox, 1 = sent)
89
+ };
90
+
91
+ return doPost("/database/getGJMessages20.php", params);
92
+ }
93
+
94
+ module.exports = { upload, read, messages };
@@ -0,0 +1,53 @@
1
+ const https = require("https");
2
+ const querystring = require("querystring");
3
+
4
+ const GD_SERVER = "www.boomlings.com";
5
+ const SECRET = "Wmfd2893gb7"; // standard for user/score APIs
6
+
7
+ function doPost(path, params) {
8
+ return new Promise((resolve, reject) => {
9
+ const postData = querystring.stringify(params);
10
+
11
+ const options = {
12
+ hostname: GD_SERVER,
13
+ path,
14
+ method: "POST",
15
+ headers: {
16
+ "Content-Type": "application/x-www-form-urlencoded",
17
+ "Content-Length": Buffer.byteLength(postData),
18
+ "User-Agent": "GeometryDash/2.11"
19
+ }
20
+ };
21
+
22
+ const req = https.request(options, (res) => {
23
+ let data = "";
24
+ res.on("data", chunk => data += chunk);
25
+ res.on("end", () => resolve(data));
26
+ });
27
+
28
+ req.on("error", reject);
29
+ req.write(postData);
30
+ req.end();
31
+ });
32
+ }
33
+
34
+ /**
35
+ * Get scores for a level
36
+ * @param {Object} opts
37
+ * Required fields:
38
+ * - accountID
39
+ * - gjp2 (SHA1 hash of your password)
40
+ * - levelID
41
+ */
42
+ async function getScores({ accountID, gjp2, levelID }) {
43
+ const params = {
44
+ accountID,
45
+ gjp2,
46
+ levelID,
47
+ secret: SECRET
48
+ };
49
+
50
+ return doPost("/database/getGJScores20.php", params);
51
+ }
52
+
53
+ module.exports = { getScores };
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "gjrequest.js",
3
+ "version": "1.0.0",
4
+ "description": "Geometry Dash API - Powered by GDPlatform.",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [
10
+ "geometry dash",
11
+ "gd",
12
+ "api",
13
+ "gjrequest"
14
+ ],
15
+ "author": "SkunkPlatform",
16
+ "license": "MIT",
17
+ "type": "commonjs"
18
+ }
package/test.js ADDED
@@ -0,0 +1,21 @@
1
+ const { Client } = require("./index");
2
+
3
+ (async () => {
4
+ const client = new Client();
5
+
6
+ await client.login({
7
+ accountID: 1234567890, // AccountID
8
+ password: "insert your gd password" // Use your Geometry Dash Password on other account. ACTION REQUIRED: Environment Variables, DO NOT SHARE GD PASSWORD TO YOUR PUBLIC REPOSITORY THAT WILL POSSIBLE CAUSE RISKS. DO NOT ATTEMP TO SHARE.
9
+ });
10
+
11
+ const messages = await client.readMessages();
12
+ console.log(messages);
13
+
14
+ const submitted = await client.sendMessage(
15
+ 29294657, // Send to an Account ID
16
+ "Subject", // this subject will be base64.
17
+ "Body" // this body will be base64.
18
+ );
19
+
20
+ console.log(submitted);
21
+ })();