More schema work, moved most stuff from entities to schemas, though messy

This commit is contained in:
Rory& 2025-10-15 10:07:33 +02:00
parent e52c237326
commit 1632642b0d
74 changed files with 758 additions and 643 deletions

10
.idea/workspace.xml generated
View File

@ -19,8 +19,8 @@
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="TypeScript File" />
<option value="JavaScript File" />
<option value="TypeScript File" />
</list>
</option>
</component>
@ -61,7 +61,7 @@
"RunOnceActivity.git.unshallow": "true",
"javascript.nodejs.core.library.configured.version": "24.8.0",
"javascript.nodejs.core.library.typings.version": "24.7.0",
"last_opened_file_path": "/home/Rory/git/spacebar/server-master/src/schemas",
"last_opened_file_path": "/home/Rory/git/spacebar/server-master/src/schemas/api/guilds",
"node.js.detected.package.eslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
@ -86,18 +86,18 @@
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/schemas/api/guilds" />
<recent name="$PROJECT_DIR$/src/schemas" />
<recent name="$PROJECT_DIR$/src/schemas/api/users" />
<recent name="$PROJECT_DIR$/src/schemas/api/messages" />
<recent name="$PROJECT_DIR$/src/admin-api/routes/v0" />
<recent name="$PROJECT_DIR$/src" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/src/schemas/api/guilds" />
<recent name="$PROJECT_DIR$/src/schemas" />
<recent name="$PROJECT_DIR$/src/schemas/uncategorised" />
<recent name="$PROJECT_DIR$/src/schemas/gateway" />
<recent name="$PROJECT_DIR$/src/schemas/api/developers" />
<recent name="$PROJECT_DIR$/src/schemas/webrtc" />
</key>
</component>
<component name="RunDashboard">
@ -144,7 +144,7 @@
<workItem from="1735848116134" duration="80763000" />
<workItem from="1759868659577" duration="8847000" />
<workItem from="1760044946282" duration="43683000" />
<workItem from="1760402350251" duration="40134000" />
<workItem from="1760402350251" duration="46974000" />
</task>
<servers />
</component>

Binary file not shown.

Binary file not shown.

View File

