diff --git a/assets/openapi.json b/assets/openapi.json index 0cd1543b7..d863aed79 100644 Binary files a/assets/openapi.json and b/assets/openapi.json differ diff --git a/assets/schemas.json b/assets/schemas.json index cceb08a23..00200007b 100644 Binary files a/assets/schemas.json and b/assets/schemas.json differ diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts index c4dfb5daa..abe731318 100644 --- a/src/api/routes/users/@me/settings.ts +++ b/src/api/routes/users/@me/settings.ts @@ -68,7 +68,11 @@ router.patch( relations: { settings: true }, }); - if (!user.settings) user.settings = UserSettings.create(body); + if (!user.settings) + user.settings = UserSettings.create({ + ...body, + friend_source_flags: body.friend_source_flags ?? { all: true }, + }); else user.settings.assign(body); if (body.guild_folders) user.settings.guild_folders = body.guild_folders; diff --git a/src/database/entities/Guild.ts b/src/database/entities/Guild.ts index 62df60113..6a6a0b93a 100644 --- a/src/database/entities/Guild.ts +++ b/src/database/entities/Guild.ts @@ -18,7 +18,7 @@ import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { arrayRemove } from "@spacebar/extensions"; -import { Config, GuildWelcomeScreen, Snowflake, handleFile } from "@spacebar/util"; +import { Config, Snowflake, handleFile } from "@spacebar/util"; import { Ban } from "./Ban"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; @@ -31,7 +31,7 @@ import { Template } from "./Template"; import { User } from "./User"; import { VoiceState } from "./VoiceState"; import { Webhook } from "./Webhook"; -import { DiscoverableGuild, IntegrationGuild } from "@spacebar/schemas"; +import { DiscoverableGuild, GuildWelcomeScreen, IntegrationGuild } from "@spacebar/schemas"; import { Categories } from "@spacebar/database"; // TODO: application_command_count, application_command_counts: {1: 0, 2: 0, 3: 0} // TODO: guild_scheduled_events diff --git a/src/database/entities/Session.ts b/src/database/entities/Session.ts index be84dc005..6018c5299 100644 --- a/src/database/entities/Session.ts +++ b/src/database/entities/Session.ts @@ -19,10 +19,11 @@ import crypto from "node:crypto"; import { Column, CreateDateColumn, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm"; import { DateBuilder, TimeSpan, Random } from "@spacebar/extensions"; +import { ClientStatus, PrivateStatus } from "@spacebar/schemas"; +import { IpDataClient } from "@spacebar/util/util/networking"; +import { Activity, GatewaySession, GatewaySessionClientInfo } from "../../util/interfaces"; import { User } from "./User"; import { BaseClassWithoutId } from "./BaseClass"; -import { Activity, ClientStatus, GatewaySession, GatewaySessionClientInfo, PrivateStatus } from "../../util/interfaces"; -import { IpDataClient } from "@spacebar/util/util/networking"; @Entity({ name: "sessions", diff --git a/src/database/entities/UserSettings.ts b/src/database/entities/UserSettings.ts index 7ccb8e1ec..b8afaa4ae 100644 --- a/src/database/entities/UserSettings.ts +++ b/src/database/entities/UserSettings.ts @@ -118,7 +118,7 @@ export class UserSettings extends BaseClassWithoutId { stream_notifications_enabled: boolean = false; @Column({ nullable: true }) - theme: "dark" | "light" = "dark"; // dark + theme: "dark" | "light" | "darker" | "midnight" = "dark"; // dark @Column({ nullable: true }) timezone_offset: number = 0; // e.g -60 diff --git a/src/gateway/opcodes/PresenceUpdate.ts b/src/gateway/opcodes/PresenceUpdate.ts index a0be5f65f..abd2f026d 100644 --- a/src/gateway/opcodes/PresenceUpdate.ts +++ b/src/gateway/opcodes/PresenceUpdate.ts @@ -18,8 +18,8 @@ import { Session, User } from "@spacebar/database"; import { WebSocket, Payload } from "@spacebar/gateway"; -import { emitEvent, InternalStatusOrder, PresenceUpdateEvent, PrivateStatus } from "@spacebar/util"; -import { ActivitySchema } from "@spacebar/schemas"; +import { emitEvent, PresenceUpdateEvent } from "@spacebar/util"; +import { ActivitySchema, InternalStatusOrder, PrivateStatus } from "@spacebar/schemas"; import { check } from "./instanceOf"; export async function onPresenceUpdate(this: WebSocket, { d }: Payload) { diff --git a/src/schemas/api/guilds/index.ts b/src/schemas/api/guilds/index.ts index eee86bcee..8c638422a 100644 --- a/src/schemas/api/guilds/index.ts +++ b/src/schemas/api/guilds/index.ts @@ -17,6 +17,7 @@ */ export * from "./AuditLog"; export * from "./Automod"; +export * from "./Emoji"; export * from "./GuildProfileResponse"; export * from "./GuildSchema"; export * from "./Role"; diff --git a/src/schemas/api/messages/index.ts b/src/schemas/api/messages/index.ts index 1533dd0cb..ca8bf938b 100644 --- a/src/schemas/api/messages/index.ts +++ b/src/schemas/api/messages/index.ts @@ -16,6 +16,7 @@ along with this program. If not, see . */ +export * from "./Attachments"; export * from "./Components"; export * from "./Embeds"; export * from "./Message"; diff --git a/src/schemas/api/spacebar/AttachmentListResponse.ts b/src/schemas/api/spacebar/AttachmentListResponse.ts index fe10bde7a..26afb154e 100644 --- a/src/schemas/api/spacebar/AttachmentListResponse.ts +++ b/src/schemas/api/spacebar/AttachmentListResponse.ts @@ -16,8 +16,7 @@ along with this program. If not, see . */ -import { PublicAttachment } from "@spacebar/schemas/api/messages/Attachments"; -import { MessageReferenceIds } from "@spacebar/schemas"; +import { MessageReferenceIds, PublicAttachment } from "@spacebar/schemas"; export interface AttachmentListResponse { items: { diff --git a/src/schemas/api/users/ConnectedAccount.ts b/src/schemas/api/users/ConnectedAccount.ts index 0779a7694..b5d21b57c 100644 --- a/src/schemas/api/users/ConnectedAccount.ts +++ b/src/schemas/api/users/ConnectedAccount.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -//TODO: remove entity import -import { ConnectedAccount } from "@spacebar/database"; - -export type PublicConnectedAccount = Pick; +export interface PublicConnectedAccount { + name: string; + type: string; + verified?: boolean; +} diff --git a/src/schemas/api/users/SessionsSchemas.ts b/src/schemas/api/users/SessionsSchemas.ts index 34ad0c64e..c74863524 100644 --- a/src/schemas/api/users/SessionsSchemas.ts +++ b/src/schemas/api/users/SessionsSchemas.ts @@ -16,9 +16,7 @@ along with this program. If not, see . */ -import { ActivitySchema, Snowflake } from "@spacebar/schemas"; -// TODO: remove entity import -import { ClientStatus } from "@spacebar/util"; +import { ActivitySchema, ClientStatus, Snowflake } from "@spacebar/schemas"; export type SessionsLogoutSchema = { session_ids?: Snowflake[]; session_id_hashes?: string[] }; export type GetSessionsResponse = { user_sessions: DeviceInfo[] }; diff --git a/src/util/interfaces/Status.ts b/src/schemas/api/users/Status.ts similarity index 100% rename from src/util/interfaces/Status.ts rename to src/schemas/api/users/Status.ts diff --git a/src/schemas/api/users/UserSettings.ts b/src/schemas/api/users/UserSettings.ts index 457d6f4cc..d5ff664ec 100644 --- a/src/schemas/api/users/UserSettings.ts +++ b/src/schemas/api/users/UserSettings.ts @@ -16,11 +16,17 @@ along with this program. If not, see . */ +import { Snowflake } from "@spacebar/schemas"; + export type UserSettingsUpdateSchema = Partial; export interface UserSettingsSchema { + activity_restricted_guild_ids: Snowflake[]; + activity_joining_restricted_guild_ids: Snowflake[]; afk_timeout: number; allow_accessibility_detection: boolean; + allow_activity_party_privacy_friends: boolean; + allow_activity_party_privacy_voice_channel: boolean; animate_emoji: boolean; animate_stickers: number; contact_sync_enabled: boolean; @@ -33,24 +39,26 @@ export interface UserSettingsSchema { enable_tts_command: boolean; explicit_content_filter: number; friend_discovery_flags: number; - friend_source_flags: FriendSourceFlags; - gateway_connected: boolean; + friend_source_flags: FriendSourceFlags | null; gif_auto_play: boolean; guild_folders: GuildFolder[]; // every top guild is displayed as a "folder" - guild_positions: string[]; // guild ids ordered by position inline_attachment_media: boolean; inline_embed_media: boolean; locale: string; // en_US message_display_compact: boolean; native_phone_integration_enabled: boolean; + passwordless: boolean; render_embeds: boolean; render_reactions: boolean; restricted_guilds: string[]; show_current_game: boolean; + slayer_sdk_receive_dms_in_game: number; + soundboard_volume: number; status: "online" | "offline" | "dnd" | "idle" | "invisible"; stream_notifications_enabled: boolean; - theme: "dark" | "light"; // dark + theme: "dark" | "light" | "darker" | "midnight"; // dark timezone_offset: number; // e.g -60 + view_nsfw_commands: boolean; view_nsfw_guilds: boolean; } @@ -69,5 +77,7 @@ export interface GuildFolder { } export interface FriendSourceFlags { - all: boolean; + all?: boolean; + mutual_friends?: boolean; + mutual_guilds?: boolean; } diff --git a/src/schemas/api/users/index.ts b/src/schemas/api/users/index.ts index 5c9b20080..21a9661a4 100644 --- a/src/schemas/api/users/index.ts +++ b/src/schemas/api/users/index.ts @@ -19,6 +19,7 @@ export * from "./ConnectedAccount"; export * from "./InstanceUserDeleteSchema"; export * from "./Member"; +export * from "./SessionsSchemas"; +export * from "./Status"; export * from "./User"; export * from "./UserSettings"; -export * from "./SessionsSchemas"; diff --git a/src/schemas/responses/DiscoverableGuildsResponse.ts b/src/schemas/responses/DiscoverableGuildsResponse.ts index 6eb3de8d0..b091aaf11 100644 --- a/src/schemas/responses/DiscoverableGuildsResponse.ts +++ b/src/schemas/responses/DiscoverableGuildsResponse.ts @@ -16,8 +16,7 @@ along with this program. If not, see . */ -import { Snowflake, StickerResponse } from "@spacebar/schemas"; -import { EmojiResponse } from "@spacebar/schemas/api/guilds/Emoji"; +import { EmojiResponse, Snowflake, StickerResponse } from "@spacebar/schemas"; export interface DiscoverableGuildsResponse { total: number; diff --git a/src/schemas/responses/GuildCreateResponse.ts b/src/schemas/responses/GuildCreateResponse.ts index cd115cf32..42caf88b3 100644 --- a/src/schemas/responses/GuildCreateResponse.ts +++ b/src/schemas/responses/GuildCreateResponse.ts @@ -16,9 +16,7 @@ along with this program. If not, see . */ -// TODO: remove import from util -import { GuildWelcomeScreen } from "@spacebar/util"; -import { GuildUpdateSchema } from "@spacebar/schemas"; +import { GuildUpdateSchema, GuildWelcomeScreen } from "@spacebar/schemas"; export interface GuildCreateResponse extends Omit { id: string; diff --git a/src/schemas/responses/GuildMessagesSearchResponse.ts b/src/schemas/responses/GuildMessagesSearchResponse.ts index df582b657..72583822a 100644 --- a/src/schemas/responses/GuildMessagesSearchResponse.ts +++ b/src/schemas/responses/GuildMessagesSearchResponse.ts @@ -16,10 +16,7 @@ along with this program. If not, see . */ -// TODO: remove dependency on entities -import { Role } from "@spacebar/database"; -import { BaseMessageComponents, Embed, MessageType, Poll, PublicUser, RoleResponse } from "@spacebar/schemas"; -import { PublicAttachment } from "../api/messages/Attachments"; +import { BaseMessageComponents, Embed, MessageType, Poll, PublicUser, RoleResponse, PublicAttachment } from "@spacebar/schemas"; export interface GuildMessagesSearchMessage { id: string; diff --git a/src/schemas/responses/GuildWelcomeScreen.ts b/src/schemas/responses/GuildWelcomeScreen.ts new file mode 100644 index 000000000..d51f3de84 --- /dev/null +++ b/src/schemas/responses/GuildWelcomeScreen.ts @@ -0,0 +1,28 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2026 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +export interface GuildWelcomeScreen { + enabled: boolean; + description: string; + welcome_channels: { + description: string; + emoji_id?: string; + emoji_name?: string; + channel_id: string; + }[]; +} diff --git a/src/schemas/responses/GuildWidgetJsonResponse.ts b/src/schemas/responses/GuildWidgetJsonResponse.ts index 84fb9913d..258b51e64 100644 --- a/src/schemas/responses/GuildWidgetJsonResponse.ts +++ b/src/schemas/responses/GuildWidgetJsonResponse.ts @@ -16,8 +16,7 @@ along with this program. If not, see . */ -// TODO: remove util import -import { ClientStatus } from "@spacebar/util"; +import { ClientStatus } from "@spacebar/schemas/api/users/Status"; export interface GuildWidgetJsonResponse { id: string; diff --git a/src/schemas/responses/HubDirectoryEntriesResponse.ts b/src/schemas/responses/HubDirectoryEntriesResponse.ts index fc710aa21..f4b15e68b 100644 --- a/src/schemas/responses/HubDirectoryEntriesResponse.ts +++ b/src/schemas/responses/HubDirectoryEntriesResponse.ts @@ -26,7 +26,21 @@ export interface HubDirectoryEntry { directory_channel_id: string; guild: Guild; primary_category_id: number; - type: number; // TODO: not exactly sure what this is, channel type? + type: HubDirectoryEntryType; +} + +export enum HubDirectoryEntryType { + GUILD = 0, + GUILD_SCHEDULED_EVENT = 1, +} + +export enum HubDirectoryCategory { + UNCATEGORIZED = 0, + SCHOOL_CLUB = 1, + CLASS = 2, + STUDY_SOCIAL = 3, + SUBJECT_MAJOR = 4, + MISC = 5, } export type HubDirectoryEntriesResponse = HubDirectoryEntry[]; diff --git a/src/schemas/responses/MemberJoinGuildResponse.ts b/src/schemas/responses/MemberJoinGuildResponse.ts index 87e50239e..9d2420fb1 100644 --- a/src/schemas/responses/MemberJoinGuildResponse.ts +++ b/src/schemas/responses/MemberJoinGuildResponse.ts @@ -16,8 +16,7 @@ along with this program. If not, see . */ -import { GuildCreateResponse, RoleResponse, StickerResponse } from "@spacebar/schemas"; -import { EmojiResponse } from "@spacebar/schemas/api/guilds/Emoji"; +import { GuildCreateResponse, RoleResponse, StickerResponse, EmojiResponse } from "@spacebar/schemas"; export interface MemberJoinGuildResponse { guild: GuildCreateResponse; diff --git a/src/schemas/responses/TeamListResponse.ts b/src/schemas/responses/TeamListResponse.ts index fc4d93e29..ac5683819 100644 --- a/src/schemas/responses/TeamListResponse.ts +++ b/src/schemas/responses/TeamListResponse.ts @@ -17,6 +17,19 @@ */ // TODO: remove entity import -import { Team } from "@spacebar/database"; +import { Snowflake } from "@spacebar/schemas"; + +export interface Team { + id: Snowflake; + name: string; + icon: string | null; + owner_user_id: Snowflake; + // members?: TeamMember[]; // TODO: only in application object + // TODO: only on Get/List Team(s) w/ include_payout_account_status=true + // payout_account_status?: TeamPayoutAccountStatus | null; + // payout_account_statuses?: TeamPayoutAccount[]; + // TODO: Get Team only + // stripe_connect_account_id?: string; +} export type TeamListResponse = Team[]; diff --git a/src/schemas/responses/TokenResponse.ts b/src/schemas/responses/TokenResponse.ts index 88ac5e204..9239c1213 100644 --- a/src/schemas/responses/TokenResponse.ts +++ b/src/schemas/responses/TokenResponse.ts @@ -16,12 +16,11 @@ along with this program. If not, see . */ -// TODO: remove dependency on entities -import { BackupCode, UserSettings } from "@spacebar/database"; +import { BackupCode, UserSettingsSchema } from "@spacebar/schemas"; export interface TokenResponse { token: string; - settings: UserSettings; + settings: UserSettingsSchema; } export interface TokenOnlyResponse { diff --git a/src/schemas/responses/TypedResponses.ts b/src/schemas/responses/TypedResponses.ts index f7bd77125..e43114201 100644 --- a/src/schemas/responses/TypedResponses.ts +++ b/src/schemas/responses/TypedResponses.ts @@ -20,7 +20,7 @@ import { GeneralConfiguration, LimitsConfiguration } from "../../util/config/types"; import { DmChannelDTO } from "../../util/dtos"; // TODO: remove entity imports -import { Application, BackupCode, Categories, Channel, Guild, Invite, Member, Template, Webhook } from "@spacebar/database"; +import { Application, Categories, Channel, Guild, Invite, Template } from "@spacebar/database"; import { GuildCreateResponse, PrivateUser, PublicUser } from "@spacebar/schemas"; // TODO: remove this entire file! diff --git a/src/schemas/responses/UserProfileResponse.ts b/src/schemas/responses/UserProfileResponse.ts index daf0c460c..efb1fba1e 100644 --- a/src/schemas/responses/UserProfileResponse.ts +++ b/src/schemas/responses/UserProfileResponse.ts @@ -1,6 +1,6 @@ /* Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors + Copyright (C) 2026 Spacebar and Spacebar Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published @@ -16,20 +16,40 @@ along with this program. If not, see . */ -// TODO: remove entity imports -import { Badge, Member, User } from "@spacebar/database"; -import { PublicConnectedAccount, PublicMember, PublicUser } from "@spacebar/schemas"; +import { EmojiResponse, PublicConnectedAccount, PublicMember, PublicUser, Snowflake } from "@spacebar/schemas"; -export type MutualGuild = { +export interface ProfileBadge { + id: string; + description: string; + icon: string; + link?: string; +} + +export interface MutualGuild { id: string; nick?: string; -}; +} -export type PublicMemberProfile = Pick & { - accent_color: null; // TODO -}; +export interface ProfileMetadataResponse { + pronouns: string; + bio?: string; + banner?: string | null; + accent_color?: number | null; + theme_colors?: [number, number] | null; + popout_animation_particle_type?: Snowflake | null; + emoji?: EmojiResponse | null; + profile_effect?: ProfileEffect | null; +} -export type UserProfile = Pick; +export interface GuildProfileMetadataResponse extends ProfileMetadataResponse { + guild_id: Snowflake; + accent_color?: null; // ignored by clients +} + +export interface ProfileEffect { + id: Snowflake; + expires_at: number | null; +} export interface UserProfileResponse { user: PublicUser; @@ -39,8 +59,8 @@ export interface UserProfileResponse { mutual_guilds: MutualGuild[]; premium_type: number; profile_themes_experiment_bucket: number; - user_profile: UserProfile; + user_profile: ProfileMetadataResponse; guild_member?: PublicMember; - guild_member_profile?: PublicMemberProfile; - badges: Badge[]; + guild_member_profile?: GuildProfileMetadataResponse; + badges: ProfileBadge[]; } diff --git a/src/schemas/responses/index.ts b/src/schemas/responses/index.ts index bb870cf9f..fa6a884e6 100644 --- a/src/schemas/responses/index.ts +++ b/src/schemas/responses/index.ts @@ -39,6 +39,7 @@ export * from "./GuildPruneResponse"; export * from "./GuildRecommendationsResponse"; export * from "./GuildVanityUrl"; export * from "./GuildVoiceRegionsResponse"; +export * from "./GuildWelcomeScreen"; export * from "./GuildWidgetJsonResponse"; export * from "./GuildWidgetSettingsResponse"; export * from "./HubDirectoryEntriesResponse"; diff --git a/src/schemas/uncategorised/ActivitySchema.ts b/src/schemas/uncategorised/ActivitySchema.ts index 4e41d6604..979139c7f 100644 --- a/src/schemas/uncategorised/ActivitySchema.ts +++ b/src/schemas/uncategorised/ActivitySchema.ts @@ -16,8 +16,8 @@ along with this program. If not, see . */ -// TODO: remove entity imports -import { Activity, ClientStatus, SetPrivateStatus, Status } from "@spacebar/util"; +import { ClientStatus, SetPrivateStatus } from "@spacebar/schemas"; +import { Activity } from "@spacebar/util"; export const ActivitySchema = { $afk: Boolean, diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index e211044be..a2f6c7748 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -17,7 +17,7 @@ */ import { ConnectedAccount, Invite, Role, Emoji, Channel, User, Sticker, UserSettings, ReadState, ThreadMember } from "@spacebar/database"; -import { Activity, Presence, IReadyGuildDTO, ReadyUserGuildSettingsEntries, ReadyPrivateChannel, GuildOrUnavailable, Snowflake, PrivateStatus } from "@spacebar/util"; +import { Activity, Presence, IReadyGuildDTO, ReadyUserGuildSettingsEntries, ReadyPrivateChannel, GuildOrUnavailable, Snowflake } from "@spacebar/util"; import { JsonValue } from "@protobuf-ts/runtime"; import { ApplicationCommand, @@ -25,6 +25,7 @@ import { Interaction, InteractionFailureReason, PartialEmoji, + PrivateStatus, PublicChannel, PublicMember, PublicMessage, diff --git a/src/util/interfaces/GuildWelcomeScreen.ts b/src/util/interfaces/GuildWelcomeScreen.ts index d4de41317..e69de29bb 100644 --- a/src/util/interfaces/GuildWelcomeScreen.ts +++ b/src/util/interfaces/GuildWelcomeScreen.ts @@ -1,10 +0,0 @@ -export interface GuildWelcomeScreen { - enabled: boolean; - description: string; - welcome_channels: { - description: string; - emoji_id?: string; - emoji_name?: string; - channel_id: string; - }[]; -} diff --git a/src/util/interfaces/Presence.ts b/src/util/interfaces/Presence.ts index 40aca8dfe..8f06850bb 100644 --- a/src/util/interfaces/Presence.ts +++ b/src/util/interfaces/Presence.ts @@ -16,9 +16,8 @@ along with this program. If not, see . */ -import { ClientStatus, Status } from "./Status"; +import { ClientStatus, Status, PublicUser } from "@spacebar/schemas"; import { Activity } from "./Activity"; -import { PublicUser } from "@spacebar/schemas"; export interface Presence { user: PublicUser; diff --git a/src/util/interfaces/index.ts b/src/util/interfaces/index.ts index 63a3dc6ca..d993cefc3 100644 --- a/src/util/interfaces/index.ts +++ b/src/util/interfaces/index.ts @@ -21,4 +21,3 @@ export * from "./ConnectedAccount"; export * from "./Event"; export * from "./GuildWelcomeScreen"; export * from "./Presence"; -export * from "./Status";