From 715b575e124431943f786955d93e82679438fabc Mon Sep 17 00:00:00 2001 From: Leafus Date: Tue, 18 Aug 2026 22:41:29 +0200 Subject: [PATCH 1/2] feat: fix relationship events not sending properly --- src/api/routes/users/@me/relationships.ts | 8 ++++---- src/gateway/listener/listener.ts | 22 ++++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts index 07aa59f4d..c0a77f736 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts @@ -166,12 +166,12 @@ router.delete( const user = await User.findOneOrFail({ where: { id: req.user_id }, select: Object.fromEntries(userProjection.map((i) => [i, true])), // TODO: cleanup - relations: { relationships: true }, + relations: { relationships: { to: true } }, }); const friend = await User.findOneOrFail({ where: { id: user_id }, select: Object.fromEntries(userProjection.map((i) => [i, true])), // TODO: cleanup - relations: { relationships: true }, + relations: { relationships: { to: true } }, }); const relationship = user.relationships.find((x) => x.to_id === user_id); @@ -236,9 +236,9 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ await relationship.save(); } else { relationship = await Relationship.create({ - to_id: id, + to: friend, type: RelationshipType.blocked, - from_id: req.user_id, + from: user, }).save(); } diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts index b2b7fbf63..1047fff2f 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts @@ -185,6 +185,7 @@ export async function setupListener(this: WebSocket) { async function consume(this: WebSocket, opts: EventOpts) { const { data, event } = opts; const id = (opts.guild_id || opts.channel_id || opts.user_id || opts.session_id) as string; + const subscription_id = (data?.id as string) || id; const permission = this.permissions[id] || new Permissions("ADMINISTRATOR"); // default permission for dm const consumer = consume.bind(this); @@ -240,11 +241,11 @@ async function consume(this: WebSocket, opts: EventOpts) { case "RELATIONSHIP_REMOVE": case "CHANNEL_DELETE": case "GUILD_DELETE": - this.events[id]?.(); - delete this.events[id]; + this.events[subscription_id]?.(); + delete this.events[subscription_id]; if (event === "GUILD_DELETE" && this.ipAddress) { const ban = await Ban.findOne({ - where: { guild_id: id, user_id: this.user_id }, + where: { guild_id: subscription_id, user_id: this.user_id }, }); if (ban) { @@ -255,28 +256,29 @@ async function consume(this: WebSocket, opts: EventOpts) { break; case "CHANNEL_CREATE": if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) return; - this.events[id] = await listenEvent(id, consumer, listenOpts); + this.events[subscription_id] = await listenEvent(subscription_id, consumer, listenOpts); break; case "RELATIONSHIP_ADD": - this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); + // don't let a payload without a user object stop the dispatch below + if (data.user?.id) this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_CREATE": await Promise.all([ ...data.channels.map(async ({ id }: { id: string }) => { this.events[id] = await listenEvent(id, consumer, listenOpts); }), - listenEvent(id, consumer, listenOpts).then((ret) => (this.events[id] = ret)), + listenEvent(subscription_id, consumer, listenOpts).then((ret) => (this.events[subscription_id] = ret)), ]); break; case "CHANNEL_UPDATE": { - const exists = this.events[id]; + const exists = this.events[subscription_id]; if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { if (exists) break; - this.events[id] = await listenEvent(id, consumer, listenOpts); + this.events[subscription_id] = await listenEvent(subscription_id, consumer, listenOpts); } else { if (!exists) return; // return -> do not send channel update events for hidden channels - opts.cancel(id); - delete this.events[id]; + opts.cancel(subscription_id); + delete this.events[subscription_id]; } break; } From bfb7a2129bc0d8c4751b568f5b778ba531c6f735 Mon Sep 17 00:00:00 2001 From: Leafus Date: Tue, 18 Aug 2026 23:44:36 +0200 Subject: [PATCH 2/2] feat: read subscription ids from the payload per event --- src/api/routes/users/@me/relationships.ts | 4 +-- src/gateway/listener/listener.ts | 33 ++++++++++++++--------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts index c0a77f736..cfa364783 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts @@ -166,12 +166,12 @@ router.delete( const user = await User.findOneOrFail({ where: { id: req.user_id }, select: Object.fromEntries(userProjection.map((i) => [i, true])), // TODO: cleanup - relations: { relationships: { to: true } }, + relations: { relationships: true }, }); const friend = await User.findOneOrFail({ where: { id: user_id }, select: Object.fromEntries(userProjection.map((i) => [i, true])), // TODO: cleanup - relations: { relationships: { to: true } }, + relations: { relationships: true }, }); const relationship = user.relationships.find((x) => x.to_id === user_id); diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts index 1047fff2f..578c22bb9 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts @@ -185,7 +185,6 @@ export async function setupListener(this: WebSocket) { async function consume(this: WebSocket, opts: EventOpts) { const { data, event } = opts; const id = (opts.guild_id || opts.channel_id || opts.user_id || opts.session_id) as string; - const subscription_id = (data?.id as string) || id; const permission = this.permissions[id] || new Permissions("ADMINISTRATOR"); // default permission for dm const consumer = consume.bind(this); @@ -240,12 +239,14 @@ async function consume(this: WebSocket, opts: EventOpts) { break; case "RELATIONSHIP_REMOVE": case "CHANNEL_DELETE": - case "GUILD_DELETE": - this.events[subscription_id]?.(); - delete this.events[subscription_id]; + case "GUILD_DELETE": { + const removed_id = data.id as string; + if (!removed_id) break; + this.events[removed_id]?.(); + delete this.events[removed_id]; if (event === "GUILD_DELETE" && this.ipAddress) { const ban = await Ban.findOne({ - where: { guild_id: subscription_id, user_id: this.user_id }, + where: { guild_id: removed_id, user_id: this.user_id }, }); if (ban) { @@ -254,31 +255,37 @@ async function consume(this: WebSocket, opts: EventOpts) { } } break; - case "CHANNEL_CREATE": + } + case "CHANNEL_CREATE": { if (!permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) return; - this.events[subscription_id] = await listenEvent(subscription_id, consumer, listenOpts); + const channel_id = data.id as string; + this.events[channel_id] = await listenEvent(channel_id, consumer, listenOpts); break; + } case "RELATIONSHIP_ADD": // don't let a payload without a user object stop the dispatch below if (data.user?.id) this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; - case "GUILD_CREATE": + case "GUILD_CREATE": { + const guild_id = data.id as string; await Promise.all([ ...data.channels.map(async ({ id }: { id: string }) => { this.events[id] = await listenEvent(id, consumer, listenOpts); }), - listenEvent(subscription_id, consumer, listenOpts).then((ret) => (this.events[subscription_id] = ret)), + listenEvent(guild_id, consumer, listenOpts).then((ret) => (this.events[guild_id] = ret)), ]); break; + } case "CHANNEL_UPDATE": { - const exists = this.events[subscription_id]; + const channel_id = data.id as string; + const exists = this.events[channel_id]; if (permission.overwriteChannel(data.permission_overwrites).has("VIEW_CHANNEL")) { if (exists) break; - this.events[subscription_id] = await listenEvent(subscription_id, consumer, listenOpts); + this.events[channel_id] = await listenEvent(channel_id, consumer, listenOpts); } else { if (!exists) return; // return -> do not send channel update events for hidden channels - opts.cancel(subscription_id); - delete this.events[subscription_id]; + opts.cancel(channel_id); + delete this.events[channel_id]; } break; }