diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts index 88f95a3d3..5cd8a89ba 100644 --- a/src/api/routes/users/@me/relationships.ts +++ b/src/api/routes/users/@me/relationships.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Config, DiscordApiErrors, Relationship, RelationshipAddEvent, RelationshipRemoveEvent, RelationshipUpdateEvent, User, emitEvent } from "@spacebar/util"; +import { Channel, Config, DiscordApiErrors, Relationship, RelationshipAddEvent, RelationshipRemoveEvent, RelationshipUpdateEvent, User, emitEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { PublicUserProjection, RelationshipType, RelationshipPatchSchema } from "@spacebar/schemas"; @@ -315,5 +315,14 @@ async function updateRelationship(req: Request, res: Response, friend: User, typ } as RelationshipAddEvent), ]); + + if ( + incoming_relationship.type === RelationshipType.friends && + outgoing_relationship.type === RelationshipType.friends + ) { + await Channel.createDMChannel([id], req.user_id); + await Channel.createDMChannel([req.user_id], id); + } + return res.sendStatus(204); } diff --git a/src/gateway/events/Close.ts b/src/gateway/events/Close.ts index 988f17fa4..17781edff 100644 --- a/src/gateway/events/Close.ts +++ b/src/gateway/events/Close.ts @@ -28,7 +28,26 @@ export async function Close(this: WebSocket, code: number, reason: Buffer) { this.removeAllListeners(); if (this.session_id) { - await Session.delete({ session_id: this.session_id }); + const authSessionId = this.session?.session_id; + const closedAt = Date.now(); + + setTimeout(async () => { + try { + if (authSessionId && this.user_id) { + const s = await Session.findOne({ + where: { user_id: this.user_id, session_id: authSessionId }, + }); + if (s && (s.last_seen?.getTime() ?? 0) <= closedAt) { + await Session.update( + { user_id: this.user_id, session_id: authSessionId }, + { status: "offline", activities: [], client_status: {} } + ); + } + } + } catch(e) { + console.error("[WebSocket] Close session cleanup failed", code, e); + } + }, 10_000); const voiceState = await VoiceState.findOne({ where: { user_id: this.user_id }, diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index 00fbf6501..3a1b90ff2 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -529,12 +529,20 @@ export async function onIdentify(this: WebSocket, data: Payload) { let channelUsers = channel.recipients?.map((recipient) => recipient.user.toPublicUser()); if (channelUsers && channelUsers.length > 0) channelUsers.forEach((user) => users.add(user)); - // HACK: insert self into recipients for DMs with users that no longer exist + // HACK: insert "Deleted User" into recipients for DMs with users that no longer exist else if (channel.type === ChannelType.DM) { - const selfUser = user.toPublicUser(); - users.add(selfUser); - channelUsers ??= []; - channelUsers.push(selfUser); + const deletedUser = { + avatar: null, + discriminator: "0000", + id: "0", + public_flags: 0, + username: "Deleted User", + badge_ids: null, + }; + + users.add(deletedUser as any); + channelUsers ??= []; + channelUsers.push(deletedUser as any); } return { diff --git a/src/util/dtos/DmChannelDTO.ts b/src/util/dtos/DmChannelDTO.ts index 956fe48a4..f703630a7 100644 --- a/src/util/dtos/DmChannelDTO.ts +++ b/src/util/dtos/DmChannelDTO.ts @@ -51,6 +51,20 @@ export class DmChannelDTO { }) || [], ) ).map((u) => new MinimalPublicUserDTO(u)); + + if (obj.type === 1 && obj.recipients.length === 0) { + obj.recipients = [ + new MinimalPublicUserDTO({ + avatar: null, + discriminator: "0000", + id: "0", + public_flags: 0, + username: "Deleted User", + badge_ids: null, + } as any), + ]; + } + return obj; } diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index d43c9787b..8217a0df3 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -494,13 +494,12 @@ export class Channel extends BaseClass { } else { await emitEvent({ event: "CHANNEL_CREATE", - data: channel_dto, + data: channel_dto.excludedRecipients([creator_user_id]), user_id: creator_user_id, }); } - if (recipients.length === 1) return channel_dto; - else return channel_dto.excludedRecipients([creator_user_id]); + if (recipients.length === 1) return channel_dto.excludedRecipients([creator_user_id]); } static async removeRecipientFromChannel(channel: Channel, user_id: string) {