@ -20,14 +20,13 @@ import { route } from "@spacebar/api";
import {
Channel,
ChannelDeleteEvent,
ChannelType,
ChannelUpdateEvent,
Recipient,
emitEvent,
handleFile,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { ChannelModifySchema } from "@spacebar/schemas"
import { ChannelModifySchema, ChannelType } from "@spacebar/schemas"
const router: Router = Router({ mergeParams: true });
// TODO: delete channel

View File

@ -17,10 +17,10 @@
*/
import { randomString, route } from "@spacebar/api";
import { Channel, Guild, Invite, InviteCreateEvent, PublicInviteRelation, User, emitEvent, isTextChannel } from "@spacebar/util";
import { Channel, Guild, Invite, InviteCreateEvent, PublicInviteRelation, User, emitEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { InviteCreateSchema } from "@spacebar/schemas"
import { InviteCreateSchema, isTextChannel } from "@spacebar/schemas"
const router: Router = Router({ mergeParams: true });

View File

@ -28,14 +28,12 @@ import {
MessageReactionRemoveAllEvent,
MessageReactionRemoveEmojiEvent,
MessageReactionRemoveEvent,
PublicMemberProjection,
PublicUserProjection,
User,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { In } from "typeorm";
import { PartialEmoji } from "@spacebar/schemas"
import { PartialEmoji, PublicMemberProjection, PublicUserProjection } from "@spacebar/schemas";
const router = Router({ mergeParams: true });
// TODO: check if emoji is really an unicode emoji or a properly encoded external emoji

View File

@ -32,7 +32,6 @@ import {
User,
emitEvent,
getPermission,
isTextChannel,
getUrlSignature,
uploadFile,
NewUrlSignatureData,
@ -43,7 +42,7 @@ import { HTTPError } from "lambert-server";
import multer from "multer";
import { FindManyOptions, FindOperator, LessThan, MoreThan, MoreThanOrEqual } from "typeorm";
import { URL } from "url";
import { MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, Reaction } from "@spacebar/schemas"
import { isTextChannel, MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, Reaction } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -16,12 +16,12 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Channel, ChannelPermissionOverwrite, ChannelPermissionOverwriteType, ChannelUpdateEvent, emitEvent, Member, Role } from "@spacebar/util";
import { Channel, ChannelUpdateEvent, emitEvent, Member, Role } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { route } from "@spacebar/api";
import { ChannelPermissionOverwriteSchema } from "@spacebar/schemas"
import { ChannelPermissionOverwriteSchema, ChannelPermissionOverwrite, ChannelPermissionOverwriteType } from "@spacebar/schemas"
const router: Router = Router({ mergeParams: true });
// TODO: Only permissions your bot has in the guild or channel can be allowed/denied (unless your bot has a MANAGE_ROLES overwrite in the channel)

View File

@ -24,12 +24,11 @@ import {
emitEvent,
getPermission,
getRights,
isTextChannel,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { Between, FindManyOptions, FindOperator, Not } from "typeorm";
import { PurgeSchema } from "@spacebar/schemas"
import { isTextChannel, PurgeSchema } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -20,15 +20,14 @@ import { route } from "@spacebar/api";
import {
Channel,
ChannelRecipientAddEvent,
ChannelType,
DiscordApiErrors,
DmChannelDTO,
emitEvent,
PublicUserProjection,
Recipient,
User,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { ChannelType, PublicUserProjection } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -23,16 +23,14 @@ import {
DiscordApiErrors,
User,
Webhook,
WebhookType,
handleFile,
isTextChannel,
trimSpecial,
ValidateName,
} from "@spacebar/util";
import crypto from "crypto";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { WebhookCreateSchema } from "@spacebar/schemas"
import { isTextChannel, WebhookCreateSchema, WebhookType } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -27,13 +27,11 @@ import {
GuildMemberUpdateEvent,
handleFile,
Member,
PublicMemberProjection,
PublicUserProjection,
Role,
Sticker,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { MemberChangeSchema } from "@spacebar/schemas"
import { MemberChangeSchema, PublicMemberProjection, PublicUserProjection } from "@spacebar/schemas";
const router = Router({ mergeParams: true });

View File

@ -17,10 +17,11 @@
*/
import { route } from "@spacebar/api";
import { Member, PublicMemberProjection } from "@spacebar/util";
import { Member } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { MoreThan } from "typeorm";
import { PublicMemberProjection } from "@spacebar/schemas";
const router = Router({ mergeParams: true });

View File

@ -22,8 +22,6 @@ import {
Member,
Snowflake,
Sticker,
StickerFormatType,
StickerType,
emitEvent,
uploadFile,
Config,
@ -32,7 +30,7 @@ import {
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import multer from "multer";
import { ModifyGuildStickerSchema } from "@spacebar/schemas"
import { ModifyGuildStickerSchema, StickerFormatType, StickerType } from "@spacebar/schemas";
const router = Router({ mergeParams: true });
router.get(

View File

@ -19,13 +19,12 @@
import { route } from "@spacebar/api";
import {
Channel,
ChannelType,
Guild,
Invite,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { VanityUrlSchema } from "@spacebar/schemas"
import { ChannelType, VanityUrlSchema } from "@spacebar/schemas";
const router = Router({ mergeParams: true });

View File

@ -19,7 +19,6 @@
import { route } from "@spacebar/api";
import {
Channel,
ChannelType,
DiscordApiErrors,
emitEvent,
getPermission,
@ -27,7 +26,7 @@ import {
VoiceStateUpdateEvent,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { VoiceStateUpdateSchema } from "@spacebar/schemas"
import { ChannelType, VoiceStateUpdateSchema } from "@spacebar/schemas";
const router = Router({ mergeParams: true });
//TODO need more testing when community guild and voice stage channel are working

View File

@ -20,9 +20,9 @@ import { route } from "@spacebar/api";
import {
Application,
DiscordApiErrors,
PublicUserProjection,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { PublicUserProjection } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -21,12 +21,10 @@ import { route } from "@spacebar/api";
import {
Team,
TeamMember,
TeamMemberRole,
TeamMemberState,
User,
} from "@spacebar/util";
import { HTTPError } from "lambert-server";
import { TeamCreateSchema } from "@spacebar/schemas"
import { TeamCreateSchema, TeamMemberRole, TeamMemberState } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -20,11 +20,11 @@ import { route } from "@spacebar/api";
import {
emitEvent,
Member,
PrivateUserProjection,
User,
UserDeleteEvent,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { PrivateUserProjection } from "@spacebar/schemas";
const router = Router({ mergeParams: true });

View File

@ -24,16 +24,13 @@ import {
FieldErrors,
handleFile,
Member,
PrivateUserProjection,
PublicUser,
PublicUserProjection,
Relationship,
User,
UserUpdateEvent,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { In } from "typeorm";
import { RelationshipType, UserProfileModifySchema } from "@spacebar/schemas"
import { PrivateUserProjection, PublicUser, PublicUserProjection, RelationshipType, UserProfileModifySchema } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -23,13 +23,12 @@ import {
FieldErrors,
generateToken,
handleFile,
PrivateUserProjection,
User,
UserUpdateEvent,
} from "@spacebar/util";
import bcrypt from "bcrypt";
import { Request, Response, Router } from "express";
import { UserModifySchema } from "@spacebar/schemas"
import { PrivateUserProjection, UserModifySchema } from "@spacebar/schemas";
const router: Router = Router({ mergeParams: true });

View File

@ -20,7 +20,6 @@ import { route } from "@spacebar/api";
import {
Config,
DiscordApiErrors,
PublicUserProjection,
Relationship,
RelationshipAddEvent,
RelationshipRemoveEvent,
@ -29,7 +28,7 @@ import {
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { RelationshipType } from "@spacebar/schemas"
import { PublicUserProjection, RelationshipType } from "@spacebar/schemas";
const router = Router({ mergeParams: true });

View File

@ -20,8 +20,6 @@ import { CLOSECODES, Capabilities, OPCODES, Payload, Send, WebSocket, setupListe
import {
Application,
Config,
DMChannel,
DefaultUserGuildSettings,
EVENTEnum,
Guild,
GuildOrUnavailable,
@ -31,9 +29,6 @@ import {
OPCodes,
PresenceUpdateEvent,
PrivateSessionProjection,
PrivateUserProjection,
PublicUser,
PublicUserProjection,
ReadState,
ReadyEventData,
ReadyGuildDTO,
@ -59,7 +54,7 @@ import {
import { check } from "./instanceOf";
import { In } from "typeorm";
import { PreloadedUserSettings } from "discord-protos";
import { IdentifySchema } from "@spacebar/schemas"
import { DefaultUserGuildSettings, DMChannel, IdentifySchema, PrivateUserProjection, PublicUser, PublicUserProjection } from "@spacebar/schemas";
// TODO: user sharding
// TODO: check privileged intents, if defined in the config

View File

@ -0,0 +1,69 @@
import { Channel, Recipient } from "@spacebar/util";
import { HTTPError } from "lambert-server";
export enum ChannelType {
GUILD_TEXT = 0, // a text channel within a guild
DM = 1, // a direct message between users
GUILD_VOICE = 2, // a voice channel within a guild
GROUP_DM = 3, // a direct message between multiple users
GUILD_CATEGORY = 4, // an organizational category that contains zero or more channels
GUILD_NEWS = 5, // a channel that users can follow and crosspost into a guild or route
GUILD_STORE = 6, // a channel in which game developers can sell their things
ENCRYPTED = 7, // end-to-end encrypted channel
ENCRYPTED_THREAD = 8, // end-to-end encrypted thread channel
TRANSACTIONAL = 9, // event chain style transactional channel
GUILD_NEWS_THREAD = 10, // a temporary sub-channel within a GUILD_NEWS channel
GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel
GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission
GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience
DIRECTORY = 14, // guild directory listing channel
GUILD_FORUM = 15, // forum composed of IM threads
TICKET_TRACKER = 33, // ticket tracker, individual ticket items shall have type 12
KANBAN = 34, // confluence like kanban board
VOICELESS_WHITEBOARD = 35, // whiteboard but without voice (whiteboard + voice is the same as stage)
CUSTOM_START = 64, // start custom channel types from here
UNHANDLED = 255, // unhandled unowned pass-through channel type
}
export interface ChannelPermissionOverwrite {
allow: string;
deny: string;
id: string;
type: ChannelPermissionOverwriteType;
}
export enum ChannelPermissionOverwriteType {
role = 0,
member = 1,
group = 2,
}
export interface DMChannel extends Omit<Channel, "type" | "recipients"> {
type: ChannelType.DM | ChannelType.GROUP_DM;
recipients: Recipient[];
}
// TODO: probably more props
export function isTextChannel(type: ChannelType): boolean {
switch (type) {
case ChannelType.GUILD_STORE:
case ChannelType.GUILD_STAGE_VOICE:
case ChannelType.GUILD_CATEGORY:
case ChannelType.GUILD_FORUM:
case ChannelType.DIRECTORY:
throw new HTTPError("not a text channel", 400);
case ChannelType.DM:
case ChannelType.GROUP_DM:
case ChannelType.GUILD_NEWS:
case ChannelType.GUILD_VOICE:
case ChannelType.GUILD_NEWS_THREAD:
case ChannelType.GUILD_PUBLIC_THREAD:
case ChannelType.GUILD_PRIVATE_THREAD:
case ChannelType.GUILD_TEXT:
case ChannelType.ENCRYPTED:
case ChannelType.ENCRYPTED_THREAD:
return true;
default:
throw new HTTPError("unimplemented", 400);
}
}

View File

@ -0,0 +1,5 @@
export enum WebhookType {
Incoming = 1,
ChannelFollower = 2,
Application = 3,
}

View File

@ -0,0 +1,19 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
*/
export * from "./Channel";
export * from "./Webhook";

View File

@ -0,0 +1,62 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 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 <https://www.gnu.org/licenses/>.
*/
export interface ApplicationCommand {
id: string;
application_id: string;
name: string;
description: string;
options?: ApplicationCommandOption[];
}
export interface ApplicationCommandOption {
type: ApplicationCommandOptionType;
name: string;
description: string;
required?: boolean;
choices?: ApplicationCommandOptionChoice[];
options?: ApplicationCommandOption[];
}
export interface ApplicationCommandOptionChoice {
name: string;
value: string | number;
}
export enum ApplicationCommandOptionType {
SUB_COMMAND = 1,
SUB_COMMAND_GROUP = 2,
STRING = 3,
INTEGER = 4,
BOOLEAN = 5,
USER = 6,
CHANNEL = 7,
ROLE = 8,
}
export interface ApplicationCommandInteractionData {
id: string;
name: string;
options?: ApplicationCommandInteractionDataOption[];
}
export interface ApplicationCommandInteractionDataOption {
name: string;
value?: unknown;
options?: ApplicationCommandInteractionDataOption[];
}

View File

@ -0,0 +1,9 @@
export enum TeamMemberState {
INVITED = 1,
ACCEPTED = 2,
}
export enum TeamMemberRole {
ADMIN = "admin",
DEVELOPER = "developer",
READ_ONLY = "read_only",
}

View File

@ -15,5 +15,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export * from "./Application";
export * from "./ApplicationCreateSchema";
export * from "./ApplicationModifySchema";
export * from "./Team";

View File

@ -0,0 +1,172 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
*/
import { ChannelPermissionOverwrite } from "@spacebar/schemas";
export enum AuditLogEvents {
// guild level
GUILD_UPDATE = 1,
GUILD_IMPORT = 2,
GUILD_EXPORTED = 3,
GUILD_ARCHIVE = 4,
GUILD_UNARCHIVE = 5,
// join-leave
USER_JOIN = 6,
USER_LEAVE = 7,
// channels
CHANNEL_CREATE = 10,
CHANNEL_UPDATE = 11,
CHANNEL_DELETE = 12,
// permission overrides
CHANNEL_OVERWRITE_CREATE = 13,
CHANNEL_OVERWRITE_UPDATE = 14,
CHANNEL_OVERWRITE_DELETE = 15,
// kick and ban
MEMBER_KICK = 20,
MEMBER_PRUNE = 21,
MEMBER_BAN_ADD = 22,
MEMBER_BAN_REMOVE = 23,
// member updates
MEMBER_UPDATE = 24,
MEMBER_ROLE_UPDATE = 25,
MEMBER_MOVE = 26,
MEMBER_DISCONNECT = 27,
BOT_ADD = 28,
// roles
ROLE_CREATE = 30,
ROLE_UPDATE = 31,
ROLE_DELETE = 32,
ROLE_SWAP = 33,
// invites
INVITE_CREATE = 40,
INVITE_UPDATE = 41,
INVITE_DELETE = 42,
// webhooks
WEBHOOK_CREATE = 50,
WEBHOOK_UPDATE = 51,
WEBHOOK_DELETE = 52,
WEBHOOK_SWAP = 53,
// custom emojis
EMOJI_CREATE = 60,
EMOJI_UPDATE = 61,
EMOJI_DELETE = 62,
EMOJI_SWAP = 63,
// deletion
MESSAGE_CREATE = 70, // messages sent using non-primary seat of the user only
MESSAGE_EDIT = 71, // non-self edits only
MESSAGE_DELETE = 72,
MESSAGE_BULK_DELETE = 73,
// pinning
MESSAGE_PIN = 74,
MESSAGE_UNPIN = 75,
// integrations
INTEGRATION_CREATE = 80,
INTEGRATION_UPDATE = 81,
INTEGRATION_DELETE = 82,
// stage actions
STAGE_INSTANCE_CREATE = 83,
STAGE_INSTANCE_UPDATE = 84,
STAGE_INSTANCE_DELETE = 85,
// stickers
STICKER_CREATE = 90,
STICKER_UPDATE = 91,
STICKER_DELETE = 92,
STICKER_SWAP = 93,
// threads
THREAD_CREATE = 110,
THREAD_UPDATE = 111,
THREAD_DELETE = 112,
// application commands
APPLICATION_COMMAND_PERMISSION_UPDATE = 121,
// automod
POLICY_CREATE = 140,
POLICY_UPDATE = 141,
POLICY_DELETE = 142,
MESSAGE_BLOCKED_BY_POLICIES = 143, // in spacebar, blocked messages are stealth-dropped
// instance policies affecting the guild
GUILD_AFFECTED_BY_POLICIES = 216,
// message moves
IN_GUILD_MESSAGE_MOVE = 223,
CROSS_GUILD_MESSAGE_MOVE = 224,
// message routing
ROUTE_CREATE = 225,
ROUTE_UPDATE = 226,
}
export interface AuditLogChange {
new_value?: AuditLogChangeValue;
old_value?: AuditLogChangeValue;
key: string;
}
export interface AuditLogChangeValue {
name?: string;
description?: string;
icon_hash?: string;
splash_hash?: string;
discovery_splash_hash?: string;
banner_hash?: string;
owner_id?: string;
region?: string;
preferred_locale?: string;
afk_channel_id?: string;
afk_timeout?: number;
rules_channel_id?: string;
public_updates_channel_id?: string;
mfa_level?: number;
verification_level?: number;
explicit_content_filter?: number;
default_message_notifications?: number;
vanity_url_code?: string;
$add?: object[]; // TODO: These types are bad.
$remove?: object[];
prune_delete_days?: number;
widget_enabled?: boolean;
widget_channel_id?: string;
system_channel_id?: string;
position?: number;
topic?: string;
bitrate?: number;
permission_overwrites?: ChannelPermissionOverwrite[];
nsfw?: boolean;
application_id?: string;
rate_limit_per_user?: number;
permissions?: string;
color?: number;
hoist?: boolean;
mentionable?: boolean;
allow?: string;
deny?: string;
code?: string;
channel_id?: string;
inviter_id?: string;
max_uses?: number;
uses?: number;
max_age?: number;
temporary?: boolean;
deaf?: boolean;
mute?: boolean;
nick?: string;
avatar_hash?: string;
id?: string;
type?: number;
enable_emoticons?: boolean;
expire_behavior?: number;
expire_grace_period?: number;
user_limit?: number;
}

View File

@ -0,0 +1,37 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
*/
export type AutomodRuleTriggerMetadata = AutomodMentionSpamRule | AutomodSuspectedSpamRule | AutomodCommonlyFlaggedWordsRule | AutomodCustomWordsRule;
export class AutomodMentionSpamRule {
mention_total_limit: number;
mention_raid_protection_enabled: boolean;
}
export class AutomodSuspectedSpamRule {}
export class AutomodCommonlyFlaggedWordsRule {
allow_list: [string];
presets: [number];
}
export class AutomodCustomWordsRule {
allow_list: [string];
keyword_filter: [string];
regex_patterns: [string];
}

View File

@ -0,0 +1,24 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
*/
import { Snowflake } from "@spacebar/schemas";
export interface GuildAvailableSchema {
id: Snowflake;
available: boolean;
}

View File

@ -0,0 +1,13 @@
export class RoleColors {
primary_color: number;
secondary_color: number | undefined; // only used for "holographic" and "gradient" styles
tertiary_color?: number | undefined; // only used for "holographic" style
toJSON(): RoleColors {
return {
...this,
secondary_color: this.secondary_color ?? undefined,
tertiary_color: this.tertiary_color ?? undefined,
};
}
}

View File

@ -0,0 +1,11 @@
export enum StickerType {
STANDARD = 1,
GUILD = 2,
}
export enum StickerFormatType {
GIF = 0, // gif is a custom format type and not in discord spec
PNG = 1,
APNG = 2,
LOTTIE = 3,
}

View File

@ -0,0 +1,24 @@
import { VoiceState } from "@spacebar/util";
export enum PublicVoiceStateEnum {
user_id,
suppress,
session_id,
self_video,
self_mute,
self_deaf,
self_stream,
request_to_speak_timestamp,
mute,
deaf,
channel_id,
guild_id,
}
export type PublicVoiceStateKeys = keyof typeof PublicVoiceStateEnum;
export const PublicVoiceStateProjection = Object.values(
PublicVoiceStateEnum,
).filter((x) => typeof x === "string") as PublicVoiceStateKeys[];
export type PublicVoiceState = Pick<VoiceState, PublicVoiceStateKeys>;

View File

@ -0,0 +1,24 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
*/
export * from "./AuditLog";
export * from "./Automod";
export * from "./GuildProfileResponse";
export * from "./GuildSchema";
export * from "./Role";
export * from "./Sticker";
export * from "./VoiceState";

View File

@ -15,6 +15,8 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export * from "./channels";
export * from "./developers";
export * from "./guilds";
export * from "./messages";
export * from "./users";

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PartialUser } from "@spacebar/schemas";
import { PartialUser, Snowflake } from "@spacebar/schemas";
export enum MessageType {
DEFAULT = 0,
@ -65,7 +65,7 @@ export type PartialMessage = Pick<Message, "id">
*/
export interface PartialMessage {
id: string;
id: Snowflake;
channel_id: string;
type: MessageType;
content: string;
@ -80,7 +80,7 @@ export interface Reaction {
count: number;
//// not saved in the database // me: boolean; // whether the current user reacted using this emoji
emoji: PartialEmoji;
user_ids: string[];
user_ids: Snowflake[];
}
export interface PartialEmoji {
@ -91,7 +91,7 @@ export interface PartialEmoji {
export interface AllowedMentions {
parse?: ("users" | "roles" | "everyone")[];
roles?: string[];
users?: string[];
roles?: Snowflake[];
users?: Snowflake[];
replied_user?: boolean;
}

View File

@ -0,0 +1,6 @@
import { ConnectedAccount } from "@spacebar/util";
export type PublicConnectedAccount = Pick<
ConnectedAccount,
"name" | "type" | "verified"
>;

View File

@ -0,0 +1,85 @@
import { PublicUser } from "@spacebar/schemas";
import { Member } from "@spacebar/util";
export interface ChannelOverride {
message_notifications: number;
mute_config: MuteConfig;
muted: boolean;
channel_id: string | null;
}
export interface UserGuildSettings {
// channel_overrides: {
// channel_id: string;
// message_notifications: number;
// mute_config: MuteConfig;
// muted: boolean;
// }[];
channel_overrides: {
[channel_id: string]: ChannelOverride;
} | null;
message_notifications: number;
mobile_push: boolean;
mute_config: MuteConfig | null;
muted: boolean;
suppress_everyone: boolean;
suppress_roles: boolean;
version: number;
guild_id: string | null;
flags: number;
mute_scheduled_events: boolean;
hide_muted_channels: boolean;
notify_highlights: 0;
}
export const DefaultUserGuildSettings: UserGuildSettings = {
channel_overrides: null,
message_notifications: 1,
flags: 0,
hide_muted_channels: false,
mobile_push: true,
mute_config: null,
mute_scheduled_events: false,
muted: false,
notify_highlights: 0,
suppress_everyone: false,
suppress_roles: false,
version: 453, // ?
guild_id: null,
};
export interface MuteConfig {
end_time: number;
selected_time_window: number;
}
export type PublicMemberKeys =
| "id"
| "guild_id"
| "nick"
| "roles"
| "joined_at"
| "pending"
| "deaf"
| "mute"
| "premium_since"
| "avatar";
export const PublicMemberProjection: PublicMemberKeys[] = [
"id",
"guild_id",
"nick",
"roles",
"joined_at",
"pending",
"deaf",
"mute",
"premium_since",
"avatar",
];
export type PublicMember = Omit<Pick<Member, PublicMemberKeys>, "roles"> & {
user: PublicUser;
roles: string[]; // only role ids not objects
};

View File

@ -16,7 +16,49 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Snowflake } from "@spacebar/schemas";
import { ConnectedAccountSchema, Snowflake, UserSettingsSchema } from "@spacebar/schemas";
import { BitField } from "@spacebar/util/util";
import { Relationship, Session } from "@spacebar/util/entities";
interface UserEntityPleaseRewriteThankYou {
id: Snowflake;
username: string;
discriminator: string;
avatar?: string;
accent_color?: number;
banner?: string;
theme_colors?: number[];
pronouns?: string;
phone?: string;
desktop: boolean;
mobile: boolean;
premium: boolean;
premium_type: number;
bot: boolean;
bio: string;
system: boolean;
nsfw_allowed: boolean;
mfa_enabled: boolean;
webauthn_enabled: boolean;
created_at: Date;
premium_since: Date;
verified: boolean;
disabled: boolean;
deleted: boolean;
email?: string;
flags: number;
public_flags: number;
purchased_flags: number;
premium_usage_flags: number;
rights: string;
sessions: Session[];
relationships: Relationship[];
connected_accounts: ConnectedAccountSchema[];
fingerprints: string[];
settings?: UserSettingsSchema;
extended_settings: string;
badge_ids?: string[];
}
export interface PartialUser {
id: Snowflake;
@ -66,3 +108,77 @@ export interface PrimaryGuild {
badge: string | null;
}
export enum PublicUserEnum {
username,
discriminator,
id,
public_flags,
avatar,
accent_color,
banner,
bio,
bot,
premium_since,
premium_type,
theme_colors,
pronouns,
badge_ids,
}
export type PublicUserKeys = keyof typeof PublicUserEnum;
export enum PrivateUserEnum {
flags,
mfa_enabled,
email,
phone,
verified,
nsfw_allowed,
premium,
premium_type,
purchased_flags,
premium_usage_flags,
disabled,
settings,
// locale
}
export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;
export const PublicUserProjection = Object.values(PublicUserEnum).filter((x) => typeof x === "string") as PublicUserKeys[];
export const PrivateUserProjection = [...PublicUserProjection, ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string")] as PrivateUserKeys[];
// Private user data that should never get sent to the client
export type PublicUser = Pick<UserEntityPleaseRewriteThankYou, PublicUserKeys>;
export type PrivateUser = Pick<UserEntityPleaseRewriteThankYou, PrivateUserKeys>;
export interface UserPrivate extends Pick<UserEntityPleaseRewriteThankYou, PrivateUserKeys> {
locale: string;
}
export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);
// This causes a failure in openapi.js...?
export class UserFlags extends BitField {
static FLAGS = {
DISCORD_EMPLOYEE: BigInt(1) << BigInt(0),
PARTNERED_SERVER_OWNER: BigInt(1) << BigInt(1),
HYPESQUAD_EVENTS: BigInt(1) << BigInt(2),
BUGHUNTER_LEVEL_1: BigInt(1) << BigInt(3),
MFA_SMS: BigInt(1) << BigInt(4),
PREMIUM_PROMO_DISMISSED: BigInt(1) << BigInt(5),
HOUSE_BRAVERY: BigInt(1) << BigInt(6),
HOUSE_BRILLIANCE: BigInt(1) << BigInt(7),
HOUSE_BALANCE: BigInt(1) << BigInt(8),
EARLY_SUPPORTER: BigInt(1) << BigInt(9),
TEAM_USER: BigInt(1) << BigInt(10),
TRUST_AND_SAFETY: BigInt(1) << BigInt(11),
SYSTEM: BigInt(1) << BigInt(12),
HAS_UNREAD_URGENT_MESSAGES: BigInt(1) << BigInt(13),
BUGHUNTER_LEVEL_2: BigInt(1) << BigInt(14),
UNDERAGE_DELETED: BigInt(1) << BigInt(15),
VERIFIED_BOT: BigInt(1) << BigInt(16),
EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17),
CERTIFIED_MODERATOR: BigInt(1) << BigInt(18),
BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19),
};
}

View File

@ -15,4 +15,6 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export * from "./ConnectedAccount";
export * from "./Member";
export * from "./User";

View File

@ -20,5 +20,6 @@ export * from "./gateway";
export * from "./responses";
export * from "./uncategorised";
export * from "./webrtc";
export * from "./HelperTypes";
export * from "./Identifiers";
export * from "./Validator";

View File

@ -16,9 +16,9 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { StringStringDictionary } from "../../util/util";
// TODO: Clean up
import { StringStringDictionary } from "@spacebar/schemas";
export type CollectiblesCategoriesResponse = CollectiblesCategoryItem[];
export interface CollectiblesCategoryStyle {

View File

@ -17,7 +17,7 @@
*/
export interface GuildDiscoveryRequirementsResponse {
uild_id: string;
guild_id: string;
safe_environment: boolean;
healthy: boolean;
health_score_pending: boolean;

View File

@ -18,10 +18,9 @@
import {
Attachment,
PublicUser,
Role,
} from "../../util/entities";
import { ActionRowComponent, Embed, MessageType, Poll } from "@spacebar/schemas";
import { ActionRowComponent, Embed, MessageType, Poll, PublicUser } from "@spacebar/schemas";
export interface GuildMessagesSearchMessage {
id: string;

View File

@ -18,41 +18,15 @@
import { GeneralConfiguration, LimitsConfiguration } from "../../util/config";
import { DmChannelDTO } from "../../util/dtos";
import {
Application,
BackupCode,
Categories,
Channel,
Emoji,
Guild,
Invite,
Member,
Message,
PrivateUser,
PublicMember,
PublicUser,
Role,
Sticker,
StickerPack,
Template,
Webhook,
} from "../../util/entities";
import { Application, BackupCode, Categories, Channel, Emoji, Guild, Invite, Member, Message, Role, Sticker, StickerPack, Template, Webhook } from "../../util/entities";
import { GuildVoiceRegion } from "./GuildVoiceRegionsResponse";
import { GuildBansResponse, GuildCreateResponse } from "@spacebar/schemas"
import { GuildBansResponse, GuildCreateResponse, PrivateUser, PublicMember, PublicUser } from "@spacebar/schemas";
// removes internal properties from the guild class
export type APIGuild = Omit<
Guild,
| "afk_channel"
| "template"
| "owner"
| "public_updates_channel"
| "rules_channel"
| "system_channel"
| "widget_channel"
>;
export type APIGuild = Omit<Guild, "afk_channel" | "template" | "owner" | "public_updates_channel" | "rules_channel" | "system_channel" | "widget_channel">;
export type APIPublicUser = PublicUser;
export type APIPrivateUser = PrivateUser;
export type APIGuildArray = APIGuild[];

View File

@ -19,11 +19,13 @@
import {
Badge,
Member,
User,
} from "@spacebar/util";
import {
PublicConnectedAccount,
PublicMember,
PublicUser,
User,
} from "@spacebar/util";
} from "@spacebar/schemas";
export type MutualGuild = {
id: string;

View File

@ -15,8 +15,8 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { PublicUser } from "../../util/entities";
import { RelationshipType } from "@spacebar/schemas"
import { PublicUser, RelationshipType } from "@spacebar/schemas"
export interface UserRelationshipsResponse {
id: string;

View File

@ -35,7 +35,6 @@ export * from "./GuildBansResponse";
export * from "./GuildCreateResponse";
export * from "./GuildDiscoveryRequirements";
export * from "./GuildMessagesSearchResponse";
export * from "./GuildProfileResponse";
export * from "./GuildPruneResponse";
export * from "./GuildRecommendationsResponse";
export * from "./GuildVanityUrl";

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ChannelPermissionOverwriteType, ChannelType } from "@spacebar/util";
import { ChannelPermissionOverwriteType, ChannelType } from "@spacebar/schemas";
export interface ChannelModifySchema {
/**

View File

@ -16,6 +16,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ChannelPermissionOverwrite } from "@spacebar/util";
import { ChannelPermissionOverwrite } from "@spacebar/schemas";
export type ChannelPermissionOverwriteSchema = ChannelPermissionOverwrite;

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { ChannelOverride, UserGuildSettings } from "@spacebar/util";
import { ChannelOverride, UserGuildSettings } from "@spacebar/schemas";
// This sucks. I would use a DeepPartial, my own or typeorms, but they both generate inncorect schema
export interface UserGuildSettingsSchema

View File

@ -17,7 +17,8 @@
*/
import { MinimalPublicUserDTO } from "./UserDTO";
import { Channel, PublicUserProjection, User } from "../entities";
import { Channel, User } from "../entities";
import { PublicUserProjection } from "@spacebar/schemas";
export class DmChannelDTO {
icon: string | null;

View File

@ -18,16 +18,12 @@
import {
Channel,
ChannelOverride,
ChannelType,
Emoji,
Guild,
PublicUser,
Role,
Sticker,
UserGuildSettings,
PublicMember,
} from "../entities";
import { ChannelOverride, ChannelType, PublicMember, PublicUser, UserGuildSettings } from "@spacebar/schemas";
// TODO: this is not the best place for this type
export type ReadyUserGuildSettingsEntries = Omit<

View File

@ -16,14 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToOne,
RelationId,
} from "typeorm";
import { Column, Entity, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Team } from "./Team";
import { User } from "./User";
@ -143,48 +136,3 @@ export class Application extends BaseClass {
})
team?: Team;
}
export interface ApplicationCommand {
id: string;
application_id: string;
name: string;
description: string;
options?: ApplicationCommandOption[];
}
export interface ApplicationCommandOption {
type: ApplicationCommandOptionType;
name: string;
description: string;
required?: boolean;
choices?: ApplicationCommandOptionChoice[];
options?: ApplicationCommandOption[];
}
export interface ApplicationCommandOptionChoice {
name: string;
value: string | number;
}
export enum ApplicationCommandOptionType {
SUB_COMMAND = 1,
SUB_COMMAND_GROUP = 2,
STRING = 3,
INTEGER = 4,
BOOLEAN = 5,
USER = 6,
CHANNEL = 7,
ROLE = 8,
}
export interface ApplicationCommandInteractionData {
id: string;
name: string;
options?: ApplicationCommandInteractionDataOption[];
}
export interface ApplicationCommandInteractionDataOption {
name: string;
value?: unknown;
options?: ApplicationCommandInteractionDataOption[];
}

View File

@ -18,98 +18,8 @@
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { ChannelPermissionOverwrite } from "./Channel";
import { User } from "./User";
export enum AuditLogEvents {
// guild level
GUILD_UPDATE = 1,
GUILD_IMPORT = 2,
GUILD_EXPORTED = 3,
GUILD_ARCHIVE = 4,
GUILD_UNARCHIVE = 5,
// join-leave
USER_JOIN = 6,
USER_LEAVE = 7,
// channels
CHANNEL_CREATE = 10,
CHANNEL_UPDATE = 11,
CHANNEL_DELETE = 12,
// permission overrides
CHANNEL_OVERWRITE_CREATE = 13,
CHANNEL_OVERWRITE_UPDATE = 14,
CHANNEL_OVERWRITE_DELETE = 15,
// kick and ban
MEMBER_KICK = 20,
MEMBER_PRUNE = 21,
MEMBER_BAN_ADD = 22,
MEMBER_BAN_REMOVE = 23,
// member updates
MEMBER_UPDATE = 24,
MEMBER_ROLE_UPDATE = 25,
MEMBER_MOVE = 26,
MEMBER_DISCONNECT = 27,
BOT_ADD = 28,
// roles
ROLE_CREATE = 30,
ROLE_UPDATE = 31,
ROLE_DELETE = 32,
ROLE_SWAP = 33,
// invites
INVITE_CREATE = 40,
INVITE_UPDATE = 41,
INVITE_DELETE = 42,
// webhooks
WEBHOOK_CREATE = 50,
WEBHOOK_UPDATE = 51,
WEBHOOK_DELETE = 52,
WEBHOOK_SWAP = 53,
// custom emojis
EMOJI_CREATE = 60,
EMOJI_UPDATE = 61,
EMOJI_DELETE = 62,
EMOJI_SWAP = 63,
// deletion
MESSAGE_CREATE = 70, // messages sent using non-primary seat of the user only
MESSAGE_EDIT = 71, // non-self edits only
MESSAGE_DELETE = 72,
MESSAGE_BULK_DELETE = 73,
// pinning
MESSAGE_PIN = 74,
MESSAGE_UNPIN = 75,
// integrations
INTEGRATION_CREATE = 80,
INTEGRATION_UPDATE = 81,
INTEGRATION_DELETE = 82,
// stage actions
STAGE_INSTANCE_CREATE = 83,
STAGE_INSTANCE_UPDATE = 84,
STAGE_INSTANCE_DELETE = 85,
// stickers
STICKER_CREATE = 90,
STICKER_UPDATE = 91,
STICKER_DELETE = 92,
STICKER_SWAP = 93,
// threads
THREAD_CREATE = 110,
THREAD_UPDATE = 111,
THREAD_DELETE = 112,
// application commands
APPLICATION_COMMAND_PERMISSION_UPDATE = 121,
// automod
POLICY_CREATE = 140,
POLICY_UPDATE = 141,
POLICY_DELETE = 142,
MESSAGE_BLOCKED_BY_POLICIES = 143, // in spacebar, blocked messages are stealth-dropped
// instance policies affecting the guild
GUILD_AFFECTED_BY_POLICIES = 216,
// message moves
IN_GUILD_MESSAGE_MOVE = 223,
CROSS_GUILD_MESSAGE_MOVE = 224,
// message routing
ROUTE_CREATE = 225,
ROUTE_UPDATE = 226,
}
import { AuditLogChange, AuditLogEvents } from "@spacebar/schemas";
@Entity({
name: "audit_logs",
@ -150,65 +60,3 @@ export class AuditLog extends BaseClass {
reason?: string;
}
export interface AuditLogChange {
new_value?: AuditLogChangeValue;
old_value?: AuditLogChangeValue;
key: string;
}
export interface AuditLogChangeValue {
name?: string;
description?: string;
icon_hash?: string;
splash_hash?: string;
discovery_splash_hash?: string;
banner_hash?: string;
owner_id?: string;
region?: string;
preferred_locale?: string;
afk_channel_id?: string;
afk_timeout?: number;
rules_channel_id?: string;
public_updates_channel_id?: string;
mfa_level?: number;
verification_level?: number;
explicit_content_filter?: number;
default_message_notifications?: number;
vanity_url_code?: string;
$add?: object[]; // TODO: These types are bad.
$remove?: object[];
prune_delete_days?: number;
widget_enabled?: boolean;
widget_channel_id?: string;
system_channel_id?: string;
position?: number;
topic?: string;
bitrate?: number;
permission_overwrites?: ChannelPermissionOverwrite[];
nsfw?: boolean;
application_id?: string;
rate_limit_per_user?: number;
permissions?: string;
color?: number;
hoist?: boolean;
mentionable?: boolean;
allow?: string;
deny?: string;
code?: string;
channel_id?: string;
inviter_id?: string;
max_uses?: number;
uses?: number;
max_age?: number;
temporary?: boolean;
deaf?: boolean;
mute?: boolean;
nick?: string;
avatar_hash?: string;
id?: string;
type?: number;
enable_emoticons?: boolean;
expire_behavior?: number;
expire_grace_period?: number;
user_limit?: number;
}

View File

@ -19,24 +19,7 @@
import { BaseClass } from "./BaseClass";
import { Entity, JoinColumn, ManyToOne, Column } from "typeorm";
import { User } from "./User";
export class AutomodMentionSpamRule {
mention_total_limit: number;
mention_raid_protection_enabled: boolean;
}
export class AutomodSuspectedSpamRule {}
export class AutomodCommonlyFlaggedWordsRule {
allow_list: [string];
presets: [number];
}
export class AutomodCustomWordsRule {
allow_list: [string];
keyword_filter: [string];
regex_patterns: [string];
}
import { AutomodRuleTriggerMetadata } from "@spacebar/schemas";
@Entity({
name: "automod_rules",
@ -75,10 +58,7 @@ export class AutomodRule extends BaseClass {
nullable: true,
})
trigger_metadata?: // this is null for "Block suspected spam content"
| AutomodMentionSpamRule
| AutomodSuspectedSpamRule
| AutomodCommonlyFlaggedWordsRule
| AutomodCustomWordsRule;
| AutomodRuleTriggerMetadata;
@Column({
type: "simple-json",

View File

@ -34,34 +34,11 @@ import { Invite } from "./Invite";
import { Message } from "./Message";
import { ReadState } from "./ReadState";
import { Recipient } from "./Recipient";
import { PublicUserProjection, User } from "./User";
import { User } from "./User";
import { VoiceState } from "./VoiceState";
import { Webhook } from "./Webhook";
import { Member } from "./Member";
export enum ChannelType {
GUILD_TEXT = 0, // a text channel within a guild
DM = 1, // a direct message between users
GUILD_VOICE = 2, // a voice channel within a guild
GROUP_DM = 3, // a direct message between multiple users
GUILD_CATEGORY = 4, // an organizational category that contains zero or more channels
GUILD_NEWS = 5, // a channel that users can follow and crosspost into a guild or route
GUILD_STORE = 6, // a channel in which game developers can sell their things
ENCRYPTED = 7, // end-to-end encrypted channel
ENCRYPTED_THREAD = 8, // end-to-end encrypted thread channel
TRANSACTIONAL = 9, // event chain style transactional channel
GUILD_NEWS_THREAD = 10, // a temporary sub-channel within a GUILD_NEWS channel
GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel
GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission
GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience
DIRECTORY = 14, // guild directory listing channel
GUILD_FORUM = 15, // forum composed of IM threads
TICKET_TRACKER = 33, // ticket tracker, individual ticket items shall have type 12
KANBAN = 34, // confluence like kanban board
VOICELESS_WHITEBOARD = 35, // whiteboard but without voice (whiteboard + voice is the same as stage)
CUSTOM_START = 64, // start custom channel types from here
UNHANDLED = 255, // unhandled unowned pass-through channel type
}
import { ChannelPermissionOverwrite, ChannelPermissionOverwriteType, ChannelType, PublicUserProjection } from "@spacebar/schemas";
@Entity({
name: "channels",
@ -613,46 +590,3 @@ export class Channel extends BaseClass {
};
}
}
export interface ChannelPermissionOverwrite {
allow: string;
deny: string;
id: string;
type: ChannelPermissionOverwriteType;
}
export enum ChannelPermissionOverwriteType {
role = 0,
member = 1,
group = 2,
}
export interface DMChannel extends Omit<Channel, "type" | "recipients"> {
type: ChannelType.DM | ChannelType.GROUP_DM;
recipients: Recipient[];
}
// TODO: probably more props
export function isTextChannel(type: ChannelType): boolean {
switch (type) {
case ChannelType.GUILD_STORE:
case ChannelType.GUILD_STAGE_VOICE:
case ChannelType.GUILD_CATEGORY:
case ChannelType.GUILD_FORUM:
case ChannelType.DIRECTORY:
throw new HTTPError("not a text channel", 400);
case ChannelType.DM:
case ChannelType.GROUP_DM:
case ChannelType.GUILD_NEWS:
case ChannelType.GUILD_VOICE:
case ChannelType.GUILD_NEWS_THREAD:
case ChannelType.GUILD_PUBLIC_THREAD:
case ChannelType.GUILD_PRIVATE_THREAD:
case ChannelType.GUILD_TEXT:
case ChannelType.ENCRYPTED:
case ChannelType.ENCRYPTED_THREAD:
return true;
default:
throw new HTTPError("unimplemented", 400);
}
}

View File

@ -21,11 +21,6 @@ import { ConnectedAccountTokenData } from "../interfaces";
import { BaseClass } from "./BaseClass";
import { User } from "./User";
export type PublicConnectedAccount = Pick<
ConnectedAccount,
"name" | "type" | "verified"
>;
@Entity({
name: "connected_accounts",
})

View File

@ -47,7 +47,8 @@ import { BaseClassWithoutId } from "./BaseClass";
import { Guild } from "./Guild";
import { Message } from "./Message";
import { Role } from "./Role";
import { PublicUser, User } from "./User";
import { User } from "./User";
import { PublicMember, PublicMemberProjection, UserGuildSettings } from "@spacebar/schemas";
export const MemberPrivateProjection: (keyof Member)[] = [
"id",
@ -472,86 +473,3 @@ export class Member extends BaseClassWithoutId {
return member as PublicMember;
}
}
export interface ChannelOverride {
message_notifications: number;
mute_config: MuteConfig;
muted: boolean;
channel_id: string | null;
}
export interface UserGuildSettings {
// channel_overrides: {
// channel_id: string;
// message_notifications: number;
// mute_config: MuteConfig;
// muted: boolean;
// }[];
channel_overrides: {
[channel_id: string]: ChannelOverride;
} | null;
message_notifications: number;
mobile_push: boolean;
mute_config: MuteConfig | null;
muted: boolean;
suppress_everyone: boolean;
suppress_roles: boolean;
version: number;
guild_id: string | null;
flags: number;
mute_scheduled_events: boolean;
hide_muted_channels: boolean;
notify_highlights: 0;
}
export const DefaultUserGuildSettings: UserGuildSettings = {
channel_overrides: null,
message_notifications: 1,
flags: 0,
hide_muted_channels: false,
mobile_push: true,
mute_config: null,
mute_scheduled_events: false,
muted: false,
notify_highlights: 0,
suppress_everyone: false,
suppress_roles: false,
version: 453, // ?
guild_id: null,
};
export interface MuteConfig {
end_time: number;
selected_time_window: number;
}
export type PublicMemberKeys =
| "id"
| "guild_id"
| "nick"
| "roles"
| "joined_at"
| "pending"
| "deaf"
| "mute"
| "premium_since"
| "avatar";
export const PublicMemberProjection: PublicMemberKeys[] = [
"id",
"guild_id",
"nick",
"roles",
"joined_at",
"pending",
"deaf",
"mute",
"premium_since",
"avatar",
];
export type PublicMember = Omit<Pick<Member, PublicMemberKeys>, "roles"> & {
user: PublicUser;
roles: string[]; // only role ids not objects
};

View File

@ -20,20 +20,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
export class RoleColors {
primary_color: number;
secondary_color: number | undefined; // only used for "holographic" and "gradient" styles
tertiary_color?: number | undefined; // only used for "holographic" style
toJSON(): RoleColors {
return {
...this,
secondary_color: this.secondary_color ?? undefined,
tertiary_color: this.tertiary_color ?? undefined,
};
}
}
import { RoleColors } from "@spacebar/schemas";
@Entity({
name: "roles",

View File

@ -20,18 +20,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { Guild } from "./Guild";
import { User } from "./User";
export enum StickerType {
STANDARD = 1,
GUILD = 2,
}
export enum StickerFormatType {
GIF = 0, // gif is a custom format type and not in discord spec
PNG = 1,
APNG = 2,
LOTTIE = 3,
}
import { StickerFormatType, StickerType } from "@spacebar/schemas";
@Entity({
name: "stickers",

View File

@ -19,16 +19,7 @@
import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm";
import { BaseClass } from "./BaseClass";
import { User } from "./User";
export enum TeamMemberState {
INVITED = 1,
ACCEPTED = 2,
}
export enum TeamMemberRole {
ADMIN = "admin",
DEVELOPER = "developer",
READ_ONLY = "read_only",
}
import { TeamMemberRole, TeamMemberState } from "@spacebar/schemas";
@Entity({
name: "team_members",

View File

@ -18,8 +18,8 @@
import { Request } from "express";
import { Column, Entity, FindOneOptions, JoinColumn, OneToMany, OneToOne } from "typeorm";
import { Channel, ChannelType, Config, Email, FieldErrors, Snowflake, trimSpecial } from "..";
import { BitField, Random } from "../util";
import { Channel, Config, Email, FieldErrors, Snowflake, trimSpecial } from "..";
import { Random } from "../util";
import { BaseClass } from "./BaseClass";
import { ConnectedAccount } from "./ConnectedAccount";
import { Member } from "./Member";
@ -27,52 +27,7 @@ import { Relationship } from "./Relationship";
import { SecurityKey } from "./SecurityKey";
import { Session } from "./Session";
import { UserSettings } from "./UserSettings";
export enum PublicUserEnum {
username,
discriminator,
id,
public_flags,
avatar,
accent_color,
banner,
bio,
bot,
premium_since,
premium_type,
theme_colors,
pronouns,
badge_ids,
}
export type PublicUserKeys = keyof typeof PublicUserEnum;
export enum PrivateUserEnum {
flags,
mfa_enabled,
email,
phone,
verified,
nsfw_allowed,
premium,
premium_type,
purchased_flags,
premium_usage_flags,
disabled,
settings,
// locale
}
export type PrivateUserKeys = keyof typeof PrivateUserEnum | PublicUserKeys;
export const PublicUserProjection = Object.values(PublicUserEnum).filter((x) => typeof x === "string") as PublicUserKeys[];
export const PrivateUserProjection = [...PublicUserProjection, ...Object.values(PrivateUserEnum).filter((x) => typeof x === "string")] as PrivateUserKeys[];
// Private user data that should never get sent to the client
export type PublicUser = Pick<User, PublicUserKeys>;
export type PrivateUser = Pick<User, PrivateUserKeys>;
export interface UserPrivate extends Pick<User, PrivateUserKeys> {
locale: string;
}
import { ChannelType, PrivateUserProjection, PublicUser, PublicUserProjection, UserPrivate } from "@spacebar/schemas";
@Entity({
name: "users",
@ -396,30 +351,3 @@ export class User extends BaseClass {
return qry.single((_) => true);
}
}
export const CUSTOM_USER_FLAG_OFFSET = BigInt(1) << BigInt(32);
export class UserFlags extends BitField {
static FLAGS = {
DISCORD_EMPLOYEE: BigInt(1) << BigInt(0),
PARTNERED_SERVER_OWNER: BigInt(1) << BigInt(1),
HYPESQUAD_EVENTS: BigInt(1) << BigInt(2),
BUGHUNTER_LEVEL_1: BigInt(1) << BigInt(3),
MFA_SMS: BigInt(1) << BigInt(4),
PREMIUM_PROMO_DISMISSED: BigInt(1) << BigInt(5),
HOUSE_BRAVERY: BigInt(1) << BigInt(6),
HOUSE_BRILLIANCE: BigInt(1) << BigInt(7),
HOUSE_BALANCE: BigInt(1) << BigInt(8),
EARLY_SUPPORTER: BigInt(1) << BigInt(9),
TEAM_USER: BigInt(1) << BigInt(10),
TRUST_AND_SAFETY: BigInt(1) << BigInt(11),
SYSTEM: BigInt(1) << BigInt(12),
HAS_UNREAD_URGENT_MESSAGES: BigInt(1) << BigInt(13),
BUGHUNTER_LEVEL_2: BigInt(1) << BigInt(14),
UNDERAGE_DELETED: BigInt(1) << BigInt(15),
VERIFIED_BOT: BigInt(1) << BigInt(16),
EARLY_VERIFIED_BOT_DEVELOPER: BigInt(1) << BigInt(17),
CERTIFIED_MODERATOR: BigInt(1) << BigInt(18),
BOT_HTTP_INTERACTIONS: BigInt(1) << BigInt(19),
};
}

View File

@ -18,7 +18,6 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
import { BaseClassWithoutId } from "./BaseClass";
import { User } from "./User";
import { CustomStatus, FriendSourceFlags, GuildFolder } from "@spacebar/schemas"
@Entity({

View File

@ -22,29 +22,7 @@ import { Channel } from "./Channel";
import { Guild } from "./Guild";
import { Member } from "./Member";
import { User } from "./User";
export enum PublicVoiceStateEnum {
user_id,
suppress,
session_id,
self_video,
self_mute,
self_deaf,
self_stream,
request_to_speak_timestamp,
mute,
deaf,
channel_id,
guild_id,
}
export type PublicVoiceStateKeys = keyof typeof PublicVoiceStateEnum;
export const PublicVoiceStateProjection = Object.values(
PublicVoiceStateEnum,
).filter((x) => typeof x === "string") as PublicVoiceStateKeys[];
export type PublicVoiceState = Pick<VoiceState, PublicVoiceStateKeys>;
import { PublicVoiceState, PublicVoiceStateProjection } from "@spacebar/schemas";
//https://gist.github.com/vassjozsef/e482c65df6ee1facaace8b3c9ff66145#file-voice_state-ex
@Entity({

View File

@ -22,12 +22,7 @@ import { BaseClass } from "./BaseClass";
import { Channel } from "./Channel";
import { Guild } from "./Guild";
import { User } from "./User";
export enum WebhookType {
Incoming = 1,
ChannelFollower = 2,
Application = 3,
}
import { WebhookType } from "@spacebar/schemas";
@Entity({
name: "webhooks",

View File

@ -19,14 +19,11 @@
import {
ConnectedAccount,
Interaction,
ApplicationCommand,
Message,
Invite,
Role,
Emoji,
PublicMember,
Channel,
PublicUser,
User,
Sticker,
Activity,
@ -35,14 +32,12 @@ import {
UserSettings,
IReadyGuildDTO,
ReadState,
UserPrivate,
ReadyUserGuildSettingsEntries,
ReadyPrivateChannel,
GuildOrUnavailable,
PublicVoiceState,
} from "@spacebar/util";
import { JsonValue } from "@protobuf-ts/runtime";
import { GuildCreateResponse, PartialEmoji, RelationshipType } from "@spacebar/schemas"
import { ApplicationCommand, GuildCreateResponse, PartialEmoji, PublicMember, PublicUser, PublicVoiceState, RelationshipType, UserPrivate } from "@spacebar/schemas";
export interface Event {
guild_id?: string;

View File

@ -18,7 +18,7 @@
import { ClientStatus, Status } from "./Status";
import { Activity } from "./Activity";
import { PublicUser } from "../entities/User";
import { PublicUser } from "@spacebar/schemas";
export interface Presence {
user: PublicUser;

View File

@ -2,9 +2,10 @@
// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah
// @fc-license-skip
import { Channel, ChannelPermissionOverwrite, ChannelPermissionOverwriteType, Guild, Member, Role } from "../entities";
import { Channel, Guild, Member, Role } from "../entities";
import { BitField, BitFieldResolvable, BitFlag } from "./BitField";
import { HTTPError } from "lambert-server";
import { ChannelPermissionOverwrite, ChannelPermissionOverwriteType } from "@spacebar/schemas";
export type PermissionResolvable = bigint | number | Permissions | PermissionResolvable[] | PermissionString;

View File

@ -46,6 +46,6 @@ export * from "./WebAuthn";
export * from "./Gifs";
export * from "./Application";
export * from "./NameValidation";
export * from "./HelperTypes";
export * from "../../schemas/HelperTypes";
export * from "./extensions";
export * from "./Random";