diff --git a/package-lock.json b/package-lock.json index 8ffdc66d..bf6d8cc1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,12 @@ "fosscord-server-util": "github:fosscord/fosscord-server-util", "jsonwebtoken": "^8.5.1", "lambert-db": "^1.1.8", - "lambert-server": "^1.1.3", + "lambert-server": "^1.1.6", "missing-native-js-functions": "^1.2.3", "node-fetch": "^2.6.1", "uuid": "^8.3.2", - "ws": "^7.4.2" + "ws": "^7.4.2", + "zlib-sync": "^0.1.7" }, "devDependencies": { "@types/jsonwebtoken": "^8.5.0", @@ -549,7 +550,7 @@ }, "node_modules/fosscord-server-util": { "version": "1.0.0", - "resolved": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#64ab9e049ad4d3b68c0b9a9ce2e08363e77d3c84", + "resolved": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#a888147dfc44710b0e7c70d04b7a841a181fb71d", "license": "ISC", "dependencies": { "jsonwebtoken": "^8.5.1", @@ -782,9 +783,9 @@ } }, "node_modules/lambert-server": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.1.3.tgz", - "integrity": "sha512-eBgxIY5l6MvmyH+P+VXkf3DIImOERv6I/o/awsz1fSW5ngZtGuJCaFC342APhcpJmg528H8fZhSB2riM6z8fig==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.1.6.tgz", + "integrity": "sha512-dNfSmzgue+BwFRbCGvSxMhBIXGspOcFt2r1fAf3tdPDHvgZY2nuDBcEAYFNC2Yy3yXbxiFdVykFWQvxDRrUxtA==", "dependencies": { "body-parser": "^1.19.0", "express": "^4.17.1", @@ -1791,6 +1792,15 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "node_modules/zlib-sync": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/zlib-sync/-/zlib-sync-0.1.7.tgz", + "integrity": "sha512-UmciU6ZrIwtwPC8noMzq+kGMdiWwNRZ3wC0SbED4Ew5Ikqx14MqDPRs/Pbk+3rZPh5SzsOgUBs1WRE0iieddpg==", + "hasInstallScript": true, + "dependencies": { + "nan": "^2.14.0" + } } }, "dependencies": { @@ -2216,7 +2226,7 @@ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, "fosscord-server-util": { - "version": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#64ab9e049ad4d3b68c0b9a9ce2e08363e77d3c84", + "version": "git+ssh://git@github.com/fosscord/fosscord-server-util.git#a888147dfc44710b0e7c70d04b7a841a181fb71d", "from": "fosscord-server-util@github:fosscord/fosscord-server-util", "requires": { "jsonwebtoken": "^8.5.1", @@ -2395,9 +2405,9 @@ } }, "lambert-server": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.1.3.tgz", - "integrity": "sha512-eBgxIY5l6MvmyH+P+VXkf3DIImOERv6I/o/awsz1fSW5ngZtGuJCaFC342APhcpJmg528H8fZhSB2riM6z8fig==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lambert-server/-/lambert-server-1.1.6.tgz", + "integrity": "sha512-dNfSmzgue+BwFRbCGvSxMhBIXGspOcFt2r1fAf3tdPDHvgZY2nuDBcEAYFNC2Yy3yXbxiFdVykFWQvxDRrUxtA==", "requires": { "body-parser": "^1.19.0", "express": "^4.17.1", @@ -3136,6 +3146,14 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "zlib-sync": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/zlib-sync/-/zlib-sync-0.1.7.tgz", + "integrity": "sha512-UmciU6ZrIwtwPC8noMzq+kGMdiWwNRZ3wC0SbED4Ew5Ikqx14MqDPRs/Pbk+3rZPh5SzsOgUBs1WRE0iieddpg==", + "requires": { + "nan": "^2.14.0" + } } } } diff --git a/package.json b/package.json index 3f4823c4..cf1923cc 100644 --- a/package.json +++ b/package.json @@ -17,11 +17,12 @@ "fosscord-server-util": "github:fosscord/fosscord-server-util", "jsonwebtoken": "^8.5.1", "lambert-db": "^1.1.8", - "lambert-server": "^1.1.3", + "lambert-server": "^1.1.6", "missing-native-js-functions": "^1.2.3", "node-fetch": "^2.6.1", "uuid": "^8.3.2", - "ws": "^7.4.2" + "ws": "^7.4.2", + "zlib-sync": "^0.1.7" }, "devDependencies": { "@types/jsonwebtoken": "^8.5.0", diff --git a/src/Server.ts b/src/Server.ts index df5ec905..ea1e39b0 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -9,7 +9,15 @@ import Config from "./util/Config"; export class Server { public ws: WebSocketServer; constructor() { - this.ws = new WebSocketServer({ port: 8080, maxPayload: 4096 }); + this.ws = new WebSocketServer({ + port: 8080, + maxPayload: 4096, + // perMessageDeflate: { + // zlibDeflateOptions: { + // chunkSize: 65536, + // }, + // }, + }); this.ws.on("connection", Connection); } diff --git a/src/listener/listener.ts b/src/listener/listener.ts index b6ae0ac6..5f3ee93b 100644 --- a/src/listener/listener.ts +++ b/src/listener/listener.ts @@ -1,14 +1,15 @@ import { db, Event, MongooseCache, UserModel, getPermission, Permissions } from "fosscord-server-util"; +import { OPCODES } from "../util/Constants"; +import { Send } from "../util/Send"; import WebSocket from "../util/WebSocket"; -// TODO: bot sharding // TODO: close connection on Invalidated Token // TODO: check intent // TODO: Guild Member Update is sent for current-user updates regardless of whether the GUILD_MEMBERS intent is set. // ? How to resubscribe MongooseCache for new dm channel events? Maybe directly send them to the user_id regardless of the channel_id? -> max overhead of creating 10 events in database for dm user group. Or a new field in event -> recipient_ids? export async function setupListener(this: WebSocket) { - const user = await UserModel.findOne({ id: this.user_id }).exec(); + const user = await UserModel.findOne({ id: this.user_id }).lean().exec(); const eventStream = new MongooseCache( db.collection("events"), @@ -23,6 +24,7 @@ export async function setupListener(this: WebSocket) { export async function dispatch(this: WebSocket, document: Event) { var permission = new Permissions("ADMINISTRATOR"); // default permission for dms + console.log("event", document); if (document.guild_id) { if (!this.intents.has("GUILDS")) return; @@ -30,8 +32,6 @@ export async function dispatch(this: WebSocket, document: Event) { permission = await getPermission(this.user_id, document.guild_id, channel_id); } - console.log("event", document); - // check intents: https://discord.com/developers/docs/topics/gateway#gateway-intents switch (document.event) { case "GUILD_CREATE": @@ -93,7 +93,7 @@ export async function dispatch(this: WebSocket, document: Event) { if (!this.intents.has("GUILD_MESSAGE_TYPING") && document.guild_id) return; if (!this.intents.has("DIRECT_MESSAGE_TYPING") && !document.guild_id) return; break; - case "READY": + case "READY": // will be sent by the gateway case "USER_UPDATE": case "APPLICATION_COMMAND_CREATE": case "APPLICATION_COMMAND_DELETE": @@ -149,7 +149,7 @@ export async function dispatch(this: WebSocket, document: Event) { case "CHANNEL_DELETE": case "CHANNEL_UPDATE": case "GUILD_EMOJI_UPDATE": - case "READY": + case "READY": // will be sent by the gateway case "USER_UPDATE": case "APPLICATION_COMMAND_CREATE": case "APPLICATION_COMMAND_DELETE": @@ -160,5 +160,10 @@ export async function dispatch(this: WebSocket, document: Event) { break; } - return this.emit(document.event, document.data); + return Send(this, { + op: OPCODES.Dispatch, + t: document.event, + d: document.data, + s: this.sequence++, + }); } diff --git a/src/opcodes/Identify.ts b/src/opcodes/Identify.ts index fdde79fc..61238e62 100644 --- a/src/opcodes/Identify.ts +++ b/src/opcodes/Identify.ts @@ -48,6 +48,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { const channels = await ChannelModel.find({ recipients: this.user_id }).lean().exec(); const user = await UserModel.findOne({ id: this.user_id }).lean().exec(); + if (!user) return this.close(CLOSECODES.Authentication_failed); + const public_user = { username: user.username, discriminator: user.discriminator, diff --git a/src/schema/Activity.ts b/src/schema/Activity.ts index 00772743..45bd6fef 100644 --- a/src/schema/Activity.ts +++ b/src/schema/Activity.ts @@ -1,42 +1,10 @@ +import { Activity } from "fosscord-server-util"; import { EmojiSchema } from "./Emoji"; export const ActivitySchema = { afk: Boolean, status: String, - $activities: [ - { - name: String, // the activity's name - type: Number, // activity type // TODO: check if its between range 0-5 - $url: String, // stream url, is validated when type is 1 - $created_at: Number, // unix timestamp of when the activity was added to the user's session - $timestamps: { - // unix timestamps for start and/or end of the game - start: Number, - end: Number, - }, - $application_id: BigInt, // application id for the game - $details: String, - $State: String, - $emoji: EmojiSchema, - $party: { - $id: String, - $size: [Number], // used to show the party's current and maximum size // TODO: array length 2 - }, - $assets: { - $large_image: String, // the id for a large asset of the activity, usually a snowflake - $large_text: String, // text displayed when hovering over the large image of the activity - $small_image: String, // the id for a small asset of the activity, usually a snowflake - $small_text: String, // text displayed when hovering over the small image of the activity - }, - $secrets: { - $join: String, // the secret for joining a party - $spectate: String, // the secret for spectating a game - $match: String, // the secret for a specific instanced match - }, - $instance: Boolean, - flags: BigInt, // activity flags OR d together, describes what the payload includes - }, - ], + $activities: [Activity], $since: Number, // unix time (in milliseconds) of when the client went idle, or null if the client is not idle }; @@ -56,7 +24,7 @@ export interface ActivitySchema { }; application_id?: bigint; // application id for the game details?: string; - State?: string; + state?: string; emoji?: EmojiSchema; party?: { id?: string; diff --git a/src/schema/Identify.ts b/src/schema/Identify.ts index cfc3702a..0c458f14 100644 --- a/src/schema/Identify.ts +++ b/src/schema/Identify.ts @@ -2,32 +2,72 @@ import { ActivitySchema } from "./Activity"; export const IdentifySchema = { token: String, - intents: BigInt, // discord uses a Integer for bitfields we use bigints tho. | instanceOf will automatically convert the Number to a BigInt + $intents: BigInt, // discord uses a Integer for bitfields we use bigints tho. | instanceOf will automatically convert the Number to a BigInt $properties: { - // bruh discord really uses $ in the property key, so we need to double prefix it, because instanceOf treats $ (prefix) as a optional key + // bruh discord really uses $ in the property key for bots, so we need to double prefix it, because instanceOf treats $ (prefix) as a optional key + $os: String, + $browser: String, + $device: String, $$os: String, $$browser: String, $$device: String, + $browser_user_agent: String, + $browser_version: String, + $os_version: String, + $referrer: String, + $referring_domain: String, + $referrer_current: String, + $referring_domain_current: String, + $release_channel: String, + $client_build_number: Number, + $client_event_source: String, }, $presence: ActivitySchema, $compress: Boolean, $large_threshold: Number, $shard: [Number], $guild_subscriptions: Boolean, + $capabilities: Number, + $client_state: { + $guild_hashes: Object, + $highest_last_message_id: BigInt, + $read_state_version: Number, + $user_guild_settings_version: Number, + }, }; export interface IdentifySchema { token: string; properties: { // bruh discord really uses $ in the property key, so we need to double prefix it, because instanceOf treats $ (prefix) as a optional key - $$os: string; - $$browser: string; - $$device: string; + os?: string; + browser?: string; + device?: string; + $os?: string; + $browser?: string; + $device?: string; + browser_user_agent?: string; + browser_version?: string; + os_version?: string; + referrer?: string; + referring_domain?: string; + referrer_current?: string; + referring_domain_current?: string; + release_channel?: "stable" | "dev" | "ptb" | "canary"; + client_build_number?: number; + client_event_source?: any; }; - intents: bigint; // discord uses a Integer for bitfields we use bigints tho. | instanceOf will automatically convert the Number to a BigInt + intents?: bigint; // discord uses a Integer for bitfields we use bigints tho. | instanceOf will automatically convert the Number to a BigInt presence?: ActivitySchema; compress?: boolean; large_threshold?: number; shard?: [number]; guild_subscriptions?: boolean; + capabilities?: number; + client_state?: { + guild_hashes?: any; + highest_last_message_id?: bigint; + read_state_version?: number; + user_guild_settings_version?: number; + }; } diff --git a/src/util/setHeartbeat.ts b/src/util/setHeartbeat.ts index 1fe657a8..a59dd251 100644 --- a/src/util/setHeartbeat.ts +++ b/src/util/setHeartbeat.ts @@ -6,5 +6,5 @@ export function setHeartbeat(socket: WebSocket) { socket.heartbeatTimeout = setTimeout(() => { return socket.close(4009); - }, 1000 * 30); + }, 1000 * 45); } diff --git a/tsconfig.json b/tsconfig.json index f02e8ec0..1d9c6abf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -66,6 +66,7 @@ /* 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. */, + "resolveJsonModule": true } }