discord-protos 1.0.4 → 1.2.43
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/.github/workflows/update.yml +45 -12
- package/.prettierrc.yml +1 -0
- package/MANIFEST.in +4 -0
- package/README.md +47 -16
- package/discord_protos/__init__.py +40 -0
- package/discord_protos/discord_kkv_store_value_models/v1/AcknowledgedApplicationDisclosures.proto +22 -0
- package/discord_protos/discord_kkv_store_value_models/v1/AcknowledgedApplicationDisclosures_pb2.py +42 -0
- package/discord_protos/discord_kkv_store_value_models/v1/ApplicationUserRoleConnection.proto +10 -0
- package/discord_protos/discord_kkv_store_value_models/v1/ApplicationUserRoleConnection_pb2.py +40 -0
- package/{out → discord_protos/discord_users/v1}/FrecencyUserSettings.proto +31 -5
- package/discord_protos/discord_users/v1/FrecencyUserSettings_pb2.py +100 -0
- package/discord_protos/discord_users/v1/PreloadedUserSettings.proto +497 -0
- package/discord_protos/discord_users/v1/PreloadedUserSettings_pb2.py +208 -0
- package/discord_protos/premium_marketing/v1/PremiumMarketingComponentProperties.proto +62 -0
- package/discord_protos/premium_marketing/v1/PremiumMarketingComponentProperties_pb2.py +60 -0
- package/dist/discord_protos/discord_kkv_store_value_models/v1/AcknowledgedApplicationDisclosures.d.ts +71 -0
- package/dist/discord_protos/discord_kkv_store_value_models/v1/AcknowledgedApplicationDisclosures.js +131 -0
- package/dist/discord_protos/discord_kkv_store_value_models/v1/ApplicationUserRoleConnection.d.ts +41 -0
- package/dist/discord_protos/discord_kkv_store_value_models/v1/ApplicationUserRoleConnection.js +94 -0
- package/dist/{proto → discord_protos/discord_users/v1}/FrecencyUserSettings.d.ts +184 -53
- package/dist/{proto → discord_protos/discord_users/v1}/FrecencyUserSettings.js +469 -96
- package/dist/discord_protos/discord_users/v1/PreloadedUserSettings.d.ts +2035 -0
- package/dist/discord_protos/discord_users/v1/PreloadedUserSettings.js +4200 -0
- package/dist/{proto → discord_protos}/google/protobuf/timestamp.d.ts +3 -6
- package/dist/{proto → discord_protos}/google/protobuf/timestamp.js +11 -11
- package/dist/{proto → discord_protos}/google/protobuf/wrappers.d.ts +9 -9
- package/dist/{proto → discord_protos}/google/protobuf/wrappers.js +28 -30
- package/dist/discord_protos/premium_marketing/v1/PremiumMarketingComponentProperties.d.ts +261 -0
- package/dist/discord_protos/premium_marketing/v1/PremiumMarketingComponentProperties.js +558 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +34 -4
- package/dist/load.js +144 -8
- package/package.json +14 -13
- package/pyproject.toml +21 -0
- package/requirements.txt +1 -0
- package/scripts/parse.js +269 -0
- package/scripts/preload.js +17 -0
- package/setup.py +42 -0
- package/tsconfig.json +6 -4
- package/dist/parse.d.ts +0 -35
- package/dist/parse.js +0 -175
- package/dist/proto/PreloadedUserSettings.d.ts +0 -1022
- package/dist/proto/PreloadedUserSettings.js +0 -2235
- package/dist/test.d.ts +0 -1
- package/dist/test.js +0 -19
- package/out/PreloadedUserSettings.proto +0 -240
- package/package-lock.json +0 -1755
package/dist/load.js
CHANGED
|
@@ -3,17 +3,153 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const puppeteer_1 = require("puppeteer");
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
5
|
const path_1 = require("path");
|
|
6
|
-
const
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
// For the JS template, all we need to do is update the exports at the bottom
|
|
8
|
+
const JS_TEMPLATE = `import { MessageType } from "@protobuf-ts/runtime";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Supports both node and web environments, replacement of previous Buffer.from() being node-only.
|
|
12
|
+
* This is specific to this package's usage, and not a replacement of Buffer.from() altogether
|
|
13
|
+
*/
|
|
14
|
+
const compatBuffer = {
|
|
15
|
+
from: function (input: string | Uint8Array, encoding?: string) {
|
|
16
|
+
if (typeof input === "string" && encoding === "base64") {
|
|
17
|
+
const encodedBytes = atob(input);
|
|
18
|
+
const bytes = new Uint8Array(encodedBytes.length);
|
|
19
|
+
for (let i = 0; i < encodedBytes.length; i++) {
|
|
20
|
+
bytes[i] = encodedBytes.charCodeAt(i);
|
|
21
|
+
}
|
|
22
|
+
return bytes;
|
|
23
|
+
} else if (!encoding && input instanceof Uint8Array) {
|
|
24
|
+
return input;
|
|
25
|
+
}
|
|
26
|
+
throw new Error("Invalid input type.");
|
|
27
|
+
},
|
|
28
|
+
toBase64String: function (buffer: Uint8Array) {
|
|
29
|
+
let encodedBytes = "";
|
|
30
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
31
|
+
encodedBytes += String.fromCharCode(buffer[i]);
|
|
32
|
+
}
|
|
33
|
+
return btoa(encodedBytes);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function toBase64(this: MessageType<any>, data) {
|
|
38
|
+
return compatBuffer.toBase64String(compatBuffer.from(this.toBinary(data)));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function fromBase64(this: MessageType<any>, base64: string) {
|
|
42
|
+
return this.fromBinary(compatBuffer.from(base64, "base64"));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare module "@protobuf-ts/runtime" {
|
|
46
|
+
interface MessageType<T> {
|
|
47
|
+
toBase64(data: T): string;
|
|
48
|
+
fromBase64(base64: string): T;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
MessageType.prototype.fromBase64 = fromBase64;
|
|
53
|
+
MessageType.prototype.toBase64 = toBase64;
|
|
54
|
+
|
|
55
|
+
{{ protos_exports }}
|
|
56
|
+
`;
|
|
57
|
+
// For the Python template, we need to update both imports and the __all__ variable, as well as bump the version number
|
|
58
|
+
const PY_TEMPLATE = `from __future__ import annotations
|
|
59
|
+
|
|
60
|
+
from enum import Enum as _Enum
|
|
61
|
+
from typing import TYPE_CHECKING
|
|
62
|
+
|
|
63
|
+
__version__ = '{{ version }}'
|
|
64
|
+
|
|
65
|
+
if TYPE_CHECKING:
|
|
66
|
+
from google.protobuf.message import Message as _Message
|
|
67
|
+
|
|
68
|
+
{{ protos_equals }} = _Message
|
|
69
|
+
else:
|
|
70
|
+
{{ protos_imports }}
|
|
71
|
+
|
|
72
|
+
__all__ = (
|
|
73
|
+
'__version__',
|
|
74
|
+
'UserSettingsType',
|
|
75
|
+
{{ exports }}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class UserSettingsType(_Enum):
|
|
80
|
+
preloaded_user_settings = 1
|
|
81
|
+
frecency_user_settings = 2
|
|
82
|
+
test_settings = 3
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
UserSettingsImpl = {
|
|
86
|
+
UserSettingsType.preloaded_user_settings: PreloadedUserSettings,
|
|
87
|
+
UserSettingsType.frecency_user_settings: FrecencyUserSettings,
|
|
88
|
+
UserSettingsType.test_settings: None,
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
// This must be run early
|
|
92
|
+
const PRELOAD_SCRIPT = (0, fs_1.readFileSync)((0, path_1.join)(__dirname, "..", "scripts", "preload.js"), "utf8");
|
|
93
|
+
const PARSE_SCRIPT = (0, fs_1.readFileSync)((0, path_1.join)(__dirname, "..", "scripts", "parse.js"), "utf8");
|
|
7
94
|
async function main() {
|
|
8
|
-
const browser = await puppeteer_1.default.launch(
|
|
9
|
-
headless: true,
|
|
10
|
-
});
|
|
95
|
+
const browser = await puppeteer_1.default.launch();
|
|
11
96
|
const page = await browser.newPage();
|
|
12
|
-
|
|
13
|
-
|
|
97
|
+
page.on("console", (msg) => console.debug(msg.text()));
|
|
98
|
+
// Preload script grabs the objects
|
|
99
|
+
await page.evaluateOnNewDocument(PRELOAD_SCRIPT);
|
|
100
|
+
await page.goto("https://canary.discord.com/app", { waitUntil: "networkidle0" });
|
|
101
|
+
const protos = await page.evaluate(`${PARSE_SCRIPT}; protos`);
|
|
102
|
+
await browser.close();
|
|
103
|
+
// Delete all existing files and folders in the discord_protos directory
|
|
104
|
+
const outputs = [(0, path_1.join)(__dirname, "..", "discord_protos"), (0, path_1.join)(__dirname, "..", "src", "discord_protos")];
|
|
105
|
+
for (const output of outputs) {
|
|
106
|
+
if ((0, fs_1.existsSync)(output)) {
|
|
107
|
+
for (const file of (0, fs_1.readdirSync)(output)) {
|
|
108
|
+
try {
|
|
109
|
+
if ((0, fs_1.readdirSync)((0, path_1.join)(output, file)).length > 0) {
|
|
110
|
+
(0, fs_1.rmSync)((0, path_1.join)(output, file), { recursive: true, force: true });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch { }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Write the protos to disk
|
|
118
|
+
const filenames = [];
|
|
14
119
|
for (const [name, proto] of Object.entries(protos)) {
|
|
15
|
-
|
|
120
|
+
const dir = (0, path_1.join)(__dirname, "..", ...proto.package.split("."));
|
|
121
|
+
const filename = (0, path_1.join)(dir, `${name}.proto`);
|
|
122
|
+
filenames.push(filename.replace((0, path_1.join)(__dirname, ".."), ".").replaceAll("\\", "/"));
|
|
123
|
+
// Ensure the directory exists
|
|
124
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
125
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
126
|
+
}
|
|
127
|
+
(0, fs_1.writeFileSync)(filename, proto.data);
|
|
16
128
|
}
|
|
17
|
-
|
|
129
|
+
// Check if we have any changes using git
|
|
130
|
+
const changes = (0, child_process_1.execSync)("git status --porcelain").toString().trim();
|
|
131
|
+
if (!changes.includes(".proto")) {
|
|
132
|
+
console.log("No changes detected, exiting...");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const packageJson = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, "..", "package.json"), "utf8"));
|
|
136
|
+
// Bump the version number
|
|
137
|
+
const version = packageJson.version.split(".").map((x) => parseInt(x)).map((x, i) => i === 2 ? x + 1 : x).join(".");
|
|
138
|
+
packageJson.version = version;
|
|
139
|
+
// This is very cursed
|
|
140
|
+
// For protoc to parse any new protos, we have to edit the script in the package.json to include the filenames
|
|
141
|
+
const js = packageJson.scripts.js.split(" ").filter((x) => !x.endsWith(".proto")).join(" ");
|
|
142
|
+
const py = packageJson.scripts.py.split(" ").filter((x) => !x.endsWith(".proto")).join(" ");
|
|
143
|
+
packageJson.scripts.js = `${js} ${filenames.join(" ")}`;
|
|
144
|
+
packageJson.scripts.py = `${py} ${filenames.join(" ")}`;
|
|
145
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(__dirname, "..", "package.json"), JSON.stringify(packageJson, null, 4));
|
|
146
|
+
// Update the JS template
|
|
147
|
+
const jsExports = filenames.map((x) => `export * from "${x.replaceAll("\\", "/").replace(".proto", "")}";`).join("\n");
|
|
148
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(__dirname, "..", "src", "index.ts"), JS_TEMPLATE.replace("{{ protos_exports }}", jsExports));
|
|
149
|
+
// Update the Python template
|
|
150
|
+
const pyExports = Object.keys(protos).map((x) => ` '${x}',`).join("\n");
|
|
151
|
+
const pyImports = filenames.map((x) => ` from ${x.replaceAll("\\", "/").replace("./discord_protos", "").replaceAll("/", ".").replace(".proto", "_pb2")} import *`).join("\n");
|
|
152
|
+
const pyEquals = Object.keys(protos).join(" = ");
|
|
153
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(__dirname, "..", "discord_protos", "__init__.py"), PY_TEMPLATE.replace("{{ version }}", version).replace("{{ exports }}", pyExports).replace("{{ protos_imports }}", pyImports).replace("{{ protos_equals }}", pyEquals));
|
|
18
154
|
}
|
|
19
155
|
main();
|
package/package.json
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discord-protos",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.43",
|
|
4
4
|
"description": "A parser for Discord's protobufs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
-
"packageManager": "pnpm@7.14.2",
|
|
8
7
|
"contributors": [
|
|
9
8
|
{
|
|
10
9
|
"name": "dolfies",
|
|
11
|
-
"email": "
|
|
12
|
-
"url": "https://
|
|
10
|
+
"email": "me@dolfi.es",
|
|
11
|
+
"url": "https://dolfi.es"
|
|
13
12
|
},
|
|
14
13
|
{
|
|
15
14
|
"name": "Samuel Scheit",
|
|
@@ -17,21 +16,23 @@
|
|
|
17
16
|
"url": "https://samuelscheit.com"
|
|
18
17
|
}
|
|
19
18
|
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"url": "https://github.com/discord-userdoccers/discord-protos"
|
|
21
|
+
},
|
|
20
22
|
"license": "MIT",
|
|
21
23
|
"scripts": {
|
|
22
|
-
"py": "protoc --
|
|
23
|
-
"js": "protoc --proto_path=./
|
|
24
|
+
"py": "protoc --proto_path=./discord_protos/ --python_out=discord_protos ./discord_protos/discord_users/v1/PreloadedUserSettings.proto ./discord_protos/discord_users/v1/FrecencyUserSettings.proto ./discord_protos/discord_kkv_store_value_models/v1/ApplicationUserRoleConnection.proto ./discord_protos/discord_kkv_store_value_models/v1/AcknowledgedApplicationDisclosures.proto ./discord_protos/premium_marketing/v1/PremiumMarketingComponentProperties.proto",
|
|
25
|
+
"js": "protoc --proto_path=./discord_protos/ --ts_out src/discord_protos ./discord_protos/discord_users/v1/PreloadedUserSettings.proto ./discord_protos/discord_users/v1/FrecencyUserSettings.proto ./discord_protos/discord_kkv_store_value_models/v1/ApplicationUserRoleConnection.proto ./discord_protos/discord_kkv_store_value_models/v1/AcknowledgedApplicationDisclosures.proto ./discord_protos/premium_marketing/v1/PremiumMarketingComponentProperties.proto",
|
|
24
26
|
"build": "tsc",
|
|
25
|
-
"load": "
|
|
26
|
-
"test": "yarn build && node dist/test.js"
|
|
27
|
+
"load": "npm run build && node dist/load.js"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@protobuf-ts/plugin": "^2.
|
|
30
|
-
"@protobuf-ts/protoc": "^2.
|
|
31
|
-
"puppeteer": "^
|
|
32
|
-
"typescript": "^
|
|
30
|
+
"@protobuf-ts/plugin": "^2.9.6",
|
|
31
|
+
"@protobuf-ts/protoc": "^2.9.6",
|
|
32
|
+
"puppeteer": "^24.6.0",
|
|
33
|
+
"typescript": "^5.8.2"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"@protobuf-ts/runtime": "^2.
|
|
36
|
+
"@protobuf-ts/runtime": "^2.9.6"
|
|
36
37
|
}
|
|
37
38
|
}
|
package/pyproject.toml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.black]
|
|
6
|
+
line-length = 125
|
|
7
|
+
skip-string-normalization = true
|
|
8
|
+
|
|
9
|
+
[tool.pyright]
|
|
10
|
+
include = ["discord_protos"]
|
|
11
|
+
exclude = [
|
|
12
|
+
"**/__pycache__",
|
|
13
|
+
"src",
|
|
14
|
+
"node_modules",
|
|
15
|
+
]
|
|
16
|
+
reportUnnecessaryTypeIgnoreComment = "warning"
|
|
17
|
+
reportUnusedImport = "error"
|
|
18
|
+
reportShadowedImports = false
|
|
19
|
+
pythonVersion = "3.8"
|
|
20
|
+
typeCheckingMode = "basic"
|
|
21
|
+
useLibraryCodeForTypes = true
|
package/requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
protobuf
|
package/scripts/parse.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// Map the type ints to their names
|
|
2
|
+
const ScalarType = {
|
|
3
|
+
1: "double",
|
|
4
|
+
2: "float",
|
|
5
|
+
3: "int64",
|
|
6
|
+
4: "uint64",
|
|
7
|
+
5: "int32",
|
|
8
|
+
6: "fixed64",
|
|
9
|
+
7: "fixed32",
|
|
10
|
+
8: "bool",
|
|
11
|
+
9: "string",
|
|
12
|
+
12: "bytes",
|
|
13
|
+
13: "uint32",
|
|
14
|
+
15: "sfixed32",
|
|
15
|
+
16: "sfixed64",
|
|
16
|
+
17: "sint32",
|
|
17
|
+
18: "sint64",
|
|
18
|
+
};
|
|
19
|
+
const RepeatType = {
|
|
20
|
+
NO: 0,
|
|
21
|
+
PACKED: 1,
|
|
22
|
+
UNPACKED: 2,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function parseType(field) {
|
|
26
|
+
// We extract the actual field if possible
|
|
27
|
+
if (typeof field === "function") {
|
|
28
|
+
field = field();
|
|
29
|
+
// If it's a real type, we just return it
|
|
30
|
+
} else if (typeof field === "number") {
|
|
31
|
+
return [ScalarType[field], []];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var type,
|
|
35
|
+
structs = [];
|
|
36
|
+
|
|
37
|
+
// The kind gives us clues on how to find the type
|
|
38
|
+
switch (field.kind) {
|
|
39
|
+
case "message":
|
|
40
|
+
type = field.T().typeName;
|
|
41
|
+
if (type.startsWith("discord")) {
|
|
42
|
+
[, type] = parseName(type);
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case "scalar":
|
|
46
|
+
type = ScalarType[field.T];
|
|
47
|
+
break;
|
|
48
|
+
case "map":
|
|
49
|
+
type = `map<${parseType(field.K)[0]}, ${parseType(field.V)[0]}>`;
|
|
50
|
+
break;
|
|
51
|
+
case "enum":
|
|
52
|
+
[, type] = parseName(field.T()[0]);
|
|
53
|
+
break;
|
|
54
|
+
default:
|
|
55
|
+
throw new Error(`Unknown field type: ${field?.kind || field}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Now we lazily discover any protos in the fields
|
|
59
|
+
for (let t of [field.T, field.K, field.V]) {
|
|
60
|
+
t = t?.T || t;
|
|
61
|
+
|
|
62
|
+
if (typeof t === "function" && (!t().typeName || t().typeName.startsWith("discord_protos"))) {
|
|
63
|
+
t = t();
|
|
64
|
+
if (Array.isArray(t)) {
|
|
65
|
+
structs.push(parseEnum(t));
|
|
66
|
+
} else {
|
|
67
|
+
const extraStruct = parseProto(t);
|
|
68
|
+
structs.push(...(extraStruct.structs || []));
|
|
69
|
+
delete extraStruct.structs;
|
|
70
|
+
structs.push(extraStruct);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [type, structs];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function parseName(name) {
|
|
79
|
+
const split = name.split(".");
|
|
80
|
+
return [split.slice(0, -1).join("."), split.slice(-1)[0]];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function convertCase(str) {
|
|
84
|
+
// This converts standard EnumType case to ENUM_TYPE
|
|
85
|
+
// it must support the special case SlayerSDKReceive to SLAYER_SDK_RECEIVE
|
|
86
|
+
return str
|
|
87
|
+
// HACK: I can't think of another way to fix this
|
|
88
|
+
.replace("DMs", "Dms")
|
|
89
|
+
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
90
|
+
.replace(/([A-Z])([A-Z][a-z])/g, "$1_$2")
|
|
91
|
+
.replace(/([a-z])(\d)/g, "$1_$2")
|
|
92
|
+
.replace(/([A-Z]+)(?=[A-Z][a-z])/g, "$1_")
|
|
93
|
+
.toUpperCase();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function camelToSnake(str) {
|
|
97
|
+
return str.replace(/(([a-z])(?=[A-Z][a-zA-Z])|([A-Z])(?=[A-Z][a-z]))/g,'$1_').toLowerCase()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function flattenField(field) {
|
|
101
|
+
const [type, structs] = parseType(field);
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
number: field.no,
|
|
105
|
+
name: field.name,
|
|
106
|
+
kind: field.kind,
|
|
107
|
+
type: type,
|
|
108
|
+
oneof: field.oneof,
|
|
109
|
+
optional: field.opt,
|
|
110
|
+
repeated: Boolean(field.repeat),
|
|
111
|
+
unpacked: field.repeat === RepeatType.UNPACKED,
|
|
112
|
+
},
|
|
113
|
+
structs,
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parseEnum(enun) {
|
|
118
|
+
const [name, data] = enun;
|
|
119
|
+
|
|
120
|
+
// Protobuf enum values conventionally have a prefix of NAME_ and we must add that back
|
|
121
|
+
const [, formattedName] = parseName(name);
|
|
122
|
+
const prefix = convertCase(formattedName) + "_";
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
name: formattedName,
|
|
126
|
+
kind: "enum",
|
|
127
|
+
values: Object.entries(data)
|
|
128
|
+
.filter(([k, _]) => isNaN(Number(k)))
|
|
129
|
+
.map(([k, v]) => ({
|
|
130
|
+
// Discord shitcode sometimes fails to strip the prefix on the special case
|
|
131
|
+
// so we check if it already has it
|
|
132
|
+
name: k.startsWith(prefix) ? k : prefix + k,
|
|
133
|
+
value: v,
|
|
134
|
+
})),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function parseProto(proto) {
|
|
139
|
+
const fields = [];
|
|
140
|
+
const structs = [];
|
|
141
|
+
proto.fields.forEach(function (field) {
|
|
142
|
+
const [f, s] = flattenField(field);
|
|
143
|
+
fields.push(f);
|
|
144
|
+
structs.push(...s);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const seen = new Set();
|
|
148
|
+
const [package, name] = parseName(proto.typeName);
|
|
149
|
+
return {
|
|
150
|
+
package,
|
|
151
|
+
name,
|
|
152
|
+
kind: "message",
|
|
153
|
+
fields: fields,
|
|
154
|
+
structs: structs.filter((v) => (seen.has(v.name) ? false : seen.add(v.name))),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function extractProtos() {
|
|
159
|
+
const results = {};
|
|
160
|
+
|
|
161
|
+
// Populated by preload.js
|
|
162
|
+
for (const proto of window.protoObjects) {
|
|
163
|
+
if (!proto?.typeName?.startsWith?.("discord_protos")) continue;
|
|
164
|
+
const [, name] = parseName(proto.typeName);
|
|
165
|
+
console.log(`Parsing ${name}...`);
|
|
166
|
+
results[name] = parseProto(proto);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Remove every proto that is mentioned in another proto
|
|
170
|
+
for (const proto of Object.values(results)) {
|
|
171
|
+
for (const struct of proto.structs) {
|
|
172
|
+
delete results[struct.name];
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function createProtoField(field) {
|
|
180
|
+
if (!field.repeated && field.unpacked)
|
|
181
|
+
throw new Error(`Field ${field.name} is not repeated but has unpacked set`);
|
|
182
|
+
return `${field.optional ? "optional " : field.repeated ? "repeated " : ""}${field.type} ${field.name} = ${field.number}${field.unpacked ? " [packed = false]" : ""};`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function createProtoFile(proto) {
|
|
186
|
+
const lines = [`syntax = "proto3";\n`, `package ${proto.package};\n`, `message ${proto.name} {`];
|
|
187
|
+
|
|
188
|
+
proto.structs.forEach((struct) => {
|
|
189
|
+
lines.push(` ${struct.kind} ${struct.name} {`);
|
|
190
|
+
|
|
191
|
+
switch (struct.kind) {
|
|
192
|
+
case "enum":
|
|
193
|
+
struct.values.forEach((value) => {
|
|
194
|
+
lines.push(` ${value.name.toUpperCase()} = ${value.value};`);
|
|
195
|
+
});
|
|
196
|
+
break;
|
|
197
|
+
case "message":
|
|
198
|
+
// Group fields by oneof if applicable
|
|
199
|
+
const oneofGroups = new Map();
|
|
200
|
+
const normalFields = [];
|
|
201
|
+
|
|
202
|
+
struct.fields.forEach((field) => {
|
|
203
|
+
if (field.oneof) {
|
|
204
|
+
if (!oneofGroups.has(field.oneof)) {
|
|
205
|
+
oneofGroups.set(field.oneof, []);
|
|
206
|
+
}
|
|
207
|
+
oneofGroups.get(field.oneof).push(field);
|
|
208
|
+
} else {
|
|
209
|
+
normalFields.push(field);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
oneofGroups.forEach((fields, oneofName) => {
|
|
214
|
+
lines.push(` oneof ${camelToSnake(oneofName)} {`);
|
|
215
|
+
fields.forEach((field) => {
|
|
216
|
+
lines.push(` ${createProtoField(field)}`);
|
|
217
|
+
});
|
|
218
|
+
lines.push(` }`);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
normalFields.forEach((field) => {
|
|
222
|
+
lines.push(` ${createProtoField(field)}`);
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
default:
|
|
226
|
+
throw new Error(`Unknown struct kind: ${struct.kind}`);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
lines.push(` }\n`);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const oneofGroups = new Map();
|
|
233
|
+
|
|
234
|
+
proto.fields.forEach((field) => {
|
|
235
|
+
if (field.oneof) {
|
|
236
|
+
if (!oneofGroups.has(field.oneof)) {
|
|
237
|
+
oneofGroups.set(field.oneof, []);
|
|
238
|
+
}
|
|
239
|
+
oneofGroups.get(field.oneof).push(createProtoField(field));
|
|
240
|
+
} else {
|
|
241
|
+
lines.push(` ${createProtoField(field)}`);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
oneofGroups.forEach((fields, oneofName) => {
|
|
246
|
+
lines.push(` oneof ${camelToSnake(oneofName)} {`);
|
|
247
|
+
fields.forEach(field => lines.push(` ${field}`));
|
|
248
|
+
lines.push(` }\n`);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Check if we're using the funny Google well-knowns and insert an import statement (I love Discord)
|
|
252
|
+
if (lines.some((line) => line.includes("google.protobuf"))) {
|
|
253
|
+
lines.splice(1, 0, `import "google/protobuf/wrappers.proto";\nimport "google/protobuf/timestamp.proto";\n`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
lines.push("}\n");
|
|
257
|
+
return lines.join("\n");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const protos = extractProtos();
|
|
261
|
+
for (const [key, proto] of Object.entries(protos)) {
|
|
262
|
+
const data = createProtoFile(proto);
|
|
263
|
+
protos[key].data = data;
|
|
264
|
+
if (window.DiscordNative?.fileManager) {
|
|
265
|
+
window.DiscordNative.fileManager.saveWithDialog(data, `${proto.name}.proto`);
|
|
266
|
+
} else {
|
|
267
|
+
console.log(data);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
window.protoObjects = [];
|
|
2
|
+
Object.defineProperty(Object.prototype, "typeName", {
|
|
3
|
+
configurable: true,
|
|
4
|
+
|
|
5
|
+
set(v) {
|
|
6
|
+
if (this.internalBinaryRead != null) {
|
|
7
|
+
window.protoObjects.push(this);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Object.defineProperty(this, "typeName", {
|
|
11
|
+
value: v,
|
|
12
|
+
configurable: true,
|
|
13
|
+
enumerable: true,
|
|
14
|
+
writable: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
package/setup.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from setuptools import setup
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
with open('requirements.txt') as f:
|
|
5
|
+
requirements = f.read().splitlines()
|
|
6
|
+
|
|
7
|
+
with open('discord_protos/__init__.py') as f:
|
|
8
|
+
version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1)
|
|
9
|
+
|
|
10
|
+
with open('README.md') as f:
|
|
11
|
+
readme = f.read()
|
|
12
|
+
|
|
13
|
+
setup(
|
|
14
|
+
name='discord-protos',
|
|
15
|
+
author='Dolfies',
|
|
16
|
+
url='https://github.com/dolfies/discord-protos',
|
|
17
|
+
project_urls={
|
|
18
|
+
'Issue tracker': 'https://github.com/dolfies/discord-protos/issues',
|
|
19
|
+
},
|
|
20
|
+
version=version,
|
|
21
|
+
packages=['discord_protos'],
|
|
22
|
+
license='MIT',
|
|
23
|
+
description='Discord user settings protobufs.',
|
|
24
|
+
long_description=readme,
|
|
25
|
+
long_description_content_type='text/markdown',
|
|
26
|
+
include_package_data=True,
|
|
27
|
+
install_requires=requirements,
|
|
28
|
+
python_requires='>=3.8.0',
|
|
29
|
+
classifiers=[
|
|
30
|
+
'License :: OSI Approved :: MIT License',
|
|
31
|
+
'Natural Language :: English',
|
|
32
|
+
'Operating System :: OS Independent',
|
|
33
|
+
'Programming Language :: Python :: 3.8',
|
|
34
|
+
'Programming Language :: Python :: 3.9',
|
|
35
|
+
'Programming Language :: Python :: 3.10',
|
|
36
|
+
'Programming Language :: Python :: 3.11',
|
|
37
|
+
'Topic :: Internet',
|
|
38
|
+
'Topic :: Software Development :: Libraries',
|
|
39
|
+
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
40
|
+
'Topic :: Utilities',
|
|
41
|
+
],
|
|
42
|
+
)
|
package/tsconfig.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
3
|
+
"target": "ESNext",
|
|
4
4
|
"module": "commonjs",
|
|
5
5
|
"allowJs": true,
|
|
6
6
|
"declaration": true,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
"strictNullChecks": true,
|
|
8
|
+
"outDir": "./dist",
|
|
9
|
+
"rootDir": "./src"
|
|
10
|
+
},
|
|
11
|
+
"include": ["src/*.ts", "src/*/*.ts"],
|
|
10
12
|
}
|
package/dist/parse.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
declare function parseType(field: any): any;
|
|
2
|
-
declare function parseName(name: any): any;
|
|
3
|
-
declare function flattenField(field: any): any[];
|
|
4
|
-
declare function parseEnum(enun: any): {
|
|
5
|
-
name: any;
|
|
6
|
-
kind: string;
|
|
7
|
-
values: {
|
|
8
|
-
name: string;
|
|
9
|
-
value: any;
|
|
10
|
-
}[];
|
|
11
|
-
};
|
|
12
|
-
declare function parseProto(proto: any): {
|
|
13
|
-
name: any;
|
|
14
|
-
kind: string;
|
|
15
|
-
fields: any[];
|
|
16
|
-
structs: any[];
|
|
17
|
-
};
|
|
18
|
-
declare function extractProtos(): {};
|
|
19
|
-
declare function createProtoField(field: any): string;
|
|
20
|
-
declare function createProtoFile(proto: any): string;
|
|
21
|
-
declare function getModules(str: any): any;
|
|
22
|
-
declare function filterMap(arr: any, callback: any): any;
|
|
23
|
-
declare const REAL_TYPES: {
|
|
24
|
-
1: string;
|
|
25
|
-
2: string;
|
|
26
|
-
3: string;
|
|
27
|
-
4: string;
|
|
28
|
-
5: string;
|
|
29
|
-
6: string;
|
|
30
|
-
8: string;
|
|
31
|
-
9: string;
|
|
32
|
-
12: string;
|
|
33
|
-
13: string;
|
|
34
|
-
};
|
|
35
|
-
declare const protos: {};
|