diff --git a/src/util/Config.ts b/src/util/Config.ts
new file mode 100644
index 00000000..9ceb8cd5
--- /dev/null
+++ b/src/util/Config.ts
@@ -0,0 +1,41 @@
+// @ts-nocheck
+import { Config } from "@fosscord/server-util";
+import { getConfigPathForFile } from "@fosscord/server-util/dist/util/Config";
+import Ajv, { JSONSchemaType } from "ajv";
+
+export interface DefaultOptions {
+ endpoint?: string;
+ security: {
+ jwtSecret: string;
+ };
+}
+
+const schema: JSONSchemaType<DefaultOptions> = {
+ type: "object",
+ properties: {
+ endpoint: {
+ type: "string",
+ nullable: true,
+ },
+ security: {
+ type: "object",
+ properties: {
+ jwtSecret: {
+ type: "string",
+ },
+ },
+ required: ["jwtSecret"],
+ },
+ },
+ required: ["security"],
+};
+
+const ajv = new Ajv();
+const validator = ajv.compile(schema);
+
+const configPath = getConfigPathForFile("fosscord", "gateway", ".json");
+export const gatewayConfig = new Config<DefaultOptions>({
+ path: configPath,
+ schemaValidator: validator,
+ schema: schema,
+});
diff --git a/src/util/Constants.ts b/src/util/Constants.ts
new file mode 100644
index 00000000..692f9028
--- /dev/null
+++ b/src/util/Constants.ts
@@ -0,0 +1,50 @@
+export enum OPCODES {
+ Dispatch = 0,
+ Heartbeat = 1,
+ Identify = 2,
+ Presence_Update = 3,
+ Voice_State_Update = 4,
+ Voice_Server_Ping = 5, // ? What is opcode 5?
+ Resume = 6,
+ Reconnect = 7,
+ Request_Guild_Members = 8,
+ Invalid_Session = 9,
+ Hello = 10,
+ Heartbeat_ACK = 11,
+ Guild_Sync = 12,
+ DM_Update = 13,
+ Lazy_Request = 14,
+ Lobby_Connect = 15,
+ Lobby_Disconnect = 16,
+ Lobby_Voice_States_Update = 17,
+ Stream_Create = 18,
+ Stream_Delete = 19,
+ Stream_Watch = 20,
+ Stream_Ping = 21,
+ Stream_Set_Paused = 22,
+ Request_Application_Commands = 24,
+}
+export enum CLOSECODES {
+ Unknown_error = 4000,
+ Unknown_opcode,
+ Decode_error,
+ Not_authenticated,
+ Authentication_failed,
+ Already_authenticated,
+ Invalid_session,
+ Invalid_seq,
+ Rate_limited,
+ Session_timed_out,
+ Invalid_shard,
+ Sharding_required,
+ Invalid_API_version,
+ Invalid_intent,
+ Disallowed_intent,
+}
+
+export interface Payload {
+ op: OPCODES;
+ d?: any;
+ s?: number;
+ t?: string;
+}
diff --git a/src/util/Send.ts b/src/util/Send.ts
new file mode 100644
index 00000000..be25ac4f
--- /dev/null
+++ b/src/util/Send.ts
@@ -0,0 +1,28 @@
+var erlpack: any;
+try {
+ erlpack = require("erlpack");
+} catch (error) {}
+import { Payload } from "../util/Constants";
+
+import WebSocket from "./WebSocket";
+
+export async function Send(socket: WebSocket, data: Payload) {
+ let buffer: Buffer | string;
+ if (socket.encoding === "etf") buffer = erlpack.pack(data);
+ // TODO: encode circular object
+ else if (socket.encoding === "json") buffer = JSON.stringify(data);
+ else return;
+ // TODO: compression
+ if (socket.deflate) {
+ socket.deflate.write(buffer);
+ socket.deflate.flush();
+ return;
+ }
+
+ return new Promise((res, rej) => {
+ socket.send(buffer, (err) => {
+ if (err) return rej(err);
+ return res(null);
+ });
+ });
+}
diff --git a/src/util/WebSocket.ts b/src/util/WebSocket.ts
new file mode 100644
index 00000000..1bd0ff2f
--- /dev/null
+++ b/src/util/WebSocket.ts
@@ -0,0 +1,23 @@
+import { Intents, Permissions } from "@fosscord/server-util";
+import WS, { Server, Data } from "ws";
+import { Deflate } from "zlib";
+import { Channel } from "amqplib";
+
+interface WebSocket extends WS {
+ version: number;
+ user_id: string;
+ encoding: "etf" | "json";
+ compress?: "zlib-stream";
+ shard_count?: bigint;
+ shard_id?: bigint;
+ deflate?: Deflate;
+ heartbeatTimeout: NodeJS.Timeout;
+ readyTimeout: NodeJS.Timeout;
+ intents: Intents;
+ sequence: number;
+ rabbitCh?: Channel & { queues: Record<string, string> };
+ permissions: Record<string, Permissions>;
+}
+
+export default WebSocket;
+export { Server, Data };
diff --git a/src/util/setHeartbeat.ts b/src/util/setHeartbeat.ts
new file mode 100644
index 00000000..9f88b481
--- /dev/null
+++ b/src/util/setHeartbeat.ts
@@ -0,0 +1,11 @@
+import { CLOSECODES } from "./Constants";
+import WebSocket from "./WebSocket";
+
+// TODO: make heartbeat timeout configurable
+export function setHeartbeat(socket: WebSocket) {
+ if (socket.heartbeatTimeout) clearTimeout(socket.heartbeatTimeout);
+
+ socket.heartbeatTimeout = setTimeout(() => {
+ return socket.close(CLOSECODES.Session_timed_out);
+ }, 1000 * 45);
+}
|