diff --git a/src/gateway/util/Constants.ts b/src/gateway/util/Constants.ts
new file mode 100644
index 00000000..ff9b5525
--- /dev/null
+++ b/src/gateway/util/Constants.ts
@@ -0,0 +1,52 @@
+// import { VoiceOPCodes } from "@fosscord/webrtc";
+
+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 /* | VoiceOPCodes */;
+ d?: any;
+ s?: number;
+ t?: string;
+}
diff --git a/src/gateway/util/Heartbeat.ts b/src/gateway/util/Heartbeat.ts
new file mode 100644
index 00000000..f6871cfe
--- /dev/null
+++ b/src/gateway/util/Heartbeat.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);
+}
diff --git a/src/gateway/util/Send.ts b/src/gateway/util/Send.ts
new file mode 100644
index 00000000..e1460846
--- /dev/null
+++ b/src/gateway/util/Send.ts
@@ -0,0 +1,32 @@
+var erlpack: any;
+try {
+ erlpack = require("@yukikaze-bot/erlpack");
+} catch (error) {
+ console.log("Missing @yukikaze-bot/erlpack, electron-based desktop clients designed for discord.com will not be able to connect!");
+}
+import { Payload, WebSocket } from "@fosscord/gateway";
+
+export 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) {
+ buffer = socket.deflate.process(buffer) as Buffer;
+ }
+
+ return new Promise((res, rej) => {
+ if (socket.readyState !== 1) {
+ // return rej("socket not open");
+ socket.close();
+ return;
+ }
+
+ socket.send(buffer, (err: any) => {
+ if (err) return rej(err);
+ return res(null);
+ });
+ });
+}
diff --git a/src/gateway/util/SessionUtils.ts b/src/gateway/util/SessionUtils.ts
new file mode 100644
index 00000000..bf854042
--- /dev/null
+++ b/src/gateway/util/SessionUtils.ts
@@ -0,0 +1,13 @@
+export function genSessionId() {
+ return genRanHex(32);
+}
+
+export function genVoiceToken() {
+ return genRanHex(16);
+}
+
+function genRanHex(size: number) {
+ return [...Array(size)]
+ .map(() => Math.floor(Math.random() * 16).toString(16))
+ .join("");
+}
diff --git a/src/gateway/util/WebSocket.ts b/src/gateway/util/WebSocket.ts
new file mode 100644
index 00000000..930fa78a
--- /dev/null
+++ b/src/gateway/util/WebSocket.ts
@@ -0,0 +1,26 @@
+import { Intents, Permissions } from "@fosscord/util";
+import WS from "ws";
+import { Deflate, Inflate } from "fast-zlib";
+// import { Client } from "@fosscord/webrtc";
+
+export interface WebSocket extends WS {
+ version: number;
+ user_id: string;
+ session_id: string;
+ encoding: "etf" | "json";
+ compress?: "zlib-stream";
+ ipAddress?: string;
+ shard_count?: bigint;
+ shard_id?: bigint;
+ deflate?: Deflate;
+ inflate?: Inflate;
+ heartbeatTimeout: NodeJS.Timeout;
+ readyTimeout: NodeJS.Timeout;
+ intents: Intents;
+ sequence: number;
+ permissions: Record<string, Permissions>;
+ events: Record<string, Function>;
+ member_events: Record<string, Function>;
+ listen_options: any;
+ // client?: Client;
+}
diff --git a/src/gateway/util/index.ts b/src/gateway/util/index.ts
new file mode 100644
index 00000000..0be5ecee
--- /dev/null
+++ b/src/gateway/util/index.ts
@@ -0,0 +1,5 @@
+export * from "./Constants";
+export * from "./Send";
+export * from "./SessionUtils";
+export * from "./Heartbeat";
+export * from "./WebSocket";
|