From e434334a21247ac29809ef5c47f4c889e28d0c20 Mon Sep 17 00:00:00 2001 From: Flam3rboy <34555296+Flam3rboy@users.noreply.github.com> Date: Fri, 21 May 2021 22:16:48 +0200 Subject: [PATCH] :sparkles: Member List --- package-lock.json | 14 ++-- package.json | 5 +- src/Server.ts | 4 ++ src/listener/listener.ts | 18 +++--- src/opcodes/LazyRequest.ts | 103 ++++++++++++++++++++---------- src/opcodes/Resume.ts | 11 +++- src/schema/LazyRequest.ts | 18 +++--- src/schema/VoiceStateUpdate.ts.ts | 2 + 8 files changed, 112 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index d72c6705..7a294f73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@fosscord/server-util": "^1.0.7", + "@fosscord/server-util": "^1.2.1", "dotenv": "^8.2.0", "jsonwebtoken": "^8.5.1", "lambert-server": "^1.1.7", @@ -30,9 +30,9 @@ } }, "node_modules/@fosscord/server-util": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.0.7.tgz", - "integrity": "sha512-3vBPCt+lwMS7wk+iRvv+V8qBSnEdNifpPxX97Lfjje/TSWI17Kg29y3BmcGJRC5TwIHTLFtgpNLmZmruhv7ziQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.2.1.tgz", + "integrity": "sha512-NAAmwDizkjR52O6ZUds+XOH8ydjo///T2EH0scuFF3HtAMjD8Yphgo7+GIKrNt5FfkTR3ma0ycrZLZe0TBE+1A==", "dependencies": { "@types/jsonwebtoken": "^8.5.0", "@types/mongoose-autopopulate": "^0.10.1", @@ -2134,9 +2134,9 @@ }, "dependencies": { "@fosscord/server-util": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.0.7.tgz", - "integrity": "sha512-3vBPCt+lwMS7wk+iRvv+V8qBSnEdNifpPxX97Lfjje/TSWI17Kg29y3BmcGJRC5TwIHTLFtgpNLmZmruhv7ziQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@fosscord/server-util/-/server-util-1.2.1.tgz", + "integrity": "sha512-NAAmwDizkjR52O6ZUds+XOH8ydjo///T2EH0scuFF3HtAMjD8Yphgo7+GIKrNt5FfkTR3ma0ycrZLZe0TBE+1A==", "requires": { "@types/jsonwebtoken": "^8.5.0", "@types/mongoose-autopopulate": "^0.10.1", diff --git a/package.json b/package.json index 74087264..32c9fb85 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,15 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "npm run build:util && npm run build && node dist/", + "start": "npm run build && node dist/", "build": "npx tsc -b .", - "build:util": "npx tsc -b ./node_modules/@fosscord/server-util/", "dev": "tsnd --respawn src/index.ts" }, "keywords": [], "author": "Fosscord", "license": "ISC", "dependencies": { - "@fosscord/server-util": "^1.0.7", + "@fosscord/server-util": "^1.2.1", "dotenv": "^8.2.0", "jsonwebtoken": "^8.5.1", "lambert-server": "^1.1.7", diff --git a/src/Server.ts b/src/Server.ts index 149883c2..193bdad1 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -6,6 +6,9 @@ import { Server as WebSocketServer } from "ws"; import { Connection } from "./events/Connection"; import Config from "./util/Config"; +// TODO: only listen/start the server if everything got initalized +// https://www.npmjs.com/package/ws use "External HTTP/S server" and listen manually at the end of listen() + var port = Number(process.env.PORT); if (isNaN(port)) port = 3002; @@ -14,6 +17,7 @@ export class Server { constructor() { this.ws = new WebSocketServer({ port, + maxPayload: 4096, // perMessageDeflate: { // zlibDeflateOptions: { diff --git a/src/listener/listener.ts b/src/listener/listener.ts index 282ebfd5..8183f70c 100644 --- a/src/listener/listener.ts +++ b/src/listener/listener.ts @@ -17,7 +17,7 @@ export interface DispatchOpts { guilds: Array; } -function getPipeline(this: WebSocket, guilds: string[], channels: string[]) { +function getPipeline(this: WebSocket, guilds: string[], channels: string[] = []) { if (this.shard_count) { guilds = guilds.filter((x) => (BigInt(x) >> 22n) % this.shard_count === this.shard_id); } @@ -54,12 +54,7 @@ export async function setupListener(this: WebSocket) { export async function dispatch(this: WebSocket, document: Event, { eventStream, guilds }: DispatchOpts) { var permission = new Permissions("ADMINISTRATOR"); // default permission for dms console.log("event", document); - - if (document.guild_id) { - if (!this.intents.has("GUILDS")) return; - const channel_id = document.channel_id || document.data?.channel_id; - permission = await getPermission(this.user_id, document.guild_id, channel_id); - } + var channel_id = document.channel_id || document.data?.channel_id; if (document.event === "GUILD_CREATE") { guilds.push(document.data.id); @@ -67,12 +62,19 @@ export async function dispatch(this: WebSocket, document: Event, { eventStream, } else if (document.event === "GUILD_DELETE") { guilds.remove(document.guild_id); eventStream.changeStream(getPipeline.call(this, guilds)); + } else if (document.event === "CHANNEL_DELETE") channel_id = null; + if (document.guild_id && !this.intents.has("GUILDS")) return; + + try { + permission = await getPermission(this.user_id, document.guild_id, channel_id); + } catch (e) { + permission = new Permissions(); } // check intents: https://discord.com/developers/docs/topics/gateway#gateway-intents switch (document.event) { - case "GUILD_CREATE": case "GUILD_DELETE": + case "GUILD_CREATE": case "GUILD_UPDATE": case "GUILD_ROLE_CREATE": case "GUILD_ROLE_UPDATE": diff --git a/src/opcodes/LazyRequest.ts b/src/opcodes/LazyRequest.ts index ba7278f0..1d52ff3c 100644 --- a/src/opcodes/LazyRequest.ts +++ b/src/opcodes/LazyRequest.ts @@ -1,5 +1,13 @@ // @ts-nocheck WIP -import { db, getPermission, MemberModel, MongooseCache, PublicUserProjection, RoleModel } from "@fosscord/server-util"; +import { + db, + getPermission, + MemberModel, + MongooseCache, + PublicUserProjection, + RoleModel, + toObject, +} from "@fosscord/server-util"; import { LazyRequest } from "../schema/LazyRequest"; import { OPCODES, Payload } from "../util/Constants"; import { Send } from "../util/Send"; @@ -9,7 +17,6 @@ import { check } from "./instanceOf"; // TODO: config: if want to list all members (even those who are offline) sorted by role, or just those who are online export async function onLazyRequest(this: WebSocket, { d }: Payload) { - return; // WIP // TODO: check data check.call(this, LazyRequest, d); const { guild_id, typing, channels, activities } = d as LazyRequest; @@ -17,37 +24,63 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { const permissions = await getPermission(this.user_id, guild_id); // MongoDB query to retrieve all hoisted roles and join them with the members and users collection - const roles = await db - .collection("roles") - .aggregate([ - { $match: { guild_id, hoist: true } }, - { $sort: { position: 1 } }, - { - $lookup: { - from: "members", - let: { id: "$id" }, - pipeline: [ - { $match: { $expr: { $in: ["$$id", "$roles"] } } }, - { $limit: 1 }, - { - $lookup: { - from: "users", - let: { user_id: "$id" }, - pipeline: [ - { $match: { $expr: { $eq: ["$id", "$$user_id"] } } }, - { $project: PublicUserProjection }, - ], - as: "user", - }, - }, - ], - as: "members", + const roles = toObject( + await db + .collection("roles") + .aggregate([ + { + $match: { + guild_id, + // hoist: true // TODO: also match @everyone role + }, }, - }, - ]) - .toArray(); + { $sort: { position: 1 } }, + { + $lookup: { + from: "members", + let: { id: "$id" }, + pipeline: [ + { $match: { $expr: { $in: ["$$id", "$roles"] } } }, + { $limit: 1 }, + { + $lookup: { + from: "users", + let: { user_id: "$id" }, + pipeline: [ + { $match: { $expr: { $eq: ["$id", "$$user_id"] } } }, + { $project: PublicUserProjection }, + ], + as: "user", + }, + }, + { + $unwind: "$user", + }, + ], + as: "members", + }, + }, + ]) + .toArray() + ); - Send(this, { + const groups = roles.map((x) => ({ id: x.id === guild_id ? "online" : x.id, count: x.members.length })); + const member_count = roles.reduce((a, b) => b.members.length + a, 0); + const items = []; + + for (const role of roles) { + items.push({ + group: { + count: role.members.length, + id: role.id, + }, + }); + for (const member of role.members) { + items.push({ member }); + } + } + + return Send(this, { op: OPCODES.Dispatch, s: this.sequence++, t: "GUILD_MEMBER_LIST_UPDATE", @@ -56,14 +89,14 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { { range: [0, 99], op: "SYNC", - items: [{ group: { id: "online", count: 0 } }], + items: items, }, ], - online_count: 1, - member_count: 1, + online_count: member_count, // TODO count online count + member_count, id: "everyone", guild_id, - groups: [{ id: "online", count: 1 }], + groups, }, }); } diff --git a/src/opcodes/Resume.ts b/src/opcodes/Resume.ts index 3c54b5c7..4efde9b0 100644 --- a/src/opcodes/Resume.ts +++ b/src/opcodes/Resume.ts @@ -1,7 +1,14 @@ import { CLOSECODES, Payload } from "../util/Constants"; +import { Send } from "../util/Send"; import WebSocket from "../util/WebSocket"; -export function onResume(this: WebSocket, data: Payload) { - return this.close(CLOSECODES.Invalid_session); +export async function onResume(this: WebSocket, data: Payload) { + console.log("Got Resume -> cancel not implemented"); + await Send(this, { + op: 9, + d: false, + }); + + // return this.close(CLOSECODES.Invalid_session); } diff --git a/src/schema/LazyRequest.ts b/src/schema/LazyRequest.ts index b8c51431..6a2ecb67 100644 --- a/src/schema/LazyRequest.ts +++ b/src/schema/LazyRequest.ts @@ -1,15 +1,17 @@ export interface LazyRequest { - activities: boolean; - channels: Record; guild_id: string; - threads: boolean; - typing: true; + channels?: Record; + activities?: boolean; + threads?: boolean; + typing?: true; + members?: any[]; } export const LazyRequest = { - activities: Boolean, - channels: Object, guild_id: String, - threads: Boolean, - typing: Boolean, + $activities: Boolean, + $channels: Object, + $typing: Boolean, + $threads: Boolean, + $members: [] as any[], }; diff --git a/src/schema/VoiceStateUpdate.ts.ts b/src/schema/VoiceStateUpdate.ts.ts index 5e3e35e0..ccadda60 100644 --- a/src/schema/VoiceStateUpdate.ts.ts +++ b/src/schema/VoiceStateUpdate.ts.ts @@ -3,6 +3,7 @@ export const VoiceStateUpdateSchema = { channel_id: String, self_mute: Boolean, self_deaf: Boolean, + self_video: Boolean, }; export interface VoiceStateUpdateSchema { @@ -10,4 +11,5 @@ export interface VoiceStateUpdateSchema { channel_id: string; self_mute: boolean; self_deaf: boolean; + self_video: boolean; }