diff options
Diffstat (limited to 'src/gateway')
-rw-r--r-- | src/gateway/Server.ts | 18 | ||||
-rw-r--r-- | src/gateway/events/Close.ts | 19 | ||||
-rw-r--r-- | src/gateway/events/Connection.ts | 58 | ||||
-rw-r--r-- | src/gateway/events/Message.ts | 30 | ||||
-rw-r--r-- | src/gateway/index.ts | 4 | ||||
-rw-r--r-- | src/gateway/listener/listener.ts | 83 | ||||
-rw-r--r-- | src/gateway/opcodes/Identify.ts | 155 | ||||
-rw-r--r-- | src/gateway/opcodes/LazyRequest.ts | 55 | ||||
-rw-r--r-- | src/gateway/opcodes/PresenceUpdate.ts | 11 | ||||
-rw-r--r-- | src/gateway/opcodes/Resume.ts | 4 | ||||
-rw-r--r-- | src/gateway/opcodes/VoiceStateUpdate.ts | 49 | ||||
-rw-r--r-- | src/gateway/opcodes/index.ts | 4 | ||||
-rw-r--r-- | src/gateway/opcodes/instanceOf.ts | 2 | ||||
-rw-r--r-- | src/gateway/start.ts | 4 | ||||
-rw-r--r-- | src/gateway/util/Constants.ts | 4 | ||||
-rw-r--r-- | src/gateway/util/Send.ts | 3 | ||||
-rw-r--r-- | src/gateway/util/SessionUtils.ts | 4 | ||||
-rw-r--r-- | src/gateway/util/index.ts | 2 |
18 files changed, 205 insertions, 304 deletions
diff --git a/src/gateway/Server.ts b/src/gateway/Server.ts index 82fbeba2..97da3fa0 100644 --- a/src/gateway/Server.ts +++ b/src/gateway/Server.ts @@ -1,9 +1,9 @@ -import dotenv from "dotenv"; -dotenv.config(); import { closeDatabase, Config, getOrInitialiseDatabase, initEvent } from "@fosscord/util"; +import dotenv from "dotenv"; +import http from "http"; import ws from "ws"; import { Connection } from "./events/Connection"; -import http from "http"; +dotenv.config(); export class Server { public ws: ws.Server; @@ -11,15 +11,7 @@ export class Server { public server: http.Server; public production: boolean; - constructor({ - port, - server, - production, - }: { - port: number; - server?: http.Server; - production?: boolean; - }) { + constructor({ port, server, production }: { port: number; server?: http.Server; production?: boolean }) { this.port = port; this.production = production || false; @@ -39,7 +31,7 @@ export class Server { this.ws = new ws.Server({ maxPayload: 4096, - noServer: true, + noServer: true }); this.ws.on("connection", Connection); this.ws.on("error", console.error); diff --git a/src/gateway/events/Close.ts b/src/gateway/events/Close.ts index 5b7c512c..34831eab 100644 --- a/src/gateway/events/Close.ts +++ b/src/gateway/events/Close.ts @@ -1,12 +1,5 @@ import { WebSocket } from "@fosscord/gateway"; -import { - emitEvent, - PresenceUpdateEvent, - PrivateSessionProjection, - Session, - SessionsReplace, - User, -} from "@fosscord/util"; +import { emitEvent, PresenceUpdateEvent, PrivateSessionProjection, Session, SessionsReplace, User } from "@fosscord/util"; export async function Close(this: WebSocket, code: number, reason: string) { console.log("[WebSocket] closed", code, reason); @@ -19,17 +12,17 @@ export async function Close(this: WebSocket, code: number, reason: string) { await Session.delete({ session_id: this.session_id }); const sessions = await Session.find({ where: { user_id: this.user_id }, - select: PrivateSessionProjection, + select: PrivateSessionProjection }); await emitEvent({ event: "SESSIONS_REPLACE", user_id: this.user_id, - data: sessions, + data: sessions } as SessionsReplace); const session = sessions.first() || { activities: [], client_info: {}, - status: "offline", + status: "offline" }; await emitEvent({ @@ -39,8 +32,8 @@ export async function Close(this: WebSocket, code: number, reason: string) { user: await User.getPublicUser(this.user_id), activities: session.activities, client_status: session?.client_info, - status: session.status, - }, + status: session.status + } } as PresenceUpdateEvent); } } diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts index 508b4741..5a5ce48f 100644 --- a/src/gateway/events/Connection.ts +++ b/src/gateway/events/Connection.ts @@ -1,13 +1,13 @@ -import WS from "ws"; import { WebSocket } from "@fosscord/gateway"; -import { Send } from "../util/Send"; +import { IncomingMessage } from "http"; +import { URL } from "url"; +import WS from "ws"; +import { createDeflate } from "zlib"; import { CLOSECODES, OPCODES } from "../util/Constants"; import { setHeartbeat } from "../util/Heartbeat"; -import { IncomingMessage } from "http"; +import { Send } from "../util/Send"; import { Close } from "./Close"; import { Message } from "./Message"; -import { createDeflate } from "zlib"; -import { URL } from "url"; let erlpack: any; try { erlpack = require("@yukikaze-bot/erlpack"); @@ -17,30 +17,26 @@ try { // TODO: specify rate limit in config // TODO: check msg max size -export async function Connection( - this: WS.Server, - socket: WebSocket, - request: IncomingMessage -) { +export async function Connection(this: WS.Server, socket: WebSocket, request: IncomingMessage) { try { // @ts-ignore socket.on("close", Close); // @ts-ignore socket.on("message", Message); - - if(process.env.WS_LOGEVENTS) - [ - "close", - "error", - "upgrade", - //"message", - "open", - "ping", - "pong", - "unexpected-response" - ].forEach(x=>{ - socket.on(x, y => console.log(x, y)); - }); + + if (process.env.WS_LOGEVENTS) + [ + "close", + "error", + "upgrade", + //"message", + "open", + "ping", + "pong", + "unexpected-response" + ].forEach((x) => { + socket.on(x, (y) => console.log(x, y)); + }); console.log(`[Gateway] Connections: ${this.clients.size}`); @@ -49,23 +45,19 @@ export async function Connection( socket.encoding = searchParams.get("encoding") || "json"; if (!["json", "etf"].includes(socket.encoding)) { if (socket.encoding === "etf" && erlpack) { - throw new Error( - "Erlpack is not installed: 'npm i @yukikaze-bot/erlpack'" - ); + throw new Error("Erlpack is not installed: 'npm i @yukikaze-bot/erlpack'"); } return socket.close(CLOSECODES.Decode_error); } // @ts-ignore socket.version = Number(searchParams.get("version")) || 8; - if (socket.version != 8) - return socket.close(CLOSECODES.Invalid_API_version); + if (socket.version != 8) return socket.close(CLOSECODES.Invalid_API_version); // @ts-ignore socket.compress = searchParams.get("compress") || ""; if (socket.compress) { - if (socket.compress !== "zlib-stream") - return socket.close(CLOSECODES.Decode_error); + if (socket.compress !== "zlib-stream") return socket.close(CLOSECODES.Decode_error); socket.deflate = createDeflate({ chunkSize: 65535 }); socket.deflate.on("data", (chunk) => socket.send(chunk)); } @@ -80,8 +72,8 @@ export async function Connection( await Send(socket, { op: OPCODES.Hello, d: { - heartbeat_interval: 1000 * 30, - }, + heartbeat_interval: 1000 * 30 + } }); socket.readyTimeout = setTimeout(() => { diff --git a/src/gateway/events/Message.ts b/src/gateway/events/Message.ts index 569f5fc7..96950a42 100644 --- a/src/gateway/events/Message.ts +++ b/src/gateway/events/Message.ts @@ -1,42 +1,39 @@ +import { Payload, WebSocket } from "@fosscord/gateway"; +import OPCodeHandlers from "../opcodes"; +import { check } from "../opcodes/instanceOf"; import { CLOSECODES } from "../util/Constants"; -import { WebSocket, Payload } from "@fosscord/gateway"; let erlpack: any; try { erlpack = require("@yukikaze-bot/erlpack"); } catch (error) {} -import OPCodeHandlers from "../opcodes"; -import { check } from "../opcodes/instanceOf"; const PayloadSchema = { op: Number, $d: Object || Number, // or number for heartbeat sequence $s: Number, - $t: String, + $t: String }; export async function Message(this: WebSocket, buffer: Buffer) { // TODO: compression let data: Payload; - if (this.encoding === "etf" && buffer instanceof Buffer) - data = erlpack.unpack(buffer); + if (this.encoding === "etf" && buffer instanceof Buffer) data = erlpack.unpack(buffer); else if (this.encoding === "json") data = JSON.parse(buffer as unknown as string); //TODO: is this even correct?? seems to work for web clients... - else if(/--debug|--inspect/.test(process.execArgv.join(' '))) { + else if (/--debug|--inspect/.test(process.execArgv.join(" "))) { debugger; return; - } - else { + } else { console.log("Invalid gateway connection! Use a debugger to inspect!"); return; } - if(process.env.WS_VERBOSE) - console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`); - if(data.op !== 1) - check.call(this, PayloadSchema, data); - else { //custom validation for numbers, because heartbeat - if(data.s || data.t || (typeof data.d !== "number" && data.d)) { + if (process.env.WS_VERBOSE) console.log(`[Websocket] Incomming message: ${JSON.stringify(data)}`); + if (data.op !== 1) check.call(this, PayloadSchema, data); + else { + //custom validation for numbers, because heartbeat + if (data.s || data.t || (typeof data.d !== "number" && data.d)) { console.log("Invalid heartbeat..."); this.close(CLOSECODES.Decode_error); } @@ -55,7 +52,6 @@ export async function Message(this: WebSocket, buffer: Buffer) { return await OPCodeHandler.call(this, data); } catch (error) { console.error(error); - if (!this.CLOSED && this.CLOSING) - return this.close(CLOSECODES.Unknown_error); + if (!this.CLOSED && this.CLOSING) return this.close(CLOSECODES.Unknown_error); } } diff --git a/src/gateway/index.ts b/src/gateway/index.ts index d77ce931..730347f9 100644 --- a/src/gateway/index.ts +++ b/src/gateway/index.ts @@ -1,4 +1,4 @@ +export * from "./listener/listener"; +export * from "./opcodes/"; export * from "./Server"; export * from "./util/"; -export * from "./opcodes/"; -export * from "./listener/listener"; diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts index 8c69e193..811318af 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts @@ -1,20 +1,20 @@ +import { WebSocket } from "@fosscord/gateway"; import { + EVENTEnum, + EventOpts, getPermission, - Permissions, - RabbitMQ, listenEvent, - EventOpts, ListenEventOpts, Member, - EVENTEnum, + Permissions, + RabbitMQ, + Recipient, Relationship, - RelationshipType, + RelationshipType } from "@fosscord/util"; +import { Channel as AMQChannel } from "amqplib"; import { OPCODES } from "../util/Constants"; import { Send } from "../util/Send"; -import { WebSocket } from "@fosscord/gateway"; -import { Channel as AMQChannel } from "amqplib"; -import { Recipient } from "@fosscord/util"; // TODO: close connection on Invalidated Token // TODO: check intent @@ -23,17 +23,14 @@ import { Recipient } from "@fosscord/util"; // Sharding: calculate if the current shard id matches the formula: shard_id = (guild_id >> 22) % num_shards // https://discord.com/developers/docs/topics/gateway#sharding -export function handlePresenceUpdate( - this: WebSocket, - { event, acknowledge, data }: EventOpts -) { +export function handlePresenceUpdate(this: WebSocket, { event, acknowledge, data }: EventOpts) { acknowledge?.(); if (event === EVENTEnum.PresenceUpdate) { return Send(this, { op: OPCODES.Dispatch, t: event, d: data, - s: this.sequence++, + s: this.sequence++ }); } } @@ -43,23 +40,25 @@ export async function setupListener(this: WebSocket) { const [members, recipients, relationships] = await Promise.all([ Member.find({ where: { id: this.user_id }, - relations: ["guild", "guild.channels"], + relations: ["guild", "guild.channels"] }), Recipient.find({ where: { user_id: this.user_id, closed: false }, - relations: ["channel"], + relations: ["channel"] }), - Relationship.find({ where: { - from_id: this.user_id, - type: RelationshipType.friends, - } }), + Relationship.find({ + where: { + from_id: this.user_id, + type: RelationshipType.friends + } + }) ]); const guilds = members.map((x) => x.guild); const dm_channels = recipients.map((x) => x.channel); const opts: { acknowledge: boolean; channel?: AMQChannel } = { - acknowledge: true, + acknowledge: true }; this.listen_options = opts; const consumer = consume.bind(this); @@ -73,11 +72,7 @@ export async function setupListener(this: WebSocket) { this.events[this.user_id] = await listenEvent(this.user_id, consumer, opts); relationships.forEach(async (relationship) => { - this.events[relationship.to_id] = await listenEvent( - relationship.to_id, - handlePresenceUpdate.bind(this), - opts - ); + this.events[relationship.to_id] = await listenEvent(relationship.to_id, handlePresenceUpdate.bind(this), opts); }); dm_channels.forEach(async (channel) => { @@ -90,16 +85,8 @@ export async function setupListener(this: WebSocket) { this.events[guild.id] = await listenEvent(guild.id, consumer, opts); guild.channels.forEach(async (channel) => { - if ( - permission - .overwriteChannel(channel.permission_overwrites!) - .has("VIEW_CHANNEL") - ) { - this.events[channel.id] = await listenEvent( - channel.id, - consumer, - opts - ); + if (permission.overwriteChannel(channel.permission_overwrites!).has("VIEW_CHANNEL")) { + this.events[channel.id] = await listenEvent(channel.id, consumer, opts); } }); }); @@ -131,11 +118,7 @@ async function consume(this: WebSocket, opts: EventOpts) { delete this.member_events[data.user.id]; case "GUILD_MEMBER_ADD": if (this.member_events[data.user.id]) break; // already subscribed - this.member_events[data.user.id] = await listenEvent( - data.user.id, - handlePresenceUpdate.bind(this), - this.listen_options - ); + this.member_events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_MEMBER_REMOVE": if (!this.member_events[data.user.id]) break; @@ -148,21 +131,13 @@ async function consume(this: WebSocket, opts: EventOpts) { opts.cancel(); break; case "CHANNEL_CREATE": - if ( - !permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { return; } this.events[id] = await listenEvent(id, consumer, listenOpts); break; case "RELATIONSHIP_ADD": - this.events[data.user.id] = await listenEvent( - data.user.id, - handlePresenceUpdate.bind(this), - this.listen_options - ); + this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_CREATE": this.events[id] = await listenEvent(id, consumer, listenOpts); @@ -170,11 +145,7 @@ async function consume(this: WebSocket, opts: EventOpts) { case "CHANNEL_UPDATE": const exists = this.events[id]; // @ts-ignore - if ( - permission - .overwriteChannel(data.permission_overwrites) - .has("VIEW_CHANNEL") - ) { + if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { if (exists) break; this.events[id] = await listenEvent(id, consumer, listenOpts); } else { @@ -244,6 +215,6 @@ async function consume(this: WebSocket, opts: EventOpts) { op: OPCODES.Dispatch, t: event, d: data, - s: this.sequence++, + s: this.sequence++ }); } diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 44db598c..ac6955fd 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -1,35 +1,35 @@ -import { WebSocket, Payload } from "@fosscord/gateway"; +import { Payload, WebSocket } from "@fosscord/gateway"; import { + Application, checkToken, + Config, + emitEvent, + EVENTEnum, + IdentifySchema, Intents, Member, - ReadyEventData, - User, - Session, - EVENTEnum, - Config, + MemberPrivateProjection, + OrmUtils, + PresenceUpdateEvent, + PrivateSessionProjection, + PrivateUserProjection, PublicMember, PublicUser, - PrivateUserProjection, ReadState, - Application, - emitEvent, + ReadyEventData, + Recipient, + Session, SessionsReplace, - PrivateSessionProjection, - MemberPrivateProjection, - PresenceUpdateEvent, - UserSettings, - IdentifySchema, + User, + UserSettings } from "@fosscord/util"; -import { Send } from "../util/Send"; +import { setupListener } from "../listener/listener"; import { CLOSECODES, OPCODES } from "../util/Constants"; +import { Send } from "../util/Send"; import { genSessionId } from "../util/SessionUtils"; -import { setupListener } from "../listener/listener"; +import { check } from "./instanceOf"; // import experiments from "./experiments.json"; const experiments: any = []; -import { check } from "./instanceOf"; -import { Recipient } from "@fosscord/util"; -import { OrmUtils } from "@fosscord/util"; // TODO: user sharding // TODO: check privileged intents, if defined in the config @@ -52,57 +52,44 @@ export async function onIdentify(this: WebSocket, data: Payload) { const session_id = genSessionId(); this.session_id = session_id; //Set the session of the WebSocket object - - const [user, read_states, members, recipients, session, application] = - await Promise.all([ - User.findOneOrFail({ - where: { id: this.user_id }, - relations: ["relationships", "relationships.to", "settings"], - select: [...PrivateUserProjection, "relationships"], - }), - ReadState.find({ where: { user_id: this.user_id } }), - Member.find({ - where: { id: this.user_id }, - select: MemberPrivateProjection, - relations: [ - "guild", - "guild.channels", - "guild.emojis", - "guild.emojis.user", - "guild.roles", - "guild.stickers", - "user", - "roles", - ], - }), - Recipient.find({ - where: { user_id: this.user_id, closed: false }, - relations: [ - "channel", - "channel.recipients", - "channel.recipients.user", - ], - // TODO: public user selection - }), - // save the session and delete it when the websocket is closed - await OrmUtils.mergeDeep(new Session(), { - user_id: this.user_id, - session_id: session_id, - // TODO: check if status is only one of: online, dnd, offline, idle - status: identify.presence?.status || "offline", //does the session always start as online? - client_info: { - //TODO read from identity - client: "desktop", - os: identify.properties?.os, - version: 0, - }, - activities: [], - }).save(), - Application.findOne({ where: { id: this.user_id } }), - ]); + + const [user, read_states, members, recipients, session, application] = await Promise.all([ + User.findOneOrFail({ + where: { id: this.user_id }, + relations: ["relationships", "relationships.to", "settings"], + select: [...PrivateUserProjection, "relationships"] + }), + ReadState.find({ where: { user_id: this.user_id } }), + Member.find({ + where: { id: this.user_id }, + select: MemberPrivateProjection, + relations: ["guild", "guild.channels", "guild.emojis", "guild.emojis.user", "guild.roles", "guild.stickers", "user", "roles"] + }), + Recipient.find({ + where: { user_id: this.user_id, closed: false }, + relations: ["channel", "channel.recipients", "channel.recipients.user"] + // TODO: public user selection + }), + // save the session and delete it when the websocket is closed + await OrmUtils.mergeDeep(new Session(), { + user_id: this.user_id, + session_id: session_id, + // TODO: check if status is only one of: online, dnd, offline, idle + status: identify.presence?.status || "offline", //does the session always start as online? + client_info: { + //TODO read from identity + client: "desktop", + os: identify.properties?.os, + version: 0 + }, + activities: [] + }).save(), + Application.findOne({ where: { id: this.user_id } }) + ]); if (!user) return this.close(CLOSECODES.Authentication_failed); - if (!user.settings) { //settings may not exist after updating... + if (!user.settings) { + //settings may not exist after updating... user.settings = new UserSettings(); user.settings.id = user.id; //await (user.settings as UserSettings).save(); @@ -132,8 +119,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { ...x, roles: x.roles.map((x) => x.id), settings: undefined, - guild: undefined, - }, + guild: undefined + } ]; }) as PublicMember[][]; let guilds = members.map((x) => ({ ...x.guild, joined_at: x.joined_at })); @@ -146,7 +133,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { op: OPCODES.Dispatch, t: EVENTEnum.GuildCreate, s: this.sequence++, - d: guild, + d: guild }); }, 500); return { id: guild.id, unavailable: true }; @@ -163,9 +150,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { //TODO is this needed? check if users in group dm that are not friends are sent in the READY event users = users.concat(x.channel.recipients as unknown as User[]); if (x.channel.isDm()) { - x.channel.recipients = x.channel.recipients!.filter( - (x) => x.id !== this.user_id - ); + x.channel.recipients = x.channel.recipients!.filter((x) => x.id !== this.user_id); } return x.channel; }); @@ -192,8 +177,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { user_id: this.user_id, data: await Session.find({ where: { user_id: this.user_id }, - select: PrivateSessionProjection, - }), + select: PrivateSessionProjection + }) } as SessionsReplace); emitEvent({ event: "PRESENCE_UPDATE", @@ -202,8 +187,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { user: await User.getPublicUser(this.user_id), activities: session.activities, client_status: session?.client_info, - status: session.status, - }, + status: session.status + } } as PresenceUpdateEvent); }); @@ -238,7 +223,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { const d: ReadyEventData = { v: 8, - application: {id: application?.id??'', flags: application?.flags??0}, //TODO: check this code! + application: { id: application?.id ?? "", flags: application?.flags ?? 0 }, //TODO: check this code! user: privateUser, user_settings: user.settings, // @ts-ignore @@ -255,12 +240,12 @@ export async function onIdentify(this: WebSocket, data: Payload) { read_state: { entries: read_states, partial: false, - version: 304128, + version: 304128 }, user_guild_settings: { entries: user_guild_settings_entries, partial: false, // TODO partial - version: 642, + version: 642 }, private_channels: channels, session_id: session_id, @@ -268,8 +253,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { connected_accounts: [], // TODO consents: { personalization: { - consented: false, // TODO - }, + consented: false // TODO + } }, country_code: user.settings.locale, friend_suggestion_count: 0, // TODO @@ -277,7 +262,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { experiments: experiments, // TODO guild_join_requests: [], // TODO what is this? users: users.filter((x) => x).unique(), - merged_members: merged_members, + merged_members: merged_members // shard // TODO: only for user sharding }; @@ -286,7 +271,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { op: OPCODES.Dispatch, t: EVENTEnum.Ready, s: this.sequence++, - d, + d }); //TODO send READY_SUPPLEMENTAL diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts index 74996f5b..ea69779e 100644 --- a/src/gateway/opcodes/LazyRequest.ts +++ b/src/gateway/opcodes/LazyRequest.ts @@ -1,9 +1,8 @@ -import { getPermission, listenEvent, Member, Role, getOrInitialiseDatabase, LazyRequest } from "@fosscord/util"; -import { Send } from "../util/Send"; +import { handlePresenceUpdate, Payload, WebSocket } from "@fosscord/gateway"; +import { getOrInitialiseDatabase, getPermission, LazyRequest, listenEvent, Member, Role } from "@fosscord/util"; import { OPCODES } from "../util/Constants"; -import { WebSocket, Payload, handlePresenceUpdate } from "@fosscord/gateway"; +import { Send } from "../util/Send"; import { check } from "./instanceOf"; -import { getRepository } from "typeorm"; // TODO: only show roles/members that have access to this channel // TODO: config: to list all members (even those who are offline) sorted by role, or just those who are online @@ -16,16 +15,16 @@ async function getMembers(guild_id: string, range: [number, number]) { // TODO: wait for typeorm to implement ordering for .find queries https://github.com/typeorm/typeorm/issues/2620 // TODO: rewrite this, released in 0.3.0 - let members: Member[] = await (await getOrInitialiseDatabase()).getRepository(Member) + let members: Member[] = await ( + await getOrInitialiseDatabase() + ) + .getRepository(Member) .createQueryBuilder("member") .where("member.guild_id = :guild_id", { guild_id }) .leftJoinAndSelect("member.roles", "role") .leftJoinAndSelect("member.user", "user") .leftJoinAndSelect("user.sessions", "session") - .addSelect( - "CASE WHEN session.status = 'offline' THEN 0 ELSE 1 END", - "_status" - ) + .addSelect("CASE WHEN session.status = 'offline' THEN 0 ELSE 1 END", "_status") .orderBy("role.position", "DESC") .addOrderBy("_status", "DESC") .addOrderBy("user.username", "ASC") @@ -44,21 +43,17 @@ async function getMembers(guild_id: string, range: [number, number]) { for (const role of member_roles) { // @ts-ignore - const [role_members, other_members] = partition(members, (m: Member) => - m.roles.find((r) => r.id === role.id) - ); + const [role_members, other_members] = partition(members, (m: Member) => m.roles.find((r) => r.id === role.id)); const group = { count: role_members.length, - id: role.id === guild_id ? "online" : role.id, + id: role.id === guild_id ? "online" : role.id }; items.push({ group }); groups.push(group); for (const member of role_members) { - const roles = member.roles - .filter((x: Role) => x.id !== guild_id) - .map((x: Role) => x.id); + const roles = member.roles.filter((x: Role) => x.id !== guild_id).map((x: Role) => x.id); const session = member.user.sessions.first(); @@ -71,10 +66,10 @@ async function getMembers(guild_id: string, range: [number, number]) { presence: { ...session, activities: session?.activities || [], - user: { id: member.user.id }, - }, - }, - } + user: { id: member.user.id } + } + } + }; if (!member?.user?.sessions || !member.user.sessions.length) { offlineItems.push(item); @@ -90,7 +85,7 @@ async function getMembers(guild_id: string, range: [number, number]) { if (offlineItems.length) { const group = { count: offlineItems.length, - id: "offline", + id: "offline" }; items.push({ group }); groups.push(group); @@ -102,7 +97,7 @@ async function getMembers(guild_id: string, range: [number, number]) { items, groups, range, - members: items.map((x) => 'member' in x ? x.member : undefined).filter(x => !!x), + members: items.map((x) => ("member" in x ? x.member : undefined)).filter((x) => !!x) }; } @@ -129,11 +124,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { op.members.forEach(async (member) => { if (this.events[member.user.id]) return; // already subscribed as friend if (this.member_events[member.user.id]) return; // already subscribed in member list - this.member_events[member.user.id] = await listenEvent( - member.user.id, - handlePresenceUpdate.bind(this), - this.listen_options - ); + this.member_events[member.user.id] = await listenEvent(member.user.id, handlePresenceUpdate.bind(this), this.listen_options); }); }); @@ -145,7 +136,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { ops: ops.map((x) => ({ items: x.items, op: "SYNC", - range: x.range, + range: x.range })), online_count: member_count, member_count, @@ -154,8 +145,8 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { groups: ops .map((x) => x.groups) .flat() - .unique(), - }, + .unique() + } }); } @@ -164,9 +155,7 @@ function partition<T>(array: T[], isValid: Function) { return array.reduce( // @ts-ignore ([pass, fail], elem) => { - return isValid(elem) - ? [[...pass, elem], fail] - : [pass, [...fail, elem]]; + return isValid(elem) ? [[...pass, elem], fail] : [pass, [...fail, elem]]; }, [[], []] ); diff --git a/src/gateway/opcodes/PresenceUpdate.ts b/src/gateway/opcodes/PresenceUpdate.ts index f31c9161..ad712234 100644 --- a/src/gateway/opcodes/PresenceUpdate.ts +++ b/src/gateway/opcodes/PresenceUpdate.ts @@ -1,4 +1,4 @@ -import { WebSocket, Payload } from "@fosscord/gateway"; +import { Payload, WebSocket } from "@fosscord/gateway"; import { ActivitySchema, emitEvent, PresenceUpdateEvent, Session, User } from "@fosscord/util"; import { check } from "./instanceOf"; @@ -6,10 +6,7 @@ export async function onPresenceUpdate(this: WebSocket, { d }: Payload) { check.call(this, ActivitySchema, d); const presence = d as ActivitySchema; - await Session.update( - { session_id: this.session_id }, - { status: presence.status, activities: presence.activities } - ); + await Session.update({ session_id: this.session_id }, { status: presence.status, activities: presence.activities }); await emitEvent({ event: "PRESENCE_UPDATE", @@ -18,7 +15,7 @@ export async function onPresenceUpdate(this: WebSocket, { d }: Payload) { user: await User.getPublicUser(this.user_id), activities: presence.activities, client_status: {}, // TODO: - status: presence.status, - }, + status: presence.status + } } as PresenceUpdateEvent); } diff --git a/src/gateway/opcodes/Resume.ts b/src/gateway/opcodes/Resume.ts index 42dc586d..f320864b 100644 --- a/src/gateway/opcodes/Resume.ts +++ b/src/gateway/opcodes/Resume.ts @@ -1,11 +1,11 @@ -import { WebSocket, Payload } from "@fosscord/gateway"; +import { Payload, WebSocket } from "@fosscord/gateway"; import { Send } from "../util/Send"; export async function onResume(this: WebSocket, data: Payload) { console.log("Got Resume -> cancel not implemented"); await Send(this, { op: 9, - d: false, + d: false }); // return this.close(CLOSECODES.Invalid_session); diff --git a/src/gateway/opcodes/VoiceStateUpdate.ts b/src/gateway/opcodes/VoiceStateUpdate.ts index c4297a68..20502584 100644 --- a/src/gateway/opcodes/VoiceStateUpdate.ts +++ b/src/gateway/opcodes/VoiceStateUpdate.ts @@ -1,18 +1,18 @@ import { Payload, WebSocket } from "@fosscord/gateway"; -import { genVoiceToken } from "../util/SessionUtils"; -import { check } from "./instanceOf"; import { Config, emitEvent, Guild, Member, + OrmUtils, + Region, VoiceServerUpdateEvent, VoiceState, VoiceStateUpdateEvent, - VoiceStateUpdateSchema, + VoiceStateUpdateSchema } from "@fosscord/util"; -import { OrmUtils } from "@fosscord/util"; -import { Region } from "@fosscord/util"; +import { genVoiceToken } from "../util/SessionUtils"; +import { check } from "./instanceOf"; // TODO: check if a voice server is setup // Notice: Bot users respect the voice channel's user limit, if set. When the voice channel is full, you will not receive the Voice State Update or Voice Server Update events in response to your own Voice State Update. Having MANAGE_CHANNELS permission bypasses this limit and allows you to join regardless of the channel being full or not. @@ -20,7 +20,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { check.call(this, VoiceStateUpdateSchema, data.d); const body = data.d as VoiceStateUpdateSchema; - if(body.guild_id == null) { + if (body.guild_id == null) { console.log(`[Gateway] VoiceStateUpdate called with guild_id == null by user ${this.user_id}!`); return; } @@ -28,26 +28,20 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { let voiceState: VoiceState; try { voiceState = await VoiceState.findOneOrFail({ - where: { user_id: this.user_id }, + where: { user_id: this.user_id } }); - if ( - voiceState.session_id !== this.session_id && - body.channel_id === null - ) { + if (voiceState.session_id !== this.session_id && body.channel_id === null) { //Should we also check guild_id === null? //changing deaf or mute on a client that's not the one with the same session of the voicestate in the database should be ignored return; } //If a user change voice channel between guild we should send a left event first - if ( - voiceState.guild_id !== body.guild_id && - voiceState.session_id === this.session_id - ) { + if (voiceState.guild_id !== body.guild_id && voiceState.session_id === this.session_id) { await emitEvent({ event: "VOICE_STATE_UPDATE", data: { ...voiceState, channel_id: null }, - guild_id: voiceState.guild_id, + guild_id: voiceState.guild_id }); } @@ -60,7 +54,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { user_id: this.user_id, deaf: false, mute: false, - suppress: false, + suppress: false }); } @@ -69,12 +63,11 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { //TODO this may fail voiceState.member = await Member.findOneOrFail({ where: { id: voiceState.user_id, guild_id: voiceState.guild_id }, - relations: ["user", "roles"], + relations: ["user", "roles"] }); //If the session changed we generate a new token - if (voiceState.session_id !== this.session_id) - voiceState.token = genVoiceToken(); + if (voiceState.session_id !== this.session_id) voiceState.token = genVoiceToken(); voiceState.session_id = this.session_id; const { id, ...newObj } = voiceState; @@ -84,8 +77,8 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { emitEvent({ event: "VOICE_STATE_UPDATE", data: newObj, - guild_id: voiceState.guild_id, - } as VoiceStateUpdateEvent), + guild_id: voiceState.guild_id + } as VoiceStateUpdateEvent) ]); //If it's null it means that we are leaving the channel and this event is not needed @@ -94,13 +87,9 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { const regions = Config.get().regions; let guildRegion: Region; if (guild && guild.region) { - guildRegion = regions.available.filter( - (r) => r.id === guild.region - )[0]; + guildRegion = regions.available.filter((r) => r.id === guild.region)[0]; } else { - guildRegion = regions.available.filter( - (r) => r.id === regions.default - )[0]; + guildRegion = regions.available.filter((r) => r.id === regions.default)[0]; } await emitEvent({ @@ -108,9 +97,9 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { data: { token: voiceState.token, guild_id: voiceState.guild_id, - endpoint: guildRegion.endpoint, + endpoint: guildRegion.endpoint }, - guild_id: voiceState.guild_id, + guild_id: voiceState.guild_id } as VoiceServerUpdateEvent); } } diff --git a/src/gateway/opcodes/index.ts b/src/gateway/opcodes/index.ts index 027739db..d5dc7de1 100644 --- a/src/gateway/opcodes/index.ts +++ b/src/gateway/opcodes/index.ts @@ -1,4 +1,4 @@ -import { WebSocket, Payload } from "@fosscord/gateway"; +import { Payload, WebSocket } from "@fosscord/gateway"; import { onHeartbeat } from "./Heartbeat"; import { onIdentify } from "./Identify"; import { onLazyRequest } from "./LazyRequest"; @@ -21,5 +21,5 @@ export default { // 9: Invalid Session // 10: Hello // 13: Dm_update - 14: onLazyRequest, + 14: onLazyRequest }; diff --git a/src/gateway/opcodes/instanceOf.ts b/src/gateway/opcodes/instanceOf.ts index eb6f6ea1..95d74963 100644 --- a/src/gateway/opcodes/instanceOf.ts +++ b/src/gateway/opcodes/instanceOf.ts @@ -1,5 +1,5 @@ -import { instanceOf } from "@fosscord/util"; import { WebSocket } from "@fosscord/gateway"; +import { instanceOf } from "@fosscord/util"; import { CLOSECODES } from "../util/Constants"; export function check(this: WebSocket, schema: any, data: any) { diff --git a/src/gateway/start.ts b/src/gateway/start.ts index 2000522a..97420d7e 100644 --- a/src/gateway/start.ts +++ b/src/gateway/start.ts @@ -1,14 +1,14 @@ process.on("uncaughtException", console.error); process.on("unhandledRejection", console.error); -import { Server } from "./Server"; import { config } from "dotenv"; +import { Server } from "./Server"; config(); let port = Number(process.env.PORT); if (isNaN(port)) port = 3002; const server = new Server({ - port, + port }); server.start(); diff --git a/src/gateway/util/Constants.ts b/src/gateway/util/Constants.ts index 692f9028..78455ff8 100644 --- a/src/gateway/util/Constants.ts +++ b/src/gateway/util/Constants.ts @@ -22,7 +22,7 @@ export enum OPCODES { Stream_Watch = 20, Stream_Ping = 21, Stream_Set_Paused = 22, - Request_Application_Commands = 24, + Request_Application_Commands = 24 } export enum CLOSECODES { Unknown_error = 4000, @@ -39,7 +39,7 @@ export enum CLOSECODES { Sharding_required, Invalid_API_version, Invalid_intent, - Disallowed_intent, + Disallowed_intent } export interface Payload { diff --git a/src/gateway/util/Send.ts b/src/gateway/util/Send.ts index 2a28d8e0..7826dd40 100644 --- a/src/gateway/util/Send.ts +++ b/src/gateway/util/Send.ts @@ -7,8 +7,7 @@ try { import { Payload, WebSocket } from "@fosscord/gateway"; export async function Send(socket: WebSocket, data: Payload) { - if(process.env.WS_VERBOSE) - console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`); + if (process.env.WS_VERBOSE) console.log(`[Websocket] Outgoing message: ${JSON.stringify(data)}`); let buffer: Buffer | string; if (socket.encoding === "etf") buffer = erlpack.pack(data); // TODO: encode circular object diff --git a/src/gateway/util/SessionUtils.ts b/src/gateway/util/SessionUtils.ts index bf854042..c66c7e76 100644 --- a/src/gateway/util/SessionUtils.ts +++ b/src/gateway/util/SessionUtils.ts @@ -7,7 +7,5 @@ export function genVoiceToken() { } function genRanHex(size: number) { - return [...Array(size)] - .map(() => Math.floor(Math.random() * 16).toString(16)) - .join(""); + return [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(""); } diff --git a/src/gateway/util/index.ts b/src/gateway/util/index.ts index 0be5ecee..a5085228 100644 --- a/src/gateway/util/index.ts +++ b/src/gateway/util/index.ts @@ -1,5 +1,5 @@ export * from "./Constants"; +export * from "./Heartbeat"; export * from "./Send"; export * from "./SessionUtils"; -export * from "./Heartbeat"; export * from "./WebSocket"; |