diff --git a/bundle/package-lock.json b/bundle/package-lock.json index 9707c7bf..8c99fce6 100644 Binary files a/bundle/package-lock.json and b/bundle/package-lock.json differ diff --git a/bundle/package.json b/bundle/package.json index 7d68427f..aedd963b 100644 --- a/bundle/package.json +++ b/bundle/package.json @@ -66,6 +66,7 @@ "@fosscord/gateway": "file:../gateway", "@sentry/node": "^6.16.1", "@sentry/tracing": "^6.16.1", + "@yukikaze-bot/erlpack": "^1.0.1", "ajv": "8.6.2", "ajv-formats": "^2.1.1", "amqplib": "^0.8.0", diff --git a/bundle/src/Server.ts b/bundle/src/Server.ts index 71a60d49..bc1d7cbc 100644 --- a/bundle/src/Server.ts +++ b/bundle/src/Server.ts @@ -50,20 +50,20 @@ async function main() { endpointPublic: `ws://localhost:${port}`, }), }, - // regions: { - // default: "fosscord", - // useDefaultAsOptimal: true, - // available: [ - // { - // id: "fosscord", - // name: "Fosscord", - // endpoint: "127.0.0.1:3001", - // vip: false, - // custom: false, - // deprecated: false, - // }, - // ], - // }, + regions: { + default: "fosscord", + useDefaultAsOptimal: true, + available: [ + { + id: "fosscord", + name: "Fosscord", + endpoint: "127.0.0.1:3004", + vip: false, + custom: false, + deprecated: false, + }, + ], + }, } as any); //Sentry diff --git a/gateway/src/schema/VoiceStateUpdateSchema.ts b/gateway/src/schema/VoiceStateUpdateSchema.ts index 9efa191e..f6480414 100644 --- a/gateway/src/schema/VoiceStateUpdateSchema.ts +++ b/gateway/src/schema/VoiceStateUpdateSchema.ts @@ -3,7 +3,8 @@ export const VoiceStateUpdateSchema = { $channel_id: String, self_mute: Boolean, self_deaf: Boolean, - self_video: Boolean, + $self_video: Boolean, //required in docs but bots don't always send it + $preferred_region: String, }; export interface VoiceStateUpdateSchema { @@ -11,5 +12,6 @@ export interface VoiceStateUpdateSchema { channel_id?: string; self_mute: boolean; self_deaf: boolean; - self_video: boolean; -} + self_video?: boolean; + preferred_region?: string; +} \ No newline at end of file diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts index 8d61b9b4..42a2c274 100644 --- a/util/src/util/Constants.ts +++ b/util/src/util/Constants.ts @@ -73,9 +73,13 @@ export const VoiceOPCodes = { HEARTBEAT: 3, SESSION_DESCRIPTION: 4, SPEAKING: 5, + HEARTBEAT_ACK: 6, + RESUME: 7, HELLO: 8, - CLIENT_CONNECT: 12, - CLIENT_DISCONNECT: 13, + RESUMED: 9, + CLIENT_CONNECT: 12, // incorrect, op 12 is probably used for video + CLIENT_DISCONNECT: 13, // incorrect + VERSION: 16, //not documented }; export const Events = { diff --git a/webrtc/.vscode/launch.json b/webrtc/.vscode/launch.json new file mode 100644 index 00000000..49584172 --- /dev/null +++ b/webrtc/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ts-node", + "type": "node", + "request": "launch", + "args": [ + "src/start.ts" + ], + "runtimeArgs": [ + "-r", + "ts-node/register" + ], + "cwd": "${workspaceRoot}", + "protocol": "inspector", + "internalConsoleOptions": "openOnSessionStart", + "sourceMaps": true, + "resolveSourceMapLocations": null, + } + ] +} \ No newline at end of file diff --git a/webrtc/package-lock.json b/webrtc/package-lock.json index a5db2de1..afba7e76 100644 Binary files a/webrtc/package-lock.json and b/webrtc/package-lock.json differ diff --git a/webrtc/package.json b/webrtc/package.json index 0f700728..b9bac356 100644 --- a/webrtc/package.json +++ b/webrtc/package.json @@ -13,12 +13,17 @@ "license": "ISC", "devDependencies": { "@types/node": "^15.6.1", + "@types/sdp-transform": "^2.4.5", "@types/ws": "^7.4.4", + "ts-node": "^10.4.0", "typescript": "^4.3.2" }, "dependencies": { - "mediasoup": "^3.7.16", + "dotenv": "^12.0.4", + "mediasoup": "^3.9.5", "node-turn": "^0.0.6", + "sdp-transform": "^2.14.1", + "tsconfig-paths": "^3.12.0", "ws": "^7.4.6" } } diff --git a/webrtc/src/Server.ts b/webrtc/src/Server.ts index 6591691c..42b82c6a 100644 --- a/webrtc/src/Server.ts +++ b/webrtc/src/Server.ts @@ -1,46 +1,125 @@ import { Server as WebSocketServer } from "ws"; -import { Config, db } from "@fosscord/util"; -import mediasoup from "mediasoup"; +import { WebSocket, CLOSECODES } from "@fosscord/gateway"; +import { Config, initDatabase } from "@fosscord/util"; +import OPCodeHandlers, { Payload } from "./opcodes"; +import { setHeartbeat } from "./util"; +import * as mediasoup from "mediasoup"; +import { types as MediasoupTypes } from "mediasoup"; + +import udp from "dgram"; var port = Number(process.env.PORT); if (isNaN(port)) port = 3004; export class Server { public ws: WebSocketServer; - public turn: any; + public mediasoupWorkers: MediasoupTypes.Worker[] = []; + public mediasoupRouters: MediasoupTypes.Router[] = []; + public mediasoupTransports: MediasoupTypes.WebRtcTransport[] = []; + public mediasoupProducers: MediasoupTypes.Producer[] = []; + public mediasoupConsumers: MediasoupTypes.Consumer[] = []; constructor() { this.ws = new WebSocketServer({ port, maxPayload: 4096, }); - this.ws.on("connection", (socket) => { - socket.on("message", (message) => { - socket.emit( - JSON.stringify({ - op: 2, - d: { - ssrc: 1, - ip: "127.0.0.1", - port: 3004, - modes: [ - "xsalsa20_poly1305", - "xsalsa20_poly1305_suffix", - "xsalsa20_poly1305_lite", - ], - heartbeat_interval: 1, - }, - }) - ); + this.ws.on("connection", async (socket: WebSocket) => { + await setHeartbeat(socket); + + socket.on("message", async (message: string) => { + const payload: Payload = JSON.parse(message); + + if (OPCodeHandlers[payload.op]) + try { + await OPCodeHandlers[payload.op].call(this, socket, payload); + } + catch (e) { + console.error(e); + socket.close(CLOSECODES.Unknown_error); + } + else { + console.error(`Unimplemented`, payload); + socket.close(CLOSECODES.Unknown_opcode); + } }); }); + } async listen(): Promise { // @ts-ignore - await (db as Promise); + await initDatabase(); await Config.init(); + await this.createWorkers(); console.log("[DB] connected"); console.log(`[WebRTC] online on 0.0.0.0:${port}`); } + + async createWorkers(): Promise { + const numWorkers = 1; + for (let i = 0; i < numWorkers; i++) { + const worker = await mediasoup.createWorker({ logLevel: "debug" }); + if (!worker) return; + + worker.on("died", () => { + console.error("mediasoup worker died"); + }); + + worker.observer.on("newrouter", async (router: MediasoupTypes.Router) => { + console.log("new router created [id:%s]", router.id); + + this.mediasoupRouters.push(router); + + router.observer.on("newtransport", async (transport: MediasoupTypes.WebRtcTransport) => { + console.log("new transport created [id:%s]", transport.id); + + await transport.enableTraceEvent(); + + transport.on("connect", () => { + console.log("transport connect") + }) + + transport.observer.on("newproducer", (producer: MediasoupTypes.Producer) => { + console.log("new producer created [id:%s]", producer.id); + + this.mediasoupProducers.push(producer); + }); + + transport.observer.on("newconsumer", (consumer: MediasoupTypes.Consumer) => { + console.log("new consumer created [id:%s]", consumer.id); + + this.mediasoupConsumers.push(consumer); + + consumer.on("rtp", (rtpPacket) => { + console.log(rtpPacket); + }); + }); + + transport.observer.on("newdataproducer", (dataProducer) => { + console.log("new data producer created [id:%s]", dataProducer.id); + }); + + transport.on("trace", (trace) => { + console.log(trace); + }); + + this.mediasoupTransports.push(transport); + }); + }); + + await worker.createRouter({ + mediaCodecs: [ + { + kind: "audio", + mimeType: "audio/opus", + clockRate: 48000, + channels: 2 + }, + ] + }); + + this.mediasoupWorkers.push(worker); + } + } } diff --git a/webrtc/src/opcodes/Connect.ts b/webrtc/src/opcodes/Connect.ts new file mode 100644 index 00000000..1f874a44 --- /dev/null +++ b/webrtc/src/opcodes/Connect.ts @@ -0,0 +1,40 @@ +import { WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { Server } from "../Server" + +/* +Sent by client: + +{ + "op": 12, + "d": { + "audio_ssrc": 0, + "video_ssrc": 0, + "rtx_ssrc": 0, + "streams": [ + { + "type": "video", + "rid": "100", + "ssrc": 0, + "active": false, + "quality": 100, + "rtx_ssrc": 0, + "max_bitrate": 2500000, + "max_framerate": 20, + "max_resolution": { + "type": "fixed", + "width": 1280, + "height": 720 + } + } + ] + } +} +*/ + +export async function onConnect(this: Server, socket: WebSocket, data: Payload) { + socket.send(JSON.stringify({ //what is op 15? + op: 15, + d: { any: 100 } + })) +} \ No newline at end of file diff --git a/webrtc/src/opcodes/Heartbeat.ts b/webrtc/src/opcodes/Heartbeat.ts new file mode 100644 index 00000000..47f33f76 --- /dev/null +++ b/webrtc/src/opcodes/Heartbeat.ts @@ -0,0 +1,8 @@ +import { WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { setHeartbeat } from "../util"; +import { Server } from "../Server" + +export async function onHeartbeat(this: Server, socket: WebSocket, data: Payload) { + await setHeartbeat(socket, data.d); +} \ No newline at end of file diff --git a/webrtc/src/opcodes/Identify.ts b/webrtc/src/opcodes/Identify.ts new file mode 100644 index 00000000..9baa16e3 --- /dev/null +++ b/webrtc/src/opcodes/Identify.ts @@ -0,0 +1,66 @@ +import { WebSocket, CLOSECODES } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { VoiceOPCodes, Session, User, Guild } from "@fosscord/util"; +import { Server } from "../Server"; + +export interface IdentifyPayload extends Payload { + d: { + server_id: string, //guild id + session_id: string, //gateway session + streams: Array<{ + type: string, + rid: string, //number + quality: number, + }>, + token: string, //voice_states token + user_id: string, + video: boolean, + }; +} + +export async function onIdentify(this: Server, socket: WebSocket, data: IdentifyPayload) { + + const session = await Session.findOneOrFail( + { session_id: data.d.session_id, }, + { + where: { user_id: data.d.user_id }, + relations: ["user"] + } + ); + const user = session.user; + const guild = await Guild.findOneOrFail({ id: data.d.server_id }, { relations: ["members"] }); + + if (!guild.members.find(x => x.id === user.id)) + return socket.close(CLOSECODES.Invalid_intent); + + var transport = this.mediasoupTransports[0] || await this.mediasoupRouters[0].createWebRtcTransport({ + listenIps: [{ ip: "10.22.64.69" }], + enableUdp: true, + enableTcp: true, + preferUdp: true, + enableSctp: true, + }); + + socket.send(JSON.stringify({ + op: VoiceOPCodes.READY, + d: { + streams: [...data.d.streams.map(x => ({ ...x, rtx_ssrc: Math.floor(Math.random() * 10000), ssrc: Math.floor(Math.random() * 10000), active: false, }))], + ssrc: Math.floor(Math.random() * 10000), + ip: transport.iceCandidates[0].ip, + port: transport.iceCandidates[0].port, + modes: [ + "aead_aes256_gcm_rtpsize", + "aead_aes256_gcm", + "xsalsa20_poly1305_lite_rtpsize", + "xsalsa20_poly1305_lite", + "xsalsa20_poly1305_suffix", + "xsalsa20_poly1305" + ], + experiments: [ + "bwe_conservative_link_estimate", + "bwe_remote_locus_client", + "fixed_keyframe_interval" + ] + }, + })); +} \ No newline at end of file diff --git a/webrtc/src/opcodes/Resume.ts b/webrtc/src/opcodes/Resume.ts new file mode 100644 index 00000000..856b550c --- /dev/null +++ b/webrtc/src/opcodes/Resume.ts @@ -0,0 +1,24 @@ +import { CLOSECODES, WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { Server } from "../Server" +import { Guild, Session, VoiceOPCodes } from "@fosscord/util"; + +export async function onResume(this: Server, socket: WebSocket, data: Payload) { + const session = await Session.findOneOrFail( + { session_id: data.d.session_id, }, + { + where: { user_id: data.d.user_id }, + relations: ["user"] + } + ); + const user = session.user; + const guild = await Guild.findOneOrFail({ id: data.d.server_id }, { relations: ["members"] }); + + if (!guild.members.find(x => x.id === user.id)) + return socket.close(CLOSECODES.Invalid_intent); + + socket.send(JSON.stringify({ + op: VoiceOPCodes.RESUMED, + d: null, + })) +} \ No newline at end of file diff --git a/webrtc/src/opcodes/SelectProtocol.ts b/webrtc/src/opcodes/SelectProtocol.ts new file mode 100644 index 00000000..dc9d2b88 --- /dev/null +++ b/webrtc/src/opcodes/SelectProtocol.ts @@ -0,0 +1,150 @@ +import { WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { VoiceOPCodes } from "@fosscord/util"; +import { Server } from "../Server"; +import * as mediasoup from "mediasoup"; +import { RtpCodecCapability } from "mediasoup/node/lib/RtpParameters"; +import * as sdpTransform from 'sdp-transform'; + + +/* + + Sent by client: +{ + "op": 1, + "d": { + "protocol": "webrtc", + "data": " + a=extmap-allow-mixed + a=ice-ufrag:vNxb + a=ice-pwd:tZvpbVPYEKcnW0gGRPq0OOnh + a=ice-options:trickle + a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level + a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time + a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 + a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid + a=rtpmap:111 opus/48000/2 + a=extmap:14 urn:ietf:params:rtp-hdrext:toffset + a=extmap:13 urn:3gpp:video-orientation + a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay + a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type + a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing + a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space + a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id + a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id + a=rtpmap:96 VP8/90000 + a=rtpmap:97 rtx/90000 + ", + "codecs": [ + { + "name": "opus", + "type": "audio", + "priority": 1000, + "payload_type": 111, + "rtx_payload_type": null + }, + { + "name": "H264", + "type": "video", + "priority": 1000, + "payload_type": 102, + "rtx_payload_type": 121 + }, + { + "name": "VP8", + "type": "video", + "priority": 2000, + "payload_type": 96, + "rtx_payload_type": 97 + }, + { + "name": "VP9", + "type": "video", + "priority": 3000, + "payload_type": 98, + "rtx_payload_type": 99 + } + ], + "rtc_connection_id": "3faa0b80-b3e2-4bae-b291-273801fbb7ab" + } +} + +Sent by server: + +{ + "op": 4, + "d": { + "video_codec": "H264", + "sdp": " + m=audio 50001 ICE/SDP + a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87 + c=IN IP4 109.200.214.158 + a=rtcp:50001 + a=ice-ufrag:CLzn + a=ice-pwd:qEmIcNwigd07mu46Ok0XCh + a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87 + a=candidate:1 1 UDP 4261412862 109.200.214.158 50001 typ host + ", + "media_session_id": "807955cb953e98c5b90704cf048e81ec", + "audio_codec": "opus" + } +} + +*/ + + +export async function onSelectProtocol(this: Server, socket: WebSocket, data: Payload) { + const rtpCapabilities = this.mediasoupRouters[0].rtpCapabilities; + const codecs = rtpCapabilities.codecs as RtpCodecCapability[]; + + const transport = this.mediasoupTransports[0]; //whatever + + const res = sdpTransform.parse(data.d.sdp); + + const videoCodec = this.mediasoupRouters[0].rtpCapabilities.codecs!.find((x: any) => x.kind === "video"); + const audioCodec = this.mediasoupRouters[0].rtpCapabilities.codecs!.find((x: any) => x.kind === "audio"); + + const producer = this.mediasoupProducers[0] || await transport.produce({ + kind: "audio", + rtpParameters: { + mid: "audio", + codecs: [{ + clockRate: audioCodec!.clockRate, + payloadType: audioCodec!.preferredPayloadType as number, + mimeType: audioCodec!.mimeType, + channels: audioCodec?.channels, + }], + headerExtensions: res.ext?.map(x => ({ + id: x.value, + uri: x.uri, + })), + }, + paused: false, + }); + + console.log("can consume: " + this.mediasoupRouters[0].canConsume({ producerId: producer.id, rtpCapabilities: rtpCapabilities })); + + const consumer = this.mediasoupConsumers[0] || await transport.consume({ + producerId: producer.id, + paused: false, + rtpCapabilities, + }); + + socket.send(JSON.stringify({ + op: VoiceOPCodes.SESSION_DESCRIPTION, + d: { + video_codec: videoCodec?.mimeType?.substring(6) || undefined, + mode: "xsalsa20_poly1305_lite", + media_session_id: transport.id, + audio_codec: audioCodec?.mimeType.substring(6), + sdp: `m=audio ${transport.iceCandidates[0].port} ICE/SDP\n` + + `a=fingerprint:sha-256 ${transport.dtlsParameters.fingerprints.find(x => x.algorithm === "sha-256")?.value}\n` + + `c=IN IPV4 ${transport.iceCandidates[0].ip}\n` + + `a=rtcp: ${transport.iceCandidates[0].port}\n` + + `a=ice-ufrag:${transport.iceParameters.usernameFragment}\n` + + `a=ice-pwd:${transport.iceParameters.password}\n` + + `a=fingerprint:sha-1 ${transport.dtlsParameters.fingerprints[0].value}\n` + + `a=candidate:1 1 ${transport.iceCandidates[0].protocol} ${transport.iceCandidates[0].priority} ${transport.iceCandidates[0].ip} ${transport.iceCandidates[0].port} typ ${transport.iceCandidates[0].type}` + } + })); +} \ No newline at end of file diff --git a/webrtc/src/opcodes/Speaking.ts b/webrtc/src/opcodes/Speaking.ts new file mode 100644 index 00000000..861a7c3d --- /dev/null +++ b/webrtc/src/opcodes/Speaking.ts @@ -0,0 +1,7 @@ +import { WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index" +import { VoiceOPCodes } from "@fosscord/util"; +import { Server } from "../Server" + +export async function onSpeaking(this: Server, socket: WebSocket, data: Payload) { +} \ No newline at end of file diff --git a/webrtc/src/opcodes/Version.ts b/webrtc/src/opcodes/Version.ts new file mode 100644 index 00000000..0ea6eb4d --- /dev/null +++ b/webrtc/src/opcodes/Version.ts @@ -0,0 +1,14 @@ +import { WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { setHeartbeat } from "../util"; +import { Server } from "../Server" + +export async function onVersion(this: Server, socket: WebSocket, data: Payload) { + socket.send(JSON.stringify({ + op: 16, + d: { + voice: "0.8.31", //version numbers? + rtc_worker: "0.3.18", + } + })) +} \ No newline at end of file diff --git a/webrtc/src/opcodes/index.ts b/webrtc/src/opcodes/index.ts new file mode 100644 index 00000000..d0f40bc2 --- /dev/null +++ b/webrtc/src/opcodes/index.ts @@ -0,0 +1,40 @@ +import { WebSocket } from "@fosscord/gateway"; +import { VoiceOPCodes } from "@fosscord/util"; + +export interface Payload { + op: number; + d: any; + s: number; + t: string; +} + +import { onIdentify } from "./Identify"; +import { onSelectProtocol } from "./SelectProtocol"; +import { onHeartbeat } from "./Heartbeat"; +import { onSpeaking } from "./Speaking"; +import { onResume } from "./Resume"; +import { onConnect } from "./Connect"; + +import { onVersion } from "./Version"; + +export type OPCodeHandler = (this: WebSocket, data: Payload) => any; + +export default { + [VoiceOPCodes.IDENTIFY]: onIdentify, //op 0 + [VoiceOPCodes.SELECT_PROTOCOL]: onSelectProtocol, //op 1 + //op 2 voice_ready + [VoiceOPCodes.HEARTBEAT]: onHeartbeat, //op 3 + //op 4 session_description + [VoiceOPCodes.SPEAKING]: onSpeaking, //op 5 + //op 6 heartbeat_ack + [VoiceOPCodes.RESUME]: onResume, //op 7 + //op 8 hello + //op 9 resumed + //op 10? + //op 11? + [VoiceOPCodes.CLIENT_CONNECT]: onConnect, //op 12 + //op 13? + //op 15? + //op 16? empty data on client send but server sends {"voice":"0.8.24+bugfix.voice.streams.opt.branch-ffcefaff7","rtc_worker":"0.3.14-crypto-collision-copy"} + [VoiceOPCodes.VERSION]: onVersion, +}; \ No newline at end of file diff --git a/webrtc/src/start.ts b/webrtc/src/start.ts index 68867a2c..98f06ad5 100644 --- a/webrtc/src/start.ts +++ b/webrtc/src/start.ts @@ -1,3 +1,10 @@ +//testing +process.env.DATABASE = "../bundle/database.db"; + +import { config } from "dotenv"; +config(); + import { Server } from "./Server"; const server = new Server(); +server.listen(); \ No newline at end of file diff --git a/webrtc/src/util/Heartbeat.ts b/webrtc/src/util/Heartbeat.ts new file mode 100644 index 00000000..8c5e3a7a --- /dev/null +++ b/webrtc/src/util/Heartbeat.ts @@ -0,0 +1,23 @@ +import { WebSocket, CLOSECODES } from "@fosscord/gateway"; +import { VoiceOPCodes } from "@fosscord/util"; + +export async function setHeartbeat(socket: WebSocket, nonce?: Number) { + if (socket.heartbeatTimeout) clearTimeout(socket.heartbeatTimeout); + + socket.heartbeatTimeout = setTimeout(() => { + return socket.close(CLOSECODES.Session_timed_out); + }, 1000 * 45); + + if (!nonce) { + socket.send(JSON.stringify({ + op: VoiceOPCodes.HELLO, + d: { + v: 5, + heartbeat_interval: 13750, + } + })); + } + else { + socket.send(JSON.stringify({ op: VoiceOPCodes.HEARTBEAT_ACK, d: nonce })); + } +} \ No newline at end of file diff --git a/webrtc/src/util/index.ts b/webrtc/src/util/index.ts new file mode 100644 index 00000000..e8557452 --- /dev/null +++ b/webrtc/src/util/index.ts @@ -0,0 +1 @@ +export * from "./Heartbeat" \ No newline at end of file diff --git a/webrtc/tsconfig.json b/webrtc/tsconfig.json index 77353db0..fb93b0bd 100644 --- a/webrtc/tsconfig.json +++ b/webrtc/tsconfig.json @@ -1,5 +1,8 @@ { "include": ["src/**/*.ts"], + "ts-node": { + "require": ["tsconfig-paths/register"], + }, "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ @@ -18,7 +21,7 @@ "sourceMap": true /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./dist/" /* Redirect output structure to the directory. */, - "rootDir": "./src/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + "rootDir": "../" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ @@ -62,11 +65,19 @@ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ + + "baseUrl": "../", + "paths": { + "@fosscord/api": ["api/src/index"], + "@fosscord/gateway": ["gateway/src/index"], + "@fosscord/cdn": ["cdn/src/index"], + "@fosscord/util": ["util/src/index"] + }, } }