diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 3df58d7de..1019af784 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -18,6 +18,7 @@ jobs: - cdn - api - gateway + - webrtc - admin-api - cdn-cs - gateway-offload diff --git a/.idea/server.iml b/.idea/server.iml index 24643cc37..566f03d69 100644 --- a/.idea/server.iml +++ b/.idea/server.iml @@ -5,6 +5,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index b9d89f055..f2c77cea1 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -17,6 +17,13 @@ + + + + + + + + + + - { "customColor": "", @@ -84,6 +94,8 @@ "npm.Start Gateway.executor": "Run", "npm.build.executor": "Run", "npm.build:src.executor": "Run", + "npm.build:src:tsgo.executor": "Run", + "npm.build:tsgo.executor": "Run", "npm.start.executor": "Debug", "prettierjs.PrettierConfiguration.Package": "/home/Rory/git/spacebar/server-master/node_modules/prettier", "ts.external.directory.path": "/home/Rory/git/spacebar/server-master/node_modules/typescript/lib" @@ -99,6 +111,24 @@ ] } } + + + + + + + + @@ -136,7 +166,7 @@ - + @@ -148,7 +178,7 @@ - @@ -185,6 +215,14 @@ + + + + + + + + @@ -215,7 +253,20 @@ 140 + + + + + + \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index a72fab0c8..0657a53dd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,6 +3,7 @@ dist node_modules .github .vscode +.openhands .idea hashes.json flake.lock @@ -10,4 +11,4 @@ extra/admin-api discord-response-samples src/util/migration/postgres-initial.ts temp -scripts/schemaExclusions.json \ No newline at end of file +scripts/schemaExclusions.json diff --git a/assets/openapi.json b/assets/openapi.json index 3b5edade2..fdd6454ab 100644 --- a/assets/openapi.json +++ b/assets/openapi.json @@ -146,1303 +146,195 @@ "version" ] }, - "ConnectedAccountCommonOAuthTokenResponse": { + "RouteResponse": { "type": "object", "properties": { - "access_token": { - "type": "string" + "status": { + "type": "integer" }, - "token_type": { - "type": "string" + "body": { + "type": "string", + "pattern": "^.*Response$" }, - "scope": { - "type": "string" + "headers": { + "$ref": "#/components/schemas/Record" + } + } + }, + "FieldErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" }, - "refresh_token": { + "message": { "type": "string" }, - "expires_in": { - "type": "integer" + "errors": { + "$ref": "#/components/schemas/ErrorList" } }, "required": [ - "access_token", - "scope", - "token_type" + "code", + "errors", + "message" ] }, - "SupabaseResponse": { + "InteractionSchema": { "type": "object", "properties": { - "status": { + "type": { + "$ref": "#/components/schemas/InteractionType" + }, + "application_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "guild_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "channel_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "message_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "message_flags": { "type": "integer" }, - "error": { - "type": "object", - "properties": { - "message": { - "type": "string" - }, - "code": { - "type": "string" - }, - "details": {} - }, - "additionalProperties": false, - "required": [ - "message" - ] - } - } - }, - "JsonRpcResponse": { - "description": "JSON-RPC 2.0 response object", - "type": "object", - "properties": { - "jsonrpc": { - "type": "string", - "const": "2.0" + "session_id": { + "type": "string" }, - "id": { - "type": "string", - "nullable": true + "data": { + "$ref": "#/components/schemas/InteractionData" }, - "result": {}, - "error": { - "description": "JSON-RPC 2.0 error object", - "$ref": "#/components/schemas/JsonRpcError" + "files": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "nonce": { + "type": "string" + }, + "analytics_location": { + "type": "string" + }, + "section_name": { + "type": "string" + }, + "source": { + "type": "string" } }, "required": [ - "id", - "jsonrpc" + "application_id", + "channel_id", + "data", + "type" ] }, - "OpenAiResponse": { - "anyOf": [ - { - "$ref": "#/components/schemas/OpenAiChatCompletionObject" - }, - { - "$ref": "#/components/schemas/OpenAIResponseObject" - }, - { - "$ref": "#/components/schemas/OpenAICreateEmbeddingsObject" + "InteractionCallbackSchema": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/InteractionCallbackType" }, - { - "$ref": "#/components/schemas/OpenAIConversationObject" + "data": { + "$ref": "#/components/schemas/Message" } + }, + "required": [ + "data", + "type" ] }, - "SuccessfulResponse": { + "InteractionCreateSchema": { "type": "object", "properties": { + "version": { + "type": "integer" + }, "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "model": { + "application_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "created": { - "type": "integer" + "type": { + "$ref": "#/components/schemas/InteractionType" }, - "created_at": { - "type": "integer" + "token": { + "type": "string" }, - "messages": { + "data": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "guild": { + "$ref": "#/components/schemas/InteractionGuild" + }, + "guild_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "guild_locale": { + "type": "string" + }, + "channel": { + "$ref": "#/components/schemas/Channel" + }, + "channel_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "member": { + "$ref": "#/components/schemas/PublicMember" + }, + "user": { + "$ref": "#/components/schemas/PublicUser" + }, + "locale": { + "type": "string" + }, + "message": { + "$ref": "#/components/schemas/Message" + }, + "app_permissions": { + "type": "string" + }, + "entitlements": { "type": "array", "items": { "type": "object", - "properties": { - "role": { - "enum": [ - "assistant", - "user" - ], - "type": "string" - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": {} - }, - { - "type": "string" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "content", - "role" - ] + "properties": {}, + "additionalProperties": true } }, - "content": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "text": { - "type": "string" - }, - "name": { - "description": "Tool name when type is tool_use", - "type": "string" - }, - "id": { - "description": "Tool invocation id when type is tool_use", - "type": "string" - }, - "input": { - "$ref": "#/components/schemas/Record" - }, - "tool_use_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - } - }, - { + "entitlement_sku_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "authorizing_integration_owners": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[0-9]+$": { "type": "string" } - ] - }, - "completion": { - "type": "string" + } }, - "input_tokens": { - "type": "integer" - }, - "usage": { - "type": "object", - "properties": { - "input_tokens": { - "type": "integer" - }, - "output_tokens": { - "type": "integer" - }, - "cache_creation_input_tokens": { - "type": "integer" - }, - "cache_read_input_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "cache_creation_input_tokens", - "cache_read_input_tokens", - "input_tokens", - "output_tokens" - ] - } - }, - "required": [ - "id", - "model" - ] - }, - "AnthropicAiResponse": { - "anyOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "error" - }, - "error": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "message": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "message", - "type" - ] - }, - "request_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "error", - "request_id", - "type" - ] - }, - { - "type": "object", - "additionalProperties": {}, - "properties": { - "id": { - "type": "string" - }, - "model": { - "type": "string" - }, - "created": { - "type": "integer" - }, - "created_at": { - "type": "integer" - }, - "messages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "role": { - "enum": [ - "assistant", - "user" - ], - "type": "string" - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": {} - }, - { - "type": "string" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "content", - "role" - ] - } - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "text": { - "type": "string" - }, - "name": { - "description": "Tool name when type is tool_use", - "type": "string" - }, - "id": { - "description": "Tool invocation id when type is tool_use", - "type": "string" - }, - "input": { - "$ref": "#/components/schemas/Record" - }, - "tool_use_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - } - }, - { - "type": "string" - } - ] - }, - "completion": { - "type": "string" - }, - "input_tokens": { - "type": "integer" - }, - "usage": { - "type": "object", - "properties": { - "input_tokens": { - "type": "integer" - }, - "output_tokens": { - "type": "integer" - }, - "cache_creation_input_tokens": { - "type": "integer" - }, - "cache_read_input_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "cache_creation_input_tokens", - "cache_read_input_tokens", - "input_tokens", - "output_tokens" - ] - } - }, - "required": [ - "id", - "model" - ] - } - ] - }, - "GenerateContentResponse": { - "description": "Google GenAI Generate Content Response", - "type": "object", - "properties": { - "candidates": { - "description": "Response variations returned by the model.", - "type": "array", - "items": { - "description": "Google GenAI Candidate", - "type": "object", - "additionalProperties": {}, - "properties": { - "content": { - "description": "Contains the multi-part content of the response.", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/components/schemas/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/components/schemas/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - }, - "finishReason": { - "description": "The reason why the model stopped generating tokens.\nIf empty, the model has not stopped generating the tokens.", - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens for this candidate.", - "type": "integer" - }, - "index": { - "description": "The index of the candidate.", - "type": "integer" - } - } - } - }, - "automaticFunctionCallingHistory": { - "description": "Timestamp when the request is made to the server.", - "type": "array", - "items": { - "description": "Google GenAI Content", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/components/schemas/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/components/schemas/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "modelVersion": { - "description": "Output only. The model version used to generate the response.", - "type": "string" - }, - "promptFeedback": { - "description": "Output only. Content filter results for a prompt sent in the request. Note: Sent only in the first stream chunk. Only happens when no candidates were generated due to content violations.", - "$ref": "#/components/schemas/Record" - }, - "responseId": { - "description": "Output only. response_id is used to identify each response. It is the encoding of the event_id.", - "type": "string" - }, - "usageMetadata": { - "description": "Usage metadata about the response(s).", - "type": "object", - "additionalProperties": {}, - "properties": { - "cacheTokensDetails": { - "description": "Output only. List of modalities of the cached content in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "cachedContentTokenCount": { - "description": "Output only. Number of tokens in the cached part in the input (the cached content).", - "type": "integer" - }, - "candidatesTokenCount": { - "description": "Number of tokens in the response(s).", - "type": "integer" - }, - "candidatesTokensDetails": { - "description": "Output only. List of modalities that were returned in the response.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "promptTokenCount": { - "description": "Number of tokens in the request. When `cached_content` is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.", - "type": "integer" - }, - "promptTokensDetails": { - "description": "Output only. List of modalities that were processed in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "thoughtsTokenCount": { - "description": "Output only. Number of tokens present in thoughts output.", - "type": "integer" - }, - "toolUsePromptTokenCount": { - "description": "Output only. Number of tokens present in tool-use prompt(s).", - "type": "integer" - }, - "toolUsePromptTokensDetails": { - "description": "Output only. List of modalities that were processed for tool-use request inputs.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "totalTokenCount": { - "description": "Total token count for prompt, response candidates, and tool-use prompts (if present).", - "type": "integer" - } - } - } - } - }, - "GoogleGenAIResponse": { - "description": "Google GenAI Generate Content Response", - "type": "object", - "properties": { - "candidates": { - "description": "Response variations returned by the model.", - "type": "array", - "items": { - "description": "Google GenAI Candidate", - "type": "object", - "additionalProperties": {}, - "properties": { - "content": { - "description": "Contains the multi-part content of the response.", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/components/schemas/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/components/schemas/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - }, - "finishReason": { - "description": "The reason why the model stopped generating tokens.\nIf empty, the model has not stopped generating the tokens.", - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens for this candidate.", - "type": "integer" - }, - "index": { - "description": "The index of the candidate.", - "type": "integer" - } - } - } - }, - "automaticFunctionCallingHistory": { - "description": "Timestamp when the request is made to the server.", - "type": "array", - "items": { - "description": "Google GenAI Content", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/components/schemas/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/components/schemas/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "modelVersion": { - "description": "Output only. The model version used to generate the response.", - "type": "string" - }, - "promptFeedback": { - "description": "Output only. Content filter results for a prompt sent in the request. Note: Sent only in the first stream chunk. Only happens when no candidates were generated due to content violations.", - "$ref": "#/components/schemas/Record" - }, - "responseId": { - "description": "Output only. response_id is used to identify each response. It is the encoding of the event_id.", - "type": "string" - }, - "usageMetadata": { - "description": "Usage metadata about the response(s).", - "type": "object", - "additionalProperties": {}, - "properties": { - "cacheTokensDetails": { - "description": "Output only. List of modalities of the cached content in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "cachedContentTokenCount": { - "description": "Output only. Number of tokens in the cached part in the input (the cached content).", - "type": "integer" - }, - "candidatesTokenCount": { - "description": "Number of tokens in the response(s).", - "type": "integer" - }, - "candidatesTokensDetails": { - "description": "Output only. List of modalities that were returned in the response.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "promptTokenCount": { - "description": "Number of tokens in the request. When `cached_content` is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.", - "type": "integer" - }, - "promptTokensDetails": { - "description": "Output only. List of modalities that were processed in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "thoughtsTokenCount": { - "description": "Output only. Number of tokens present in thoughts output.", - "type": "integer" - }, - "toolUsePromptTokenCount": { - "description": "Output only. Number of tokens present in tool-use prompt(s).", - "type": "integer" - }, - "toolUsePromptTokensDetails": { - "description": "Output only. List of modalities that were processed for tool-use request inputs.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "totalTokenCount": { - "description": "Total token count for prompt, response candidates, and tool-use prompts (if present).", - "type": "integer" - } - } - } - } - }, - "UndiciResponse": { - "type": "object", - "properties": { - "headers": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[0-9]+$": { - "type": "integer" - } - }, - "properties": { - "BYTES_PER_ELEMENT": { - "type": "integer" - }, - "buffer": { - "$ref": "#/components/schemas/ArrayBufferLike" - }, - "byteLength": { - "type": "integer" - }, - "byteOffset": { - "type": "integer" - }, - "length": { - "type": "integer" - }, - "__@toStringTag@1293": { - "type": "string", - "const": "Uint8Array" - } - }, - "required": [ - "BYTES_PER_ELEMENT", - "__@toStringTag@1293", - "buffer", - "byteLength", - "byteOffset", - "length" - ] - } - }, - "statusCode": { - "type": "integer" - }, - "statusText": { - "type": "string" - } - }, - "required": [ - "headers", - "statusCode", - "statusText" - ] - }, - "CopyResponse": { - "type": "object", - "properties": { - "length": { - "type": "integer" - }, - "name": { - "$ref": "#/components/schemas/MessageName" - }, - "binary": { - "type": "boolean" - }, - "columnTypes": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - "required": [ - "binary", - "columnTypes", - "length", - "name" - ] - }, - "RouteResponse": { - "type": "object", - "properties": { - "status": { - "type": "integer" - }, - "body": { - "type": "string", - "pattern": "^.*Response$" - }, - "headers": { - "$ref": "#/components/schemas/Record" - } - } - }, - "FieldErrorResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "errors": { - "$ref": "#/components/schemas/ErrorList" - } - }, - "required": [ - "code", - "errors", - "message" - ] - }, - "InteractionSchema": { - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/InteractionType" - }, - "application_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "guild_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "channel_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "message_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "message_flags": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "data": { - "$ref": "#/components/schemas/InteractionData" - }, - "files": { - "type": "array", - "items": { - "type": "object", - "properties": {}, - "additionalProperties": true - } - }, - "nonce": { - "type": "string" - }, - "analytics_location": { - "type": "string" - }, - "section_name": { - "type": "string" - }, - "source": { - "type": "string" - } - }, - "required": [ - "application_id", - "channel_id", - "data", - "type" - ] - }, - "InteractionCallbackSchema": { - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/InteractionCallbackType" - }, - "data": { - "$ref": "#/components/schemas/Message" - } - }, - "required": [ - "data", - "type" - ] - }, - "InteractionCreateSchema": { - "type": "object", - "properties": { - "version": { - "type": "integer" - }, - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "application_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/InteractionType" - }, - "token": { - "type": "string" - }, - "data": { - "type": "object", - "properties": {}, - "additionalProperties": true - }, - "guild": { - "$ref": "#/components/schemas/InteractionGuild" - }, - "guild_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "guild_locale": { - "type": "string" - }, - "channel": { - "$ref": "#/components/schemas/Channel" - }, - "channel_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "member": { - "$ref": "#/components/schemas/PublicMember" - }, - "user": { - "$ref": "#/components/schemas/PublicUser" - }, - "locale": { - "type": "string" - }, - "message": { - "$ref": "#/components/schemas/Message" - }, - "app_permissions": { - "type": "string" - }, - "entitlements": { - "type": "array", - "items": { - "type": "object", - "properties": {}, - "additionalProperties": true - } - }, - "entitlement_sku_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "authorizing_integration_owners": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[0-9]+$": { - "type": "string" - } - } - }, - "context": { + "context": { "type": "integer" }, "attachment_size_limit": { @@ -1583,15 +475,28 @@ "properties": { "component_type": { "enum": [ - 0, 1, + 10, + 11, + 12, + 13, + 14, + 16, + 17, + 18, + 19, 2, + 20, + 21, + 22, + 23, 3, 4, 5, 6, 7, - 8 + 8, + 9 ], "type": "number" }, @@ -2505,43 +1410,6 @@ "total" ] }, - "MessageType": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 127, - 128, - 129, - 130, - 131, - 132, - 255 - ] - }, "DmMessagesResponseSchema": { "type": "array", "items": { @@ -2833,392 +1701,128 @@ "system_channel_flags": { "type": "integer" }, - "explicit_content_filter": { - "type": "integer" - }, - "public_updates_channel_id": { - "type": "string" - }, - "afk_timeout": { - "type": "integer" - }, - "afk_channel_id": { - "type": "string" - }, - "preferred_locale": { - "type": "string" - }, - "premium_progress_bar_enabled": { - "type": "boolean" - }, - "discovery_splash": { - "type": "string" - }, - "safety_alerts_channel_id": { - "type": "string", - "nullable": true - } - }, - "required": [ - "id", - "name", - "nsfw", - "welcome_screen", - "widget_enabled" - ] - }, - "GuildDiscoveryRequirementsResponse": { - "type": "object", - "properties": { - "guild_id": { - "type": "string" - }, - "safe_environment": { - "type": "boolean" - }, - "healthy": { - "type": "boolean" - }, - "health_score_pending": { - "type": "boolean" - }, - "size": { - "type": "boolean" - }, - "nsfw_properties": {}, - "protected": { - "type": "boolean" - }, - "sufficient": { - "type": "boolean" - }, - "sufficient_without_grace_period": { - "type": "boolean" - }, - "valid_rules_channel": { - "type": "boolean" - }, - "retention_healthy": { - "type": "boolean" - }, - "engagement_healthy": { - "type": "boolean" - }, - "age": { - "type": "boolean" - }, - "minimum_age": { - "type": "integer" - }, - "health_score": { - "type": "object", - "properties": { - "avg_nonnew_participators": { - "type": "integer" - }, - "avg_nonnew_communicators": { - "type": "integer" - }, - "num_intentful_joiners": { - "type": "integer" - }, - "perc_ret_w1_intentful": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "avg_nonnew_communicators", - "avg_nonnew_participators", - "num_intentful_joiners", - "perc_ret_w1_intentful" - ] - }, - "minimum_size": { - "type": "integer" - } - }, - "required": [ - "age", - "engagement_healthy", - "guild_id", - "health_score", - "health_score_pending", - "healthy", - "minimum_age", - "minimum_size", - "nsfw_properties", - "protected", - "retention_healthy", - "safe_environment", - "size", - "sufficient", - "sufficient_without_grace_period", - "valid_rules_channel" - ] - }, - "Message": { - "type": "object", - "properties": { - "channel_id": { - "type": "string" - }, - "channel": { - "$ref": "#/components/schemas/Channel" - }, - "thread_id": { - "type": "string" - }, - "thread": { - "$ref": "#/components/schemas/Channel" - }, - "guild_id": { - "type": "string" - }, - "guild": { - "$ref": "#/components/schemas/Guild" - }, - "author_id": { - "type": "string" - }, - "author": { - "$ref": "#/components/schemas/User" - }, - "member_id": { + "explicit_content_filter": { + "type": "integer" + }, + "public_updates_channel_id": { "type": "string" }, - "member": { - "$ref": "#/components/schemas/Member" + "afk_timeout": { + "type": "integer" }, - "webhook_id": { + "afk_channel_id": { "type": "string" }, - "webhook": { - "$ref": "#/components/schemas/Webhook" - }, - "application_id": { + "preferred_locale": { "type": "string" }, - "application": { - "$ref": "#/components/schemas/Application" + "premium_progress_bar_enabled": { + "type": "boolean" }, - "content": { + "discovery_splash": { "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "edited_timestamp": { + "safety_alerts_channel_id": { "type": "string", - "format": "date-time" + "nullable": true + } + }, + "required": [ + "id", + "name", + "nsfw", + "welcome_screen", + "widget_enabled" + ] + }, + "GuildDiscoveryRequirementsResponse": { + "type": "object", + "properties": { + "guild_id": { + "type": "string" }, - "tts": { + "safe_environment": { "type": "boolean" }, - "mention_everyone": { + "healthy": { "type": "boolean" }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/User" - } - }, - "mention_roles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Role" - } - }, - "mention_channels": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Channel" - } - }, - "sticker_items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Sticker" - } + "health_score_pending": { + "type": "boolean" }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Attachment" - } + "size": { + "type": "boolean" }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Embed" - } + "nsfw_properties": {}, + "protected": { + "type": "boolean" }, - "reactions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Reaction" - } + "sufficient": { + "type": "boolean" }, - "nonce": { - "type": "string" + "sufficient_without_grace_period": { + "type": "boolean" }, - "pinned": { + "valid_rules_channel": { "type": "boolean" }, - "pinned_at": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ] + "retention_healthy": { + "type": "boolean" }, - "type": { - "$ref": "#/components/schemas/MessageType" + "engagement_healthy": { + "type": "boolean" }, - "activity": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "party_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "party_id", - "type" - ] + "age": { + "type": "boolean" }, - "flags": { + "minimum_age": { "type": "integer" }, - "message_reference": { + "health_score": { "type": "object", "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "type": { + "avg_nonnew_participators": { "type": "integer" - } - }, - "additionalProperties": false - }, - "referenced_message": { - "$ref": "#/components/schemas/Message" - }, - "interaction": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/InteractionType" - }, - "name": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "id", - "name", - "type", - "user_id" - ] - }, - "components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ActionRowComponent" - } - }, - "poll": { - "$ref": "#/components/schemas/Poll" - }, - "interaction_metadata": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/InteractionType" - }, - "user_id": { - "type": "string" - }, - "authorizing_integration_owners": { - "$ref": "#/components/schemas/Record" }, - "original_response_message_id": { - "type": "string" + "avg_nonnew_communicators": { + "type": "integer" }, - "interacted_message_id": { - "type": "string" + "num_intentful_joiners": { + "type": "integer" }, - "name": { - "type": "string" + "perc_ret_w1_intentful": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "id", - "type", - "user_id" + "avg_nonnew_communicators", + "avg_nonnew_participators", + "num_intentful_joiners", + "perc_ret_w1_intentful" ] }, - "message_snapshots": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MessageSnapshot" - } - }, - "reply_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "username": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "id": { - "type": "string" + "minimum_size": { + "type": "integer" } }, "required": [ - "channel", - "embeds", - "flags", - "id", - "mention_channels", - "mention_roles", - "mentions", - "reactions", - "timestamp", - "type" + "age", + "engagement_healthy", + "guild_id", + "health_score", + "health_score_pending", + "healthy", + "minimum_age", + "minimum_size", + "nsfw_properties", + "protected", + "retention_healthy", + "safe_environment", + "size", + "sufficient", + "sufficient_without_grace_period", + "valid_rules_channel" ] }, "GuildMessagesSearchResponse": { @@ -3885,8 +2489,14 @@ "id": { "type": "string" }, - "unavailable": { - "type": "boolean" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } }, "icon": { "type": "string" @@ -3921,6 +2531,14 @@ "member_count": { "type": "integer" }, + "get_annotations": { + "type": "object", + "additionalProperties": false + }, + "clean_data": { + "type": "object", + "additionalProperties": false + }, "roles": { "type": "array", "items": { @@ -4042,6 +2660,9 @@ "premium_tier": { "type": "integer" }, + "unavailable": { + "type": "boolean" + }, "welcome_screen": { "$ref": "#/components/schemas/GuildWelcomeScreen", "description": "DEPRECATED: Look at the new Guild onboarding screens." @@ -4069,16 +2690,24 @@ }, "discovery_excluded": { "type": "boolean" + }, + "ToGuildSource": { + "type": "object", + "additionalProperties": false } }, "required": [ + "ToGuildSource", + "__@annotationsKey@11707", "bans", "channel_ordering", "channels", + "clean_data", "discovery_excluded", "discovery_weight", "emojis", "features", + "get_annotations", "id", "insert", "invites", @@ -4568,6 +3197,9 @@ "guild_id": { "type": "string" }, + "flags": { + "type": "integer" + }, "mute": { "type": "boolean" }, @@ -4631,6 +3263,7 @@ "bio", "communication_disabled_until", "deaf", + "flags", "guild_id", "id", "joined_at", @@ -5483,17 +4116,11 @@ 18, 19, 2, - 20, - 21, 255, 3, - 33, - 34, - 35, 4, 5, 6, - 64, 7, 8, 9 @@ -5753,6 +4380,31 @@ "user_id" ] }, + "ConnectedAccountCommonOAuthTokenResponse": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "token_type": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "refresh_token": { + "type": "string" + }, + "expires_in": { + "type": "integer" + } + }, + "required": [ + "access_token", + "scope", + "token_type" + ] + }, "ConnectionCallbackSchema": { "type": "object", "properties": { @@ -6038,7 +4690,7 @@ "name": { "type": "string" }, - "avatar": { + "icon": { "type": "string", "nullable": true } @@ -6420,118 +5072,6 @@ } } }, - "Embed": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "type": { - "enum": [ - "article", - "auto_moderation_message", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { - "type": "integer" - }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" - ] - }, - "image": { - "$ref": "#/components/schemas/EmbedImage" - }, - "thumbnail": { - "$ref": "#/components/schemas/EmbedImage" - }, - "video": { - "$ref": "#/components/schemas/EmbedImage" - }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "additionalProperties": false - }, - "author": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "inline": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name", - "value" - ] - } - } - } - }, "MessageCreateSchema": { "type": "object", "properties": { @@ -6557,61 +5097,110 @@ "type": "integer" }, "embeds": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Embed" - } - }, - "embed": { - "$ref": "#/components/schemas/Embed" - }, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { + "anyOf": [ + { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Embed" } }, - "roles": { - "type": "array", - "items": { - "type": "string" - } + { + "type": "null" + } + ] + }, + "embed": { + "anyOf": [ + { + "$ref": "#/components/schemas/Embed" }, - "users": { - "type": "array", - "items": { - "type": "string" - } + { + "type": "null" + } + ] + }, + "allowed_mentions": { + "anyOf": [ + { + "type": "object", + "properties": { + "parse": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "roles": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "users": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "replied_user": { + "type": "boolean" + } + }, + "additionalProperties": false }, - "replied_user": { - "type": "boolean" + { + "type": "null" } - }, - "additionalProperties": false + ] }, "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "fail_if_not_exists": { - "type": "boolean" + "anyOf": [ + { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" + }, + "type": { + "type": "integer" + } + }, + "additionalProperties": false }, - "type": { - "type": "integer" + { + "type": "null" } - }, - "additionalProperties": false + ] }, "payload_json": { "type": "string" @@ -6817,7 +5406,14 @@ "type": "object", "properties": { "embed": { - "$ref": "#/components/schemas/Embed" + "anyOf": [ + { + "$ref": "#/components/schemas/Embed" + }, + { + "type": "null" + } + ] }, "file": { "type": "object", @@ -6834,6 +5430,126 @@ "flags": { "type": "integer" }, + "applied_tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "channel_id": { + "type": "string" + }, + "content": { + "type": "string" + }, + "mobile_network_type": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "tts": { + "type": "boolean" + }, + "embeds": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/components/schemas/Embed" + } + }, + { + "type": "null" + } + ] + }, + "allowed_mentions": { + "anyOf": [ + { + "type": "object", + "properties": { + "parse": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "roles": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "users": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "replied_user": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "message_reference": { + "anyOf": [ + { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" + }, + "type": { + "type": "integer" + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "payload_json": { + "type": "string" + }, "attachments": { "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", "type": "array", @@ -6880,84 +5596,6 @@ ] } }, - "applied_tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "channel_id": { - "type": "string" - }, - "content": { - "type": "string" - }, - "mobile_network_type": { - "type": "string" - }, - "nonce": { - "type": "string" - }, - "tts": { - "type": "boolean" - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Embed" - } - }, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - }, - "replied_user": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "fail_if_not_exists": { - "type": "boolean" - }, - "type": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "payload_json": { - "type": "string" - }, "sticker_ids": { "anyOf": [ { @@ -7199,6 +5837,9 @@ 4 ], "type": "number" + }, + "confirm_stranger_request": { + "type": "boolean" } } }, @@ -7667,37 +6308,13 @@ "type": "array", "items": { "type": "integer" - } - }, - "display_name_effect_id": { - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6 - ], - "type": "number" - }, - "display_name_font_id": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "type": "number" + } + }, + "display_name_effect_id": { + "$ref": "#/components/schemas/User_DisplayNameEffect" + }, + "display_name_font_id": { + "$ref": "#/components/schemas/User_DisplayNameFont" } } }, @@ -8042,35 +6659,6 @@ "enabled" ] }, - "TicketCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - } - } - }, - "TicketPatchSchema": { - "type": "object", - "properties": { - "owner_id": { - "type": "string", - "nullable": true - }, - "resolved": { - "type": "boolean", - "nullable": true - }, - "public": { - "type": "boolean", - "nullable": true - }, - "closed": { - "type": "boolean", - "nullable": true - } - } - }, "MessageThreadCreationSchema": { "type": "object", "properties": { @@ -8303,1274 +6891,1121 @@ "original_content_type": { "type": "string" } - }, - "additionalProperties": false, - "required": [ - "filename", - "uploaded_filename" - ] - } - ] - } - } - }, - "additionalProperties": false - } - }, - "required": [ - "name" - ] - }, - "PostDataSchema": { - "type": "object", - "properties": { - "thread_ids": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "thread_ids" - ] - }, - "TagCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "moderated": { - "type": "boolean", - "nullable": true - }, - "emoji_id": { - "type": "string", - "nullable": true - }, - "emoji_name": { - "type": "string", - "nullable": true - } - }, - "required": [ - "name" - ] - }, - "ChannelCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "type": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 2, - 20, - 21, - 255, - 3, - 33, - 34, - 35, - 4, - 5, - 6, - 64, - 7, - 8, - 9 - ], - "type": "number" - }, - "id": { - "type": "string" - }, - "flags": { - "type": "integer" - }, - "icon": { - "type": "string", - "nullable": true - }, - "parent_id": { - "type": "string" - }, - "default_auto_archive_duration": { - "type": "integer" - }, - "permission_overwrites": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/ChannelPermissionOverwriteType" - }, - "allow": { - "type": "string" - }, - "deny": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "allow", - "deny", - "id", - "type" - ] - } - }, - "video_quality_mode": { - "type": "integer" - }, - "bitrate": { - "type": "integer" - }, - "user_limit": { - "type": "integer" - }, - "nsfw": { - "type": "boolean" - }, - "rate_limit_per_user": { - "type": "integer" - }, - "topic": { - "type": "string" - }, - "default_thread_rate_limit_per_user": { - "type": "integer" - }, - "applied_tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "position": { - "type": "integer" - }, - "invitable": { - "type": "boolean" - }, - "rtc_region": { - "type": "string" - }, - "default_reaction_emoji": { - "type": "string", - "nullable": true - }, - "auto_archive_duration": { - "type": "integer" - }, - "archived": { - "type": "boolean" - }, - "locked": { - "type": "boolean" - } - } - }, - "VoiceIdentifySchema": { - "type": "object", - "properties": { - "server_id": { - "type": "string" - }, - "user_id": { - "type": "string" - }, - "session_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "token": { - "type": "string" - }, - "video": { - "type": "boolean" - }, - "streams": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "enum": [ - "audio", - "screen", - "video" - ], - "type": "string" - }, - "rid": { - "type": "string" - }, - "quality": { - "type": "integer" + }, + "additionalProperties": false, + "required": [ + "filename", + "uploaded_filename" + ] + } + ] } - }, - "additionalProperties": false, - "required": [ - "quality", - "rid", - "type" - ] - } - }, - "max_secure_frames_version": { - "type": "integer" - }, - "max_dave_protocol_version": { - "type": "integer" + } + }, + "additionalProperties": false } }, "required": [ - "server_id", - "session_id", - "token", - "user_id" + "name" ] }, - "VoiceVideoSchema": { + "PostDataSchema": { "type": "object", "properties": { - "audio_ssrc": { - "type": "integer" - }, - "video_ssrc": { - "type": "integer" - }, - "rtx_ssrc": { - "type": "integer" - }, - "user_id": { - "type": "string" - }, - "streams": { + "thread_ids": { "type": "array", "items": { - "type": "object", - "properties": { - "type": { - "enum": [ - "audio", - "screen", - "video" - ], - "type": "string" - }, - "rid": { - "type": "string" - }, - "ssrc": { - "type": "integer" - }, - "active": { - "type": "boolean" - }, - "quality": { - "type": "integer" - }, - "rtx_ssrc": { - "type": "integer" - }, - "max_bitrate": { - "type": "integer" - }, - "max_framerate": { - "type": "integer" - }, - "max_resolution": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "width": { - "type": "integer" - }, - "height": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "height", - "type", - "width" - ] - } - }, - "additionalProperties": false, - "required": [ - "active", - "max_bitrate", - "max_framerate", - "max_resolution", - "quality", - "rid", - "rtx_ssrc", - "ssrc", - "type" - ] + "type": "string" } } }, "required": [ - "audio_ssrc", - "video_ssrc" + "thread_ids" ] }, - "CreateReportSchema": { + "TagCreateSchema": { "type": "object", "properties": { - "version": { - "type": "string" - }, - "variant": { - "type": "string" - }, "name": { "type": "string" }, - "language": { - "type": "string" - }, - "breadcrumbs": { - "type": "array", - "items": { - "type": "integer" - } - }, - "elements": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "channel_id": { - "type": "string" - }, - "message_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "stage_instance_id": { - "type": "string" - }, - "guild_scheduled_event_id": { - "type": "string" - }, - "reported_user_id": { - "type": "string" - }, - "application_id": { - "type": "string" + "moderated": { + "type": "boolean", + "nullable": true }, - "user_id": { - "type": "string" + "emoji_id": { + "type": "string", + "nullable": true }, - "widget_id": { - "type": "string" + "emoji_name": { + "type": "string", + "nullable": true } }, "required": [ - "breadcrumbs", - "language", - "name", - "variant", - "version" + "name" ] }, - "SessionsLogoutSchema": { + "ChannelCreateSchema": { "type": "object", "properties": { - "session_ids": { - "type": "array", - "items": { - "type": "string" - } + "name": { + "type": "string" }, - "session_id_hashes": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "GetSessionsResponse": { - "type": "object", - "properties": { - "user_sessions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "id_hash": { - "type": "string" - }, - "status": { - "type": "string" - }, - "activities": { - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Activity" - } - } - }, - "client_status": { - "$ref": "#/components/schemas/ClientStatus" - }, - "approx_last_used_time": { + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 2, + 255, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "type": "number" + }, + "id": { + "type": "string" + }, + "flags": { + "type": "integer" + }, + "icon": { + "type": "string", + "nullable": true + }, + "parent_id": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "client_info": { - "type": "object", - "properties": { - "client": { - "type": "string" - }, - "os": { - "type": "string" - }, - "version": { - "type": "integer" - }, - "location": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "client", - "location", - "os", - "version" - ] - }, - "last_seen": { - "type": "string", - "format": "date-time" + "type": { + "$ref": "#/components/schemas/ChannelPermissionOverwriteType" }, - "last_seen_ip": { + "allow": { "type": "string" }, - "last_seen_location": { + "deny": { "type": "string" } }, "additionalProperties": false, "required": [ - "activities", - "approx_last_used_time", - "client_info", - "client_status", + "allow", + "deny", "id", - "id_hash", - "status" + "type" ] } - } - }, - "required": [ - "user_sessions" - ] - }, - "ApplicationCommandOption": { - "type": "object", - "properties": { - "type": { - "$ref": "#/components/schemas/ApplicationCommandOptionType" }, - "name": { - "type": "string" + "video_quality_mode": { + "type": "integer" }, - "description": { - "type": "string" + "bitrate": { + "type": "integer" }, - "required": { + "user_limit": { + "type": "integer" + }, + "nsfw": { "type": "boolean" }, - "choices": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ApplicationCommandOptionChoice" - } + "rate_limit_per_user": { + "type": "integer" }, - "options": { + "topic": { + "type": "string" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + }, + "applied_tags": { "type": "array", "items": { - "$ref": "#/components/schemas/ApplicationCommandOption" + "type": "string" } + }, + "position": { + "type": "integer" + }, + "invitable": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_reaction_emoji": { + "type": "string", + "nullable": true + }, + "auto_archive_duration": { + "type": "integer" + }, + "archived": { + "type": "boolean" + }, + "locked": { + "type": "boolean" } - }, - "required": [ - "description", - "name", - "type" - ] + } }, - "ApplicationCommandOptionType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ] + "ChannelPromoteSchema": { + "type": "object", + "properties": { + "position": { + "type": "integer" + } + } }, - "ApplicationCommandOptionChoice": { + "TicketCreateSchema": { "type": "object", "properties": { "name": { "type": "string" - }, - "value": { - "type": [ - "string", - "integer" - ] } - }, - "required": [ - "name", - "value" - ] + } }, - "ApplicationCommandIndexPermissions": { + "TicketPatchSchema": { "type": "object", "properties": { - "user": { - "type": "boolean" + "owner_id": { + "type": "string", + "nullable": true }, - "roles": { - "$ref": "#/components/schemas/Record" + "resolved": { + "type": "boolean", + "nullable": true }, - "channels": { - "$ref": "#/components/schemas/Record" + "public": { + "type": "boolean", + "nullable": true + }, + "closed": { + "type": "boolean", + "nullable": true } } }, - "ApplicationIntegrationType": { - "type": "number", - "enum": [ - 0, - 1 - ] - }, - "InteractionContextType": { - "type": "number", - "enum": [ - 0, - 1, - 2 - ] - }, - "JsonRpcError": { - "description": "JSON-RPC 2.0 error object", + "VoiceIdentifySchema": { "type": "object", "properties": { - "code": { - "type": "integer" + "server_id": { + "type": "string" }, - "message": { + "user_id": { + "type": "string" + }, + "session_id": { "type": "string" }, - "data": {} + "channel_id": { + "type": "string" + }, + "token": { + "type": "string" + }, + "video": { + "type": "boolean" + }, + "streams": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "enum": [ + "audio", + "screen", + "video" + ], + "type": "string" + }, + "rid": { + "type": "string" + }, + "quality": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "quality", + "rid", + "type" + ] + } + }, + "max_secure_frames_version": { + "type": "integer" + }, + "max_dave_protocol_version": { + "type": "integer" + } }, "required": [ - "code", - "message" + "server_id", + "session_id", + "token", + "user_id" ] }, - "OpenAiChatCompletionObject": { + "VoiceVideoSchema": { "type": "object", "properties": { - "id": { - "type": "string" + "audio_ssrc": { + "type": "integer" }, - "object": { - "type": "string", - "const": "chat.completion" + "video_ssrc": { + "type": "integer" }, - "created": { + "rtx_ssrc": { "type": "integer" }, - "model": { + "user_id": { "type": "string" }, - "choices": { + "streams": { "type": "array", "items": { "type": "object", "properties": { - "index": { + "type": { + "enum": [ + "audio", + "screen", + "video" + ], + "type": "string" + }, + "rid": { + "type": "string" + }, + "ssrc": { + "type": "integer" + }, + "active": { + "type": "boolean" + }, + "quality": { + "type": "integer" + }, + "rtx_ssrc": { + "type": "integer" + }, + "max_bitrate": { + "type": "integer" + }, + "max_framerate": { "type": "integer" }, - "message": { + "max_resolution": { "type": "object", "properties": { - "role": { + "type": { "type": "string" }, - "content": { - "type": [ - "null", - "string" - ] - }, - "refusal": { - "type": [ - "null", - "string" - ] - }, - "annotations": { - "type": "array", - "items": {} + "width": { + "type": "integer" }, - "tool_calls": { - "type": "array", - "items": {} + "height": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "content", - "role" - ] - }, - "logprobs": {}, - "finish_reason": { - "type": [ - "null", - "string" + "height", + "type", + "width" ] } }, "additionalProperties": false, "required": [ - "finish_reason", - "index", - "message" + "active", + "max_bitrate", + "max_framerate", + "max_resolution", + "quality", + "rid", + "rtx_ssrc", + "ssrc", + "type" ] } - }, - "usage": { - "type": "object", - "properties": { - "prompt_tokens": { - "type": "integer" - }, - "completion_tokens": { - "type": "integer" - }, - "total_tokens": { - "type": "integer" - }, - "prompt_tokens_details": { - "type": "object", - "properties": { - "cached_tokens": { - "type": "integer" - }, - "audio_tokens": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "completion_tokens_details": { - "type": "object", - "properties": { - "reasoning_tokens": { - "type": "integer" - }, - "audio_tokens": { - "type": "integer" - }, - "accepted_prediction_tokens": { - "type": "integer" - }, - "rejected_prediction_tokens": { - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false, - "required": [ - "completion_tokens", - "prompt_tokens", - "total_tokens" - ] - }, - "service_tier": { - "type": "string" - }, - "system_fingerprint": { - "type": "string" } }, "required": [ - "choices", - "created", - "id", - "model", - "object", - "usage" + "audio_ssrc", + "video_ssrc" ] }, - "OpenAIResponseObject": { + "CreateReportSchema": { "type": "object", "properties": { - "id": { + "version": { "type": "string" }, - "object": { - "type": "string", - "const": "response" + "variant": { + "type": "string" }, - "created_at": { - "type": "integer" + "name": { + "type": "string" }, - "status": { - "enum": [ - "cancelled", - "completed", - "failed", - "in_progress" - ], + "language": { "type": "string" }, - "error": { - "type": "string", - "nullable": true + "breadcrumbs": { + "type": "array", + "items": { + "type": "integer" + } }, - "incomplete_details": {}, - "instructions": {}, - "max_output_tokens": { - "type": "integer", - "nullable": true + "elements": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "channel_id": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "stage_instance_id": { + "type": "string" + }, + "guild_scheduled_event_id": { + "type": "string" + }, + "reported_user_id": { + "type": "string" + }, + "application_id": { + "type": "string" + }, + "user_id": { + "type": "string" }, - "model": { + "widget_id": { "type": "string" + } + }, + "required": [ + "breadcrumbs", + "language", + "name", + "variant", + "version" + ] + }, + "SessionsLogoutSchema": { + "type": "object", + "properties": { + "session_ids": { + "type": "array", + "items": { + "type": "string" + } }, - "output": { + "session_id_hashes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "GetSessionsResponse": { + "type": "object", + "properties": { + "user_sessions": { "type": "array", "items": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "message" - }, "id": { "type": "string" }, - "status": { + "id_hash": { "type": "string" }, - "role": { + "status": { "type": "string" }, - "content": { + "activities": { "type": "array", "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "output_text" - }, - "text": { - "type": "string" - }, - "annotations": { - "type": "array", - "items": {} - } - }, - "additionalProperties": false, - "required": [ - "annotations", - "text", - "type" - ] + "type": "array", + "items": { + "$ref": "#/components/schemas/Activity" + } } + }, + "client_status": { + "$ref": "#/components/schemas/ClientStatus" + }, + "approx_last_used_time": { + "type": "string" + }, + "client_info": { + "type": "object", + "properties": { + "client": { + "type": "string" + }, + "os": { + "type": "string" + }, + "version": { + "type": "integer" + }, + "location": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "client", + "location", + "os", + "version" + ] + }, + "last_seen": { + "type": "string", + "format": "date-time" + }, + "last_seen_ip": { + "type": "string" + }, + "last_seen_location": { + "type": "string" } }, "additionalProperties": false, "required": [ - "content", + "activities", + "approx_last_used_time", + "client_info", + "client_status", "id", - "role", - "status", - "type" + "id_hash", + "status" ] } - }, - "output_text": { + } + }, + "required": [ + "user_sessions" + ] + }, + "LobbyMemberSchema": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "parallel_tool_calls": { - "type": "boolean" - }, - "previous_response_id": { - "type": "string", - "nullable": true - }, - "reasoning": { - "type": "object", - "properties": { - "effort": { - "type": [ - "null", - "string" - ] + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/Record" }, - "summary": { - "type": [ - "null", - "string" - ] + { + "type": "null" } - }, - "additionalProperties": false, - "required": [ - "effort", - "summary" ] }, - "store": { - "type": "boolean" - }, - "temperature": { + "flags": { "type": "integer" - }, - "text": { - "type": "object", - "properties": { - "format": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] + } + }, + "required": [ + "id" + ] + }, + "LobbyCreateSchema": { + "type": "object", + "properties": { + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/Record" + }, + { + "type": "null" } - }, - "additionalProperties": false, - "required": [ - "format" ] }, - "tool_choice": { - "type": "string" - }, - "tools": { + "members": { + "maxItems": 25, "type": "array", - "items": {} + "items": { + "$ref": "#/components/schemas/LobbyMemberSchema" + } }, - "top_p": { + "idle_timeout_seconds": { + "minimum": 5, + "maximum": 604800, "type": "integer" + } + }, + "required": [ + "idle_timeout_seconds" + ] + }, + "LobbyUpdateSchema": { + "type": "object", + "properties": { + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/Record" + }, + { + "type": "null" + } + ] }, - "truncation": { - "type": "string" + "members": { + "maxItems": 25, + "type": "array", + "items": { + "$ref": "#/components/schemas/LobbyMemberSchema" + } }, - "usage": { - "type": "object", - "properties": { - "input_tokens": { - "type": "integer" - }, - "input_tokens_details": { - "type": "object", - "properties": { - "cached_tokens": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "output_tokens": { - "type": "integer" - }, - "output_tokens_details": { - "type": "object", - "properties": { - "reasoning_tokens": { - "type": "integer" - } - }, - "additionalProperties": false + "idle_timeout_seconds": { + "minimum": 5, + "maximum": 604800, + "type": "integer" + } + } + }, + "LobbyMemberUpdateSchema": { + "type": "object", + "properties": { + "metadata": { + "anyOf": [ + { + "$ref": "#/components/schemas/Record" }, - "total_tokens": { - "type": "integer" + { + "type": "null" } - }, - "additionalProperties": false, - "required": [ - "input_tokens", - "output_tokens", - "total_tokens" ] }, - "user": { - "type": "string", - "nullable": true + "flags": { + "type": "integer" + } + } + }, + "LobbyChannelLinkSchema": { + "type": "object", + "properties": { + "channel_id": { + "type": "string" + } + } + }, + "ApplicationCommandOption": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/ApplicationCommandOptionType" }, - "metadata": { - "$ref": "#/components/schemas/Record" + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "required": { + "type": "boolean" + }, + "choices": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationCommandOptionChoice" + } + }, + "options": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApplicationCommandOption" + } } }, "required": [ - "created_at", - "error", - "id", - "incomplete_details", - "instructions", - "max_output_tokens", - "metadata", - "model", - "object", - "output", - "output_text", - "parallel_tool_calls", - "previous_response_id", - "reasoning", - "status", - "store", - "temperature", - "text", - "tool_choice", - "tools", - "top_p", - "truncation", - "usage", - "user" + "description", + "name", + "type" + ] + }, + "ApplicationCommandOptionType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ] + }, + "ApplicationCommandOptionChoice": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": [ + "string", + "integer" + ] + } + }, + "required": [ + "name", + "value" + ] + }, + "ApplicationCommandIndexPermissions": { + "type": "object", + "properties": { + "user": { + "type": "boolean" + }, + "roles": { + "$ref": "#/components/schemas/Record" + }, + "channels": { + "$ref": "#/components/schemas/Record" + } + } + }, + "ApplicationIntegrationType": { + "type": "number", + "enum": [ + 0, + 1 + ] + }, + "InteractionContextType": { + "type": "number", + "enum": [ + 0, + 1, + 2 + ] + }, + "ErrorList": { + "type": "object" + }, + "InteractionType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5 ] }, - "OpenAICreateEmbeddingsObject": { + "InteractionData": { "type": "object", "properties": { - "object": { - "type": "string", - "const": "list" + "application_command": { + "type": "object", + "properties": {}, + "additionalProperties": true }, - "data": { + "attachments": { "type": "array", "items": { - "$ref": "#/components/schemas/OpenAIEmbeddingsObject" + "$ref": "#/components/schemas/UploadAttachmentRequestSchema" } }, - "model": { + "id": { "type": "string" }, - "usage": { - "type": "object", - "properties": { - "prompt_tokens": { - "type": "integer" - }, - "total_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "prompt_tokens", - "total_tokens" - ] - } - }, - "required": [ - "data", - "model", - "object", - "usage" - ] - }, - "OpenAIEmbeddingsObject": { - "type": "object", - "properties": { - "object": { - "type": "string", - "const": "embedding" + "name": { + "type": "string" }, - "embedding": { + "options": { "type": "array", "items": { - "type": "integer" + "$ref": "#/components/schemas/ApplicationCommandOption" } }, - "index": { + "type": { "type": "integer" + }, + "version": { + "type": "string" } }, "required": [ - "embedding", - "index", - "object" + "application_command", + "attachments", + "id", + "name", + "options", + "type", + "version" ] }, - "OpenAIConversationObject": { - "description": "OpenAI Conversations API Conversation object", + "UploadAttachmentRequest": { "type": "object", "properties": { "id": { "type": "string" }, - "object": { - "type": "string", - "const": "conversation" + "filename": { + "type": "string" }, - "created_at": { + "file_size": { "type": "integer" }, - "metadata": { - "$ref": "#/components/schemas/Record" - } - }, - "required": [ - "created_at", - "id", - "object" - ] - }, - "Blob": { - "description": "`Blob` class is a global reference for `import { Blob } from 'node:buffer'`\nhttps://nodejs.org/api/buffer.html#class-blob", - "type": "object", - "properties": { - "size": { - "description": "The total size of the `Blob` in bytes.", - "type": "integer" + "is_clip": { + "type": "boolean" }, - "type": { - "description": "The content-type of the `Blob`.", + "original_content_type": { "type": "string" } }, "required": [ - "size", - "type" + "file_size", + "filename" ] }, - "ArrayBufferLike": { - "anyOf": [ - { - "$ref": "#/components/schemas/ArrayBuffer" - }, - { - "$ref": "#/components/schemas/SharedArrayBuffer" - } + "InteractionCallbackType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 ] }, - "ArrayBuffer": { + "Message": { "type": "object", "properties": { - "byteLength": { - "type": "integer" + "channel_id": { + "type": "string" + }, + "channel": { + "$ref": "#/components/schemas/Channel" }, - "__@toStringTag@1293": { + "thread_id": { "type": "string" - } - }, - "required": [ - "__@toStringTag@1293", - "byteLength" - ] - }, - "SharedArrayBuffer": { - "type": "object", - "properties": { - "byteLength": { + }, + "thread": { + "$ref": "#/components/schemas/Channel" + }, + "guild_id": { + "type": "string" + }, + "guild": { + "$ref": "#/components/schemas/Guild" + }, + "author_id": { + "type": "string" + }, + "author": { + "$ref": "#/components/schemas/User" + }, + "member_id": { + "type": "string" + }, + "member": { + "$ref": "#/components/schemas/Member" + }, + "webhook_id": { + "type": "string" + }, + "webhook": { + "$ref": "#/components/schemas/Webhook" + }, + "application_id": { + "type": "string" + }, + "application": { + "$ref": "#/components/schemas/Application" + }, + "content": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "edited_timestamp": { + "type": "string", + "format": "date-time" + }, + "tts": { + "type": "boolean" + }, + "mention_everyone": { + "type": "boolean" + }, + "mentions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + }, + "mention_roles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Role" + } + }, + "mention_channels": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Channel" + } + }, + "sticker_items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Sticker" + } + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Attachment" + } + }, + "embeds": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Embed" + } + }, + "reactions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Reaction" + } + }, + "nonce": { + "type": "string" + }, + "pinned_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "pinned": { + "type": "boolean" + }, + "type": { + "$ref": "#/components/schemas/MessageType" + }, + "activity": { + "type": "object", + "properties": { + "type": { + "type": "integer" + }, + "party_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "party_id", + "type" + ] + }, + "flags": { "type": "integer" }, - "__@species@16534": { - "$ref": "#/components/schemas/SharedArrayBuffer" + "message_reference": { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "type": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "referenced_message": { + "anyOf": [ + { + "$ref": "#/components/schemas/Message" + }, + { + "type": "null" + } + ] }, - "__@toStringTag@1293": { - "type": "string", - "const": "SharedArrayBuffer" - } - }, - "required": [ - "__@species@16534", - "__@toStringTag@1293", - "byteLength" - ] - }, - "MessageName": { - "enum": [ - "authenticationCleartextPassword", - "authenticationMD5Password", - "authenticationOk", - "authenticationSASL", - "authenticationSASLContinue", - "authenticationSASLFinal", - "backendKeyData", - "bindComplete", - "closeComplete", - "commandComplete", - "copyData", - "copyDone", - "copyInResponse", - "copyOutResponse", - "dataRow", - "emptyQuery", - "error", - "noData", - "notice", - "notification", - "parameterDescription", - "parameterStatus", - "parseComplete", - "portalSuspended", - "readyForQuery", - "replicationStart", - "rowDescription" - ], - "type": "string" - }, - "ErrorList": { - "type": "object" - }, - "InteractionType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5 - ] - }, - "InteractionData": { - "type": "object", - "properties": { - "application_command": { + "interaction": { "type": "object", - "properties": {}, - "additionalProperties": true + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/InteractionType" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "id", + "name", + "type" + ] }, - "attachments": { + "interaction_metadata": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/InteractionType" + }, + "user_id": { + "type": "string" + }, + "authorizing_integration_owners": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "name": { + "type": "string" + }, + "command_type": { + "$ref": "#/components/schemas/ApplicationCommandType" + } + }, + "additionalProperties": false, + "required": [ + "authorizing_integration_owners", + "command_type", + "id", + "name", + "type", + "user_id" + ] + }, + "components": { "type": "array", "items": { - "$ref": "#/components/schemas/UploadAttachmentRequestSchema" + "$ref": "#/components/schemas/ActionRowComponent" } }, - "id": { + "poll": { + "$ref": "#/components/schemas/Poll" + }, + "username": { "type": "string" }, - "name": { + "avatar": { "type": "string" }, - "options": { + "message_snapshots": { "type": "array", "items": { - "$ref": "#/components/schemas/ApplicationCommandOption" + "$ref": "#/components/schemas/MessageSnapshot" } }, - "type": { - "type": "integer" + "reply_ids": { + "type": "array", + "items": { + "type": "string" + } }, - "version": { - "type": "string" - } - }, - "required": [ - "application_command", - "attachments", - "id", - "name", - "options", - "type", - "version" - ] - }, - "UploadAttachmentRequest": { - "type": "object", - "properties": { "id": { "type": "string" }, - "filename": { - "type": "string" - }, - "file_size": { - "type": "integer" - }, - "is_clip": { - "type": "boolean" - }, - "original_content_type": { - "type": "string" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ - "file_size", - "filename" - ] - }, - "InteractionCallbackType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12 + "__@annotationsKey@11707", + "channel", + "embeds", + "flags", + "id", + "mention_channels", + "mention_roles", + "mentions", + "message_snapshots", + "pinned", + "reactions", + "timestamp", + "type" ] }, "Channel": { @@ -9625,7 +8060,15 @@ "$ref": "#/components/schemas/User" }, "last_pin_timestamp": { - "type": "integer" + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] }, "default_auto_archive_duration": { "type": "integer" @@ -9726,9 +8169,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "created_at", "flags", "id", @@ -9762,12 +8215,6 @@ 17, 18, 19, - 20, - 21, - 33, - 34, - 35, - 64, 255 ] }, @@ -9791,9 +8238,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "channel", "channel_id", "closed", @@ -9992,9 +8449,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "bio", "bot", "connected_accounts", @@ -10091,9 +8558,19 @@ }, "session_nickname": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "activities", "client_info", "client_status", @@ -10384,9 +8861,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "from", "from_id", "id", @@ -10470,9 +8957,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "external_id", "id", "name", @@ -10674,9 +9171,19 @@ "view_nsfw_guilds": { "type": "boolean", "default": true + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "afk_timeout", "allow_accessibility_detection", "animate_emoji", @@ -10790,9 +9297,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "counter", "id", "key_id", @@ -10949,9 +9466,19 @@ }, "flags": { "$ref": "#/components/schemas/ThreadMemberFlags" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "channel", "flags", "id", @@ -11051,13 +9578,28 @@ }, "collectibles": { "$ref": "#/components/schemas/Collectibles" + }, + "flags": { + "type": "integer", + "default": 0 + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "banner", "bio", "communication_disabled_until", "deaf", + "flags", "guild", "guild_id", "id", @@ -11296,9 +9838,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "bans", "channel_ordering", "channels", @@ -11352,9 +9904,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "executor", "executor_id", "guild", @@ -11423,9 +9985,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "color", "colors", "flags", @@ -11497,9 +10069,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "code", "created_at", "creator", @@ -11556,9 +10138,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "animated", "available", "groups", @@ -11614,9 +10206,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "format_type", "id", "name", @@ -11650,9 +10252,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "id", "name", "stickers" @@ -11732,9 +10344,19 @@ }, "flags": { "type": "integer" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "channel", "channel_id", "code", @@ -11807,9 +10429,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "channel", "channel_id", "deaf", @@ -11883,9 +10515,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "application", "application_id", "avatar", @@ -12041,9 +10683,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "description", "discoverability_state", "discovery_eligibility_flags", @@ -12085,9 +10737,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "id", "members", "name", @@ -12124,9 +10786,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "id", "membership_state", "permissions", @@ -12381,39 +11053,74 @@ "last_message_id": { "type": "string" }, - "public_ack": { + "last_acked_id": { "type": "string" }, "notifications_cursor": { "type": "string" }, + "mention_count": { + "type": "integer" + }, + "badge_count": { + "type": "integer" + }, "last_pin_timestamp": { "type": "string", "format": "date-time" }, - "mention_count": { - "type": "integer" + "read_state_type": { + "$ref": "#/components/schemas/ReadStateType" }, - "manual": { - "type": "boolean" + "flags": { + "$ref": "#/components/schemas/ReadStateFlags" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", + "badge_count", "channel", "channel_id", + "flags", "id", - "last_message_id", - "manual", "mention_count", "notifications_cursor", - "public_ack", + "read_state_type", "user", "user_id" ] }, + "ReadStateType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ] + }, + "ReadStateFlags": { + "type": "number", + "enum": [ + 1, + 2, + 4 + ] + }, "ThreadMetadata": { "type": "object", "properties": { @@ -12468,9 +11175,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "channel", "channel_id", "id", @@ -12493,34 +11210,155 @@ "proxy_url": { "type": "string" }, - "height": { - "type": "integer" + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + }, + "content_type": { + "type": "string" + }, + "message_id": { + "type": "string" + }, + "message": { + "$ref": "#/components/schemas/Message" + }, + "id": { + "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "required": [ + "__@annotationsKey@11707", + "filename", + "id", + "message", + "message_id", + "proxy_url", + "size", + "url" + ] + }, + "Embed": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "color": { + "type": "integer" + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/components/schemas/EmbedImage" }, - "width": { - "type": "integer" + "thumbnail": { + "$ref": "#/components/schemas/EmbedImage" }, - "content_type": { - "type": "string" + "video": { + "$ref": "#/components/schemas/EmbedImage" }, - "message_id": { - "type": "string" + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false }, - "message": { - "$ref": "#/components/schemas/Message" + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false }, - "id": { - "type": "string" + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name", + "value" + ] + } } - }, - "required": [ - "filename", - "id", - "message", - "message_id", - "proxy_url", - "size", - "url" - ] + } }, "EmbedImage": { "type": "object", @@ -12562,20 +11400,122 @@ ] }, "PartialEmoji": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] }, - "animated": { - "type": "boolean" + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] } - }, - "required": [ - "name" + ] + }, + "MessageType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 255 + ] + }, + "ApplicationCommandType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4 ] }, "ActionRowComponent": { @@ -12626,7 +11566,44 @@ "type": "string" }, "emoji": { - "$ref": "#/components/schemas/PartialEmoji" + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + } + ] }, "custom_id": { "type": "string" @@ -12790,7 +11767,44 @@ "type": "string" }, "emoji": { - "$ref": "#/components/schemas/PartialEmoji" + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + } + ] }, "default": { "type": "boolean" @@ -12887,7 +11901,44 @@ "type": "string" }, "emoji": { - "$ref": "#/components/schemas/PartialEmoji" + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + } + ] } } }, @@ -12948,24 +11999,41 @@ "message": { "type": "object", "properties": { - "attachments": { + "content": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "edited_timestamp": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "mentions": { "type": "array", "items": { - "$ref": "#/components/schemas/Attachment" + "type": "string" } }, - "components": { + "mention_roles": { "type": "array", "items": { - "$ref": "#/components/schemas/ActionRowComponent" + "type": "string" } }, - "content": { - "type": "string" - }, - "edited_timestamp": { - "type": "string", - "format": "date-time" + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Attachment" + } }, "embeds": { "type": "array", @@ -12973,69 +12041,42 @@ "$ref": "#/components/schemas/Embed" } }, + "type": { + "$ref": "#/components/schemas/MessageType" + }, "flags": { "type": "integer" }, - "mention_roles": { + "components": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MessageComponent" } }, - "mentions": { + "resolved": { "type": "array", "items": { - "type": "string" + "type": "object", + "properties": {}, + "additionalProperties": true } }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "type": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 127, - 128, - 129, - 13, - 130, - 131, - 132, - 14, - 15, - 16, - 17, - 18, - 19, - 2, - 20, - 21, - 22, - 23, - 24, - 255, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "type": "number" + "sticker_items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Sticker" + } } }, "additionalProperties": false, "required": [ "content", "embeds", + "flags", "mention_roles", - "mentions" + "mentions", + "timestamp", + "type" ] } }, @@ -13043,6 +12084,44 @@ "message" ] }, + "MessageComponent": { + "type": "object", + "properties": { + "type": { + "$ref": "#/components/schemas/MessageComponentType" + } + }, + "required": [ + "type" + ] + }, + "MessageComponentType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23 + ] + }, "InteractionGuild": { "type": "object", "properties": { @@ -13075,6 +12154,9 @@ "guild_id": { "type": "string" }, + "flags": { + "type": "integer" + }, "mute": { "type": "boolean" }, @@ -13138,6 +12220,7 @@ "bio", "communication_disabled_until", "deaf", + "flags", "guild_id", "id", "joined_at", @@ -13707,175 +12790,26 @@ "type": "integer" } }, - "button_colors": { - "type": "array", - "items": { - "type": "integer" - } - }, - "confetti_colors": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - "required": [ - "background_colors", - "button_colors", - "confetti_colors" - ] - }, - "CollectiblesCategoryProductItem": { - "type": "object", - "properties": { - "sku_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "store_listing_id": { - "type": "string" - }, - "banner": { - "type": "string" - }, - "unpublished_at": { - "type": "string", - "nullable": true - }, - "styles": { - "$ref": "#/components/schemas/CollectiblesCategoryStyle" - }, - "prices": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "country_prices": { - "$ref": "#/components/schemas/CountryPrice" - } - }, - "additionalProperties": false, - "required": [ - "country_prices" - ] - } - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProductItem" - } - }, - "type": { - "type": "integer" - }, - "premium_type": { - "type": "integer" - }, - "category_sku_id": { - "type": "string" - }, - "google_sku_ids": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "variants": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProductItemVariant" - } - } - }, - "required": [ - "banner", - "category_sku_id", - "google_sku_ids", - "items", - "name", - "premium_type", - "prices", - "sku_id", - "store_listing_id", - "styles", - "summary", - "type", - "unpublished_at" - ] - }, - "CountryPrice": { - "type": "object", - "properties": { - "country_code": { - "type": "string" - }, - "prices": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PriceEntry" - } - } - }, - "required": [ - "country_code", - "prices" - ] - }, - "PriceEntry": { - "type": "object", - "properties": { - "amount": { - "type": "integer" - }, - "currency": { - "type": "string" - }, - "exponent": { - "type": "integer" - } - }, - "required": [ - "amount", - "currency", - "exponent" - ] - }, - "ProductItem": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "sku_id": { - "type": "string" - }, - "asset": { - "type": "string" - }, - "label": { - "type": "string" + "button_colors": { + "type": "array", + "items": { + "type": "integer" + } }, - "palette": { - "type": "string" + "confetti_colors": { + "type": "array", + "items": { + "type": "integer" + } } }, "required": [ - "id", - "sku_id", - "type" + "background_colors", + "button_colors", + "confetti_colors" ] }, - "ProductItemVariant": { + "CollectiblesCategoryProductItem": { "type": "object", "properties": { "sku_id": { @@ -13884,15 +12818,9 @@ "name": { "type": "string" }, - "name_localizations": { - "type": "null" - }, "summary": { "type": "string" }, - "summary_localizations": { - "type": "null" - }, "store_listing_id": { "type": "string" }, @@ -13942,1086 +12870,695 @@ "type": "string" } }, - "base_variant_sku_id": { - "type": "string" - }, - "base_variant_name": { - "type": "string" - }, - "variant_label": { - "type": "string" - }, - "variant_value": { - "type": "string" - } - }, - "required": [ - "base_variant_name", - "base_variant_sku_id", - "category_sku_id", - "items", - "name", - "name_localizations", - "premium_type", - "prices", - "sku_id", - "store_listing_id", - "summary", - "summary_localizations", - "type", - "variant_label", - "variant_value" - ] - }, - "StaticAnimatedAsset": { - "type": "object", - "properties": { - "animated": { - "type": "string", - "nullable": true - }, - "static": { - "type": "string" - } - }, - "required": [ - "animated", - "static" - ] - }, - "CollectiblesMarketingItem": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "version": { - "type": "integer" - }, - "title": { - "type": "string" - }, - "body": { - "type": "string" - } - }, - "required": [ - "body", - "title", - "type", - "version" - ] - }, - "AnyShopBlock": { - "anyOf": [ - { - "$ref": "#/components/schemas/ItemRowShopBlock" - }, - { - "$ref": "#/components/schemas/BundleTileRowShopBlock" - }, - { - "$ref": "#/components/schemas/ItemCollectionShopBlock" - } - ] - }, - "ItemRowShopBlock": { - "type": "object", - "properties": { - "type": { - "type": "integer", - "const": 0 - }, - "category_sku_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "category_store_listing_id": { - "type": "string" - }, - "banner_asset": { - "$ref": "#/components/schemas/StaticAnimatedAsset" - }, - "logo_url": { - "type": "string" - }, - "unpublished_at": { - "type": "string", - "nullable": true - }, - "summary": { - "type": "string" - }, - "ranked_sku_ids": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "banner_asset", - "category_sku_id", - "category_store_listing_id", - "logo_url", - "name", - "ranked_sku_ids", - "summary", - "type", - "unpublished_at" - ] - }, - "BundleTileRowShopBlock": { - "type": "object", - "properties": { - "type": { - "type": "integer", - "const": 1 - }, - "subblocks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ShopBlockSubBlock" - } - } - }, - "required": [ - "subblocks", - "type" - ] - }, - "ShopBlockSubBlock": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "category_store_listing_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "unpublished_at": { - "type": "string", - "nullable": true - }, - "banner_url": { - "type": "string" - }, - "body_text": { - "type": "string", - "nullable": true - }, - "banner_text_color": { - "type": "integer", - "nullable": true - } - }, - "required": [ - "banner_text_color", - "banner_url", - "body_text", - "category_store_listing_id", - "name", - "type", - "unpublished_at" - ] - }, - "ItemCollectionShopBlock": { - "type": "object", - "properties": { - "type": { - "type": "integer", - "const": 2 - }, - "ranked_sku_ids": { + "variants": { "type": "array", "items": { - "type": "string" - } - }, - "sorted_sku_ids": { - "type": "object", - "properties": { - "recommended": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "null" - } - ] - }, - "popular": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": [ - "popular", - "recommended" - ] + "$ref": "#/components/schemas/ProductItemVariant" + } } }, "required": [ - "ranked_sku_ids", - "sorted_sku_ids", - "type" + "banner", + "category_sku_id", + "google_sku_ids", + "items", + "name", + "premium_type", + "prices", + "sku_id", + "store_listing_id", + "styles", + "summary", + "type", + "unpublished_at" ] }, - "PartialMessage": { - "description": "https://docs.discord.food/resources/message#partial-message-structure", + "CountryPrice": { "type": "object", "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/MessageType" - }, - "content": { + "country_code": { "type": "string" }, - "author": { - "$ref": "#/components/schemas/PartialUser" - }, - "flags": { - "type": "integer" - }, - "application_id": { - "type": "string" + "prices": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PriceEntry" + } } }, "required": [ - "author", - "channel_id", - "content", - "id", - "type" + "country_code", + "prices" ] }, - "PartialUser": { + "PriceEntry": { "type": "object", "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "username": { - "type": "string" + "amount": { + "type": "integer" }, - "discriminator": { + "currency": { "type": "string" }, - "global_name": { - "type": "string", - "nullable": true - }, - "avatar": { - "type": "string", - "nullable": true - }, - "avatar_decoration_data": { - "anyOf": [ - { - "$ref": "#/components/schemas/AvatarDecorationData" - }, - { - "type": "null" - } - ] - }, - "collectibles": { - "anyOf": [ - { - "$ref": "#/components/schemas/Collectibles" - }, - { - "type": "null" - } - ] - }, - "display_name_styles": { - "anyOf": [ - { - "$ref": "#/components/schemas/DisplayNameStyle" - }, - { - "type": "null" - } - ] - }, - "primary_guild": { - "anyOf": [ - { - "$ref": "#/components/schemas/PrimaryGuild" - }, - { - "type": "null" - } - ] - }, - "bot": { - "type": "boolean" - }, - "system": { - "type": "boolean" - }, - "banner": { - "type": "string", - "nullable": true - }, - "accent_color": { - "type": "integer", - "nullable": true - }, - "public_flags": { + "exponent": { "type": "integer" } }, "required": [ - "avatar", - "discriminator", - "id", - "username" + "amount", + "currency", + "exponent" ] }, - "HubGuild": { + "ProductItem": { "type": "object", "properties": { - "icon": { - "type": "string" + "type": { + "type": "integer" }, "id": { "type": "string" }, - "name": { + "sku_id": { + "type": "string" + }, + "asset": { + "type": "string" + }, + "label": { + "type": "string" + }, + "palette": { "type": "string" } }, "required": [ - "icon", "id", - "name" + "sku_id", + "type" ] }, - "EmojiGuild": { + "ProductItemVariant": { "type": "object", "properties": { - "id": { + "sku_id": { "type": "string" }, "name": { "type": "string" }, - "icon": { - "type": "string", - "nullable": true + "name_localizations": { + "type": "null" }, - "description": { + "summary": { + "type": "string" + }, + "summary_localizations": { + "type": "null" + }, + "store_listing_id": { + "type": "string" + }, + "banner": { + "type": "string" + }, + "unpublished_at": { "type": "string", "nullable": true }, - "features": { - "type": "array", - "items": { - "type": "string" + "styles": { + "$ref": "#/components/schemas/CollectiblesCategoryStyle" + }, + "prices": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "country_prices": { + "$ref": "#/components/schemas/CountryPrice" + } + }, + "additionalProperties": false, + "required": [ + "country_prices" + ] } }, - "emojis": { + "items": { "type": "array", "items": { - "$ref": "#/components/schemas/Emoji" + "$ref": "#/components/schemas/ProductItem" } }, - "premium_tier": { + "type": { "type": "integer" }, - "premium_subscription_count": { + "premium_type": { "type": "integer" }, - "approximate_member_count": { - "type": "integer" + "category_sku_id": { + "type": "string" }, - "approximate_presence_count": { - "type": "integer" + "google_sku_ids": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "base_variant_sku_id": { + "type": "string" + }, + "base_variant_name": { + "type": "string" + }, + "variant_label": { + "type": "string" + }, + "variant_value": { + "type": "string" } }, "required": [ - "emojis", - "features", - "id", + "base_variant_name", + "base_variant_sku_id", + "category_sku_id", + "items", "name", - "premium_tier" + "name_localizations", + "premium_type", + "prices", + "sku_id", + "store_listing_id", + "summary", + "summary_localizations", + "type", + "variant_label", + "variant_value" ] }, - "EmojiApplication": { + "StaticAnimatedAsset": { "type": "object", "properties": { - "id": { - "type": "string" + "animated": { + "type": "string", + "nullable": true }, - "name": { + "static": { "type": "string" } }, "required": [ - "id", - "name" + "animated", + "static" ] }, - "GuildMessagesSearchMessage": { + "CollectiblesMarketingItem": { "type": "object", "properties": { - "id": { - "type": "string" - }, "type": { - "$ref": "#/components/schemas/MessageType" + "type": "integer" }, - "content": { - "type": "string" + "version": { + "type": "integer" }, - "channel_id": { + "title": { "type": "string" }, - "author": { - "$ref": "#/components/schemas/PublicUser" - }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Attachment" - } - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Embed_1" - } - }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicUser" - } - }, - "mention_roles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Role" - } - }, - "pinned": { - "type": "boolean" - }, - "mention_everyone": { - "type": "boolean" - }, - "tts": { - "type": "boolean" - }, - "timestamp": { + "body": { "type": "string" - }, - "edited_timestamp": { - "type": "string", - "nullable": true - }, - "flags": { - "type": "integer" - }, - "components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ActionRowComponent_1" - } - }, - "poll": { - "$ref": "#/components/schemas/Poll_1" - }, - "hit": { - "type": "boolean", - "const": true } }, "required": [ - "attachments", - "author", - "channel_id", - "components", - "edited_timestamp", - "embeds", - "flags", - "hit", - "id", - "mention_roles", - "mentions", - "pinned", - "poll", - "timestamp", - "tts", - "type" + "body", + "title", + "type", + "version" ] }, - "MessageType_1": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 127, - 128, - 129, - 130, - 131, - 132, - 255 + "AnyShopBlock": { + "anyOf": [ + { + "$ref": "#/components/schemas/ItemRowShopBlock" + }, + { + "$ref": "#/components/schemas/BundleTileRowShopBlock" + }, + { + "$ref": "#/components/schemas/ItemCollectionShopBlock" + } ] }, - "Embed_1": { + "ItemRowShopBlock": { "type": "object", "properties": { - "title": { - "type": "string" - }, "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" + "type": "integer", + "const": 0 }, - "description": { + "category_sku_id": { "type": "string" }, - "url": { + "name": { "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { - "type": "integer" - }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" - ] - }, - "image": { - "$ref": "#/components/schemas/EmbedImage_1" + "category_store_listing_id": { + "type": "string" }, - "thumbnail": { - "$ref": "#/components/schemas/EmbedImage_1" + "banner_asset": { + "$ref": "#/components/schemas/StaticAnimatedAsset" }, - "video": { - "$ref": "#/components/schemas/EmbedImage_1" + "logo_url": { + "type": "string" }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "additionalProperties": false + "unpublished_at": { + "type": "string", + "nullable": true }, - "author": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false + "summary": { + "type": "string" }, - "fields": { + "ranked_sku_ids": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "inline": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name", - "value" - ] - } - } - } - }, - "EmbedImage_1": { - "type": "object", - "properties": { - "url": { - "type": "string" - }, - "proxy_url": { - "type": "string" - }, - "height": { - "type": "integer" - }, - "width": { - "type": "integer" + "type": "string" + } } - } + }, + "required": [ + "banner_asset", + "category_sku_id", + "category_store_listing_id", + "logo_url", + "name", + "ranked_sku_ids", + "summary", + "type", + "unpublished_at" + ] }, - "ActionRowComponent_1": { + "BundleTileRowShopBlock": { "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/MessageComponentType.ActionRow_1" + "type": "integer", + "const": 1 }, - "components": { + "subblocks": { "type": "array", "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/ButtonComponent_1" - }, - { - "$ref": "#/components/schemas/SelectMenuComponent_1" - }, - { - "$ref": "#/components/schemas/StringSelectMenuComponent_1" - }, - { - "$ref": "#/components/schemas/TextInputComponent_1" - } - ] + "$ref": "#/components/schemas/ShopBlockSubBlock" } } }, "required": [ - "components", + "subblocks", "type" ] }, - "MessageComponentType.ActionRow_1": { - "type": "number", - "const": 1 - }, - "ButtonComponent_1": { + "ShopBlockSubBlock": { "type": "object", "properties": { "type": { - "$ref": "#/components/schemas/MessageComponentType.Button_1" - }, - "style": { - "$ref": "#/components/schemas/ButtonStyle_1" + "type": "integer" }, - "label": { + "category_store_listing_id": { "type": "string" }, - "emoji": { - "$ref": "#/components/schemas/PartialEmoji_1" - }, - "custom_id": { + "name": { "type": "string" }, - "sku_id": { - "type": "string" + "unpublished_at": { + "type": "string", + "nullable": true }, - "url": { + "banner_url": { "type": "string" }, - "disabled": { - "type": "boolean" + "body_text": { + "type": "string", + "nullable": true + }, + "banner_text_color": { + "type": "integer", + "nullable": true } }, "required": [ - "style", - "type" + "banner_text_color", + "banner_url", + "body_text", + "category_store_listing_id", + "name", + "type", + "unpublished_at" ] }, - "MessageComponentType.Button_1": { - "type": "number", - "const": 2 - }, - "ButtonStyle_1": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6 + "ItemCollectionShopBlock": { + "type": "object", + "properties": { + "type": { + "type": "integer", + "const": 2 + }, + "ranked_sku_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "sorted_sku_ids": { + "type": "object", + "properties": { + "recommended": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "popular": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "popular", + "recommended" + ] + } + }, + "required": [ + "ranked_sku_ids", + "sorted_sku_ids", + "type" ] }, - "PartialEmoji_1": { + "PartialMessage": { + "description": "https://docs.discord.food/resources/message#partial-message-structure", "type": "object", "properties": { "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "name": { + "channel_id": { "type": "string" }, - "animated": { - "type": "boolean" + "type": { + "$ref": "#/components/schemas/MessageType" + }, + "content": { + "type": "string" + }, + "author": { + "$ref": "#/components/schemas/PartialUser" + }, + "flags": { + "type": "integer" + }, + "application_id": { + "type": "string" } }, "required": [ - "name" + "author", + "channel_id", + "content", + "id", + "type" ] }, - "SelectMenuComponent_1": { + "PartialUser": { "type": "object", "properties": { - "type": { - "enum": [ - 3, - 5, - 6, - 7, - 8 - ], - "type": "number" - }, - "custom_id": { + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "channel_types": { - "type": "array", - "items": { - "type": "integer" - } + "username": { + "type": "string" }, - "placeholder": { + "discriminator": { "type": "string" }, - "default_values": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SelectMenuDefaultOption_1" - } + "global_name": { + "type": "string", + "nullable": true }, - "min_values": { - "type": "integer" + "avatar": { + "type": "string", + "nullable": true }, - "max_values": { - "type": "integer" + "avatar_decoration_data": { + "anyOf": [ + { + "$ref": "#/components/schemas/AvatarDecorationData" + }, + { + "type": "null" + } + ] }, - "disabled": { + "collectibles": { + "anyOf": [ + { + "$ref": "#/components/schemas/Collectibles" + }, + { + "type": "null" + } + ] + }, + "display_name_styles": { + "anyOf": [ + { + "$ref": "#/components/schemas/DisplayNameStyle" + }, + { + "type": "null" + } + ] + }, + "primary_guild": { + "anyOf": [ + { + "$ref": "#/components/schemas/PrimaryGuild" + }, + { + "type": "null" + } + ] + }, + "bot": { + "type": "boolean" + }, + "system": { "type": "boolean" + }, + "banner": { + "type": "string", + "nullable": true + }, + "accent_color": { + "type": "integer", + "nullable": true + }, + "public_flags": { + "type": "integer" } }, "required": [ - "custom_id", - "type" + "avatar", + "discriminator", + "id", + "username" ] }, - "SelectMenuDefaultOption_1": { + "HubGuild": { "type": "object", "properties": { + "icon": { + "type": "string" + }, "id": { "type": "string" }, - "type": { - "enum": [ - "channel", - "role", - "user" - ], + "name": { "type": "string" } }, "required": [ + "icon", "id", - "type" + "name" ] }, - "StringSelectMenuComponent_1": { + "EmojiGuild": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/MessageComponentType.StringSelect_1" - }, - "options": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SelectMenuOption_1" - } + "id": { + "type": "string" }, - "custom_id": { + "name": { "type": "string" }, - "channel_types": { + "icon": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "features": { "type": "array", "items": { - "type": "integer" + "type": "string" } }, - "placeholder": { - "type": "string" - }, - "default_values": { + "emojis": { "type": "array", "items": { - "$ref": "#/components/schemas/SelectMenuDefaultOption_1" + "$ref": "#/components/schemas/Emoji" } }, - "min_values": { + "premium_tier": { "type": "integer" }, - "max_values": { + "premium_subscription_count": { "type": "integer" }, - "disabled": { - "type": "boolean" + "approximate_member_count": { + "type": "integer" + }, + "approximate_presence_count": { + "type": "integer" } }, "required": [ - "custom_id", - "options", - "type" + "emojis", + "features", + "id", + "name", + "premium_tier" ] }, - "MessageComponentType.StringSelect_1": { - "type": "number", - "const": 3 - }, - "SelectMenuOption_1": { + "EmojiApplication": { "type": "object", "properties": { - "label": { - "type": "string" - }, - "value": { + "id": { "type": "string" }, - "description": { + "name": { "type": "string" - }, - "emoji": { - "$ref": "#/components/schemas/PartialEmoji_1" - }, - "default": { - "type": "boolean" } }, "required": [ - "label", - "value" + "id", + "name" ] }, - "TextInputComponent_1": { + "GuildMessagesSearchMessage": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/MessageComponentType.TextInput_1" - }, - "custom_id": { + "id": { "type": "string" }, - "style": { - "$ref": "#/components/schemas/TextInputStyle_1" + "type": { + "$ref": "#/components/schemas/MessageType" }, - "label": { + "content": { "type": "string" }, - "min_length": { - "type": "integer" + "channel_id": { + "type": "string" }, - "max_length": { - "type": "integer" + "author": { + "$ref": "#/components/schemas/PublicUser" }, - "required": { - "type": "boolean" + "attachments": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Attachment" + } }, - "value": { - "type": "string" + "embeds": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Embed" + } }, - "placeholder": { - "type": "string" - } - }, - "required": [ - "custom_id", - "label", - "style", - "type" - ] - }, - "MessageComponentType.TextInput_1": { - "type": "number", - "const": 4 - }, - "TextInputStyle_1": { - "type": "number", - "enum": [ - 1, - 2 - ] - }, - "Poll_1": { - "type": "object", - "properties": { - "question": { - "$ref": "#/components/schemas/PollMedia_1" + "mentions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicUser" + } }, - "answers": { + "mention_roles": { "type": "array", "items": { - "$ref": "#/components/schemas/PollAnswer_1" + "$ref": "#/components/schemas/Role" } }, - "expiry": { - "type": "string", - "format": "date-time" + "pinned": { + "type": "boolean" }, - "allow_multiselect": { + "mention_everyone": { "type": "boolean" }, - "results": { - "$ref": "#/components/schemas/PollResult_1" - } - }, - "required": [ - "allow_multiselect", - "answers", - "expiry", - "question" - ] - }, - "PollMedia_1": { - "type": "object", - "properties": { - "text": { - "type": "string" + "tts": { + "type": "boolean" }, - "emoji": { - "$ref": "#/components/schemas/PartialEmoji_1" - } - } - }, - "PollAnswer_1": { - "type": "object", - "properties": { - "answer_id": { + "timestamp": { "type": "string" }, - "poll_media": { - "$ref": "#/components/schemas/PollMedia_1" - } - }, - "required": [ - "poll_media" - ] - }, - "PollResult_1": { - "type": "object", - "properties": { - "is_finalized": { - "type": "boolean" + "edited_timestamp": { + "type": "string", + "nullable": true }, - "answer_counts": { + "flags": { + "type": "integer" + }, + "components": { "type": "array", "items": { - "$ref": "#/components/schemas/PollAnswerCount_1" + "$ref": "#/components/schemas/ActionRowComponent" } - } - }, - "required": [ - "answer_counts", - "is_finalized" - ] - }, - "PollAnswerCount_1": { - "type": "object", - "properties": { - "id": { - "type": "string" }, - "count": { - "type": "integer" + "poll": { + "$ref": "#/components/schemas/Poll" }, - "me_voted": { - "type": "boolean" + "hit": { + "type": "boolean", + "const": true } }, "required": [ - "count", + "attachments", + "author", + "channel_id", + "components", + "edited_timestamp", + "embeds", + "flags", + "hit", "id", - "me_voted" + "mention_roles", + "mentions", + "pinned", + "poll", + "timestamp", + "tts", + "type" ] }, "GuildVanityUrl": { @@ -15122,7 +13659,7 @@ "length": { "type": "integer" }, - "__@unscopables@700": { + "__@unscopables@697": { "type": "object", "additionalProperties": false, "patternProperties": { @@ -15248,28 +13785,17 @@ "with": { "type": "boolean" }, - "remove": { - "type": "boolean" - }, - "distinct": { - "description": "Returns a new array with duplicate elements removed.", + "__@iterator@695": { "type": "boolean" }, - "single": { - "description": "Returns the only element matching the predicate, or undefined.\nThrows if more than one element matches.", - "type": "boolean" - }, - "__@iterator@698": { - "type": "boolean" - }, - "__@unscopables@700": { + "__@unscopables@697": { "type": "boolean" } } } }, "required": [ - "__@unscopables@700", + "__@unscopables@697", "length" ] }, @@ -15300,9 +13826,19 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "code", "consumed", "expired", @@ -15412,9 +13948,19 @@ }, "icon": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "id", "is_primary", "localizations", @@ -15791,9 +14337,19 @@ }, "link": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "required": [ + "__@annotationsKey@11707", "description", "icon", "id" @@ -15805,6 +14361,11 @@ "parse": { "type": "array", "items": { + "enum": [ + "everyone", + "roles", + "users" + ], "type": "string" } }, @@ -15825,6 +14386,8 @@ } } }, + "User_DisplayNameEffect": {}, + "User_DisplayNameFont": {}, "MessageActivity": { "type": "object", "properties": { @@ -17926,7 +16489,6 @@ "bearer": [] } ], - "description": "Returns all consents for the authenticated user. Supports filtering by consent_type, status, service_id, item_id, and target_user_id. For HB 805 pairwise consents (O(n²) per item), use item_id and target_user_id filters.", "summary": "List consents for the current user", "responses": { "200": { @@ -17934,166 +16496,26 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserConsentListResponse" + "$ref": "#/components/schemas/any" } } } } }, - "parameters": [ - { - "name": "consent_type", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by consent type" - }, - { - "name": "status", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by consent status" - }, - { - "name": "service_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by service ID" - }, - { - "name": "item_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by item ID (for per-item consents)" - }, - { - "name": "target_user_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by target user ID (for HB 805 pairwise consents)" - } - ], "tags": [ "users" ] } }, "/users/@me/consents/{service_id}/": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "description": "Returns the consent record for a specific service for the authenticated user. For HB 805 pairwise consents, use item_id and target_user_id to identify specific consent records.", - "summary": "Get consent details for a specific service", - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserConsentResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } - } - }, - "parameters": [ - { - "name": "service_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "service_id" - }, - { - "name": "consent_type", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by consent type" - }, - { - "name": "item_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by item ID (for per-item consents)" - }, - { - "name": "target_user_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Filter by target user ID (for HB 805 pairwise consents)" - } - ], - "tags": [ - "users" - ] - }, "put": { "security": [ { "bearer": [] } ], - "description": "Grants consent for a service. Supports GDPR-compliant consent with basis documents for off-platform consents. For HB 805 pairwise consents (O(n²) where n is number of persons), use target_user_id to specify who receives consent to view/share. Based on GNAP (RFC 9635) grant negotiation principles.", - "summary": "Grant consent for a service", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserConsentGrantSchema" - } - } - } - }, + "summary": "Grant consent for a service for the current user", "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserConsentResponse" - } - } - } - }, "204": { "description": "", "content": { @@ -18126,8 +16548,7 @@ "bearer": [] } ], - "description": "Revokes consent for a service. Per GDPR Article 7(3), withdrawal of consent is as easy as giving consent. For HB 805 pairwise consents, specify item_id and target_user_id. The consent record is retained with RETRACTED status for audit purposes.", - "summary": "Revoke consent for a service (GDPR-compliant)", + "summary": "Revoke consent for a service for the current user", "responses": { "204": { "description": "", @@ -18149,33 +16570,6 @@ "type": "string" }, "description": "service_id" - }, - { - "name": "consent_type", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Consent type to revoke" - }, - { - "name": "item_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Item ID for per-item consent revocation" - }, - { - "name": "target_user_id", - "in": "query", - "required": false, - "schema": { - "type": "string" - }, - "description": "Target user ID for HB 805 pairwise consent revocation" } ], "tags": [ @@ -18782,7 +17176,6 @@ ] }, "post": { - "x-right-required": "CREATE_DMS", "security": [ { "bearer": [] @@ -18806,118 +17199,17 @@ "schema": { "$ref": "#/components/schemas/DmChannelDTO" } - } - } - } - }, - "tags": [ - "users" - ] - } - }, - "/users/@me/billing/subscriptions/": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "tags": [ - "users" - ] - } - }, - "/users/@me/billing/payment-sources/": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "tags": [ - "users" - ] - }, - "post": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "tags": [ - "users" - ] - } - }, - "/users/@me/billing/payment-sources/{payment_source_id}": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "payment_source_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "payment_source_id" - } - ], - "tags": [ - "users" - ] - }, - "patch": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "payment_source_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "payment_source_id" + } + } } - ], + }, "tags": [ "users" ] - }, - "delete": { + } + }, + "/users/@me/billing/subscriptions/": { + "get": { "security": [ { "bearer": [] @@ -18928,23 +17220,12 @@ "description": "No description available" } }, - "parameters": [ - { - "name": "payment_source_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "payment_source_id" - } - ], "tags": [ "users" ] } }, - "/users/@me/billing/location-info/": { + "/users/@me/billing/payment-sources/": { "get": { "security": [ { @@ -18959,10 +17240,8 @@ "tags": [ "users" ] - } - }, - "/users/@me/billing/country-code/": { - "get": { + }, + "post": { "security": [ { "bearer": [] @@ -18978,7 +17257,7 @@ ] } }, - "/users/@me/applications/{app_id}/entitlements/": { + "/users/@me/billing/payment-sources/{payment_source_id}": { "get": { "security": [ { @@ -18992,22 +17271,20 @@ }, "parameters": [ { - "name": "app_id", + "name": "payment_source_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "app_id" + "description": "payment_source_id" } ], "tags": [ "users" ] - } - }, - "/users/@me/applications/{application_id}/entitlements/": { - "get": { + }, + "patch": { "security": [ { "bearer": [] @@ -19020,22 +17297,20 @@ }, "parameters": [ { - "name": "application_id", + "name": "payment_source_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "application_id" + "description": "payment_source_id" } ], "tags": [ "users" ] - } - }, - "/users/@me/affinities/users/": { - "get": { + }, + "delete": { "security": [ { "bearer": [] @@ -19046,12 +17321,23 @@ "description": "No description available" } }, + "parameters": [ + { + "name": "payment_source_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "payment_source_id" + } + ], "tags": [ "users" ] } }, - "/users/@me/affinities/guilds/": { + "/users/@me/billing/location-info/": { "get": { "security": [ { @@ -19068,7 +17354,7 @@ ] } }, - "/users/@me/activities/statistics/applications/": { + "/users/@me/billing/country-code/": { "get": { "security": [ { @@ -19085,7 +17371,7 @@ ] } }, - "/users/{id}/relationships/": { + "/users/@me/applications/{application_id}/entitlements/": { "get": { "security": [ { @@ -19093,36 +17379,19 @@ } ], "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserRelationsResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } + "default": { + "description": "No description available" } }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" } ], "tags": [ @@ -19130,78 +17399,24 @@ ] } }, - "/users/{id}/profile/": { + "/users/@me/affinities/users/": { "get": { "security": [ { "bearer": [] } ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserProfileResponse" - } - } - } - } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "id" - } - ], - "tags": [ - "users" - ] - }, - "patch": { - "security": [ - { - "bearer": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UserProfileModifySchema" - } - } - } - }, "responses": { "default": { "description": "No description available" } }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "id" - } - ], "tags": [ "users" ] } }, - "/users/{id}/": { + "/users/@me/affinities/guilds/": { "get": { "security": [ { @@ -19209,77 +17424,27 @@ } ], "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIPublicUser" - } - } - } + "default": { + "description": "No description available" } }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "id" - } - ], "tags": [ "users" ] } }, - "/users/{id}/delete/": { - "post": { - "x-right-required": "MANAGE_USERS", + "/users/@me/activities/statistics/applications/": { + "get": { "security": [ { "bearer": [] } ], "responses": { - "204": { + "default": { "description": "No description available" - }, - "403": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } - }, - "404": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } } }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "id" - } - ], "tags": [ "users" ] @@ -19851,34 +18016,6 @@ ] } }, - "/store/published-listings/applications/{id}": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "id" - } - ], - "tags": [ - "store" - ] - } - }, "/store/published-listings/applications/{application_id}/subscription-plans/": { "get": { "security": [ @@ -19935,37 +18072,36 @@ ] } }, - "/store/published-listings/applications/{id}/subscription-plans/": { - "get": { + "/stop/": { + "post": { + "x-right-required": "OPERATOR", "security": [ { "bearer": [] } ], "responses": { - "default": { + "200": { "description": "No description available" + }, + "403": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIErrorResponse" + } + } + } } - }, - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "id" - } - ], + }, "tags": [ - "store" + "stop" ] } }, - "/stop/": { - "post": { - "x-right-required": "OPERATOR", + "/stickers/{sticker_id}/": { + "get": { "security": [ { "bearer": [] @@ -19973,25 +18109,33 @@ ], "responses": { "200": { - "description": "No description available" - }, - "403": { "description": "", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/APIErrorResponse" + "$ref": "#/components/schemas/Sticker" } } } } }, + "parameters": [ + { + "name": "sticker_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "sticker_id" + } + ], "tags": [ - "stop" + "stickers" ] } }, - "/stickers/{sticker_id}/": { + "/stickers/{sticker_id}/guild": { "get": { "security": [ { @@ -21116,21 +19260,6 @@ "tags": [ "policies" ] - }, - "patch": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "tags": [ - "policies" - ] } }, "/policies/instance/config/": { @@ -25219,7 +23348,6 @@ ] }, "post": { - "x-right-required": "CREATE_CHANNELS", "x-permission-required": "MANAGE_CHANNELS", "security": [ { @@ -25252,7 +23380,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/APIError Response" + "$ref": "#/components/schemas/APIErrorResponse" } } } @@ -26686,7 +24814,7 @@ ] } }, - "/channels/{channel_id}/tickets/tickets": { + "/channels/{channel_id}/tickets/": { "post": { "security": [ { @@ -26740,7 +24868,7 @@ ] } }, - "/channels/{channel_id}/ticket/ticket": { + "/channels/{channel_id}/ticket/": { "patch": { "security": [ { @@ -29336,6 +27464,43 @@ ] } }, + "/channels/{channel_id}/attachments/{cloud_attachment_url}": { + "delete": { + "security": [ + { + "bearer": [] + } + ], + "responses": { + "default": { + "description": "No description available" + } + }, + "parameters": [ + { + "name": "channel_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "channel_id" + }, + { + "name": "cloud_attachment_url", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "cloud_attachment_url" + } + ], + "tags": [ + "channels" + ] + } + }, "/categories/": { "get": { "security": [ @@ -29988,7 +28153,6 @@ ] }, "post": { - "x-right-required": "CREATE_APPLICATIONS", "security": [ { "bearer": [] @@ -30201,7 +28365,6 @@ ] }, "patch": { - "x-right-required": "MANAGE_APPLICATIONS", "security": [ { "bearer": [] @@ -30257,7 +28420,6 @@ }, "/applications/{application_id}/delete": { "post": { - "x-right-required": "MANAGE_APPLICATIONS", "security": [ { "bearer": [] @@ -30519,145 +28681,8 @@ "tags": [ "applications" ] - } - }, - "/applications/{application_id}/entitlements/": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApplicationEntitlementsResponse" - } - } - } - } - }, - "parameters": [ - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "application_id" - } - ], - "tags": [ - "applications" - ] - } - }, - "/applications/{application_id}/commands/": { - "get": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "application_id" - } - ], - "tags": [ - "applications" - ] - }, - "post": { - "security": [ - { - "bearer": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApplicationCommandCreateSchema" - } - } - } - }, - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "application_id" - } - ], - "tags": [ - "applications" - ] }, - "put": { - "security": [ - { - "bearer": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkApplicationCommandCreateSchema" - } - } - } - }, - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "application_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "application_id" - } - ], - "tags": [ - "applications" - ] - } - }, - "/applications/{application_id}/commands/{command_id}/": { - "get": { + "delete": { "security": [ { "bearer": [] @@ -30679,49 +28704,13 @@ "description": "application_id" }, { - "name": "command_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "command_id" - } - ], - "tags": [ - "applications" - ] - }, - "patch": { - "security": [ - { - "bearer": [] - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApplicationCommandCreateSchema" - } - } - } - }, - "responses": { - "default": { - "description": "No description available" - } - }, - "parameters": [ - { - "name": "application_id", + "name": "guild_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "application_id" + "description": "guild_id" }, { "name": "command_id", @@ -30738,31 +28727,20 @@ ] } }, - "/applications/{application_id}/bot/": { - "post": { - "x-right-required": "MANAGE_APPLICATIONS", + "/applications/{application_id}/entitlements/": { + "get": { "security": [ { "bearer": [] } ], "responses": { - "204": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TokenOnlyResponse" - } - } - } - }, - "400": { + "200": { "description": "", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/APIErrorResponse" + "$ref": "#/components/schemas/ApplicationEntitlementsResponse" } } } @@ -30782,44 +28760,18 @@ "tags": [ "applications" ] - }, - "patch": { - "x-right-required": "MANAGE_APPLICATIONS", + } + }, + "/applications/{application_id}/commands/": { + "get": { "security": [ { "bearer": [] } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BotModifySchema" - } - } - } - }, "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Application" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } + "default": { + "description": "No description available" } }, "parameters": [ @@ -30836,38 +28788,28 @@ "tags": [ "applications" ] - } - }, - "/applications/{application_id}/bot/reset": { + }, "post": { - "x-right-required": "MANAGE_APPLICATIONS", "security": [ { "bearer": [] } ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TokenResponse" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApplicationCommandCreateSchema" } } } }, + "responses": { + "default": { + "description": "No description available" + } + }, "parameters": [ { "name": "application_id", @@ -30882,36 +28824,37 @@ "tags": [ "applications" ] - } - }, - "/applications/{id}/skus/": { - "get": { + }, + "put": { "security": [ { "bearer": [] } ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApplicationSkusResponse" - } + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkApplicationCommandCreateSchema" } } } }, + "responses": { + "default": { + "description": "No description available" + } + }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" } ], "tags": [ @@ -30919,7 +28862,7 @@ ] } }, - "/applications/{id}/": { + "/applications/{application_id}/commands/{command_id}/": { "get": { "security": [ { @@ -30927,36 +28870,28 @@ } ], "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Application" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } + "default": { + "description": "No description available" } }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" + }, + { + "name": "command_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "command_id" } ], "tags": [ @@ -30974,115 +28909,69 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApplicationModifySchema" + "$ref": "#/components/schemas/ApplicationCommandCreateSchema" } } } }, "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Application" - } - } - } - }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } + "default": { + "description": "No description available" } }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" - } - ], - "tags": [ - "applications" - ] - } - }, - "/applications/{id}/delete": { - "post": { - "security": [ - { - "bearer": [] - } - ], - "responses": { - "200": { - "description": "No description available" + "description": "application_id" }, - "400": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/APIErrorResponse" - } - } - } - } - }, - "parameters": [ { - "name": "id", + "name": "command_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "command_id" } ], "tags": [ "applications" ] - } - }, - "/applications/{id}/entitlements/": { - "get": { + }, + "delete": { "security": [ { "bearer": [] } ], "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ApplicationEntitlementsResponse" - } - } - } + "default": { + "description": "No description available" } }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" + }, + { + "name": "command_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "command_id" } ], "tags": [ @@ -31090,7 +28979,7 @@ ] } }, - "/applications/{id}/bot/": { + "/applications/{application_id}/bot/": { "post": { "security": [ { @@ -31121,13 +29010,13 @@ }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" } ], "tags": [ @@ -31174,13 +29063,13 @@ }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" } ], "tags": [ @@ -31188,7 +29077,7 @@ ] } }, - "/applications/{id}/bot/reset": { + "/applications/{application_id}/bot/reset": { "post": { "security": [ { @@ -31219,13 +29108,13 @@ }, "parameters": [ { - "name": "id", + "name": "application_id", "in": "path", "required": true, "schema": { "type": "string" }, - "description": "id" + "description": "application_id" } ], "tags": [ diff --git a/assets/schemas.json b/assets/schemas.json index ff14634f8..11038a7fc 100644 --- a/assets/schemas.json +++ b/assets/schemas.json @@ -125,4841 +125,1536 @@ ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ConnectedAccountCommonOAuthTokenResponse": { + "RouteResponse": { "type": "object", "properties": { - "access_token": { + "status": { + "type": "integer" + }, + "body": { + "type": "string", + "pattern": "^.*Response$" + }, + "headers": { + "$ref": "#/definitions/Record" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "FieldErrorResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "message": { "type": "string" }, - "token_type": { + "errors": { + "$ref": "#/definitions/ErrorList" + } + }, + "additionalProperties": false, + "required": [ + "code", + "errors", + "message" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InteractionSchema": { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/InteractionType" + }, + "application_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "scope": { + "guild_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "refresh_token": { + "channel_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "expires_in": { + "message_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "message_flags": { "type": "integer" + }, + "session_id": { + "type": "string" + }, + "data": { + "$ref": "#/definitions/InteractionData" + }, + "files": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "nonce": { + "type": "string" + }, + "analytics_location": { + "type": "string" + }, + "section_name": { + "type": "string" + }, + "source": { + "type": "string" } }, "additionalProperties": false, "required": [ - "access_token", - "scope", - "token_type" + "application_id", + "channel_id", + "data", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SupabaseResponse": { + "InteractionCallbackSchema": { "type": "object", "properties": { - "status": { - "type": "integer" + "type": { + "$ref": "#/definitions/InteractionCallbackType" }, - "error": { - "type": "object", - "properties": { - "message": { - "type": "string" - }, - "code": { - "type": "string" - }, - "details": {} - }, - "additionalProperties": false, - "required": [ - "message" - ] + "data": { + "$ref": "#/definitions/Message" } }, "additionalProperties": false, + "required": [ + "data", + "type" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "JsonRpcResponse": { - "description": "JSON-RPC 2.0 response object", + "InteractionCreateSchema": { "type": "object", "properties": { - "jsonrpc": { - "type": "string", - "const": "2.0" + "version": { + "type": "integer" }, "id": { - "type": [ - "null", - "string", - "integer" - ] + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" }, - "result": {}, - "error": { - "description": "JSON-RPC 2.0 error object", - "$ref": "#/definitions/JsonRpcError" + "application_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "type": { + "$ref": "#/definitions/InteractionType" + }, + "token": { + "type": "string" + }, + "data": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "guild": { + "$ref": "#/definitions/InteractionGuild" + }, + "guild_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "guild_locale": { + "type": "string" + }, + "channel": { + "$ref": "#/definitions/Channel" + }, + "channel_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "member": { + "$ref": "#/definitions/PublicMember" + }, + "user": { + "$ref": "#/definitions/PublicUser" + }, + "locale": { + "type": "string" + }, + "message": { + "$ref": "#/definitions/Message" + }, + "app_permissions": { + "type": "string" + }, + "entitlements": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "entitlement_sku_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "authorizing_integration_owners": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[0-9]+$": { + "type": "string" + } + } + }, + "context": { + "type": "integer" + }, + "attachment_size_limit": { + "type": "integer" } }, "additionalProperties": false, "required": [ + "app_permissions", + "application_id", + "attachment_size_limit", "id", - "jsonrpc" + "token", + "type", + "version" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "OpenAiResponse": { - "anyOf": [ - { - "$ref": "#/definitions/OpenAiChatCompletionObject" - }, - { - "$ref": "#/definitions/OpenAIResponseObject" - }, - { - "$ref": "#/definitions/OpenAICreateEmbeddingsObject" + "ApplicationCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" }, - { - "$ref": "#/definitions/OpenAIConversationObject" + "team_id": { + "type": "string" } + }, + "additionalProperties": false, + "required": [ + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SuccessfulResponse": { + "ApplicationModifySchema": { "type": "object", - "additionalProperties": {}, "properties": { - "id": { + "description": { "type": "string" }, - "model": { + "icon": { "type": "string" }, - "created": { - "type": "integer" + "cover_image": { + "type": "string" }, - "created_at": { - "type": "integer" + "interactions_endpoint_url": { + "type": "string" }, - "messages": { + "max_participants": { + "type": [ + "null", + "integer" + ] + }, + "name": { + "type": "string" + }, + "privacy_policy_url": { + "type": "string" + }, + "role_connections_verification_url": { + "type": "string" + }, + "tags": { "type": "array", "items": { - "type": "object", - "properties": { - "role": { - "enum": [ - "assistant", - "user" - ], - "type": "string" - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": {} - }, - { - "type": "string" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "content", - "role" - ] - } - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "text": { - "type": "string" - }, - "name": { - "description": "Tool name when type is tool_use", - "type": "string" - }, - "id": { - "description": "Tool invocation id when type is tool_use", - "type": "string" - }, - "input": { - "$ref": "#/definitions/Record" - }, - "tool_use_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - } - }, - { - "type": "string" - } - ] - }, - "completion": { - "type": "string" - }, - "input_tokens": { - "type": "integer" - }, - "usage": { - "type": "object", - "properties": { - "input_tokens": { - "type": "integer" - }, - "output_tokens": { - "type": "integer" - }, - "cache_creation_input_tokens": { - "type": "integer" - }, - "cache_read_input_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "cache_creation_input_tokens", - "cache_read_input_tokens", - "input_tokens", - "output_tokens" - ] - } - }, - "required": [ - "id", - "model" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "AnthropicAiResponse": { - "anyOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "error" - }, - "error": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "message": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "message", - "type" - ] - }, - "request_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "error", - "request_id", - "type" - ] - }, - { - "type": "object", - "additionalProperties": {}, - "properties": { - "id": { - "type": "string" - }, - "model": { - "type": "string" - }, - "created": { - "type": "integer" - }, - "created_at": { - "type": "integer" - }, - "messages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "role": { - "enum": [ - "assistant", - "user" - ], - "type": "string" - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": {} - }, - { - "type": "string" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "content", - "role" - ] - } - }, - "content": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "text": { - "type": "string" - }, - "name": { - "description": "Tool name when type is tool_use", - "type": "string" - }, - "id": { - "description": "Tool invocation id when type is tool_use", - "type": "string" - }, - "input": { - "$ref": "#/definitions/Record" - }, - "tool_use_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - } - }, - { - "type": "string" - } - ] - }, - "completion": { - "type": "string" - }, - "input_tokens": { - "type": "integer" - }, - "usage": { - "type": "object", - "properties": { - "input_tokens": { - "type": "integer" - }, - "output_tokens": { - "type": "integer" - }, - "cache_creation_input_tokens": { - "type": "integer" - }, - "cache_read_input_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "cache_creation_input_tokens", - "cache_read_input_tokens", - "input_tokens", - "output_tokens" - ] - } - }, - "required": [ - "id", - "model" - ] - } - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GenerateContentResponse": { - "description": "Google GenAI Generate Content Response", - "type": "object", - "additionalProperties": {}, - "properties": { - "candidates": { - "description": "Response variations returned by the model.", - "type": "array", - "items": { - "description": "Google GenAI Candidate", - "type": "object", - "additionalProperties": {}, - "properties": { - "content": { - "description": "Contains the multi-part content of the response.", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/definitions/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/definitions/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - }, - "finishReason": { - "description": "The reason why the model stopped generating tokens.\nIf empty, the model has not stopped generating the tokens.", - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens for this candidate.", - "type": "integer" - }, - "index": { - "description": "The index of the candidate.", - "type": "integer" - } - } - } - }, - "automaticFunctionCallingHistory": { - "description": "Timestamp when the request is made to the server.", - "type": "array", - "items": { - "description": "Google GenAI Content", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/definitions/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/definitions/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "modelVersion": { - "description": "Output only. The model version used to generate the response.", - "type": "string" - }, - "promptFeedback": { - "description": "Output only. Content filter results for a prompt sent in the request. Note: Sent only in the first stream chunk. Only happens when no candidates were generated due to content violations.", - "$ref": "#/definitions/Record" - }, - "responseId": { - "description": "Output only. response_id is used to identify each response. It is the encoding of the event_id.", - "type": "string" - }, - "usageMetadata": { - "description": "Usage metadata about the response(s).", - "type": "object", - "additionalProperties": {}, - "properties": { - "cacheTokensDetails": { - "description": "Output only. List of modalities of the cached content in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "cachedContentTokenCount": { - "description": "Output only. Number of tokens in the cached part in the input (the cached content).", - "type": "integer" - }, - "candidatesTokenCount": { - "description": "Number of tokens in the response(s).", - "type": "integer" - }, - "candidatesTokensDetails": { - "description": "Output only. List of modalities that were returned in the response.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "promptTokenCount": { - "description": "Number of tokens in the request. When `cached_content` is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.", - "type": "integer" - }, - "promptTokensDetails": { - "description": "Output only. List of modalities that were processed in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "thoughtsTokenCount": { - "description": "Output only. Number of tokens present in thoughts output.", - "type": "integer" - }, - "toolUsePromptTokenCount": { - "description": "Output only. Number of tokens present in tool-use prompt(s).", - "type": "integer" - }, - "toolUsePromptTokensDetails": { - "description": "Output only. List of modalities that were processed for tool-use request inputs.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "totalTokenCount": { - "description": "Total token count for prompt, response candidates, and tool-use prompts (if present).", - "type": "integer" - } - } - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GoogleGenAIResponse": { - "description": "Google GenAI Generate Content Response", - "type": "object", - "additionalProperties": {}, - "properties": { - "candidates": { - "description": "Response variations returned by the model.", - "type": "array", - "items": { - "description": "Google GenAI Candidate", - "type": "object", - "additionalProperties": {}, - "properties": { - "content": { - "description": "Contains the multi-part content of the response.", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/definitions/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/definitions/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - }, - "finishReason": { - "description": "The reason why the model stopped generating tokens.\nIf empty, the model has not stopped generating the tokens.", - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens for this candidate.", - "type": "integer" - }, - "index": { - "description": "The index of the candidate.", - "type": "integer" - } - } - } - }, - "automaticFunctionCallingHistory": { - "description": "Timestamp when the request is made to the server.", - "type": "array", - "items": { - "description": "Google GenAI Content", - "type": "object", - "properties": { - "parts": { - "description": "List of parts that constitute a single message.\nEach part may have a different IANA MIME type.", - "type": "array", - "items": { - "description": "Google GenAI Content Part", - "type": "object", - "properties": { - "videoMetadata": { - "description": "Metadata for a given video." - }, - "thought": { - "description": "Indicates if the part is thought from the model.", - "type": "boolean" - }, - "inlineData": { - "description": "Optional. Inlined bytes data.", - "$ref": "#/definitions/Blob" - }, - "fileData": { - "description": "Optional. URI based data." - }, - "thoughtSignature": { - "description": "An opaque signature for the thought so it can be reused in subsequent requests.", - "type": "string" - }, - "functionCall": { - "description": "A predicted [FunctionCall] returned from the model that contains a string\n representing the [FunctionDeclaration.name] and a structured JSON object\n containing the parameters and their values.", - "type": "object", - "properties": { - "id": { - "description": "The unique id of the function call. If populated, the client to execute the\n`function_call` and return the response with the matching `id`.", - "type": "string" - }, - "args": { - "description": "Optional. The function parameters and values in JSON object format. See [FunctionDeclaration.parameters] for parameter details.", - "$ref": "#/definitions/Record" - }, - "name": { - "description": "Required. The name of the function to call. Matches [FunctionDeclaration.name].", - "type": "string" - } - }, - "additionalProperties": false - }, - "codeExecutionResult": { - "description": "Optional. Result of executing the [ExecutableCode]." - }, - "executableCode": { - "description": "Optional. Code generated by the model that is meant to be executed." - }, - "functionResponse": { - "description": "Optional. The result output of a [FunctionCall] that contains a string representing the [FunctionDeclaration.name] and a structured JSON object containing any output from the function call. It is used as context to the model." - }, - "text": { - "description": "Optional. Text part (can be code).", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "role": { - "description": "Optional. The producer of the content. Must be either 'user' or\n'model'. Useful to set for multi-turn conversations, otherwise can be\nempty. If role is not specified, SDK will determine the role.", - "type": "string" - } - }, - "additionalProperties": false - } - }, - "modelVersion": { - "description": "Output only. The model version used to generate the response.", - "type": "string" - }, - "promptFeedback": { - "description": "Output only. Content filter results for a prompt sent in the request. Note: Sent only in the first stream chunk. Only happens when no candidates were generated due to content violations.", - "$ref": "#/definitions/Record" - }, - "responseId": { - "description": "Output only. response_id is used to identify each response. It is the encoding of the event_id.", - "type": "string" - }, - "usageMetadata": { - "description": "Usage metadata about the response(s).", - "type": "object", - "additionalProperties": {}, - "properties": { - "cacheTokensDetails": { - "description": "Output only. List of modalities of the cached content in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "cachedContentTokenCount": { - "description": "Output only. Number of tokens in the cached part in the input (the cached content).", - "type": "integer" - }, - "candidatesTokenCount": { - "description": "Number of tokens in the response(s).", - "type": "integer" - }, - "candidatesTokensDetails": { - "description": "Output only. List of modalities that were returned in the response.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "promptTokenCount": { - "description": "Number of tokens in the request. When `cached_content` is set, this is still the total effective prompt size meaning this includes the number of tokens in the cached content.", - "type": "integer" - }, - "promptTokensDetails": { - "description": "Output only. List of modalities that were processed in the request input.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "thoughtsTokenCount": { - "description": "Output only. Number of tokens present in thoughts output.", - "type": "integer" - }, - "toolUsePromptTokenCount": { - "description": "Output only. Number of tokens present in tool-use prompt(s).", - "type": "integer" - }, - "toolUsePromptTokensDetails": { - "description": "Output only. List of modalities that were processed for tool-use request inputs.", - "type": "array", - "items": { - "description": "Google GenAI Modality Token Count", - "type": "object", - "properties": { - "modality": { - "description": "The modality associated with this token count.", - "enum": [ - "AUDIO", - "DOCUMENT", - "IMAGE", - "MODALITY_UNSPECIFIED", - "TEXT", - "VIDEO" - ], - "type": "string" - }, - "tokenCount": { - "description": "Number of tokens.", - "type": "integer" - } - }, - "additionalProperties": false - } - }, - "totalTokenCount": { - "description": "Total token count for prompt, response candidates, and tool-use prompts (if present).", - "type": "integer" - } - } - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UndiciResponse": { - "type": "object", - "properties": { - "headers": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[0-9]+$": { - "type": "integer" - } - }, - "properties": { - "BYTES_PER_ELEMENT": { - "type": "integer" - }, - "buffer": { - "$ref": "#/definitions/ArrayBufferLike" - }, - "byteLength": { - "type": "integer" - }, - "byteOffset": { - "type": "integer" - }, - "length": { - "type": "integer" - }, - "__@toStringTag@1293": { - "type": "string", - "const": "Uint8Array" - } - }, - "required": [ - "BYTES_PER_ELEMENT", - "__@toStringTag@1293", - "buffer", - "byteLength", - "byteOffset", - "length" - ] - } - }, - "statusCode": { - "type": "integer" - }, - "statusText": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "headers", - "statusCode", - "statusText" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "CopyResponse": { - "type": "object", - "properties": { - "length": { - "type": "integer" - }, - "name": { - "$ref": "#/definitions/MessageName" - }, - "binary": { - "type": "boolean" - }, - "columnTypes": { - "type": "array", - "items": { - "type": "integer" - } - } - }, - "additionalProperties": false, - "required": [ - "binary", - "columnTypes", - "length", - "name" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RouteResponse": { - "type": "object", - "properties": { - "status": { - "type": "integer" - }, - "body": { - "type": "string", - "pattern": "^.*Response$" - }, - "headers": { - "$ref": "#/definitions/Record" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "FieldErrorResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "errors": { - "$ref": "#/definitions/ErrorList" - } - }, - "additionalProperties": false, - "required": [ - "code", - "errors", - "message" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InteractionSchema": { - "type": "object", - "properties": { - "type": { - "$ref": "#/definitions/InteractionType" - }, - "application_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "guild_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "channel_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "message_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "message_flags": { - "type": "integer" - }, - "session_id": { - "type": "string" - }, - "data": { - "$ref": "#/definitions/InteractionData" - }, - "files": { - "type": "array", - "items": { - "type": "object", - "properties": {}, - "additionalProperties": true - } - }, - "nonce": { - "type": "string" - }, - "analytics_location": { - "type": "string" - }, - "section_name": { - "type": "string" - }, - "source": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "application_id", - "channel_id", - "data", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InteractionCallbackSchema": { - "type": "object", - "properties": { - "type": { - "$ref": "#/definitions/InteractionCallbackType" - }, - "data": { - "$ref": "#/definitions/Message" - } - }, - "additionalProperties": false, - "required": [ - "data", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InteractionCreateSchema": { - "type": "object", - "properties": { - "version": { - "type": "integer" - }, - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "application_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "token": { - "type": "string" - }, - "data": { - "type": "object", - "properties": {}, - "additionalProperties": true - }, - "guild": { - "$ref": "#/definitions/InteractionGuild" - }, - "guild_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "guild_locale": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "channel_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "member": { - "$ref": "#/definitions/PublicMember" - }, - "user": { - "$ref": "#/definitions/PublicUser" - }, - "locale": { - "type": "string" - }, - "message": { - "$ref": "#/definitions/Message" - }, - "app_permissions": { - "type": "string" - }, - "entitlements": { - "type": "array", - "items": { - "type": "object", - "properties": {}, - "additionalProperties": true - } - }, - "entitlement_sku_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "authorizing_integration_owners": { - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^[0-9]+$": { - "type": "string" - } - } - }, - "context": { - "type": "integer" - }, - "attachment_size_limit": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "app_permissions", - "application_id", - "attachment_size_limit", - "id", - "token", - "type", - "version" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ApplicationCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "team_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "name" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ApplicationModifySchema": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "icon": { - "type": "string" - }, - "cover_image": { - "type": "string" - }, - "interactions_endpoint_url": { - "type": "string" - }, - "max_participants": { - "type": [ - "null", - "integer" - ] - }, - "name": { - "type": "string" - }, - "privacy_policy_url": { - "type": "string" - }, - "role_connections_verification_url": { - "type": "string" - }, - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "terms_of_service_url": { - "type": "string" - }, - "bot_public": { - "type": "boolean" - }, - "bot_require_code_grant": { - "type": "boolean" - }, - "flags": { - "type": "integer" - }, - "custom_install_url": { - "type": "string" - }, - "guild_id": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SendableApplicationCommandDataSchema": { - "type": "object", - "properties": { - "id": { - "$ref": "#/definitions/Snowflake" - }, - "type": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" - }, - "name": { - "type": "string" - }, - "version": { - "$ref": "#/definitions/Snowflake" - }, - "application_command": { - "type": "object", - "properties": {}, - "additionalProperties": true - }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/ApplicationCommandOption" - } - }, - "target_id": { - "description": "A container for useful snowflake-related methods.", - "$ref": "#/definitions/Snowflake" - }, - "attachments": { - "type": "array", - "items": { - "type": "object", - "properties": {}, - "additionalProperties": true - } - } - }, - "additionalProperties": false, - "required": [ - "id", - "name", - "version" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SendableMessageComponentDataSchema": { - "type": "object", - "properties": { - "component_type": { - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 - ], - "type": "number" - }, - "type": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" - }, - "custom_id": { - "type": "string" - }, - "values": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/Snowflake" - } - } - ] - } - }, - "additionalProperties": false, - "required": [ - "custom_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SendableModalSubmitDataSchema": { - "type": "object", - "properties": { - "id": { - "$ref": "#/definitions/Snowflake" - }, - "custom_id": { - "type": "string" - }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/UploadAttachmentRequestSchema" - } - } - }, - "additionalProperties": false, - "required": [ - "custom_id", - "id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildProfileResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "icon_hash": { - "type": [ - "null", - "string" - ] - }, - "member_count": { - "type": "integer" - }, - "online_count": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "brand_color_primary": { - "type": "string" - }, - "banner_hash": { - "type": [ - "null", - "string" - ] - }, - "game_application_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "game_activity": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/GameActivity" - } - }, - "tag": { - "type": [ - "null", - "string" - ] - }, - "badge": { - "$ref": "#/definitions/GuildBadgeType" - }, - "badge_color_primary": { - "type": "string" - }, - "badge_color_secondary": { - "type": "string" - }, - "badge_hash": { - "type": "string" - }, - "traits": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildTrait" - } - }, - "features": { - "type": "array", - "items": { - "type": "string" - } - }, - "visibility": { - "$ref": "#/definitions/GuildVisibilityLevel" - }, - "custom_banner_hash": { - "type": [ - "null", - "string" - ] - }, - "premium_subscription_count": { - "type": "integer" - }, - "premium_tier": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "badge", - "badge_color_primary", - "badge_color_secondary", - "badge_hash", - "banner_hash", - "brand_color_primary", - "custom_banner_hash", - "description", - "features", - "game_activity", - "game_application_ids", - "icon_hash", - "id", - "member_count", - "name", - "online_count", - "premium_subscription_count", - "premium_tier", - "tag", - "traits", - "visibility" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildAvailableSchema": { - "type": "object", - "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "available": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "available", - "id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InstanceUserDeleteSchema": { - "$ref": "#/definitions/InstanceUserDeleteSchemaContent", - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UserSettingsUpdateSchema": { - "type": "object", - "properties": { - "afk_timeout": { - "type": "integer" - }, - "allow_accessibility_detection": { - "type": "boolean" - }, - "animate_emoji": { - "type": "boolean" - }, - "animate_stickers": { - "type": "integer" - }, - "contact_sync_enabled": { - "type": "boolean" - }, - "convert_emoticons": { - "type": "boolean" - }, - "custom_status": { - "anyOf": [ - { - "$ref": "#/definitions/CustomStatus" - }, - { - "type": "null" - } - ] - }, - "default_guilds_restricted": { - "type": "boolean" - }, - "detect_platform_accounts": { - "type": "boolean" - }, - "developer_mode": { - "type": "boolean" - }, - "disable_games_tab": { - "type": "boolean" - }, - "enable_tts_command": { - "type": "boolean" - }, - "explicit_content_filter": { - "type": "integer" - }, - "friend_discovery_flags": { - "type": "integer" - }, - "friend_source_flags": { - "$ref": "#/definitions/FriendSourceFlags" - }, - "gateway_connected": { - "type": "boolean" - }, - "gif_auto_play": { - "type": "boolean" - }, - "guild_folders": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildFolder" - } - }, - "guild_positions": { - "type": "array", - "items": { - "type": "string" - } - }, - "inline_attachment_media": { - "type": "boolean" - }, - "inline_embed_media": { - "type": "boolean" - }, - "locale": { - "type": "string" - }, - "message_display_compact": { - "type": "boolean" - }, - "native_phone_integration_enabled": { - "type": "boolean" - }, - "render_embeds": { - "type": "boolean" - }, - "render_reactions": { - "type": "boolean" - }, - "restricted_guilds": { - "type": "array", - "items": { - "type": "string" - } - }, - "show_current_game": { - "type": "boolean" - }, - "status": { - "enum": [ - "dnd", - "idle", - "invisible", - "offline", - "online" - ], - "type": "string" - }, - "stream_notifications_enabled": { - "type": "boolean" - }, - "theme": { - "enum": [ - "dark", - "light" - ], - "type": "string" - }, - "timezone_offset": { - "type": "integer" - }, - "view_nsfw_guilds": { - "type": "boolean" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UserSettingsSchema": { - "type": "object", - "properties": { - "afk_timeout": { - "type": "integer" - }, - "allow_accessibility_detection": { - "type": "boolean" - }, - "animate_emoji": { - "type": "boolean" - }, - "animate_stickers": { - "type": "integer" - }, - "contact_sync_enabled": { - "type": "boolean" - }, - "convert_emoticons": { - "type": "boolean" - }, - "custom_status": { - "anyOf": [ - { - "$ref": "#/definitions/CustomStatus" - }, - { - "type": "null" - } - ] - }, - "default_guilds_restricted": { - "type": "boolean" - }, - "detect_platform_accounts": { - "type": "boolean" - }, - "developer_mode": { - "type": "boolean" - }, - "disable_games_tab": { - "type": "boolean" - }, - "enable_tts_command": { - "type": "boolean" - }, - "explicit_content_filter": { - "type": "integer" - }, - "friend_discovery_flags": { - "type": "integer" - }, - "friend_source_flags": { - "$ref": "#/definitions/FriendSourceFlags" - }, - "gateway_connected": { - "type": "boolean" - }, - "gif_auto_play": { - "type": "boolean" - }, - "guild_folders": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildFolder" - } - }, - "guild_positions": { - "type": "array", - "items": { - "type": "string" - } - }, - "inline_attachment_media": { - "type": "boolean" - }, - "inline_embed_media": { - "type": "boolean" - }, - "locale": { - "type": "string" - }, - "message_display_compact": { - "type": "boolean" - }, - "native_phone_integration_enabled": { - "type": "boolean" - }, - "render_embeds": { - "type": "boolean" - }, - "render_reactions": { - "type": "boolean" - }, - "restricted_guilds": { - "type": "array", - "items": { - "type": "string" - } - }, - "show_current_game": { - "type": "boolean" - }, - "status": { - "enum": [ - "dnd", - "idle", - "invisible", - "offline", - "online" - ], - "type": "string" - }, - "stream_notifications_enabled": { - "type": "boolean" - }, - "theme": { - "enum": [ - "dark", - "light" - ], - "type": "string" - }, - "timezone_offset": { - "type": "integer" - }, - "view_nsfw_guilds": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "afk_timeout", - "allow_accessibility_detection", - "animate_emoji", - "animate_stickers", - "contact_sync_enabled", - "convert_emoticons", - "custom_status", - "default_guilds_restricted", - "detect_platform_accounts", - "developer_mode", - "disable_games_tab", - "enable_tts_command", - "explicit_content_filter", - "friend_discovery_flags", - "friend_source_flags", - "gateway_connected", - "gif_auto_play", - "guild_folders", - "guild_positions", - "inline_attachment_media", - "inline_embed_media", - "locale", - "message_display_compact", - "native_phone_integration_enabled", - "render_embeds", - "render_reactions", - "restricted_guilds", - "show_current_game", - "status", - "stream_notifications_enabled", - "theme", - "timezone_offset", - "view_nsfw_guilds" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "IdentifySchema": { - "type": "object", - "properties": { - "token": { - "type": "string" - }, - "properties": { - "type": "object", - "properties": { - "os": { - "type": "string" - }, - "os_atch": { - "type": "string" - }, - "browser": { - "type": "string" - }, - "device": { - "type": "string" - }, - "$os": { - "type": "string" - }, - "$browser": { - "type": "string" - }, - "$device": { - "type": "string" - }, - "browser_user_agent": { - "type": "string" - }, - "browser_version": { - "type": "string" - }, - "os_version": { - "type": "string" - }, - "referrer": { - "type": "string" - }, - "referring_domain": { - "type": "string" - }, - "referrer_current": { - "type": "string" - }, - "referring_domain_current": { - "type": "string" - }, - "release_channel": { - "enum": [ - "canary", - "dev", - "ptb", - "stable" - ], - "type": "string" - }, - "client_build_number": { - "type": "integer" - }, - "client_event_source": { - "type": "string" - }, - "client_version": { - "type": "string" - }, - "system_locale": { - "type": "string" - } - }, - "additionalProperties": false - }, - "intents": { - "type": "bigint" - }, - "presence": { - "$ref": "#/definitions/ActivitySchema" - }, - "compress": { - "type": "boolean" - }, - "large_threshold": { - "type": "integer" - }, - "largeThreshold": { - "type": "integer" - }, - "shard": { - "minItems": 2, - "maxItems": 2, - "type": "array", - "items": { - "type": "bigint" - } - }, - "guild_subscriptions": { - "type": "boolean" - }, - "capabilities": { - "type": "integer" - }, - "client_state": { - "type": "object", - "properties": { - "guild_hashes": {}, - "highest_last_message_id": { - "type": "integer" - }, - "read_state_version": { - "type": "integer" - }, - "user_guild_settings_version": { - "type": "integer" - }, - "user_settings_version": { - "type": "integer" - }, - "useruser_guild_settings_version": { - "type": "integer" - }, - "private_channels_version": { - "type": "integer" - }, - "guild_versions": {}, - "api_code_version": { - "type": "integer" - }, - "initial_guild_id": { - "type": "string" - } - }, - "additionalProperties": false - }, - "clientState": { - "type": "object", - "properties": { - "guildHashes": {}, - "highestLastMessageId": { - "type": "integer" - }, - "readStateVersion": { - "type": "integer" - }, - "userGuildSettingsVersion": { - "type": "integer" - }, - "useruserGuildSettingsVersion": { - "type": "integer" - }, - "guildVersions": {}, - "apiCodeVersion": { - "type": "integer" - }, - "initialGuildId": { - "type": "string" - } - }, - "additionalProperties": false - }, - "v": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "properties", - "token" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "StreamCreateSchema": { - "type": "object", - "properties": { - "type": { - "enum": [ - "call", - "guild" - ], - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "preferred_region": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "channel_id", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "StreamDeleteSchema": { - "type": "object", - "properties": { - "stream_key": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "stream_key" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "StreamWatchSchema": { - "type": "object", - "properties": { - "stream_key": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "stream_key" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIErrorResponse": { - "type": "object", - "properties": { - "code": { - "type": "integer" - }, - "message": { - "type": "string" - }, - "errors": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "_errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "message": { - "type": "string" - }, - "code": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "code", - "message" - ] - } - } - }, - "additionalProperties": false, - "required": [ - "_errors" - ] - } - } - }, - "additionalProperties": false, - "required": [ - "code", - "errors", - "message" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "CaptchaRequiredResponse": { - "type": "object", - "properties": { - "captcha_key": { - "type": "string" - }, - "captcha_sitekey": { - "type": "string" - }, - "captcha_service": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "captcha_key", - "captcha_service", - "captcha_sitekey" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIErrorOrCaptchaResponse": { - "anyOf": [ - { - "$ref": "#/definitions/APIErrorResponse" - }, - { - "$ref": "#/definitions/CaptchaRequiredResponse" - } - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "AccountStandingResponse": { - "type": "object", - "properties": { - "classifications": { - "type": "array", - "items": { - "$ref": "#/definitions/Classification" - } - }, - "guild_classifications": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildClassification" - } - }, - "account_standing": { - "type": "object", - "properties": { - "state": { - "$ref": "#/definitions/AccountStandingState" - } - }, - "additionalProperties": false, - "required": [ - "state" - ] - }, - "is_dsa_eligible": { - "type": "boolean" - }, - "username": { - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "is_appeal_eligible": { - "type": "boolean" - }, - "appeal_eligibility": { - "type": "array", - "items": { - "$ref": "#/definitions/AppealEligibility" - } - } - }, - "additionalProperties": false, - "required": [ - "account_standing", - "appeal_eligibility", - "classifications", - "discriminator", - "guild_classifications", - "is_appeal_eligible", - "is_dsa_eligible", - "username" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "BackupCodesChallengeResponse": { - "type": "object", - "properties": { - "nonce": { - "type": "string" - }, - "regenerate_nonce": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "nonce", - "regenerate_nonce" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "CollectiblesCategoriesResponse": { - "type": "array", - "items": { - "$ref": "#/definitions/CollectiblesCategoryItem" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "CollectiblesMarketingResponse": { - "type": "object", - "properties": { - "marketings": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/CollectiblesMarketingItem" - } - } - }, - "additionalProperties": false, - "required": [ - "marketings" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "CollectiblesShopResponse": { - "type": "object", - "properties": { - "shop_blocks": { - "type": "array", - "items": { - "$ref": "#/definitions/AnyShopBlock" - } - }, - "categories": { - "type": "array", - "items": { - "$ref": "#/definitions/CollectiblesCategoryItem" - } - } - }, - "additionalProperties": false, - "required": [ - "categories", - "shop_blocks" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "DiscoverableGuildsResponse": { - "type": "object", - "properties": { - "total": { - "type": "integer" - }, - "guilds": { - "type": "array", - "items": { - "$ref": "#/definitions/Guild" - } - }, - "offset": { - "type": "integer" - }, - "limit": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "guilds", - "limit", - "offset", - "total" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "DmMessagesResponseSchema": { - "type": "array", - "items": { - "$ref": "#/definitions/PartialMessage" - }, - "definitions": { - "MessageType": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 127, - 128, - 129, - 130, - 131, - 132, - 255 - ] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "EmailDomainLookupResponse": { - "type": "object", - "properties": { - "guilds_info": { - "type": "array", - "items": { - "$ref": "#/definitions/HubGuild" - } - }, - "has_matching_guild": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "guilds_info", - "has_matching_guild" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "EmailDomainLookupVerifyCodeResponse": { - "type": "object", - "properties": { - "guild": { - "$ref": "#/definitions/Guild" - }, - "joined": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "guild", - "joined" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "EmojiSourceResponse": { - "type": "object", - "properties": { - "type": { - "enum": [ - "APPLICATION", - "GUILD" - ], - "type": "string" - }, - "guild": { - "anyOf": [ - { - "$ref": "#/definitions/EmojiGuild" - }, - { - "type": "null" - } - ] - }, - "application": { - "anyOf": [ - { - "$ref": "#/definitions/EmojiApplication" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GatewayBotResponse": { - "type": "object", - "properties": { - "url": { - "type": "string" - }, - "shards": { - "type": "integer" - }, - "session_start_limit": { - "type": "object", - "properties": { - "total": { - "type": "integer" - }, - "remaining": { - "type": "integer" - }, - "reset_after": { - "type": "integer" - }, - "max_concurrency": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "max_concurrency", - "remaining", - "reset_after", - "total" - ] - } - }, - "additionalProperties": false, - "required": [ - "session_start_limit", - "shards", - "url" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GatewayResponse": { - "type": "object", - "properties": { - "url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "url" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GenerateRegistrationTokensResponse": { - "type": "object", - "properties": { - "tokens": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": [ - "tokens" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildBansResponse": { - "type": "object", - "properties": { - "reason": { - "type": [ - "null", - "string" - ] - }, - "user": { - "type": "object", - "properties": { - "username": { - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "id": { - "type": "string" - }, - "avatar": { - "type": [ - "null", - "string" - ] - }, - "public_flags": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "avatar", - "discriminator", - "id", - "public_flags", - "username" - ] - } - }, - "additionalProperties": false, - "required": [ - "reason", - "user" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildCreateResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "primary_category_id": { - "type": "string" - }, - "large": { - "type": "boolean" - }, - "max_members": { - "type": "integer" - }, - "max_presences": { - "type": "integer" - }, - "max_video_channel_users": { - "type": "integer" - }, - "member_count": { - "type": "integer" - }, - "presence_count": { - "type": "integer" - }, - "template_id": { - "type": "string" - }, - "mfa_level": { - "type": "integer" - }, - "owner_id": { - "type": "string" - }, - "premium_subscription_count": { - "type": "integer" - }, - "premium_tier": { - "type": "integer" - }, - "welcome_screen": { - "$ref": "#/definitions/GuildWelcomeScreen" - }, - "widget_channel_id": { - "type": "string" - }, - "widget_enabled": { - "type": "boolean" - }, - "nsfw_level": { - "type": "integer" - }, - "nsfw": { - "type": "boolean" - }, - "parent": { - "type": "string" - }, - "region": { - "type": "string" - }, - "icon": { - "type": [ - "null", - "string" - ] - }, - "banner": { - "type": [ - "null", - "string" - ] - }, - "system_channel_id": { - "type": "string" - }, - "rules_channel_id": { - "type": "string" - }, - "guild_template_code": { - "type": "string" - }, - "staff_only": { - "type": "boolean" - }, - "splash": { - "type": [ - "null", - "string" - ] - }, - "description": { - "type": "string" - }, - "features": { - "type": "array", - "items": { - "type": "string" - } - }, - "verification_level": { - "type": "integer" - }, - "default_message_notifications": { - "type": "integer" - }, - "system_channel_flags": { - "type": "integer" - }, - "explicit_content_filter": { - "type": "integer" - }, - "public_updates_channel_id": { - "type": "string" - }, - "afk_timeout": { - "type": "integer" - }, - "afk_channel_id": { - "type": "string" - }, - "preferred_locale": { - "type": "string" - }, - "premium_progress_bar_enabled": { - "type": "boolean" - }, - "discovery_splash": { - "type": "string" - }, - "safety_alerts_channel_id": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "id", - "name", - "nsfw", - "welcome_screen", - "widget_enabled" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildDiscoveryRequirementsResponse": { - "type": "object", - "properties": { - "guild_id": { - "type": "string" - }, - "safe_environment": { - "type": "boolean" - }, - "healthy": { - "type": "boolean" - }, - "health_score_pending": { - "type": "boolean" - }, - "size": { - "type": "boolean" - }, - "nsfw_properties": {}, - "protected": { - "type": "boolean" - }, - "sufficient": { - "type": "boolean" - }, - "sufficient_without_grace_period": { - "type": "boolean" - }, - "valid_rules_channel": { - "type": "boolean" - }, - "retention_healthy": { - "type": "boolean" - }, - "engagement_healthy": { - "type": "boolean" - }, - "age": { - "type": "boolean" - }, - "minimum_age": { - "type": "integer" - }, - "health_score": { - "type": "object", - "properties": { - "avg_nonnew_participators": { - "type": "integer" - }, - "avg_nonnew_communicators": { - "type": "integer" - }, - "num_intentful_joiners": { - "type": "integer" - }, - "perc_ret_w1_intentful": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "avg_nonnew_communicators", - "avg_nonnew_participators", - "num_intentful_joiners", - "perc_ret_w1_intentful" - ] - }, - "minimum_size": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "age", - "engagement_healthy", - "guild_id", - "health_score", - "health_score_pending", - "healthy", - "minimum_age", - "minimum_size", - "nsfw_properties", - "protected", - "retention_healthy", - "safe_environment", - "size", - "sufficient", - "sufficient_without_grace_period", - "valid_rules_channel" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildMessagesSearchResponse": { - "type": "object", - "properties": { - "messages": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildMessagesSearchMessage" - } - }, - "total_results": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "messages", - "total_results" - ], - "definitions": { - "MessageType": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 127, - 128, - 129, - 130, - 131, - 132, - 255 - ] - }, - "Message": { - "type": "object", - "properties": { - "channel_id": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "thread_id": { - "type": "string" - }, - "thread": { - "$ref": "#/definitions/Channel" - }, - "guild_id": { - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "author_id": { - "type": "string" - }, - "author": { - "$ref": "#/definitions/User" - }, - "member_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/Member" - }, - "webhook_id": { - "type": "string" - }, - "webhook": { - "$ref": "#/definitions/Webhook" - }, - "application_id": { - "type": "string" - }, - "application": { - "$ref": "#/definitions/Application" - }, - "content": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "edited_timestamp": { - "type": "string", - "format": "date-time" - }, - "tts": { - "type": "boolean" - }, - "mention_everyone": { - "type": "boolean" - }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/definitions/User" - } - }, - "mention_roles": { - "type": "array", - "items": { - "$ref": "#/definitions/Role" - } - }, - "mention_channels": { - "type": "array", - "items": { - "$ref": "#/definitions/Channel" - } - }, - "sticker_items": { - "type": "array", - "items": { - "$ref": "#/definitions/Sticker" - } - }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/Attachment" - } - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } - }, - "reactions": { - "type": "array", - "items": { - "$ref": "#/definitions/Reaction" - } - }, - "nonce": { - "type": "string" - }, - "pinned": { - "type": "boolean" - }, - "pinned_at": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ] - }, - "type": { - "$ref": "#/definitions/MessageType_1" - }, - "activity": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "party_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "party_id", - "type" - ] - }, - "flags": { - "type": "integer" - }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "type": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "referenced_message": { - "$ref": "#/definitions/Message" - }, - "interaction": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "name": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "id", - "name", - "type", - "user_id" - ] - }, - "components": { - "type": "array", - "items": { - "$ref": "#/definitions/ActionRowComponent" - } - }, - "poll": { - "$ref": "#/definitions/Poll" - }, - "interaction_metadata": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "user_id": { - "type": "string" - }, - "authorizing_integration_owners": { - "$ref": "#/definitions/Record" - }, - "original_response_message_id": { - "type": "string" - }, - "interacted_message_id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "id", - "type", - "user_id" - ] - }, - "message_snapshots": { - "type": "array", - "items": { - "$ref": "#/definitions/MessageSnapshot" - } - }, - "reply_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "username": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "channel", - "embeds", - "flags", - "id", - "mention_channels", - "mention_roles", - "mentions", - "reactions", - "timestamp", - "type" - ] - } - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildPruneResponse": { - "type": "object", - "properties": { - "pruned": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "pruned" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildPurgeResponse": { - "type": "object", - "properties": { - "purged": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "purged" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildRecommendationsResponse": { - "type": "object", - "properties": { - "recommended_guilds": { - "type": "array", - "items": { - "$ref": "#/definitions/Guild" - } - }, - "load_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "load_id", - "recommended_guilds" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildVanityUrlResponse": { - "anyOf": [ - { - "$ref": "#/definitions/GuildVanityUrl" - }, - { - "$ref": "#/definitions/GuildVanityUrlNoInvite" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/GuildVanityUrl" - } - } - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildVanityUrlCreateResponse": { - "type": "object", - "properties": { - "code": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "code" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildWidgetJsonResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "instant_invite": { - "type": "string" - }, - "channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "position": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "id", - "name", - "position" - ] - } - }, - "members": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "username": { - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "avatar": { - "type": [ - "null", - "string" - ] - }, - "status": { - "$ref": "#/definitions/ClientStatus" - }, - "avatar_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "avatar", - "avatar_url", - "discriminator", - "id", - "status", - "username" - ] - } - }, - "presence_count": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "channels", - "id", - "instant_invite", - "members", - "name", - "presence_count" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildWidgetSettingsResponse": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "channel_id": { - "anyOf": [ - { - "$ref": "#/definitions/Snowflake" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "channel_id", - "enabled" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "HubDirectoryEntriesResponse": { - "type": "array", - "items": { - "$ref": "#/definitions/HubDirectoryEntry" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "HubWaitlistSignupResponse": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "email_domain": { - "type": "string" - }, - "school": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "email", - "email_domain", - "school", - "user_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InstanceDomainsResponse": { - "type": "object", - "properties": { - "admin": { - "type": "string" - }, - "api": { - "type": "string" - }, - "apiEndpoint": { - "type": "string" - }, - "cdn": { - "type": "string" - }, - "gateway": { - "type": "string" - }, - "defaultApiVersion": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "api", - "apiEndpoint", - "cdn", - "defaultApiVersion", - "gateway" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InstancePingResponse": { - "type": "object", - "properties": { - "ping": { - "type": "string", - "const": "pong!" - }, - "instance": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "description": { - "type": [ - "null", - "string" - ] - }, - "image": { - "type": [ - "null", - "string" - ] - }, - "correspondenceEmail": { - "type": [ - "null", - "string" - ] - }, - "correspondenceUserID": { - "type": [ - "null", - "string" - ] - }, - "frontPage": { - "type": [ - "null", - "string" - ] - }, - "tosPage": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "correspondenceEmail", - "correspondenceUserID", - "description", - "frontPage", - "id", - "image", - "name", - "tosPage" - ] - } - }, - "additionalProperties": false, - "required": [ - "instance", - "ping" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InstanceStatsResponse": { - "type": "object", - "properties": { - "counts": { - "type": "object", - "properties": { - "user": { - "type": "integer" - }, - "guild": { - "type": "integer" - }, - "message": { - "type": "integer" - }, - "members": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "guild", - "members", - "message", - "user" - ] - } - }, - "additionalProperties": false, - "required": [ - "counts" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "LocationMetadataResponse": { - "type": "object", - "properties": { - "consent_required": { - "type": "boolean" - }, - "country_code": { - "type": "string" - }, - "promotional_email_opt_in": { - "type": "object", - "properties": { - "required": { - "type": "boolean", - "const": true - }, - "pre_checked": { - "type": "boolean", - "const": false - } - }, - "additionalProperties": false, - "required": [ - "pre_checked", - "required" - ] - } - }, - "additionalProperties": false, - "required": [ - "consent_required", - "country_code", - "promotional_email_opt_in" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MemberJoinGuildResponse": { - "type": "object", - "properties": { - "guild": { - "$ref": "#/definitions/GuildCreateResponse" - }, - "emojis": { - "type": "array", - "items": { - "$ref": "#/definitions/Emoji" - } - }, - "roles": { - "type": "array", - "items": { - "$ref": "#/definitions/Role" + "type": "string" } }, - "stickers": { - "type": "array", - "items": { - "$ref": "#/definitions/Sticker" - } - } - }, - "additionalProperties": false, - "required": [ - "emojis", - "guild", - "roles", - "stickers" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "OAuthAuthorizeResponse": { - "type": "object", - "properties": { - "location": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "location" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PreloadMessagesResponseSchema": { - "type": "array", - "items": { - "$ref": "#/definitions/Message" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "RefreshUrlsResponse": { - "type": "object", - "properties": { - "refreshed_urls": { - "type": "array", - "items": { - "$ref": "#/definitions/RefreshedUrl" - } - } - }, - "additionalProperties": false, - "required": [ - "refreshed_urls" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SettingsProtoResponse": { - "type": "object", - "properties": { - "settings": { + "terms_of_service_url": { "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "settings" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SettingsProtoUpdateResponse": { - "type": "object", - "properties": { - "out_of_date": { + }, + "bot_public": { "type": "boolean" }, - "settings": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "settings" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SettingsProtoJsonResponse": { - "type": "object", - "properties": { - "settings": { - "$ref": "#/definitions/JsonValue" - } - }, - "additionalProperties": false, - "required": [ - "settings" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SettingsProtoUpdateJsonResponse": { - "type": "object", - "properties": { - "out_of_date": { + "bot_require_code_grant": { "type": "boolean" }, - "settings": { - "$ref": "#/definitions/JsonValue" + "flags": { + "type": "integer" + }, + "custom_install_url": { + "type": "string" + }, + "guild_id": { + "type": "string" } }, "additionalProperties": false, - "required": [ - "settings" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TeamListResponse": { - "type": "array", - "items": { - "$ref": "#/definitions/Team" - }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TenorGifResponse": { + "SendableApplicationCommandDataSchema": { "type": "object", "properties": { "id": { - "type": "string" - }, - "title": { - "type": "string" - }, - "url": { - "type": "string" + "$ref": "#/definitions/Snowflake" }, - "src": { - "type": "string" + "type": { + "enum": [ + 1, + 2, + 3, + 4 + ], + "type": "number" }, - "gif_src": { + "name": { "type": "string" }, - "width": { - "type": "integer" - }, - "height": { - "type": "integer" + "version": { + "$ref": "#/definitions/Snowflake" }, - "preview": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "gif_src", - "height", - "id", - "preview", - "src", - "title", - "url", - "width" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TenorTrendingResponse": { - "type": "object", - "properties": { - "categories": { + "application_command": { "type": "object", - "properties": { - "tags": { - "type": "array", - "items": { - "type": "object", - "properties": { - "searchterm": { - "type": "string" - }, - "path": { - "type": "string" - }, - "image": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "image", - "name", - "path", - "searchterm" - ] - } - } - }, - "additionalProperties": false, - "required": [ - "tags" - ] + "properties": {}, + "additionalProperties": true }, - "gifs": { + "options": { "type": "array", "items": { - "$ref": "#/definitions/TenorGifResponse" + "$ref": "#/definitions/ApplicationCommandOption" } - } - }, - "additionalProperties": false, - "required": [ - "categories", - "gifs" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TenorGifsResponse": { - "type": "array", - "items": { - "$ref": "#/definitions/TenorGifResponse" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TokenResponse": { - "type": "object", - "properties": { - "token": { - "type": "string" }, - "settings": { - "$ref": "#/definitions/UserSettings" + "target_id": { + "description": "A container for useful snowflake-related methods.", + "$ref": "#/definitions/Snowflake" + }, + "attachments": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "additionalProperties": true + } } }, "additionalProperties": false, "required": [ - "settings", - "token" + "id", + "name", + "version" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TokenOnlyResponse": { + "SendableMessageComponentDataSchema": { "type": "object", "properties": { - "token": { + "component_type": { + "enum": [ + 1, + 10, + 11, + 12, + 13, + 14, + 16, + 17, + 18, + 19, + 2, + 20, + 21, + 22, + 23, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "type": "number" + }, + "type": { + "enum": [ + 1, + 2, + 3, + 4 + ], + "type": "number" + }, + "custom_id": { "type": "string" + }, + "values": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/Snowflake" + } + } + ] } }, "additionalProperties": false, "required": [ - "token" + "custom_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TokenWithBackupCodesResponse": { + "SendableModalSubmitDataSchema": { "type": "object", "properties": { - "token": { + "id": { + "$ref": "#/definitions/Snowflake" + }, + "custom_id": { "type": "string" }, - "backup_codes": { + "attachments": { "type": "array", "items": { - "$ref": "#/definitions/BackupCode" + "$ref": "#/definitions/UploadAttachmentRequestSchema" } } }, "additionalProperties": false, "required": [ - "backup_codes", - "token" + "custom_id", + "id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIGuild": { + "GuildProfileResponse": { "type": "object", "properties": { - "name": { + "id": { "type": "string" }, - "region": { + "name": { "type": "string" }, - "insert": { - "type": "object", - "additionalProperties": false - }, - "id": { - "type": "string" + "icon_hash": { + "type": [ + "null", + "string" + ] }, - "unavailable": { - "type": "boolean" + "member_count": { + "type": "integer" }, - "icon": { - "type": "string" + "online_count": { + "type": "integer" }, - "parent": { + "description": { "type": "string" }, - "owner_id": { + "brand_color_primary": { "type": "string" }, - "nsfw": { - "type": "boolean" - }, - "invites": { - "type": "array", - "items": { - "$ref": "#/definitions/Invite" - } - }, - "voice_states": { - "type": "array", - "items": { - "$ref": "#/definitions/VoiceState" - } - }, - "webhooks": { - "type": "array", - "items": { - "$ref": "#/definitions/Webhook" - } - }, - "member_count": { - "type": "integer" + "banner_hash": { + "type": [ + "null", + "string" + ] }, - "roles": { + "game_application_ids": { "type": "array", "items": { - "$ref": "#/definitions/Role" + "type": "string" } }, - "banner": { - "type": "string" - }, - "channels": { - "type": "array", - "items": { - "$ref": "#/definitions/Channel" + "game_activity": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/GameActivity" } }, - "system_channel_id": { + "tag": { "type": [ "null", "string" ] }, - "rules_channel_id": { - "type": [ - "null", - "string" - ] + "badge": { + "$ref": "#/definitions/GuildBadgeType" }, - "splash": { + "badge_color_primary": { "type": "string" }, - "description": { + "badge_color_secondary": { "type": "string" }, + "badge_hash": { + "type": "string" + }, + "traits": { + "type": "array", + "items": { + "$ref": "#/definitions/GuildTrait" + } + }, "features": { "type": "array", "items": { "type": "string" } }, - "verification_level": { - "type": "integer" - }, - "default_message_notifications": { - "type": "integer" - }, - "system_channel_flags": { - "type": "integer" - }, - "explicit_content_filter": { - "type": "integer" + "visibility": { + "$ref": "#/definitions/GuildVisibilityLevel" }, - "public_updates_channel_id": { + "custom_banner_hash": { "type": [ "null", "string" ] }, + "premium_subscription_count": { + "type": "integer" + }, + "premium_tier": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "badge", + "badge_color_primary", + "badge_color_secondary", + "badge_hash", + "banner_hash", + "brand_color_primary", + "custom_banner_hash", + "description", + "features", + "game_activity", + "game_application_ids", + "icon_hash", + "id", + "member_count", + "name", + "online_count", + "premium_subscription_count", + "premium_tier", + "tag", + "traits", + "visibility" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildAvailableSchema": { + "type": "object", + "properties": { + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" + }, + "available": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "available", + "id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InstanceUserDeleteSchema": { + "$ref": "#/definitions/InstanceUserDeleteSchemaContent", + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserSettingsUpdateSchema": { + "type": "object", + "properties": { "afk_timeout": { "type": "integer" }, - "afk_channel_id": { - "type": [ - "null", - "string" - ] + "allow_accessibility_detection": { + "type": "boolean" + }, + "animate_emoji": { + "type": "boolean" }, - "preferred_locale": { - "type": "string" + "animate_stickers": { + "type": "integer" }, - "premium_progress_bar_enabled": { + "contact_sync_enabled": { "type": "boolean" }, - "discovery_splash": { - "type": "string" + "convert_emoticons": { + "type": "boolean" }, - "bans": { - "type": "array", - "items": { - "$ref": "#/definitions/Ban" - } + "custom_status": { + "anyOf": [ + { + "$ref": "#/definitions/CustomStatus" + }, + { + "type": "null" + } + ] }, - "primary_category_id": { - "type": "string" + "default_guilds_restricted": { + "type": "boolean" }, - "large": { + "detect_platform_accounts": { "type": "boolean" }, - "max_members": { - "type": "integer" + "developer_mode": { + "type": "boolean" }, - "max_presences": { - "type": "integer" + "disable_games_tab": { + "type": "boolean" }, - "max_video_channel_users": { + "enable_tts_command": { + "type": "boolean" + }, + "explicit_content_filter": { "type": "integer" }, - "presence_count": { + "friend_discovery_flags": { "type": "integer" }, - "members": { - "type": "array", - "items": { - "$ref": "#/definitions/Member" - } + "friend_source_flags": { + "$ref": "#/definitions/FriendSourceFlags" }, - "template_id": { - "type": "string" + "gateway_connected": { + "type": "boolean" }, - "emojis": { + "gif_auto_play": { + "type": "boolean" + }, + "guild_folders": { "type": "array", "items": { - "$ref": "#/definitions/Emoji" + "$ref": "#/definitions/GuildFolder" } }, - "stickers": { + "guild_positions": { "type": "array", "items": { - "$ref": "#/definitions/Sticker" + "type": "string" } }, - "mfa_level": { - "type": "integer" - }, - "premium_subscription_count": { - "type": "integer" - }, - "premium_tier": { - "type": "integer" + "inline_attachment_media": { + "type": "boolean" }, - "welcome_screen": { - "$ref": "#/definitions/GuildWelcomeScreen", - "description": "DEPRECATED: Look at the new Guild onboarding screens." + "inline_embed_media": { + "type": "boolean" }, - "widget_channel_id": { + "locale": { "type": "string" }, - "widget_enabled": { + "message_display_compact": { "type": "boolean" }, - "nsfw_level": { - "type": "integer" + "native_phone_integration_enabled": { + "type": "boolean" }, - "permissions": { - "type": "integer" + "render_embeds": { + "type": "boolean" }, - "channel_ordering": { + "render_reactions": { + "type": "boolean" + }, + "restricted_guilds": { "type": "array", "items": { "type": "string" } }, - "discovery_weight": { + "show_current_game": { + "type": "boolean" + }, + "status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" + ], + "type": "string" + }, + "stream_notifications_enabled": { + "type": "boolean" + }, + "theme": { + "enum": [ + "dark", + "light" + ], + "type": "string" + }, + "timezone_offset": { "type": "integer" }, - "discovery_excluded": { + "view_nsfw_guilds": { "type": "boolean" } }, "additionalProperties": false, - "required": [ - "bans", - "channel_ordering", - "channels", - "discovery_excluded", - "discovery_weight", - "emojis", - "features", - "id", - "insert", - "invites", - "members", - "name", - "nsfw", - "premium_progress_bar_enabled", - "public_updates_channel_id", - "roles", - "stickers", - "unavailable", - "voice_states", - "webhooks", - "welcome_screen", - "widget_enabled" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIPublicUser": { + "UserSettingsSchema": { "type": "object", "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "premium_since": { - "type": "string", - "format": "date-time" - }, - "avatar": { - "type": "string" - }, - "banner": { - "type": "string" - }, - "bio": { - "type": "string" - }, - "theme_colors": { - "type": "array", - "items": { - "type": "integer" - } - }, - "pronouns": { - "type": "string" + "afk_timeout": { + "type": "integer" }, - "username": { - "type": "string" + "allow_accessibility_detection": { + "type": "boolean" }, - "discriminator": { - "type": "string" + "animate_emoji": { + "type": "boolean" }, - "public_flags": { + "animate_stickers": { "type": "integer" }, - "accent_color": { - "type": "integer" + "contact_sync_enabled": { + "type": "boolean" }, - "bot": { + "convert_emoticons": { "type": "boolean" }, - "premium_type": { - "type": "integer" + "custom_status": { + "anyOf": [ + { + "$ref": "#/definitions/CustomStatus" + }, + { + "type": "null" + } + ] }, - "badge_ids": { - "type": "array", - "items": { - "type": "string" - } + "default_guilds_restricted": { + "type": "boolean" }, - "avatar_decoration_data": { - "$ref": "#/definitions/AvatarDecorationData" + "detect_platform_accounts": { + "type": "boolean" }, - "display_name_styles": { - "$ref": "#/definitions/DisplayNameStyle" + "developer_mode": { + "type": "boolean" }, - "collectibles": { - "$ref": "#/definitions/Collectibles" + "disable_games_tab": { + "type": "boolean" }, - "primary_guild": { - "$ref": "#/definitions/PrimaryGuild" - } - }, - "additionalProperties": false, - "required": [ - "bio", - "bot", - "discriminator", - "id", - "premium_since", - "premium_type", - "public_flags", - "username" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIPrivateUser": { - "type": "object", - "properties": { - "email": { - "type": "string" + "enable_tts_command": { + "type": "boolean" }, - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" + "explicit_content_filter": { + "type": "integer" }, - "flags": { + "friend_discovery_flags": { "type": "integer" }, - "verified": { - "type": "boolean" - }, - "premium_since": { - "type": "string", - "format": "date-time" + "friend_source_flags": { + "$ref": "#/definitions/FriendSourceFlags" }, - "avatar": { - "type": "string" + "gateway_connected": { + "type": "boolean" }, - "banner": { - "type": "string" + "gif_auto_play": { + "type": "boolean" }, - "bio": { - "type": "string" + "guild_folders": { + "type": "array", + "items": { + "$ref": "#/definitions/GuildFolder" + } }, - "theme_colors": { + "guild_positions": { "type": "array", "items": { - "type": "integer" + "type": "string" } }, - "pronouns": { - "type": "string" + "inline_attachment_media": { + "type": "boolean" }, - "username": { - "type": "string" + "inline_embed_media": { + "type": "boolean" }, - "discriminator": { + "locale": { "type": "string" }, - "public_flags": { - "type": "integer" + "message_display_compact": { + "type": "boolean" }, - "accent_color": { - "type": "integer" + "native_phone_integration_enabled": { + "type": "boolean" }, - "bot": { + "render_embeds": { "type": "boolean" }, - "premium_type": { - "type": "integer" + "render_reactions": { + "type": "boolean" }, - "badge_ids": { + "restricted_guilds": { "type": "array", "items": { "type": "string" } }, - "avatar_decoration_data": { - "$ref": "#/definitions/AvatarDecorationData" - }, - "display_name_styles": { - "$ref": "#/definitions/DisplayNameStyle" - }, - "collectibles": { - "$ref": "#/definitions/Collectibles" + "show_current_game": { + "type": "boolean" }, - "primary_guild": { - "$ref": "#/definitions/PrimaryGuild" + "status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" + ], + "type": "string" }, - "mfa_enabled": { + "stream_notifications_enabled": { "type": "boolean" }, - "phone": { + "theme": { + "enum": [ + "dark", + "light" + ], "type": "string" }, - "nsfw_allowed": { + "timezone_offset": { + "type": "integer" + }, + "view_nsfw_guilds": { "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "afk_timeout", + "allow_accessibility_detection", + "animate_emoji", + "animate_stickers", + "contact_sync_enabled", + "convert_emoticons", + "custom_status", + "default_guilds_restricted", + "detect_platform_accounts", + "developer_mode", + "disable_games_tab", + "enable_tts_command", + "explicit_content_filter", + "friend_discovery_flags", + "friend_source_flags", + "gateway_connected", + "gif_auto_play", + "guild_folders", + "guild_positions", + "inline_attachment_media", + "inline_embed_media", + "locale", + "message_display_compact", + "native_phone_integration_enabled", + "render_embeds", + "render_reactions", + "restricted_guilds", + "show_current_game", + "status", + "stream_notifications_enabled", + "theme", + "timezone_offset", + "view_nsfw_guilds" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "IdentifySchema": { + "type": "object", + "properties": { + "token": { + "type": "string" }, - "premium": { + "properties": { + "type": "object", + "properties": { + "os": { + "type": "string" + }, + "os_atch": { + "type": "string" + }, + "browser": { + "type": "string" + }, + "device": { + "type": "string" + }, + "$os": { + "type": "string" + }, + "$browser": { + "type": "string" + }, + "$device": { + "type": "string" + }, + "browser_user_agent": { + "type": "string" + }, + "browser_version": { + "type": "string" + }, + "os_version": { + "type": "string" + }, + "referrer": { + "type": "string" + }, + "referring_domain": { + "type": "string" + }, + "referrer_current": { + "type": "string" + }, + "referring_domain_current": { + "type": "string" + }, + "release_channel": { + "enum": [ + "canary", + "dev", + "ptb", + "stable" + ], + "type": "string" + }, + "client_build_number": { + "type": "integer" + }, + "client_event_source": { + "type": "string" + }, + "client_version": { + "type": "string" + }, + "system_locale": { + "type": "string" + } + }, + "additionalProperties": false + }, + "intents": { + "type": "bigint" + }, + "presence": { + "$ref": "#/definitions/ActivitySchema" + }, + "compress": { "type": "boolean" }, - "purchased_flags": { + "large_threshold": { "type": "integer" }, - "premium_usage_flags": { + "largeThreshold": { + "type": "integer" + }, + "shard": { + "minItems": 2, + "maxItems": 2, + "type": "array", + "items": { + "type": "bigint" + } + }, + "guild_subscriptions": { + "type": "boolean" + }, + "capabilities": { "type": "integer" }, - "disabled": { - "type": "boolean" + "client_state": { + "type": "object", + "properties": { + "guild_hashes": {}, + "highest_last_message_id": { + "type": "integer" + }, + "read_state_version": { + "type": "integer" + }, + "user_guild_settings_version": { + "type": "integer" + }, + "user_settings_version": { + "type": "integer" + }, + "useruser_guild_settings_version": { + "type": "integer" + }, + "private_channels_version": { + "type": "integer" + }, + "guild_versions": {}, + "api_code_version": { + "type": "integer" + }, + "initial_guild_id": { + "type": "string" + } + }, + "additionalProperties": false + }, + "clientState": { + "type": "object", + "properties": { + "guildHashes": {}, + "highestLastMessageId": { + "type": "integer" + }, + "readStateVersion": { + "type": "integer" + }, + "userGuildSettingsVersion": { + "type": "integer" + }, + "useruserGuildSettingsVersion": { + "type": "integer" + }, + "guildVersions": {}, + "apiCodeVersion": { + "type": "integer" + }, + "initialGuildId": { + "type": "string" + } + }, + "additionalProperties": false }, - "settings": { - "$ref": "#/definitions/UserSettingsSchema" + "v": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "bio", - "bot", - "disabled", - "discriminator", - "flags", - "id", - "mfa_enabled", - "nsfw_allowed", - "premium", - "premium_since", - "premium_type", - "premium_usage_flags", - "public_flags", - "purchased_flags", - "username", - "verified" + "properties", + "token" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIGuildArray": { - "type": "array", - "items": { - "$ref": "#/definitions/APIGuild" + "StreamCreateSchema": { + "type": "object", + "properties": { + "type": { + "enum": [ + "call", + "guild" + ], + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "preferred_region": { + "type": "string" + } }, + "additionalProperties": false, + "required": [ + "channel_id", + "type" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIDMChannelArray": { - "type": "array", - "items": { - "$ref": "#/definitions/DmChannelDTO" + "StreamDeleteSchema": { + "type": "object", + "properties": { + "stream_key": { + "type": "string" + } }, + "additionalProperties": false, + "required": [ + "stream_key" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIBackupCodeArray": { - "type": "array", - "items": { - "$ref": "#/definitions/BackupCode" + "StreamWatchSchema": { + "type": "object", + "properties": { + "stream_key": { + "type": "string" + } }, + "additionalProperties": false, + "required": [ + "stream_key" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserUpdateResponse": { + "APIErrorResponse": { "type": "object", "properties": { - "newToken": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "flags": { + "code": { "type": "integer" }, - "verified": { - "type": "boolean" - }, - "premium_since": { - "type": "string", - "format": "date-time" + "message": { + "type": "string" }, - "avatar": { + "errors": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "_errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "code", + "message" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "_errors" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "code", + "errors", + "message" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "CaptchaRequiredResponse": { + "type": "object", + "properties": { + "captcha_key": { "type": "string" }, - "banner": { + "captcha_sitekey": { "type": "string" }, - "bio": { + "captcha_service": { "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "captcha_key", + "captcha_service", + "captcha_sitekey" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIErrorOrCaptchaResponse": { + "anyOf": [ + { + "$ref": "#/definitions/APIErrorResponse" }, - "theme_colors": { + { + "$ref": "#/definitions/CaptchaRequiredResponse" + } + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AccountStandingResponse": { + "type": "object", + "properties": { + "classifications": { "type": "array", "items": { - "type": "integer" + "$ref": "#/definitions/Classification" } }, - "pronouns": { - "type": "string" - }, - "username": { - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "public_flags": { - "type": "integer" - }, - "accent_color": { - "type": "integer" - }, - "bot": { - "type": "boolean" - }, - "premium_type": { - "type": "integer" - }, - "badge_ids": { + "guild_classifications": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/GuildClassification" } }, - "avatar_decoration_data": { - "$ref": "#/definitions/AvatarDecorationData" - }, - "display_name_styles": { - "$ref": "#/definitions/DisplayNameStyle" - }, - "collectibles": { - "$ref": "#/definitions/Collectibles" - }, - "primary_guild": { - "$ref": "#/definitions/PrimaryGuild" - }, - "mfa_enabled": { - "type": "boolean" - }, - "phone": { - "type": "string" - }, - "nsfw_allowed": { - "type": "boolean" + "account_standing": { + "type": "object", + "properties": { + "state": { + "$ref": "#/definitions/AccountStandingState" + } + }, + "additionalProperties": false, + "required": [ + "state" + ] }, - "premium": { + "is_dsa_eligible": { "type": "boolean" }, - "purchased_flags": { - "type": "integer" + "username": { + "type": "string" }, - "premium_usage_flags": { - "type": "integer" + "discriminator": { + "type": "string" }, - "disabled": { + "is_appeal_eligible": { "type": "boolean" }, - "settings": { - "$ref": "#/definitions/UserSettingsSchema" + "appeal_eligibility": { + "type": "array", + "items": { + "$ref": "#/definitions/AppealEligibility" + } } }, "additionalProperties": false, "required": [ - "bio", - "bot", - "disabled", + "account_standing", + "appeal_eligibility", + "classifications", "discriminator", - "flags", - "id", - "mfa_enabled", - "nsfw_allowed", - "premium", - "premium_since", - "premium_type", - "premium_usage_flags", - "public_flags", - "purchased_flags", - "username", - "verified" + "guild_classifications", + "is_appeal_eligible", + "is_dsa_eligible", + "username" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ApplicationDetectableResponse": { - "type": "array", - "items": {}, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ApplicationEntitlementsResponse": { - "type": "array", - "items": {}, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ApplicationSkusResponse": { - "type": "array", - "items": {}, + "BackupCodesChallengeResponse": { + "type": "object", + "properties": { + "nonce": { + "type": "string" + }, + "regenerate_nonce": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "nonce", + "regenerate_nonce" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIApplicationArray": { + "CollectiblesCategoriesResponse": { "type": "array", "items": { - "$ref": "#/definitions/Application" + "$ref": "#/definitions/CollectiblesCategoryItem" }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIBansArray": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildBansResponse" + "CollectiblesMarketingResponse": { + "type": "object", + "properties": { + "marketings": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/CollectiblesMarketingItem" + } + } }, + "additionalProperties": false, + "required": [ + "marketings" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIInviteArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Invite" + "CollectiblesShopResponse": { + "type": "object", + "properties": { + "shop_blocks": { + "type": "array", + "items": { + "$ref": "#/definitions/AnyShopBlock" + } + }, + "categories": { + "type": "array", + "items": { + "$ref": "#/definitions/CollectiblesCategoryItem" + } + } }, + "additionalProperties": false, + "required": [ + "categories", + "shop_blocks" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIMessageArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Message" + "DiscoverableGuildsResponse": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "guilds": { + "type": "array", + "items": { + "$ref": "#/definitions/Guild" + } + }, + "offset": { + "type": "integer" + }, + "limit": { + "type": "integer" + } }, + "additionalProperties": false, + "required": [ + "guilds", + "limit", + "offset", + "total" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIWebhookArray": { + "DmMessagesResponseSchema": { "type": "array", "items": { - "$ref": "#/definitions/Webhook" + "$ref": "#/definitions/PartialMessage" }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIDiscoveryCategoryArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Categories" + "EmailDomainLookupResponse": { + "type": "object", + "properties": { + "guilds_info": { + "type": "array", + "items": { + "$ref": "#/definitions/HubGuild" + } + }, + "has_matching_guild": { + "type": "boolean" + } }, + "additionalProperties": false, + "required": [ + "guilds_info", + "has_matching_guild" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIGeneralConfiguration": { + "EmailDomainLookupVerifyCodeResponse": { "type": "object", "properties": { - "instanceName": { - "type": "string", - "default": "Spacebar Instance" - }, - "serverName": { - "type": [ - "null", - "string" - ], - "default": null - }, - "instanceDescription": { - "type": [ - "null", - "string" - ], - "default": "This is a Spacebar instance made in the pre-release days" - }, - "frontPage": { - "type": [ - "null", - "string" - ], - "default": null - }, - "tosPage": { - "type": [ - "null", - "string" - ], - "default": null - }, - "correspondenceEmail": { - "type": [ - "null", - "string" - ], - "default": null - }, - "correspondenceUserID": { - "type": [ - "null", - "string" - ], - "default": null + "guild": { + "$ref": "#/definitions/Guild" }, - "image": { - "type": [ - "null", - "string" + "joined": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "guild", + "joined" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "EmojiSourceResponse": { + "type": "object", + "properties": { + "type": { + "enum": [ + "APPLICATION", + "GUILD" ], - "default": null - }, - "instanceId": { "type": "string" }, - "autoCreateBotUsers": { - "type": "boolean", - "default": false + "guild": { + "anyOf": [ + { + "$ref": "#/definitions/EmojiGuild" + }, + { + "type": "null" + } + ] + }, + "application": { + "anyOf": [ + { + "$ref": "#/definitions/EmojiApplication" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false, "required": [ - "autoCreateBotUsers", - "correspondenceEmail", - "correspondenceUserID", - "frontPage", - "image", - "instanceDescription", - "instanceId", - "instanceName", - "serverName", - "tosPage" + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIChannelArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Channel" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIEmojiArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Emoji" + "GatewayBotResponse": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "shards": { + "type": "integer" + }, + "session_start_limit": { + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "reset_after": { + "type": "integer" + }, + "max_concurrency": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "max_concurrency", + "remaining", + "reset_after", + "total" + ] + } }, + "additionalProperties": false, + "required": [ + "session_start_limit", + "shards", + "url" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIMemberArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Member" + "GatewayResponse": { + "type": "object", + "properties": { + "url": { + "type": "string" + } }, + "additionalProperties": false, + "required": [ + "url" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIPublicMember": { - "additionalProperties": false, + "GenerateRegistrationTokensResponse": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "mute": { - "type": "boolean" - }, - "deaf": { - "type": "boolean" - }, - "nick": { - "type": "string" - }, - "joined_at": { - "type": "string", - "format": "date-time" - }, - "pending": { - "type": "boolean" - }, - "premium_since": { - "type": "integer" - }, - "avatar": { - "type": "string" - }, - "banner": { - "type": "string" - }, - "bio": { - "type": "string" - }, - "theme_colors": { + "tokens": { "type": "array", "items": { - "type": "integer" + "type": "string" } + } + }, + "additionalProperties": false, + "required": [ + "tokens" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildBansResponse": { + "type": "object", + "properties": { + "reason": { + "type": [ + "null", + "string" + ] }, - "pronouns": { - "type": "string" - }, - "communication_disabled_until": { - "anyOf": [ - { - "type": "string", - "format": "date-time" + "user": { + "type": "object", + "properties": { + "username": { + "type": "string" }, - { - "type": "null" + "discriminator": { + "type": "string" + }, + "id": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + }, + "public_flags": { + "type": "integer" } + }, + "additionalProperties": false, + "required": [ + "avatar", + "discriminator", + "id", + "public_flags", + "username" ] - }, - "user": { - "$ref": "#/definitions/PublicUser" - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } } }, + "additionalProperties": false, "required": [ - "banner", - "bio", - "communication_disabled_until", - "deaf", - "guild_id", - "id", - "joined_at", - "mute", - "pending", - "roles", + "reason", "user" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIGuildWithJoinedAt": { + "GuildCreateResponse": { "type": "object", "properties": { - "joined_at": { - "type": "string" - }, "id": { "type": "string" }, @@ -5102,208 +1797,237 @@ "additionalProperties": false, "required": [ "id", - "joined_at", - "large", - "max_members", - "max_presences", - "max_video_channel_users", - "member_count", - "mfa_level", "name", "nsfw", - "nsfw_level", - "owner_id", - "parent", - "premium_subscription_count", - "premium_tier", - "presence_count", - "primary_category_id", - "template_id", - "welcome_screen", - "widget_channel_id", - "widget_enabled" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIRoleArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Role" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIStickerArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Sticker" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APITemplateArray": { - "type": "array", - "items": { - "$ref": "#/definitions/Template" - }, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "APIGuildVoiceRegion": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildVoiceRegion" - }, + "welcome_screen", + "widget_enabled" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APILimitsConfiguration": { + "GuildDiscoveryRequirementsResponse": { "type": "object", "properties": { - "user": { - "$ref": "#/definitions/UserLimits" + "guild_id": { + "type": "string" }, - "guild": { - "$ref": "#/definitions/GuildLimits" + "safe_environment": { + "type": "boolean" }, - "message": { - "$ref": "#/definitions/MessageLimits" + "healthy": { + "type": "boolean" }, - "channel": { - "$ref": "#/definitions/ChannelLimits" + "health_score_pending": { + "type": "boolean" }, - "rate": { - "$ref": "#/definitions/RateLimits" + "size": { + "type": "boolean" }, - "absoluteRate": { - "$ref": "#/definitions/GlobalRateLimits" + "nsfw_properties": {}, + "protected": { + "type": "boolean" + }, + "sufficient": { + "type": "boolean" + }, + "sufficient_without_grace_period": { + "type": "boolean" + }, + "valid_rules_channel": { + "type": "boolean" + }, + "retention_healthy": { + "type": "boolean" + }, + "engagement_healthy": { + "type": "boolean" + }, + "age": { + "type": "boolean" + }, + "minimum_age": { + "type": "integer" + }, + "health_score": { + "type": "object", + "properties": { + "avg_nonnew_participators": { + "type": "integer" + }, + "avg_nonnew_communicators": { + "type": "integer" + }, + "num_intentful_joiners": { + "type": "integer" + }, + "perc_ret_w1_intentful": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "avg_nonnew_communicators", + "avg_nonnew_participators", + "num_intentful_joiners", + "perc_ret_w1_intentful" + ] + }, + "minimum_size": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "absoluteRate", - "channel", - "guild", - "message", - "rate", - "user" + "age", + "engagement_healthy", + "guild_id", + "health_score", + "health_score_pending", + "healthy", + "minimum_age", + "minimum_size", + "nsfw_properties", + "protected", + "retention_healthy", + "safe_environment", + "size", + "sufficient", + "sufficient_without_grace_period", + "valid_rules_channel" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIStickerPackArray": { - "type": "array", - "items": { - "$ref": "#/definitions/StickerPack" + "GuildMessagesSearchResponse": { + "type": "object", + "properties": { + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/GuildMessagesSearchMessage" + } + }, + "total_results": { + "type": "integer" + } }, + "additionalProperties": false, + "required": [ + "messages", + "total_results" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "APIConnectionsConfiguration": { + "GuildPruneResponse": { "type": "object", + "properties": { + "pruned": { + "type": "integer" + } + }, "additionalProperties": false, + "required": [ + "pruned" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UpdatesResponse": { + "GuildPurgeResponse": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "pub_date": { - "type": "string" - }, - "url": { - "type": "string" - }, - "notes": { - "type": [ - "null", - "string" - ] + "purged": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "name", - "notes", - "pub_date", - "url" + "purged" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UploadAttachmentResponseSchema": { + "GuildRecommendationsResponse": { "type": "object", "properties": { - "attachments": { + "recommended_guilds": { "type": "array", "items": { - "$ref": "#/definitions/UploadAttachmentResponse" + "$ref": "#/definitions/Guild" } + }, + "load_id": { + "type": "string" } }, "additionalProperties": false, "required": [ - "attachments" + "load_id", + "recommended_guilds" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UploadAttachmentResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "upload_url": { - "type": "string" + "GuildVanityUrlResponse": { + "anyOf": [ + { + "$ref": "#/definitions/GuildVanityUrl" }, - "upload_filename": { - "type": "string" + { + "$ref": "#/definitions/GuildVanityUrlNoInvite" }, - "original_content_type": { - "type": "string" + { + "type": "array", + "items": { + "$ref": "#/definitions/GuildVanityUrl" + } } - }, - "additionalProperties": false, - "required": [ - "upload_filename", - "upload_url" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserNoteResponse": { + "GuildVanityUrlCreateResponse": { "type": "object", "properties": { - "note": { - "type": "string" - }, - "note_user_id": { - "type": "string" - }, - "user_id": { + "code": { "type": "string" } }, "additionalProperties": false, "required": [ - "note", - "note_user_id", - "user_id" + "code" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserProfileResponse": { + "GuildWidgetJsonResponse": { "type": "object", "properties": { - "user": { - "$ref": "#/definitions/PublicUser" + "id": { + "type": "string" }, - "connected_accounts": { - "$ref": "#/definitions/PublicConnectedAccount" + "name": { + "type": "string" }, - "premium_guild_since": { - "type": "string", - "format": "date-time" + "instant_invite": { + "type": "string" }, - "premium_since": { - "type": "string", - "format": "date-time" + "channels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "id", + "name", + "position" + ] + } }, - "mutual_guilds": { + "members": { "type": "array", "items": { "type": "object", @@ -5311,1178 +2035,1510 @@ "id": { "type": "string" }, - "nick": { + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + }, + "status": { + "$ref": "#/definitions/ClientStatus" + }, + "avatar_url": { "type": "string" } }, "additionalProperties": false, "required": [ - "id" + "avatar", + "avatar_url", + "discriminator", + "id", + "status", + "username" ] } }, - "premium_type": { - "type": "integer" - }, - "profile_themes_experiment_bucket": { + "presence_count": { "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "channels", + "id", + "instant_invite", + "members", + "name", + "presence_count" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildWidgetSettingsResponse": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" }, - "user_profile": { - "$ref": "#/definitions/UserProfile" - }, - "guild_member": { - "$ref": "#/definitions/PublicMember" - }, - "guild_member_profile": { - "$ref": "#/definitions/PublicMemberProfile" - }, - "badges": { - "type": "array", - "items": { - "$ref": "#/definitions/Badge" - } + "channel_id": { + "anyOf": [ + { + "$ref": "#/definitions/Snowflake" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false, "required": [ - "badges", - "connected_accounts", - "mutual_guilds", - "premium_type", - "profile_themes_experiment_bucket", - "user", - "user_profile" + "channel_id", + "enabled" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserRelationsResponse": { + "HubDirectoryEntriesResponse": { "type": "array", "items": { - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "username": { - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "public_flags": { - "type": "integer" - } - }, - "required": [ - "discriminator", - "id", - "public_flags", - "username" - ] + "$ref": "#/definitions/HubDirectoryEntry" }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserRelationshipsResponse": { + "HubWaitlistSignupResponse": { "type": "object", "properties": { - "id": { + "email": { "type": "string" }, - "type": { - "$ref": "#/definitions/RelationshipType" + "email_domain": { + "type": "string" }, - "nickname": { - "type": "null" + "school": { + "type": "string" }, - "user": { - "$ref": "#/definitions/PublicUser" + "user_id": { + "type": "string" } }, "additionalProperties": false, "required": [ - "id", - "nickname", - "type", - "user" + "email", + "email_domain", + "school", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebAuthnCreateResponse": { + "InstanceDomainsResponse": { "type": "object", "properties": { - "name": { + "admin": { "type": "string" }, - "id": { + "api": { + "type": "string" + }, + "apiEndpoint": { + "type": "string" + }, + "cdn": { + "type": "string" + }, + "gateway": { + "type": "string" + }, + "defaultApiVersion": { "type": "string" } }, "additionalProperties": false, "required": [ - "id", - "name" + "api", + "apiEndpoint", + "cdn", + "defaultApiVersion", + "gateway" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebhookCreateResponse": { + "InstancePingResponse": { "type": "object", "properties": { - "user": { - "$ref": "#/definitions/User" + "ping": { + "type": "string", + "const": "pong!" }, - "hook": { - "$ref": "#/definitions/Webhook" + "instance": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": [ + "null", + "string" + ] + }, + "image": { + "type": [ + "null", + "string" + ] + }, + "correspondenceEmail": { + "type": [ + "null", + "string" + ] + }, + "correspondenceUserID": { + "type": [ + "null", + "string" + ] + }, + "frontPage": { + "type": [ + "null", + "string" + ] + }, + "tosPage": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "required": [ + "correspondenceEmail", + "correspondenceUserID", + "description", + "frontPage", + "id", + "image", + "name", + "tosPage" + ] } }, "additionalProperties": false, "required": [ - "hook", - "user" + "instance", + "ping" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AckBulkSchema": { + "InstanceStatsResponse": { "type": "object", "properties": { - "read_states": { - "type": "array", - "items": { - "type": "object", - "properties": { - "channel_id": { - "type": "string" - }, - "message_id": { - "type": "string" - }, - "read_state_type": { - "type": "integer" - } + "counts": { + "type": "object", + "properties": { + "user": { + "type": "integer" }, - "additionalProperties": false, - "required": [ - "channel_id", - "message_id", - "read_state_type" - ] - } + "guild": { + "type": "integer" + }, + "message": { + "type": "integer" + }, + "members": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "guild", + "members", + "message", + "user" + ] } }, "additionalProperties": false, "required": [ - "read_states" + "counts" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ActivitySchema": { + "LocationMetadataResponse": { "type": "object", "properties": { - "afk": { + "consent_required": { "type": "boolean" }, - "status": { - "$ref": "#/definitions/Status" - }, - "activities": { - "type": "array", - "items": { - "$ref": "#/definitions/Activity" - } + "country_code": { + "type": "string" }, - "since": { - "type": "integer" + "promotional_email_opt_in": { + "type": "object", + "properties": { + "required": { + "type": "boolean", + "const": true + }, + "pre_checked": { + "type": "boolean", + "const": false + } + }, + "additionalProperties": false, + "required": [ + "pre_checked", + "required" + ] } }, "additionalProperties": false, "required": [ - "status" + "consent_required", + "country_code", + "promotional_email_opt_in" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ApplicationAuthorizeSchema": { + "MemberJoinGuildResponse": { "type": "object", "properties": { - "authorize": { - "type": "boolean" - }, - "guild_id": { - "type": "string" + "guild": { + "$ref": "#/definitions/GuildCreateResponse" }, - "permissions": { - "type": "string" + "emojis": { + "type": "array", + "items": { + "$ref": "#/definitions/Emoji" + } }, - "captcha_key": { - "type": "string" + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" + } }, - "code": { - "minLength": 6, - "maxLength": 6, - "type": "string" + "stickers": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" + } } }, "additionalProperties": false, "required": [ - "authorize", - "guild_id", - "permissions" + "emojis", + "guild", + "roles", + "stickers" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AutomodMentionSpamRuleSchema": { + "OAuthAuthorizeResponse": { "type": "object", "properties": { - "mention_total_limit": { - "type": "integer" - }, - "mention_raid_protection_enabled": { - "type": "boolean" + "location": { + "type": "string" } }, "additionalProperties": false, "required": [ - "mention_raid_protection_enabled", - "mention_total_limit" + "location" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AutomodSuspectedSpamRuleSchema": { - "type": "object", - "additionalProperties": false, + "PreloadMessagesResponseSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/Message" + }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "AutomodCommonlyFlaggedWordsRuleSchema": { + "RefreshUrlsResponse": { "type": "object", "properties": { - "allow_list": { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 - }, - "presets": { + "refreshed_urls": { "type": "array", - "items": [ - { - "type": "integer" - } - ], - "minItems": 1, - "maxItems": 1 + "items": { + "$ref": "#/definitions/RefreshedUrl" + } } }, "additionalProperties": false, "required": [ - "allow_list", - "presets" + "refreshed_urls" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AutomodCustomWordsRuleSchema": { + "SettingsProtoResponse": { "type": "object", "properties": { - "allow_list": { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 - }, - "keyword_filter": { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 - }, - "regex_patterns": { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 + "settings": { + "type": "string" } }, "additionalProperties": false, "required": [ - "allow_list", - "keyword_filter", - "regex_patterns" + "settings" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AutomodRuleSchema": { + "SettingsProtoUpdateResponse": { "type": "object", "properties": { - "creator_id": { - "type": "string" - }, - "enabled": { + "out_of_date": { "type": "boolean" }, - "event_type": { - "type": "integer" - }, - "exempt_channels": { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 - }, - "exempt_roles": { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 - }, - "guild_id": { - "type": "string" - }, - "name": { + "settings": { "type": "string" - }, - "position": { - "type": "integer" - }, - "trigger_type": { - "type": "integer" - }, - "trigger_metadata": { - "anyOf": [ - { - "$ref": "#/definitions/AutomodMentionSpamRuleSchema" - }, - { - "$ref": "#/definitions/AutomodSuspectedSpamRuleSchema" - }, - { - "$ref": "#/definitions/AutomodCommonlyFlaggedWordsRuleSchema" - }, - { - "$ref": "#/definitions/AutomodCustomWordsRuleSchema" - } - ] } }, "additionalProperties": false, "required": [ - "creator_id", - "enabled", - "event_type", - "exempt_channels", - "exempt_roles", - "guild_id", - "name", - "position", - "trigger_metadata", - "trigger_type" + "settings" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BackupCodesChallengeSchema": { + "SettingsProtoJsonResponse": { "type": "object", "properties": { - "password": { - "minLength": 1, - "maxLength": 72, - "type": "string" + "settings": { + "$ref": "#/definitions/JsonValue" } }, "additionalProperties": false, "required": [ - "password" + "settings" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BanCreateSchema": { + "SettingsProtoUpdateJsonResponse": { "type": "object", "properties": { - "delete_message_seconds": { - "type": "integer" - }, - "delete_message_days": { - "type": "integer" + "out_of_date": { + "type": "boolean" }, - "reason": { - "type": "string" + "settings": { + "$ref": "#/definitions/JsonValue" } }, "additionalProperties": false, + "required": [ + "settings" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BanModeratorSchema": { + "TeamListResponse": { + "type": "array", + "items": { + "$ref": "#/definitions/Team" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TenorGifResponse": { "type": "object", "properties": { "id": { "type": "string" }, - "user_id": { + "title": { "type": "string" }, - "guild_id": { + "url": { "type": "string" }, - "executor_id": { + "src": { "type": "string" }, - "reason": { + "gif_src": { + "type": "string" + }, + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + }, + "preview": { "type": "string" } }, "additionalProperties": false, "required": [ - "executor_id", - "guild_id", + "gif_src", + "height", "id", - "user_id" + "preview", + "src", + "title", + "url", + "width" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BanRegistrySchema": { + "TenorTrendingResponse": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "user_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "executor_id": { - "type": "string" - }, - "ip": { - "type": "string" + "categories": { + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "searchterm": { + "type": "string" + }, + "path": { + "type": "string" + }, + "image": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "image", + "name", + "path", + "searchterm" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "tags" + ] }, - "reason": { - "type": "string" + "gifs": { + "type": "array", + "items": { + "$ref": "#/definitions/TenorGifResponse" + } } }, "additionalProperties": false, "required": [ - "executor_id", - "guild_id", - "id", - "user_id" + "categories", + "gifs" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BotModifySchema": { + "TenorGifsResponse": { + "type": "array", + "items": { + "$ref": "#/definitions/TenorGifResponse" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TokenResponse": { "type": "object", "properties": { - "avatar": { - "type": "string" - }, - "username": { + "token": { "type": "string" }, - "banner": { - "type": "string" + "settings": { + "$ref": "#/definitions/UserSettings" } }, "additionalProperties": false, + "required": [ + "settings", + "token" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BulkBanSchema": { + "TokenOnlyResponse": { "type": "object", "properties": { - "user_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "delete_message_seconds": { - "type": "integer" + "token": { + "type": "string" } }, "additionalProperties": false, "required": [ - "user_ids" + "token" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BulkDeleteSchema": { + "TokenWithBackupCodesResponse": { "type": "object", "properties": { - "messages": { + "token": { + "type": "string" + }, + "backup_codes": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/BackupCode" } } }, "additionalProperties": false, "required": [ - "messages" + "backup_codes", + "token" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ChannelModifySchema": { + "APIGuild": { "type": "object", "properties": { "name": { "type": "string" }, - "type": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 2, - 20, - 21, - 255, - 3, - 33, - 34, - 35, - 4, - 5, - 6, - 64, - 7, - 8, - 9 - ], - "type": "number" + "region": { + "type": "string" }, - "topic": { + "insert": { + "type": "object", + "additionalProperties": false + }, + "id": { + "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "icon": { + "type": "string" + }, + "parent": { + "type": "string" + }, + "owner_id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "invites": { + "type": "array", + "items": { + "$ref": "#/definitions/Invite" + } + }, + "voice_states": { + "type": "array", + "items": { + "$ref": "#/definitions/VoiceState" + } + }, + "webhooks": { + "type": "array", + "items": { + "$ref": "#/definitions/Webhook" + } + }, + "member_count": { + "type": "integer" + }, + "get_annotations": { + "type": "object", + "additionalProperties": false + }, + "clean_data": { + "type": "object", + "additionalProperties": false + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" + } + }, + "banner": { "type": "string" }, - "icon": { + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/Channel" + } + }, + "system_channel_id": { "type": [ "null", "string" ] }, - "bitrate": { + "rules_channel_id": { + "type": [ + "null", + "string" + ] + }, + "splash": { + "type": "string" + }, + "description": { + "type": "string" + }, + "features": { + "type": "array", + "items": { + "type": "string" + } + }, + "verification_level": { "type": "integer" }, - "user_limit": { + "default_message_notifications": { "type": "integer" }, - "rate_limit_per_user": { + "system_channel_flags": { "type": "integer" }, - "position": { + "explicit_content_filter": { "type": "integer" }, - "invitable": { + "public_updates_channel_id": { + "type": [ + "null", + "string" + ] + }, + "afk_timeout": { + "type": "integer" + }, + "afk_channel_id": { + "type": [ + "null", + "string" + ] + }, + "preferred_locale": { + "type": "string" + }, + "premium_progress_bar_enabled": { "type": "boolean" }, - "permission_overwrites": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" - }, - "allow": { - "type": "string" - }, - "deny": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "allow", - "deny", - "id", - "type" - ] - } + "discovery_splash": { + "type": "string" }, - "applied_tags": { + "bans": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/Ban" } }, - "parent_id": { - "type": "string" - }, - "id": { + "primary_category_id": { "type": "string" }, - "nsfw": { + "large": { "type": "boolean" }, - "rtc_region": { - "type": "string" + "max_members": { + "type": "integer" }, - "default_auto_archive_duration": { + "max_presences": { "type": "integer" }, - "default_reaction_emoji": { - "type": [ - "null", - "string" - ] + "max_video_channel_users": { + "type": "integer" }, - "flags": { + "presence_count": { "type": "integer" }, - "default_thread_rate_limit_per_user": { + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } + }, + "template_id": { + "type": "string" + }, + "emojis": { + "type": "array", + "items": { + "$ref": "#/definitions/Emoji" + } + }, + "stickers": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" + } + }, + "mfa_level": { "type": "integer" }, - "video_quality_mode": { + "premium_subscription_count": { "type": "integer" }, - "auto_archive_duration": { + "premium_tier": { "type": "integer" }, - "archived": { + "unavailable": { "type": "boolean" }, - "locked": { + "welcome_screen": { + "$ref": "#/definitions/GuildWelcomeScreen", + "description": "DEPRECATED: Look at the new Guild onboarding screens." + }, + "widget_channel_id": { + "type": "string" + }, + "widget_enabled": { "type": "boolean" }, - "available_tags": { + "nsfw_level": { + "type": "integer" + }, + "permissions": { + "type": "integer" + }, + "channel_ordering": { "type": "array", "items": { - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "moderated": { - "type": [ - "null", - "boolean" - ] - }, - "emoji_id": { - "type": [ - "null", - "string" - ] - }, - "emoji_name": { - "type": [ - "null", - "string" - ] - }, - "id": { - "type": "string" - } - }, - "required": [ - "id", - "name" - ] + "type": "string" } - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ChannelPermissionOverwriteSchema": { - "type": "object", - "properties": { - "allow": { - "type": "string" }, - "deny": { - "type": "string" + "discovery_weight": { + "type": "integer" }, - "id": { - "type": "string" + "discovery_excluded": { + "type": "boolean" }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" + "ToGuildSource": { + "type": "object", + "additionalProperties": false } }, "additionalProperties": false, "required": [ - "allow", - "deny", + "ToGuildSource", + "__@annotationsKey@11707", + "bans", + "channel_ordering", + "channels", + "clean_data", + "discovery_excluded", + "discovery_weight", + "emojis", + "features", + "get_annotations", "id", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ChannelReorderSchema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "position": { - "type": "integer" - }, - "lock_permissions": { - "type": "boolean" - }, - "parent_id": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "id" - ] - }, + "insert", + "invites", + "members", + "name", + "nsfw", + "premium_progress_bar_enabled", + "public_updates_channel_id", + "roles", + "stickers", + "unavailable", + "voice_states", + "webhooks", + "welcome_screen", + "widget_enabled" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CodesVerificationSchema": { + "APIPublicUser": { "type": "object", "properties": { - "key": { + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "nonce": { + "premium_since": { + "type": "string", + "format": "date-time" + }, + "avatar": { "type": "string" }, - "regenerate": { + "banner": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "theme_colors": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pronouns": { + "type": "string" + }, + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "public_flags": { + "type": "integer" + }, + "accent_color": { + "type": "integer" + }, + "bot": { "type": "boolean" + }, + "premium_type": { + "type": "integer" + }, + "badge_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "avatar_decoration_data": { + "$ref": "#/definitions/AvatarDecorationData" + }, + "display_name_styles": { + "$ref": "#/definitions/DisplayNameStyle" + }, + "collectibles": { + "$ref": "#/definitions/Collectibles" + }, + "primary_guild": { + "$ref": "#/definitions/PrimaryGuild" } }, "additionalProperties": false, "required": [ - "key", - "nonce" + "bio", + "bot", + "discriminator", + "id", + "premium_since", + "premium_type", + "public_flags", + "username" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ConnectedAccountSchema": { + "APIPrivateUser": { "type": "object", "properties": { - "external_id": { + "email": { "type": "string" }, - "user_id": { + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "token_data": { - "$ref": "#/definitions/ConnectedAccountTokenData" + "flags": { + "type": "integer" }, - "friend_sync": { + "verified": { "type": "boolean" }, - "name": { + "premium_since": { + "type": "string", + "format": "date-time" + }, + "avatar": { "type": "string" }, - "revoked": { - "type": "boolean" + "banner": { + "type": "string" }, - "show_activity": { - "type": "integer" + "bio": { + "type": "string" }, - "type": { + "theme_colors": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pronouns": { "type": "string" }, - "verified": { + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "public_flags": { + "type": "integer" + }, + "accent_color": { + "type": "integer" + }, + "bot": { "type": "boolean" }, - "visibility": { + "premium_type": { "type": "integer" }, - "integrations": { + "badge_ids": { "type": "array", "items": { "type": "string" } }, - "metadata_": {}, - "metadata_visibility": { + "avatar_decoration_data": { + "$ref": "#/definitions/AvatarDecorationData" + }, + "display_name_styles": { + "$ref": "#/definitions/DisplayNameStyle" + }, + "collectibles": { + "$ref": "#/definitions/Collectibles" + }, + "primary_guild": { + "$ref": "#/definitions/PrimaryGuild" + }, + "mfa_enabled": { + "type": "boolean" + }, + "phone": { + "type": "string" + }, + "nsfw_allowed": { + "type": "boolean" + }, + "premium": { + "type": "boolean" + }, + "purchased_flags": { "type": "integer" }, - "two_way_link": { + "premium_usage_flags": { + "type": "integer" + }, + "disabled": { "type": "boolean" + }, + "settings": { + "$ref": "#/definitions/UserSettingsSchema" } }, "additionalProperties": false, "required": [ - "external_id", - "name", - "type", - "user_id" + "bio", + "bot", + "disabled", + "discriminator", + "flags", + "id", + "mfa_enabled", + "nsfw_allowed", + "premium", + "premium_since", + "premium_type", + "premium_usage_flags", + "public_flags", + "purchased_flags", + "username", + "verified" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ConnectionCallbackSchema": { + "APIGuildArray": { + "type": "array", + "items": { + "$ref": "#/definitions/APIGuild" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIDMChannelArray": { + "type": "array", + "items": { + "$ref": "#/definitions/DmChannelDTO" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIBackupCodeArray": { + "type": "array", + "items": { + "$ref": "#/definitions/BackupCode" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserUpdateResponse": { "type": "object", "properties": { - "code": { + "newToken": { "type": "string" }, - "state": { + "email": { "type": "string" }, - "insecure": { - "type": "boolean" + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" }, - "friend_sync": { - "type": "boolean" + "flags": { + "type": "integer" }, - "openid_params": {} - }, - "additionalProperties": false, - "required": [ - "friend_sync", - "insecure", - "state" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ConnectionUpdateSchema": { - "type": "object", - "properties": { - "visibility": { + "verified": { "type": "boolean" }, - "show_activity": { - "type": "boolean" + "premium_since": { + "type": "string", + "format": "date-time" }, - "metadata_visibility": { - "type": "boolean" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "DmChannelCreateSchema": { - "type": "object", - "properties": { - "name": { + "avatar": { "type": "string" }, - "recipients": { + "banner": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "theme_colors": { "type": "array", "items": { - "type": "string" + "type": "integer" } }, - "recipient_id": { + "pronouns": { "type": "string" }, - "access_tokens": { + "username": { + "type": "string" + }, + "discriminator": { + "type": "string" + }, + "public_flags": { + "type": "integer" + }, + "accent_color": { + "type": "integer" + }, + "bot": { + "type": "boolean" + }, + "premium_type": { + "type": "integer" + }, + "badge_ids": { "type": "array", "items": { "type": "string" } - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "EmailDomainLookupSchema": { - "type": "object", - "properties": { - "allow_multiple_guilds": { + }, + "avatar_decoration_data": { + "$ref": "#/definitions/AvatarDecorationData" + }, + "display_name_styles": { + "$ref": "#/definitions/DisplayNameStyle" + }, + "collectibles": { + "$ref": "#/definitions/Collectibles" + }, + "primary_guild": { + "$ref": "#/definitions/PrimaryGuild" + }, + "mfa_enabled": { "type": "boolean" }, - "email": { + "phone": { "type": "string" }, - "use_verification_code": { + "nsfw_allowed": { "type": "boolean" }, - "guild_id": { - "type": "string" + "premium": { + "type": "boolean" + }, + "purchased_flags": { + "type": "integer" + }, + "premium_usage_flags": { + "type": "integer" + }, + "disabled": { + "type": "boolean" + }, + "settings": { + "$ref": "#/definitions/UserSettingsSchema" } }, "additionalProperties": false, "required": [ - "allow_multiple_guilds", - "email", - "use_verification_code" + "bio", + "bot", + "disabled", + "discriminator", + "flags", + "id", + "mfa_enabled", + "nsfw_allowed", + "premium", + "premium_since", + "premium_type", + "premium_usage_flags", + "public_flags", + "purchased_flags", + "username", + "verified" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmailDomainLookupVerifyCodeSchema": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "code": { - "type": "string" - } + "ApplicationDetectableResponse": { + "type": "array", + "items": {}, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ApplicationEntitlementsResponse": { + "type": "array", + "items": {}, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ApplicationSkusResponse": { + "type": "array", + "items": {}, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIApplicationArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Application" }, - "additionalProperties": false, - "required": [ - "code", - "email", - "guild_id" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmojiCreateSchema": { + "APIBansArray": { + "type": "array", + "items": { + "$ref": "#/definitions/GuildBansResponse" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIInviteArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Invite" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIMessageArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Message" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIWebhookArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Webhook" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIDiscoveryCategoryArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Categories" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIGeneralConfiguration": { "type": "object", "properties": { - "name": { - "type": "string" + "instanceName": { + "type": "string", + "default": "Spacebar Instance" }, - "image": { - "type": "string" + "serverName": { + "type": [ + "null", + "string" + ], + "default": null }, - "require_colons": { + "instanceDescription": { "type": [ "null", - "boolean" - ] + "string" + ], + "default": "This is a Spacebar instance made in the pre-release days" }, - "roles": { - "type": "array", - "items": { - "type": "string" - } + "frontPage": { + "type": [ + "null", + "string" + ], + "default": null + }, + "tosPage": { + "type": [ + "null", + "string" + ], + "default": null + }, + "correspondenceEmail": { + "type": [ + "null", + "string" + ], + "default": null + }, + "correspondenceUserID": { + "type": [ + "null", + "string" + ], + "default": null + }, + "image": { + "type": [ + "null", + "string" + ], + "default": null + }, + "instanceId": { + "type": "string" + }, + "autoCreateBotUsers": { + "type": "boolean", + "default": false } }, "additionalProperties": false, "required": [ - "image" + "autoCreateBotUsers", + "correspondenceEmail", + "correspondenceUserID", + "frontPage", + "image", + "instanceDescription", + "instanceId", + "instanceName", + "serverName", + "tosPage" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmojiModifySchema": { + "APIChannelArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Channel" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIEmojiArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Emoji" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIMemberArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIPublicMember": { + "additionalProperties": false, "type": "object", "properties": { - "name": { + "id": { "type": "string" }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ForgotPasswordSchema": { - "type": "object", - "properties": { - "login": { + "guild_id": { "type": "string" }, - "captcha_key": { + "flags": { + "type": "integer" + }, + "mute": { + "type": "boolean" + }, + "deaf": { + "type": "boolean" + }, + "nick": { "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "login" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GreetRequestSchema": { - "type": "object", - "properties": { - "sticker_ids": { + }, + "joined_at": { + "type": "string", + "format": "date-time" + }, + "pending": { + "type": "boolean" + }, + "premium_since": { + "type": "integer" + }, + "avatar": { + "type": "string" + }, + "banner": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "theme_colors": { "type": "array", "items": { - "type": "string" + "type": "integer" } }, - "allowed_mentions": { - "$ref": "#/definitions/AllowedMentions" + "pronouns": { + "type": "string" }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" + "communication_disabled_until": { + "anyOf": [ + { + "type": "string", + "format": "date-time" }, - "fail_if_not_exists": { - "type": "boolean" + { + "type": "null" } - }, - "additionalProperties": false, - "required": [ - "message_id" ] + }, + "user": { + "$ref": "#/definitions/PublicUser" + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } } }, - "additionalProperties": false, "required": [ - "sticker_ids" + "banner", + "bio", + "communication_disabled_until", + "deaf", + "flags", + "guild_id", + "id", + "joined_at", + "mute", + "pending", + "roles", + "user" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildCreateSchema": { + "APIGuildWithJoinedAt": { "type": "object", "properties": { + "joined_at": { + "type": "string" + }, + "id": { + "type": "string" + }, "name": { - "maxLength": 100, "type": "string" }, - "region": { + "primary_category_id": { "type": "string" }, - "icon": { - "type": [ - "null", - "string" - ] + "large": { + "type": "boolean" }, - "channels": { - "type": "array", - "items": { - "$ref": "#/definitions/ChannelCreateSchema" - } + "max_members": { + "type": "integer" }, - "system_channel_id": { - "type": "string" + "max_presences": { + "type": "integer" }, - "rules_channel_id": { + "max_video_channel_users": { + "type": "integer" + }, + "member_count": { + "type": "integer" + }, + "presence_count": { + "type": "integer" + }, + "template_id": { "type": "string" }, - "guild_template_code": { + "mfa_level": { + "type": "integer" + }, + "owner_id": { "type": "string" }, - "staff_only": { - "type": "boolean" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildSubscriptionsBulkSchema": { - "type": "object", - "properties": { - "subscriptions": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/GuildSubscriptionSchema" - } - } - }, - "additionalProperties": false, - "required": [ - "subscriptions" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildSubscriptionSchema": { - "type": "object", - "properties": { - "channels": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "integer" - } - } - } + "premium_subscription_count": { + "type": "integer" }, - "members": { - "type": "array", - "items": { - "type": "string" - } + "premium_tier": { + "type": "integer" }, - "activities": { - "type": "boolean" + "welcome_screen": { + "$ref": "#/definitions/GuildWelcomeScreen" }, - "threads": { - "type": "boolean" + "widget_channel_id": { + "type": "string" }, - "typing": { - "const": true, + "widget_enabled": { "type": "boolean" }, - "member_updates": { + "nsfw_level": { + "type": "integer" + }, + "nsfw": { "type": "boolean" }, - "thread_member_lists": { - "type": "array", - "items": {} - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildTemplateCreateSchema": { - "type": "object", - "properties": { - "name": { + "parent": { "type": "string" }, - "avatar": { + "region": { + "type": "string" + }, + "icon": { "type": [ "null", "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "name" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildUpdateSchema": { - "type": "object", - "properties": { + ] + }, "banner": { "type": [ "null", "string" ] }, + "system_channel_id": { + "type": "string" + }, + "rules_channel_id": { + "type": "string" + }, + "guild_template_code": { + "type": "string" + }, + "staff_only": { + "type": "boolean" + }, "splash": { "type": [ "null", @@ -6533,288 +3589,120 @@ "null", "string" ] - }, - "name": { - "maxLength": 100, - "type": "string" - }, - "region": { - "type": "string" - }, - "icon": { - "type": [ - "null", - "string" - ] - }, - "system_channel_id": { - "type": "string" - }, - "rules_channel_id": { - "type": "string" - }, - "guild_template_code": { - "type": "string" - }, - "staff_only": { - "type": "boolean" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildUpdateWelcomeScreenSchema": { - "type": "object", - "properties": { - "welcome_channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "channel_id": { - "type": "string" - }, - "description": { - "type": "string" - }, - "emoji_id": { - "type": "string" - }, - "emoji_name": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "channel_id", - "description" - ] - } - }, - "enabled": { - "type": "boolean" - }, - "description": { - "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "HubWaitlistSignupSchema": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "school": { - "type": "string" } }, "additionalProperties": false, "required": [ - "email", - "school" + "id", + "joined_at", + "large", + "max_members", + "max_presences", + "max_video_channel_users", + "member_count", + "mfa_level", + "name", + "nsfw", + "nsfw_level", + "owner_id", + "parent", + "premium_subscription_count", + "premium_tier", + "presence_count", + "primary_category_id", + "template_id", + "welcome_screen", + "widget_channel_id", + "widget_enabled" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "InviteCreateSchema": { - "type": "object", - "properties": { - "target_user_id": { - "type": "string" - }, - "target_type": { - "type": "string" - }, - "validate": { - "type": "string" - }, - "max_age": { - "type": "integer" - }, - "max_uses": { - "type": "integer" - }, - "temporary": { - "type": "boolean" - }, - "unique": { - "type": "boolean" - }, - "target_user": { - "type": "string" - }, - "target_user_type": { - "type": "integer" - }, - "flags": { - "type": "integer" - } + "APIRoleArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" }, - "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "MFAResponse": { - "type": "object", - "properties": { - "ticket": { - "type": "string" - }, - "mfa": { - "type": "boolean", - "const": true - }, - "sms": { - "type": "boolean", - "const": false - }, - "token": { - "type": "null" - } + "APIStickerArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" }, - "additionalProperties": false, - "required": [ - "mfa", - "sms", - "ticket", - "token" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebAuthnResponse": { - "type": "object", - "properties": { - "webauthn": { - "type": "string" - }, - "ticket": { - "type": "string" - }, - "mfa": { - "type": "boolean", - "const": true - }, - "sms": { - "type": "boolean", - "const": false - }, - "token": { - "type": "null" - } + "APITemplateArray": { + "type": "array", + "items": { + "$ref": "#/definitions/Template" }, - "additionalProperties": false, - "required": [ - "mfa", - "sms", - "ticket", - "token", - "webauthn" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "LoginResponse": { - "anyOf": [ - { - "$ref": "#/definitions/TokenResponse" - }, - { - "$ref": "#/definitions/MFAResponse" - }, - { - "$ref": "#/definitions/WebAuthnResponse" - } - ], + "APIGuildVoiceRegion": { + "type": "array", + "items": { + "$ref": "#/definitions/GuildVoiceRegion" + }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "LoginSchema": { + "APILimitsConfiguration": { "type": "object", "properties": { - "login": { - "type": "string" + "user": { + "$ref": "#/definitions/UserLimits" }, - "password": { - "minLength": 1, - "maxLength": 72, - "type": "string" + "guild": { + "$ref": "#/definitions/GuildLimits" }, - "undelete": { - "type": "boolean" + "message": { + "$ref": "#/definitions/MessageLimits" }, - "captcha_key": { - "type": "string" + "channel": { + "$ref": "#/definitions/ChannelLimits" }, - "login_source": { - "type": "string" + "rate": { + "$ref": "#/definitions/RateLimits" }, - "gift_code_sku_id": { - "type": "string" + "absoluteRate": { + "$ref": "#/definitions/GlobalRateLimits" } }, "additionalProperties": false, "required": [ - "login", - "password" + "absoluteRate", + "channel", + "guild", + "message", + "rate", + "user" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MemberChangeProfileSchema": { - "type": "object", - "properties": { - "banner": { - "type": [ - "null", - "string" - ] - }, - "nick": { - "type": "string" - }, - "bio": { - "type": "string" - }, - "pronouns": { - "type": "string" - }, - "theme_colors": { - "items": [ - { - "type": "integer" - }, - { - "type": "integer" - } - ], - "type": "array", - "minItems": 2, - "maxItems": 2 - } + "APIStickerPackArray": { + "type": "array", + "items": { + "$ref": "#/definitions/StickerPack" }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "APIConnectionsConfiguration": { + "type": "object", "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "MemberChangeSchema": { + "UpdatesResponse": { "type": "object", "properties": { - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "nick": { + "name": { "type": "string" }, - "avatar": { - "type": [ - "null", - "string" - ] + "pub_date": { + "type": "string" }, - "bio": { + "url": { "type": "string" }, - "communication_disabled_until": { + "notes": { "type": [ "null", "string" @@ -6822,1569 +3710,1582 @@ } }, "additionalProperties": false, + "required": [ + "name", + "notes", + "pub_date", + "url" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MemberNickChangeSchema": { + "UploadAttachmentResponseSchema": { "type": "object", "properties": { - "nick": { - "type": "string" + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/UploadAttachmentResponse" + } } }, "additionalProperties": false, "required": [ - "nick" + "attachments" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageAcknowledgeSchema": { + "UploadAttachmentResponse": { "type": "object", "properties": { - "manual": { - "type": "boolean" - }, - "mention_count": { - "type": "integer" + "id": { + "type": "string" }, - "flags": { - "enum": [ - 0, - 1, - 2, - 4 - ], - "type": "number" + "upload_url": { + "type": "string" }, - "last_viewed": { - "type": "integer" + "upload_filename": { + "type": "string" }, - "token": { + "original_content_type": { "type": "string" } }, "additionalProperties": false, + "required": [ + "upload_filename", + "upload_url" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AcknowledgeDeleteSchema": { + "UserNoteResponse": { "type": "object", "properties": { - "read_state_type": { - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5 - ], - "type": "number" + "note": { + "type": "string" }, - "version": { - "type": "integer" + "note_user_id": { + "type": "string" + }, + "user_id": { + "type": "string" } }, "additionalProperties": false, + "required": [ + "note", + "note_user_id", + "user_id" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageCreateSchema": { + "UserProfileResponse": { "type": "object", "properties": { - "type": { - "type": "integer" - }, - "content": { - "type": "string" - }, - "mobile_network_type": { - "type": "string" - }, - "nonce": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "tts": { - "type": "boolean" - }, - "flags": { - "type": "integer" - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } - }, - "embed": { - "$ref": "#/definitions/Embed" - }, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - }, - "replied_user": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "fail_if_not_exists": { - "type": "boolean" - }, - "type": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "payload_json": { - "type": "string" - }, - "file": { - "type": "object", - "properties": { - "filename": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename" - ] - }, - "attachments": { - "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "id" - ] + "user": { + "$ref": "#/definitions/PublicUser" + }, + "connected_accounts": { + "$ref": "#/definitions/PublicConnectedAccount" + }, + "premium_guild_since": { + "type": "string", + "format": "date-time" + }, + "premium_since": { + "type": "string", + "format": "date-time" + }, + "mutual_guilds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" }, - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - }, - "uploaded_filename": { - "type": "string" - }, - "original_content_type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "uploaded_filename" - ] + "nick": { + "type": "string" } + }, + "additionalProperties": false, + "required": [ + "id" ] } }, - "sticker_ids": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "null" - } - ] + "premium_type": { + "type": "integer" }, - "components": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/ActionRowComponent" - } - }, - { - "type": "null" - } - ] + "profile_themes_experiment_bucket": { + "type": "integer" }, - "poll": { - "$ref": "#/definitions/PollCreationSchema" + "user_profile": { + "$ref": "#/definitions/UserProfile" }, - "enforce_nonce": { - "type": "boolean" + "guild_member": { + "$ref": "#/definitions/PublicMember" }, - "applied_tags": { + "guild_member_profile": { + "$ref": "#/definitions/PublicMemberProfile" + }, + "badges": { "type": "array", "items": { + "$ref": "#/definitions/Badge" + } + } + }, + "additionalProperties": false, + "required": [ + "badges", + "connected_accounts", + "mutual_guilds", + "premium_type", + "profile_themes_experiment_bucket", + "user", + "user_profile" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserRelationsResponse": { + "type": "array", + "items": { + "additionalProperties": false, + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "username": { + "type": "string" + }, + "discriminator": { "type": "string" + }, + "avatar": { + "type": "string" + }, + "public_flags": { + "type": "integer" } }, - "thread_name": { + "required": [ + "discriminator", + "id", + "public_flags", + "username" + ] + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserRelationshipsResponse": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "avatar_url": { + "type": { + "$ref": "#/definitions/RelationshipType" + }, + "nickname": { + "type": "null" + }, + "user": { + "$ref": "#/definitions/PublicUser" + } + }, + "additionalProperties": false, + "required": [ + "id", + "nickname", + "type", + "user" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WebAuthnCreateResponse": { + "type": "object", + "properties": { + "name": { "type": "string" }, - "interaction": { - "$ref": "#/definitions/MessageInteractionSchema" + "id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "id", + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WebhookCreateResponse": { + "type": "object", + "properties": { + "user": { + "$ref": "#/definitions/User" }, - "interaction_metadata": { - "$ref": "#/definitions/MessageInteractionSchema" + "hook": { + "$ref": "#/definitions/Webhook" } }, "additionalProperties": false, - "definitions": { - "Embed": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { - "type": "integer" - }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" - ] - }, - "image": { - "$ref": "#/definitions/EmbedImage" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage" - }, - "video": { - "$ref": "#/definitions/EmbedImage" - }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } + "required": [ + "hook", + "user" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AckBulkSchema": { + "type": "object", + "properties": { + "read_states": { + "type": "array", + "items": { + "type": "object", + "properties": { + "channel_id": { + "type": "string" }, - "additionalProperties": false - }, - "author": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } + "message_id": { + "type": "string" }, - "additionalProperties": false - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "inline": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name", - "value" - ] + "read_state_type": { + "type": "integer" } - } - }, - "additionalProperties": false + }, + "additionalProperties": false, + "required": [ + "channel_id", + "message_id", + "read_state_type" + ] + } } }, + "additionalProperties": false, + "required": [ + "read_states" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PollCreationSchema": { + "ActivitySchema": { "type": "object", "properties": { - "question": { - "$ref": "#/definitions/PollMedia" + "afk": { + "type": "boolean" }, - "answers": { + "status": { + "$ref": "#/definitions/Status" + }, + "activities": { "type": "array", "items": { - "$ref": "#/definitions/PollAnswer" + "$ref": "#/definitions/Activity" } }, - "duration": { - "type": "integer" - }, - "allow_multiselect": { - "type": "boolean" - }, - "layout_type": { + "since": { "type": "integer" } }, "additionalProperties": false, "required": [ - "answers", - "question" + "status" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageInteractionSchema": { + "ApplicationAuthorizeSchema": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" + "authorize": { + "type": "boolean" }, - "name": { + "guild_id": { "type": "string" }, - "command_type": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" - }, - "ephemerality_reason": { - "type": "integer" - }, - "user": { - "$ref": "#/definitions/PublicUser" - }, - "user_id": { + "permissions": { "type": "string" }, - "authorizing_integration_owners": { - "type": "object", - "properties": {}, - "additionalProperties": true - }, - "original_response_message_id": { - "description": "A container for useful snowflake-related methods.", - "$ref": "#/definitions/Snowflake" - }, - "interacted_message_id": { - "description": "A container for useful snowflake-related methods.", - "$ref": "#/definitions/Snowflake" - }, - "triggering_interaction_metadata": { - "$ref": "#/definitions/MessageInteractionSchema" - }, - "target_user": { - "$ref": "#/definitions/PublicUser" + "captcha_key": { + "type": "string" }, - "target_message_id": { - "description": "A container for useful snowflake-related methods.", - "$ref": "#/definitions/Snowflake" + "code": { + "minLength": 6, + "maxLength": 6, + "type": "string" } }, "additionalProperties": false, "required": [ - "id", - "name", - "type" + "authorize", + "guild_id", + "permissions" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageEditSchema": { + "AutomodMentionSpamRuleSchema": { "type": "object", "properties": { - "embed": { - "$ref": "#/definitions/Embed" + "mention_total_limit": { + "type": "integer" }, - "file": { - "type": "object", - "properties": { - "filename": { + "mention_raid_protection_enabled": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "mention_raid_protection_enabled", + "mention_total_limit" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AutomodSuspectedSpamRuleSchema": { + "type": "object", + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AutomodCommonlyFlaggedWordsRuleSchema": { + "type": "object", + "properties": { + "allow_list": { + "type": "array", + "items": [ + { "type": "string" } - }, - "additionalProperties": false, - "required": [ - "filename" - ] - }, - "flags": { - "type": "integer" + ], + "minItems": 1, + "maxItems": 1 }, - "attachments": { - "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", + "presets": { "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "id" - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - }, - "uploaded_filename": { - "type": "string" - }, - "original_content_type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "uploaded_filename" - ] - } - ] - } - }, - "applied_tags": { + "items": [ + { + "type": "integer" + } + ], + "minItems": 1, + "maxItems": 1 + } + }, + "additionalProperties": false, + "required": [ + "allow_list", + "presets" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AutomodCustomWordsRuleSchema": { + "type": "object", + "properties": { + "allow_list": { "type": "array", - "items": { - "type": "string" - } - }, - "channel_id": { - "type": "string" - }, - "content": { - "type": "string" + "items": [ + { + "type": "string" + } + ], + "minItems": 1, + "maxItems": 1 }, - "mobile_network_type": { - "type": "string" + "keyword_filter": { + "type": "array", + "items": [ + { + "type": "string" + } + ], + "minItems": 1, + "maxItems": 1 }, - "nonce": { + "regex_patterns": { + "type": "array", + "items": [ + { + "type": "string" + } + ], + "minItems": 1, + "maxItems": 1 + } + }, + "additionalProperties": false, + "required": [ + "allow_list", + "keyword_filter", + "regex_patterns" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AutomodRuleSchema": { + "type": "object", + "properties": { + "creator_id": { "type": "string" }, - "tts": { + "enabled": { "type": "boolean" }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } + "event_type": { + "type": "integer" }, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - }, - "replied_user": { - "type": "boolean" + "exempt_channels": { + "type": "array", + "items": [ + { + "type": "string" } - }, - "additionalProperties": false + ], + "minItems": 1, + "maxItems": 1 }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { + "exempt_roles": { + "type": "array", + "items": [ + { "type": "string" - }, - "fail_if_not_exists": { - "type": "boolean" - }, - "type": { - "type": "integer" } - }, - "additionalProperties": false + ], + "minItems": 1, + "maxItems": 1 }, - "payload_json": { + "guild_id": { "type": "string" }, - "sticker_ids": { + "name": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "trigger_type": { + "type": "integer" + }, + "trigger_metadata": { "anyOf": [ { - "type": "array", - "items": { - "type": "string" - } + "$ref": "#/definitions/AutomodMentionSpamRuleSchema" }, { - "type": "null" - } - ] - }, - "components": { - "anyOf": [ + "$ref": "#/definitions/AutomodSuspectedSpamRuleSchema" + }, { - "type": "array", - "items": { - "$ref": "#/definitions/ActionRowComponent" - } + "$ref": "#/definitions/AutomodCommonlyFlaggedWordsRuleSchema" }, { - "type": "null" + "$ref": "#/definitions/AutomodCustomWordsRuleSchema" } ] - }, - "poll": { - "$ref": "#/definitions/PollCreationSchema" - }, - "enforce_nonce": { - "type": "boolean" - }, - "thread_name": { - "type": "string" - }, - "avatar_url": { + } + }, + "additionalProperties": false, + "required": [ + "creator_id", + "enabled", + "event_type", + "exempt_channels", + "exempt_roles", + "guild_id", + "name", + "position", + "trigger_metadata", + "trigger_type" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BackupCodesChallengeSchema": { + "type": "object", + "properties": { + "password": { + "minLength": 1, + "maxLength": 72, "type": "string" - }, - "interaction": { - "$ref": "#/definitions/MessageInteractionSchema" - }, - "interaction_metadata": { - "$ref": "#/definitions/MessageInteractionSchema" } }, "additionalProperties": false, - "definitions": { - "Embed": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { - "type": "integer" - }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" - ] - }, - "image": { - "$ref": "#/definitions/EmbedImage" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage" - }, - "video": { - "$ref": "#/definitions/EmbedImage" - }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "additionalProperties": false - }, - "author": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "inline": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name", - "value" - ] - } - } - }, - "additionalProperties": false + "required": [ + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BanCreateSchema": { + "type": "object", + "properties": { + "delete_message_seconds": { + "type": "integer" + }, + "delete_message_days": { + "type": "integer" + }, + "reason": { + "type": "string" } }, + "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "MfaCodesSchema": { + "BanModeratorSchema": { "type": "object", "properties": { - "password": { - "minLength": 1, - "maxLength": 72, + "id": { "type": "string" }, - "regenerate": { - "type": "boolean" + "user_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "executor_id": { + "type": "string" + }, + "reason": { + "type": "string" } }, "additionalProperties": false, "required": [ - "password" + "executor_id", + "guild_id", + "id", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ModifyGuildStickerSchema": { + "BanRegistrySchema": { "type": "object", "properties": { - "name": { - "minLength": 2, - "maxLength": 30, + "id": { "type": "string" }, - "description": { - "maxLength": 100, + "user_id": { "type": "string" }, - "tags": { - "maxLength": 200, + "guild_id": { + "type": "string" + }, + "executor_id": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "reason": { "type": "string" } }, "additionalProperties": false, "required": [ - "name", - "tags" + "executor_id", + "guild_id", + "id", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PasswordResetSchema": { + "BotModifySchema": { "type": "object", "properties": { - "password": { - "minLength": 1, - "maxLength": 72, + "avatar": { "type": "string" }, - "token": { + "username": { + "type": "string" + }, + "banner": { "type": "string" } }, "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BulkBanSchema": { + "type": "object", + "properties": { + "user_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "delete_message_seconds": { + "type": "integer" + } + }, + "additionalProperties": false, "required": [ - "password", - "token" + "user_ids" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PreloadMessagesRequestSchema": { + "BulkDeleteSchema": { "type": "object", "properties": { - "channels": { + "messages": { "type": "array", "items": { "type": "string" } + } + }, + "additionalProperties": false, + "required": [ + "messages" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "type": "string" }, - "channel_ids": { + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 2, + 255, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "invitable": { + "type": "boolean" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "applied_tags": { "type": "array", "items": { "type": "string" } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "default_reaction_emoji": { + "type": [ + "null", + "string" + ] + }, + "flags": { + "type": "integer" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + }, + "video_quality_mode": { + "type": "integer" + }, + "auto_archive_duration": { + "type": "integer" + }, + "archived": { + "type": "boolean" + }, + "locked": { + "type": "boolean" + }, + "available_tags": { + "type": "array", + "items": { + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "moderated": { + "type": [ + "null", + "boolean" + ] + }, + "emoji_id": { + "type": [ + "null", + "string" + ] + }, + "emoji_name": { + "type": [ + "null", + "string" + ] + }, + "id": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ] + } } }, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "PruneSchema": { + "ChannelPermissionOverwriteSchema": { "type": "object", "properties": { - "days": { - "type": "integer" + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" } }, "additionalProperties": false, "required": [ - "days" + "allow", + "deny", + "id", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PurgeSchema": { - "type": "object", - "properties": { - "before": { - "type": "string" + "ChannelReorderSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "lock_permissions": { + "type": "boolean" + }, + "parent_id": { + "type": [ + "null", + "string" + ] + } }, - "after": { - "type": "string" - } + "additionalProperties": false, + "required": [ + "id" + ] }, - "additionalProperties": false, - "required": [ - "after", - "before" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RefreshUrlsRequestSchema": { + "CodesVerificationSchema": { "type": "object", "properties": { - "attachment_urls": { - "type": "array", - "items": { - "type": "string" - } + "key": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "regenerate": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "attachment_urls" + "key", + "nonce" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RegisterSchema": { + "ConnectedAccountSchema": { "type": "object", "properties": { - "username": { - "minLength": 2, + "external_id": { "type": "string" }, - "password": { - "minLength": 1, - "maxLength": 72, + "user_id": { "type": "string" }, - "consent": { + "token_data": { + "$ref": "#/definitions/ConnectedAccountTokenData" + }, + "friend_sync": { "type": "boolean" }, - "email": { - "format": "email", + "name": { "type": "string" }, - "fingerprint": { - "type": "string" + "revoked": { + "type": "boolean" }, - "invite": { - "type": "string" + "show_activity": { + "type": "integer" }, - "date_of_birth": { + "type": { "type": "string" }, - "gift_code_sku_id": { - "type": "string" + "verified": { + "type": "boolean" }, - "captcha_key": { - "type": "string" + "visibility": { + "type": "integer" }, - "promotional_email_opt_in": { - "type": "boolean" + "integrations": { + "type": "array", + "items": { + "type": "string" + } }, - "unique_username_registration": { - "type": "boolean" + "metadata_": {}, + "metadata_visibility": { + "type": "integer" }, - "global_name": { - "type": "string" + "two_way_link": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "consent", - "username" + "external_id", + "name", + "type", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RelationshipPatchSchema": { + "ConnectedAccountCommonOAuthTokenResponse": { "type": "object", "properties": { - "nickname": { + "access_token": { + "type": "string" + }, + "token_type": { "type": "string" + }, + "scope": { + "type": "string" + }, + "refresh_token": { + "type": "string" + }, + "expires_in": { + "type": "integer" } }, "additionalProperties": false, + "required": [ + "access_token", + "scope", + "token_type" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RelationshipPostSchema": { + "ConnectionCallbackSchema": { "type": "object", "properties": { - "discriminator": { + "code": { "type": "string" }, - "username": { + "state": { "type": "string" - } + }, + "insecure": { + "type": "boolean" + }, + "friend_sync": { + "type": "boolean" + }, + "openid_params": {} }, "additionalProperties": false, "required": [ - "discriminator", - "username" + "friend_sync", + "insecure", + "state" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RelationshipPutSchema": { + "ConnectionUpdateSchema": { "type": "object", "properties": { - "type": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" + "visibility": { + "type": "boolean" + }, + "show_activity": { + "type": "boolean" + }, + "metadata_visibility": { + "type": "boolean" } }, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "RequestGuildMembersSchema": { + "DmChannelCreateSchema": { "type": "object", "properties": { - "guild_id": { - "anyOf": [ - { - "type": "array", - "items": [ - { - "type": "string" - } - ], - "minItems": 1, - "maxItems": 1 - }, - { - "type": "string" - } - ] - }, - "query": { + "name": { "type": "string" }, - "limit": { - "type": "integer" + "recipients": { + "type": "array", + "items": { + "type": "string" + } }, - "presences": { + "recipient_id": { + "type": "string" + }, + "access_tokens": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "EmailDomainLookupSchema": { + "type": "object", + "properties": { + "allow_multiple_guilds": { "type": "boolean" }, - "user_ids": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] + "email": { + "type": "string" }, - "nonce": { + "use_verification_code": { + "type": "boolean" + }, + "guild_id": { "type": "string" } }, "additionalProperties": false, "required": [ - "guild_id" + "allow_multiple_guilds", + "email", + "use_verification_code" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RoleModifySchema": { + "EmailDomainLookupVerifyCodeSchema": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "permissions": { + "email": { "type": "string" }, - "color": { - "type": "integer" - }, - "hoist": { - "type": "boolean" - }, - "mentionable": { - "type": "boolean" - }, - "position": { - "type": "integer" - }, - "icon": { + "guild_id": { "type": "string" }, - "unicode_emoji": { + "code": { "type": "string" - }, - "colors": { - "type": "object", - "properties": { - "primary_color": { - "type": "integer" - }, - "secondary_color": { - "type": [ - "null", - "integer" - ] - }, - "tertiary_color": { - "type": [ - "null", - "integer" - ] - } - }, - "additionalProperties": false, - "required": [ - "primary_color" - ] } }, "additionalProperties": false, + "required": [ + "code", + "email", + "guild_id" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RolePositionUpdateSchema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { + "EmojiCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "image": { + "type": "string" + }, + "require_colons": { + "type": [ + "null", + "boolean" + ] + }, + "roles": { + "type": "array", + "items": { "type": "string" - }, - "position": { - "type": "integer" } - }, - "additionalProperties": false, - "required": [ - "id", - "position" - ] + } }, + "additionalProperties": false, + "required": [ + "image" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SelectProtocolSchema": { + "EmojiModifySchema": { "type": "object", "properties": { - "protocol": { - "enum": [ - "udp", - "webrtc" - ], - "type": "string" - }, - "data": { - "anyOf": [ - { - "type": "object", - "properties": { - "address": { - "type": "string" - }, - "port": { - "type": "integer" - }, - "mode": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "address", - "mode", - "port" - ] - }, - { - "type": "string" - } - ] - }, - "sdp": { + "name": { "type": "string" }, - "codecs": { + "roles": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "enum": [ - "H264", - "VP8", - "VP9", - "opus" - ], - "type": "string" - }, - "type": { - "enum": [ - "audio", - "video" - ], - "type": "string" - }, - "priority": { - "type": "integer" - }, - "payload_type": { - "type": "integer" - }, - "rtx_payload_type": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "name", - "payload_type", - "priority", - "type" - ] + "type": "string" } - }, - "rtc_connection_id": { - "type": "string" } }, "additionalProperties": false, - "required": [ - "data", - "protocol" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SettingsProtoUpdateSchema": { + "ForgotPasswordSchema": { "type": "object", "properties": { - "settings": { + "login": { "type": "string" }, - "required_data_version": { - "type": "integer" + "captcha_key": { + "type": "string" } }, "additionalProperties": false, "required": [ - "settings" + "login" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SettingsProtoUpdateJsonSchema": { + "GreetRequestSchema": { "type": "object", "properties": { - "settings": { - "$ref": "#/definitions/JsonValue" + "sticker_ids": { + "type": "array", + "items": { + "type": "string" + } }, - "required_data_version": { - "type": "integer" + "allowed_mentions": { + "$ref": "#/definitions/AllowedMentions" + }, + "message_reference": { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "message_id" + ] } }, "additionalProperties": false, "required": [ - "settings" + "sticker_ids" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TeamCreateSchema": { + "GuildCreateSchema": { "type": "object", "properties": { "name": { + "maxLength": 100, + "type": "string" + }, + "region": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/ChannelCreateSchema" + } + }, + "system_channel_id": { + "type": "string" + }, + "rules_channel_id": { "type": "string" + }, + "guild_template_code": { + "type": "string" + }, + "staff_only": { + "type": "boolean" } }, "additionalProperties": false, - "required": [ - "name" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TemplateCreateSchema": { + "GuildSubscriptionsBulkSchema": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" + "subscriptions": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/GuildSubscriptionSchema" + } } }, "additionalProperties": false, "required": [ - "name" + "subscriptions" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TemplateModifySchema": { + "GuildSubscriptionSchema": { "type": "object", "properties": { - "name": { - "type": "string" + "channels": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "integer" + } + } + } }, - "description": { - "type": "string" + "members": { + "type": "array", + "items": { + "type": "string" + } + }, + "activities": { + "type": "boolean" + }, + "threads": { + "type": "boolean" + }, + "typing": { + "const": true, + "type": "boolean" + }, + "member_updates": { + "type": "boolean" + }, + "thread_member_lists": { + "type": "array", + "items": {} } }, "additionalProperties": false, - "required": [ - "name" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TotpDisableSchema": { + "GuildTemplateCreateSchema": { "type": "object", "properties": { - "code": { - "minLength": 6, - "maxLength": 6, + "name": { "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, "required": [ - "code" + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TotpEnableSchema": { + "GuildUpdateSchema": { "type": "object", "properties": { - "password": { - "minLength": 1, - "maxLength": 72, + "banner": { + "type": [ + "null", + "string" + ] + }, + "splash": { + "type": [ + "null", + "string" + ] + }, + "description": { "type": "string" }, - "code": { - "minLength": 6, - "maxLength": 6, + "features": { + "type": "array", + "items": { + "type": "string" + } + }, + "verification_level": { + "type": "integer" + }, + "default_message_notifications": { + "type": "integer" + }, + "system_channel_flags": { + "type": "integer" + }, + "explicit_content_filter": { + "type": "integer" + }, + "public_updates_channel_id": { "type": "string" }, - "secret": { + "afk_timeout": { + "type": "integer" + }, + "afk_channel_id": { "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "password" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TotpSchema": { - "type": "object", - "properties": { - "code": { + }, + "preferred_locale": { "type": "string" }, - "ticket": { + "premium_progress_bar_enabled": { + "type": "boolean" + }, + "discovery_splash": { "type": "string" }, - "gift_code_sku_id": { + "safety_alerts_channel_id": { "type": [ "null", "string" ] }, - "login_source": { + "name": { + "maxLength": 100, + "type": "string" + }, + "region": { + "type": "string" + }, + "icon": { "type": [ "null", "string" ] + }, + "system_channel_id": { + "type": "string" + }, + "rules_channel_id": { + "type": "string" + }, + "guild_template_code": { + "type": "string" + }, + "staff_only": { + "type": "boolean" } }, "additionalProperties": false, - "required": [ - "code", - "ticket" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UploadAttachmentRequestSchema": { + "GuildUpdateWelcomeScreenSchema": { "type": "object", "properties": { - "files": { + "welcome_channels": { "type": "array", "items": { - "$ref": "#/definitions/UploadAttachmentRequest" + "type": "object", + "properties": { + "channel_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "emoji_id": { + "type": "string" + }, + "emoji_name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "channel_id", + "description" + ] } + }, + "enabled": { + "type": "boolean" + }, + "description": { + "type": "string" } }, "additionalProperties": false, - "required": [ - "files" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserDeleteSchema": { + "HubWaitlistSignupSchema": { "type": "object", "properties": { - "user_id": { + "email": { + "type": "string" + }, + "school": { "type": "string" } }, "additionalProperties": false, "required": [ - "user_id" + "email", + "school" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserGuildSettingsSchema": { + "InviteCreateSchema": { "type": "object", "properties": { - "channel_overrides": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChannelOverride" - } - }, - "version": { - "type": "integer" + "target_user_id": { + "type": "string" }, - "message_notifications": { - "type": "integer" + "target_type": { + "type": "string" }, - "mobile_push": { - "type": "boolean" + "validate": { + "type": "string" }, - "mute_config": { - "anyOf": [ - { - "$ref": "#/definitions/MuteConfig" - }, - { - "type": "null" - } - ] + "max_age": { + "type": "integer" }, - "muted": { - "type": "boolean" + "max_uses": { + "type": "integer" }, - "suppress_everyone": { + "temporary": { "type": "boolean" }, - "suppress_roles": { + "unique": { "type": "boolean" }, - "guild_id": { - "type": [ - "null", - "string" - ] + "target_user": { + "type": "string" + }, + "target_user_type": { + "type": "integer" }, "flags": { "type": "integer" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MFAResponse": { + "type": "object", + "properties": { + "ticket": { + "type": "string" }, - "mute_scheduled_events": { - "type": "boolean" + "mfa": { + "type": "boolean", + "const": true }, - "hide_muted_channels": { - "type": "boolean" + "sms": { + "type": "boolean", + "const": false }, - "notify_highlights": { - "const": 0, - "type": "number" + "token": { + "type": "null" } }, "additionalProperties": false, + "required": [ + "mfa", + "sms", + "ticket", + "token" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserModifySchema": { + "WebAuthnResponse": { "type": "object", "properties": { - "username": { - "minLength": 2, + "webauthn": { "type": "string" }, - "avatar": { - "type": [ - "null", - "string" - ] - }, - "bio": { + "ticket": { "type": "string" }, - "accent_color": { - "type": "integer" + "mfa": { + "type": "boolean", + "const": true }, - "banner": { - "type": [ - "null", - "string" - ] + "sms": { + "type": "boolean", + "const": false }, - "password": { - "minLength": 1, - "maxLength": 72, + "token": { + "type": "null" + } + }, + "additionalProperties": false, + "required": [ + "mfa", + "sms", + "ticket", + "token", + "webauthn" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LoginResponse": { + "anyOf": [ + { + "$ref": "#/definitions/TokenResponse" + }, + { + "$ref": "#/definitions/MFAResponse" + }, + { + "$ref": "#/definitions/WebAuthnResponse" + } + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LoginSchema": { + "type": "object", + "properties": { + "login": { "type": "string" }, - "new_password": { + "password": { "minLength": 1, "maxLength": 72, "type": "string" }, - "code": { - "minLength": 6, - "maxLength": 6, - "type": "string" + "undelete": { + "type": "boolean" }, - "email": { - "format": "email", + "captcha_key": { "type": "string" }, - "discriminator": { - "minLength": 4, - "maxLength": 4, + "login_source": { "type": "string" }, - "display_name_colors": { - "type": "array", - "items": { - "type": "integer" - } - }, - "display_name_effect_id": { - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6 - ], - "type": "number" - }, - "display_name_font_id": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "type": "number" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UserNoteUpdateSchema": { - "type": "object", - "properties": { - "note": { + "gift_code_sku_id": { "type": "string" } }, "additionalProperties": false, "required": [ - "note" + "login", + "password" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserProfileModifySchema": { + "MemberChangeProfileSchema": { "type": "object", "properties": { - "bio": { - "type": "string" - }, - "accent_color": { - "type": [ - "null", - "integer" - ] - }, "banner": { "type": [ "null", "string" ] }, + "nick": { + "type": "string" + }, + "bio": { + "type": "string" + }, "pronouns": { "type": "string" }, @@ -8405,206 +5306,447 @@ "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VanityUrlSchema": { + "MemberChangeSchema": { "type": "object", "properties": { - "code": { - "minLength": 1, - "maxLength": 20, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "nick": { + "type": "string" + }, + "avatar": { + "type": [ + "null", + "string" + ] + }, + "bio": { "type": "string" + }, + "communication_disabled_until": { + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VerifyEmailSchema": { + "MemberNickChangeSchema": { "type": "object", "properties": { - "captcha_key": { - "type": [ - "null", - "string" - ] + "nick": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "nick" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MessageAcknowledgeSchema": { + "type": "object", + "properties": { + "manual": { + "type": "boolean" + }, + "mention_count": { + "type": "integer" + }, + "flags": { + "enum": [ + 0, + 1, + 2, + 4 + ], + "type": "number" + }, + "last_viewed": { + "type": "integer" + }, + "token": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AcknowledgeDeleteSchema": { + "type": "object", + "properties": { + "read_state_type": { + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "type": "number" }, - "token": { - "type": "string" + "version": { + "type": "integer" } }, "additionalProperties": false, - "required": [ - "token" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceStateUpdateSchema": { + "MessageCreateSchema": { "type": "object", "properties": { - "guild_id": { + "type": { + "type": "integer" + }, + "content": { + "type": "string" + }, + "mobile_network_type": { + "type": "string" + }, + "nonce": { "type": "string" }, "channel_id": { "type": "string" }, - "self_mute": { + "tts": { "type": "boolean" }, - "self_deaf": { - "type": "boolean" + "flags": { + "type": "integer" }, - "self_video": { - "type": "boolean" + "embeds": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } + }, + { + "type": "null" + } + ] }, - "preferred_region": { + "embed": { + "anyOf": [ + { + "$ref": "#/definitions/Embed" + }, + { + "type": "null" + } + ] + }, + "allowed_mentions": { + "anyOf": [ + { + "type": "object", + "properties": { + "parse": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "roles": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "users": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "replied_user": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "message_reference": { + "anyOf": [ + { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" + }, + "type": { + "type": "integer" + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "payload_json": { "type": "string" }, - "request_to_speak_timestamp": { - "type": "string", - "format": "date-time" + "file": { + "type": "object", + "properties": { + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename" + ] }, - "suppress": { + "attachments": { + "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename", + "id" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "uploaded_filename": { + "type": "string" + }, + "original_content_type": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename", + "uploaded_filename" + ] + } + ] + } + }, + "sticker_ids": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "components": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/ActionRowComponent" + } + }, + { + "type": "null" + } + ] + }, + "poll": { + "$ref": "#/definitions/PollCreationSchema" + }, + "enforce_nonce": { "type": "boolean" }, - "flags": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "self_deaf", - "self_mute" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GenerateWebAuthnCredentialsSchema": { - "type": "object", - "properties": { - "password": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "password" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "CreateWebAuthnCredentialSchema": { - "type": "object", - "properties": { - "credential": { - "type": "string" + "applied_tags": { + "type": "array", + "items": { + "type": "string" + } }, - "name": { + "thread_name": { "type": "string" }, - "ticket": { + "avatar_url": { "type": "string" + }, + "interaction": { + "$ref": "#/definitions/MessageInteractionSchema" + }, + "interaction_metadata": { + "$ref": "#/definitions/MessageInteractionSchema" } }, "additionalProperties": false, - "required": [ - "credential", - "name", - "ticket" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "WebAuthnPostSchema": { - "anyOf": [ - { - "$ref": "#/definitions/GenerateWebAuthnCredentialsSchema" - }, - { - "$ref": "#/definitions/CreateWebAuthnCredentialSchema" - } - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebAuthnTotpSchema": { + "PollCreationSchema": { "type": "object", "properties": { - "code": { - "type": "string" + "question": { + "$ref": "#/definitions/PollMedia" }, - "ticket": { - "type": "string" + "answers": { + "type": "array", + "items": { + "$ref": "#/definitions/PollAnswer" + } + }, + "duration": { + "type": "integer" + }, + "allow_multiselect": { + "type": "boolean" + }, + "layout_type": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "code", - "ticket" + "answers", + "question" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebhookCreateSchema": { + "MessageInteractionSchema": { "type": "object", "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/InteractionType" + }, "name": { - "maxLength": 80, "type": "string" }, - "avatar": { + "command_type": { + "enum": [ + 1, + 2, + 3, + 4 + ], + "type": "number" + }, + "ephemerality_reason": { + "type": "integer" + }, + "user": { + "$ref": "#/definitions/PublicUser" + }, + "user_id": { "type": "string" + }, + "authorizing_integration_owners": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "original_response_message_id": { + "description": "A container for useful snowflake-related methods.", + "$ref": "#/definitions/Snowflake" + }, + "interacted_message_id": { + "description": "A container for useful snowflake-related methods.", + "$ref": "#/definitions/Snowflake" + }, + "triggering_interaction_metadata": { + "$ref": "#/definitions/MessageInteractionSchema" + }, + "target_user": { + "$ref": "#/definitions/PublicUser" + }, + "target_message_id": { + "description": "A container for useful snowflake-related methods.", + "$ref": "#/definitions/Snowflake" } }, "additionalProperties": false, "required": [ - "name" + "id", + "name", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebhookExecuteSchema": { + "MessageEditSchema": { "type": "object", "properties": { - "content": { - "type": "string" - }, - "username": { - "type": "string" - }, - "avatar_url": { - "type": "string" - }, - "tts": { - "type": "boolean" - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } - }, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "users": { - "type": "array", - "items": { - "type": "string" - } + "embed": { + "anyOf": [ + { + "$ref": "#/definitions/Embed" }, - "replied_user": { - "type": "boolean" + { + "type": "null" } - }, - "additionalProperties": false - }, - "components": { - "type": "array", - "items": {} + ] }, "file": { "type": "object", @@ -8618,679 +5760,717 @@ "filename" ] }, - "payload_json": { - "type": "string" - }, - "attachments": { - "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "id" - ] - } - }, "flags": { "type": "integer" }, - "thread_name": { - "type": "string" - }, "applied_tags": { "type": "array", "items": { "type": "string" } }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "fail_if_not_exists": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "message_id" - ] + "channel_id": { + "type": "string" }, - "sticker_ids": { - "type": "array", - "items": { - "type": "string" - } + "content": { + "type": "string" + }, + "mobile_network_type": { + "type": "string" }, "nonce": { "type": "string" }, - "enforce_nonce": { + "tts": { "type": "boolean" }, - "poll": { - "$ref": "#/definitions/PollCreationSchema" - } - }, - "additionalProperties": false, - "definitions": { - "Embed": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { - "type": "integer" - }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" - ] - }, - "image": { - "$ref": "#/definitions/EmbedImage" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage" - }, - "video": { - "$ref": "#/definitions/EmbedImage" + "embeds": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } }, - "provider": { + { + "type": "null" + } + ] + }, + "allowed_mentions": { + "anyOf": [ + { "type": "object", "properties": { - "name": { - "type": "string" + "parse": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] }, - "url": { - "type": "string" + "roles": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "users": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "replied_user": { + "type": "boolean" } }, "additionalProperties": false }, - "author": { + { + "type": "null" + } + ] + }, + "message_reference": { + "anyOf": [ + { "type": "object", "properties": { - "name": { + "message_id": { "type": "string" }, - "url": { + "channel_id": { "type": "string" }, - "icon_url": { + "guild_id": { "type": "string" }, - "proxy_icon_url": { - "type": "string" + "fail_if_not_exists": { + "type": "boolean" + }, + "type": { + "type": "integer" } }, "additionalProperties": false }, - "fields": { - "type": "array", - "items": { + { + "type": "null" + } + ] + }, + "payload_json": { + "type": "string" + }, + "attachments": { + "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", + "type": "array", + "items": { + "anyOf": [ + { "type": "object", "properties": { - "name": { + "id": { + "type": "string" + }, + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename", + "id" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "filename": { "type": "string" }, - "value": { + "uploaded_filename": { "type": "string" }, - "inline": { - "type": "boolean" + "original_content_type": { + "type": "string" } }, "additionalProperties": false, "required": [ - "name", - "value" + "filename", + "uploaded_filename" ] } + ] + } + }, + "sticker_ids": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" } - }, - "additionalProperties": false + ] + }, + "components": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/ActionRowComponent" + } + }, + { + "type": "null" + } + ] + }, + "poll": { + "$ref": "#/definitions/PollCreationSchema" + }, + "enforce_nonce": { + "type": "boolean" + }, + "thread_name": { + "type": "string" + }, + "avatar_url": { + "type": "string" + }, + "interaction": { + "$ref": "#/definitions/MessageInteractionSchema" + }, + "interaction_metadata": { + "$ref": "#/definitions/MessageInteractionSchema" } }, + "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebhookUpdateSchema": { + "MfaCodesSchema": { + "type": "object", + "properties": { + "password": { + "minLength": 1, + "maxLength": 72, + "type": "string" + }, + "regenerate": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ModifyGuildStickerSchema": { "type": "object", "properties": { "name": { + "minLength": 2, + "maxLength": 30, "type": "string" }, - "avatar": { + "description": { + "maxLength": 100, "type": "string" }, - "channel_id": { + "tags": { + "maxLength": 200, "type": "string" } }, "additionalProperties": false, + "required": [ + "name", + "tags" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "WidgetModifySchema": { + "PasswordResetSchema": { "type": "object", "properties": { - "enabled": { - "type": "boolean" + "password": { + "minLength": 1, + "maxLength": 72, + "type": "string" }, - "channel_id": { + "token": { "type": "string" } }, "additionalProperties": false, "required": [ - "channel_id", - "enabled" + "password", + "token" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TicketCreateSchema": { + "PreloadMessagesRequestSchema": { "type": "object", "properties": { - "name": { - "type": "string" + "channels": { + "type": "array", + "items": { + "type": "string" + } + }, + "channel_ids": { + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TicketPatchSchema": { + "PruneSchema": { "type": "object", "properties": { - "owner_id": { - "type": [ - "null", - "string" - ] - }, - "resolved": { - "type": [ - "null", - "boolean" - ] - }, - "public": { - "type": [ - "null", - "boolean" - ] - }, - "closed": { - "type": [ - "null", - "boolean" - ] + "days": { + "type": "integer" } }, "additionalProperties": false, + "required": [ + "days" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageThreadCreationSchema": { + "PurgeSchema": { "type": "object", "properties": { - "auto_archive_duration": { - "type": "integer" - }, - "rate_limit_per_user": { - "type": "integer" - }, - "name": { + "before": { "type": "string" }, - "location": { + "after": { "type": "string" - }, - "type": { - "type": "integer" } }, "additionalProperties": false, "required": [ - "name" + "after", + "before" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RefreshUrlsRequestSchema": { + "type": "object", + "properties": { + "attachment_urls": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "attachment_urls" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ApplicationCommandCreateSchema": { + "RegisterSchema": { "type": "object", "properties": { - "type": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" + "username": { + "minLength": 2, + "type": "string" }, - "name": { + "password": { + "minLength": 1, + "maxLength": 72, "type": "string" }, - "name_localizations": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "consent": { + "type": "boolean" }, - "description": { + "email": { + "format": "email", "type": "string" }, - "description_localizations": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "fingerprint": { + "type": "string" }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/ApplicationCommandOption" - } + "invite": { + "type": "string" }, - "default_member_permissions": { + "date_of_birth": { "type": "string" }, - "dm_permission": { - "type": "boolean" + "gift_code_sku_id": { + "type": "string" }, - "nsfw": { - "type": "boolean" + "captcha_key": { + "type": "string" }, - "integration_types": { - "type": "array", - "items": { - "$ref": "#/definitions/ApplicationIntegrationType" - } + "promotional_email_opt_in": { + "type": "boolean" }, - "contexts": { - "type": "array", - "items": { - "$ref": "#/definitions/InteractionContextType" - } + "unique_username_registration": { + "type": "boolean" }, - "handler": { - "enum": [ - 1, - 2, - 3 - ], - "type": "number" + "global_name": { + "type": "string" } }, "additionalProperties": false, "required": [ - "name" + "consent", + "username" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BulkApplicationCommandCreateSchema": { - "type": "array", - "items": { - "$ref": "#/definitions/ApplicationCommandCreateSchema" + "RelationshipPatchSchema": { + "type": "object", + "properties": { + "nickname": { + "type": "string" + } }, + "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "ThreadCreationSchema": { + "RelationshipPostSchema": { "type": "object", "properties": { - "auto_archive_duration": { - "type": "integer" - }, - "rate_limit_per_user": { - "type": "integer" - }, - "name": { + "discriminator": { "type": "string" }, + "username": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "discriminator", + "username" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RelationshipPutSchema": { + "type": "object", + "properties": { "type": { "enum": [ - 11, - 12 + 1, + 2, + 3, + 4 ], "type": "number" }, - "invitable": { + "confirm_stranger_request": { "type": "boolean" - }, - "applied_tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "location": { - "type": "string" - }, - "message": { - "type": "object", - "properties": { - "content": { - "type": "string" - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } - }, - "allowed_mentions": { - "type": "object", - "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } - }, - "roles": { - "type": "array", - "items": { - "type": "string" - } - }, - "users": { - "type": "array", - "items": { - "type": "string" - } - }, - "replied_user": { - "type": "boolean" - } - }, - "additionalProperties": false - }, - "components": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/ActionRowComponent" - } - }, - { - "type": "null" - } - ] - }, - "sticker_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "activity": { - "$ref": "#/definitions/MessageActivity" - }, - "application_id": { - "type": "string" - }, - "flags": { - "type": "integer" - }, - "attachments": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "id" - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" - }, - "uploaded_filename": { - "type": "string" - }, - "original_content_type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "filename", - "uploaded_filename" - ] - } - ] + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RequestGuildMembersSchema": { + "type": "object", + "properties": { + "guild_id": { + "anyOf": [ + { + "type": "array", + "items": [ + { + "type": "string" + } + ], + "minItems": 1, + "maxItems": 1 + }, + { + "type": "string" + } + ] + }, + "query": { + "type": "string" + }, + "limit": { + "type": "integer" + }, + "presences": { + "type": "boolean" + }, + "user_ids": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" } + }, + { + "type": "string" } - }, - "additionalProperties": false + ] + }, + "nonce": { + "type": "string" } }, "additionalProperties": false, "required": [ - "name" + "guild_id" ], - "definitions": { - "Embed": { + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RoleModifySchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "color": { + "type": "integer" + }, + "hoist": { + "type": "boolean" + }, + "mentionable": { + "type": "boolean" + }, + "position": { + "type": "integer" + }, + "icon": { + "type": "string" + }, + "unicode_emoji": { + "type": "string" + }, + "colors": { "type": "object", "properties": { - "title": { - "type": "string" - }, - "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" - }, - "description": { - "type": "string" - }, - "url": { - "type": "string" - }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { + "primary_color": { "type": "integer" }, - "footer": { + "secondary_color": { + "type": [ + "null", + "integer" + ] + }, + "tertiary_color": { + "type": [ + "null", + "integer" + ] + } + }, + "additionalProperties": false, + "required": [ + "primary_color" + ] + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "RolePositionUpdateSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "position": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "id", + "position" + ] + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "SelectProtocolSchema": { + "type": "object", + "properties": { + "protocol": { + "enum": [ + "udp", + "webrtc" + ], + "type": "string" + }, + "data": { + "anyOf": [ + { "type": "object", "properties": { - "text": { + "address": { "type": "string" }, - "icon_url": { - "type": "string" + "port": { + "type": "integer" }, - "proxy_icon_url": { + "mode": { "type": "string" } }, "additionalProperties": false, "required": [ - "text" + "address", + "mode", + "port" ] }, - "image": { - "$ref": "#/definitions/EmbedImage" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage" - }, - "video": { - "$ref": "#/definitions/EmbedImage" - }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } + { + "type": "string" + } + ] + }, + "sdp": { + "type": "string" + }, + "codecs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "enum": [ + "H264", + "VP8", + "VP9", + "opus" + ], + "type": "string" }, - "additionalProperties": false - }, - "author": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } + "type": { + "enum": [ + "audio", + "video" + ], + "type": "string" }, - "additionalProperties": false - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "inline": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name", - "value" - ] + "priority": { + "type": "integer" + }, + "payload_type": { + "type": "integer" + }, + "rtx_payload_type": { + "type": "integer" } - } - }, - "additionalProperties": false + }, + "additionalProperties": false, + "required": [ + "name", + "payload_type", + "priority", + "type" + ] + } + }, + "rtc_connection_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "data", + "protocol" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "SettingsProtoUpdateSchema": { + "type": "object", + "properties": { + "settings": { + "type": "string" + }, + "required_data_version": { + "type": "integer" } }, + "additionalProperties": false, + "required": [ + "settings" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PostDataSchema": { + "SettingsProtoUpdateJsonSchema": { "type": "object", "properties": { - "thread_ids": { - "type": "array", - "items": { - "type": "string" - } + "settings": { + "$ref": "#/definitions/JsonValue" + }, + "required_data_version": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "thread_ids" + "settings" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TagCreateSchema": { + "TeamCreateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TemplateCreateSchema": { "type": "object", "properties": { "name": { "type": "string" }, - "moderated": { - "type": [ - "null", - "boolean" - ] - }, - "emoji_id": { - "type": [ - "null", - "string" - ] - }, - "emoji_name": { - "type": [ - "null", - "string" - ] + "description": { + "type": "string" } }, "additionalProperties": false, @@ -9299,1724 +6479,1588 @@ ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ChannelCreateSchema": { + "TemplateModifySchema": { "type": "object", "properties": { "name": { "type": "string" }, - "type": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 2, - 20, - 21, - 255, - 3, - 33, - 34, - 35, - 4, - 5, - 6, - 64, - 7, - 8, - 9 - ], - "type": "number" + "description": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TotpDisableSchema": { + "type": "object", + "properties": { + "code": { + "minLength": 6, + "maxLength": 6, + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "code" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TotpEnableSchema": { + "type": "object", + "properties": { + "password": { + "minLength": 1, + "maxLength": 72, + "type": "string" }, - "id": { + "code": { + "minLength": 6, + "maxLength": 6, "type": "string" }, - "flags": { - "type": "integer" + "secret": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "password" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TotpSchema": { + "type": "object", + "properties": { + "code": { + "type": "string" }, - "icon": { + "ticket": { + "type": "string" + }, + "gift_code_sku_id": { "type": [ "null", "string" ] }, - "parent_id": { - "type": "string" - }, - "default_auto_archive_duration": { - "type": "integer" - }, - "permission_overwrites": { + "login_source": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "required": [ + "code", + "ticket" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UploadAttachmentRequestSchema": { + "type": "object", + "properties": { + "files": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" - }, - "allow": { - "type": "string" - }, - "deny": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "allow", - "deny", - "id", - "type" - ] + "$ref": "#/definitions/UploadAttachmentRequest" + } + } + }, + "additionalProperties": false, + "required": [ + "files" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserDeleteSchema": { + "type": "object", + "properties": { + "user_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "user_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UserGuildSettingsSchema": { + "type": "object", + "properties": { + "channel_overrides": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ChannelOverride" } }, - "video_quality_mode": { - "type": "integer" - }, - "bitrate": { + "version": { "type": "integer" }, - "user_limit": { + "message_notifications": { "type": "integer" }, - "nsfw": { + "mobile_push": { "type": "boolean" }, - "rate_limit_per_user": { - "type": "integer" - }, - "topic": { - "type": "string" - }, - "default_thread_rate_limit_per_user": { - "type": "integer" - }, - "applied_tags": { - "type": "array", - "items": { - "type": "string" - } + "mute_config": { + "anyOf": [ + { + "$ref": "#/definitions/MuteConfig" + }, + { + "type": "null" + } + ] }, - "position": { - "type": "integer" + "muted": { + "type": "boolean" }, - "invitable": { + "suppress_everyone": { "type": "boolean" }, - "rtc_region": { - "type": "string" + "suppress_roles": { + "type": "boolean" }, - "default_reaction_emoji": { + "guild_id": { "type": [ "null", "string" ] }, - "auto_archive_duration": { + "flags": { "type": "integer" }, - "archived": { + "mute_scheduled_events": { "type": "boolean" }, - "locked": { + "hide_muted_channels": { "type": "boolean" + }, + "notify_highlights": { + "const": 0, + "type": "number" } }, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceIdentifySchema": { + "UserModifySchema": { "type": "object", "properties": { - "server_id": { + "username": { + "minLength": 2, "type": "string" }, - "user_id": { + "avatar": { + "type": [ + "null", + "string" + ] + }, + "bio": { "type": "string" }, - "session_id": { + "accent_color": { + "type": "integer" + }, + "banner": { + "type": [ + "null", + "string" + ] + }, + "password": { + "minLength": 1, + "maxLength": 72, "type": "string" }, - "channel_id": { + "new_password": { + "minLength": 1, + "maxLength": 72, "type": "string" }, - "token": { + "code": { + "minLength": 6, + "maxLength": 6, "type": "string" }, - "video": { - "type": "boolean" + "email": { + "format": "email", + "type": "string" }, - "streams": { + "discriminator": { + "minLength": 4, + "maxLength": 4, + "type": "string" + }, + "display_name_colors": { "type": "array", "items": { - "type": "object", - "properties": { - "type": { - "enum": [ - "audio", - "screen", - "video" - ], - "type": "string" - }, - "rid": { - "type": "string" - }, - "quality": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "quality", - "rid", - "type" - ] + "type": "integer" } }, - "max_secure_frames_version": { - "type": "integer" + "display_name_effect_id": { + "$ref": "#/definitions/User_DisplayNameEffect" }, - "max_dave_protocol_version": { - "type": "integer" + "display_name_font_id": { + "$ref": "#/definitions/User_DisplayNameFont" } }, "additionalProperties": false, - "required": [ - "server_id", - "session_id", - "token", - "user_id" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceVideoSchema": { + "UserNoteUpdateSchema": { "type": "object", "properties": { - "audio_ssrc": { - "type": "integer" - }, - "video_ssrc": { - "type": "integer" - }, - "rtx_ssrc": { - "type": "integer" - }, - "user_id": { + "note": { "type": "string" - }, - "streams": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "enum": [ - "audio", - "screen", - "video" - ], - "type": "string" - }, - "rid": { - "type": "string" - }, - "ssrc": { - "type": "integer" - }, - "active": { - "type": "boolean" - }, - "quality": { - "type": "integer" - }, - "rtx_ssrc": { - "type": "integer" - }, - "max_bitrate": { - "type": "integer" - }, - "max_framerate": { - "type": "integer" - }, - "max_resolution": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "width": { - "type": "integer" - }, - "height": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "height", - "type", - "width" - ] - } - }, - "additionalProperties": false, - "required": [ - "active", - "max_bitrate", - "max_framerate", - "max_resolution", - "quality", - "rid", - "rtx_ssrc", - "ssrc", - "type" - ] - } } }, "additionalProperties": false, "required": [ - "audio_ssrc", - "video_ssrc" + "note" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CreateReportSchema": { + "UserProfileModifySchema": { "type": "object", "properties": { - "version": { - "type": "string" - }, - "variant": { - "type": "string" - }, - "name": { - "type": "string" - }, - "language": { - "type": "string" - }, - "breadcrumbs": { - "type": "array", - "items": { - "type": "integer" - } - }, - "elements": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "channel_id": { - "type": "string" - }, - "message_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "stage_instance_id": { - "type": "string" - }, - "guild_scheduled_event_id": { + "bio": { "type": "string" }, - "reported_user_id": { - "type": "string" + "accent_color": { + "type": [ + "null", + "integer" + ] }, - "application_id": { - "type": "string" + "banner": { + "type": [ + "null", + "string" + ] }, - "user_id": { + "pronouns": { "type": "string" }, - "widget_id": { - "type": "string" + "theme_colors": { + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ], + "type": "array", + "minItems": 2, + "maxItems": 2 } }, "additionalProperties": false, - "required": [ - "breadcrumbs", - "language", - "name", - "variant", - "version" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SessionsLogoutSchema": { + "VanityUrlSchema": { "type": "object", "properties": { - "session_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "session_id_hashes": { - "type": "array", - "items": { - "type": "string" - } + "code": { + "minLength": 1, + "maxLength": 20, + "type": "string" } }, "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "GetSessionsResponse": { + "VerifyEmailSchema": { "type": "object", "properties": { - "user_sessions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "id_hash": { - "type": "string" - }, - "status": { - "type": "string" - }, - "activities": { - "type": "array", - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/Activity" - } - } - }, - "client_status": { - "$ref": "#/definitions/ClientStatus" - }, - "approx_last_used_time": { - "type": "string" - }, - "client_info": { - "type": "object", - "properties": { - "client": { - "type": "string" - }, - "os": { - "type": "string" - }, - "version": { - "type": "integer" - }, - "location": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "client", - "location", - "os", - "version" - ] - }, - "last_seen": { - "type": "string", - "format": "date-time" - }, - "last_seen_ip": { - "type": "string" - }, - "last_seen_location": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "activities", - "approx_last_used_time", - "client_info", - "client_status", - "id", - "id_hash", - "status" - ] - } + "captcha_key": { + "type": [ + "null", + "string" + ] + }, + "token": { + "type": "string" } }, "additionalProperties": false, "required": [ - "user_sessions" + "token" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ApplicationCommandOption": { + "VoiceStateUpdateSchema": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/ApplicationCommandOptionType" - }, - "name": { + "guild_id": { "type": "string" }, - "description": { + "channel_id": { "type": "string" }, - "required": { + "self_mute": { "type": "boolean" }, - "choices": { - "type": "array", - "items": { - "$ref": "#/definitions/ApplicationCommandOptionChoice" - } + "self_deaf": { + "type": "boolean" }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/ApplicationCommandOption" - } + "self_video": { + "type": "boolean" + }, + "preferred_region": { + "type": "string" + }, + "request_to_speak_timestamp": { + "type": "string", + "format": "date-time" + }, + "suppress": { + "type": "boolean" + }, + "flags": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "description", - "name", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ApplicationCommandOptionType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8 + "self_deaf", + "self_mute" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ApplicationCommandOptionChoice": { + "GenerateWebAuthnCredentialsSchema": { "type": "object", "properties": { - "name": { + "password": { "type": "string" - }, - "value": { - "type": [ - "string", - "integer" - ] } }, "additionalProperties": false, "required": [ - "name", - "value" + "password" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ApplicationCommandIndexPermissions": { + "CreateWebAuthnCredentialSchema": { "type": "object", "properties": { - "user": { - "type": "boolean" + "credential": { + "type": "string" }, - "roles": { - "$ref": "#/definitions/Record" + "name": { + "type": "string" }, - "channels": { - "$ref": "#/definitions/Record" + "ticket": { + "type": "string" } }, "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ApplicationIntegrationType": { - "type": "number", - "enum": [ - 0, - 1 + "required": [ + "credential", + "name", + "ticket" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "InteractionContextType": { - "type": "number", - "enum": [ - 0, - 1, - 2 + "WebAuthnPostSchema": { + "anyOf": [ + { + "$ref": "#/definitions/GenerateWebAuthnCredentialsSchema" + }, + { + "$ref": "#/definitions/CreateWebAuthnCredentialSchema" + } ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "JsonRpcError": { - "description": "JSON-RPC 2.0 error object", + "WebAuthnTotpSchema": { "type": "object", "properties": { "code": { - "type": "integer" - }, - "message": { "type": "string" }, - "data": {} + "ticket": { + "type": "string" + } }, "additionalProperties": false, "required": [ "code", - "message" + "ticket" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WebhookCreateSchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 80, + "type": "string" + }, + "avatar": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "OpenAiChatCompletionObject": { + "WebhookExecuteSchema": { "type": "object", "properties": { - "id": { + "content": { "type": "string" }, - "object": { - "type": "string", - "const": "chat.completion" - }, - "created": { - "type": "integer" + "username": { + "type": "string" }, - "model": { + "avatar_url": { "type": "string" }, - "choices": { + "tts": { + "type": "boolean" + }, + "embeds": { "type": "array", "items": { - "type": "object", - "properties": { - "index": { - "type": "integer" - }, - "message": { - "type": "object", - "properties": { - "role": { - "type": "string" - }, - "content": { - "type": [ - "null", - "string" - ] - }, - "refusal": { - "type": [ - "null", - "string" - ] - }, - "annotations": { - "type": "array", - "items": {} - }, - "tool_calls": { - "type": "array", - "items": {} - } - }, - "additionalProperties": false, - "required": [ - "content", - "role" - ] - }, - "logprobs": {}, - "finish_reason": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "finish_reason", - "index", - "message" - ] + "$ref": "#/definitions/Embed" } }, - "usage": { + "allowed_mentions": { "type": "object", "properties": { - "prompt_tokens": { - "type": "integer" - }, - "completion_tokens": { - "type": "integer" + "parse": { + "type": "array", + "items": { + "type": "string" + } }, - "total_tokens": { - "type": "integer" + "roles": { + "type": "array", + "items": { + "type": "string" + } }, - "prompt_tokens_details": { - "type": "object", - "properties": { - "cached_tokens": { - "type": "integer" - }, - "audio_tokens": { - "type": "integer" - } - }, - "additionalProperties": false + "users": { + "type": "array", + "items": { + "type": "string" + } }, - "completion_tokens_details": { - "type": "object", - "properties": { - "reasoning_tokens": { - "type": "integer" - }, - "audio_tokens": { - "type": "integer" - }, - "accepted_prediction_tokens": { - "type": "integer" - }, - "rejected_prediction_tokens": { - "type": "integer" - } - }, - "additionalProperties": false + "replied_user": { + "type": "boolean" } }, - "additionalProperties": false, - "required": [ - "completion_tokens", - "prompt_tokens", - "total_tokens" - ] - }, - "service_tier": { - "type": "string" - }, - "system_fingerprint": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "choices", - "created", - "id", - "model", - "object", - "usage" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "OpenAIResponseObject": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "object": { - "type": "string", - "const": "response" - }, - "created_at": { - "type": "integer" - }, - "status": { - "enum": [ - "cancelled", - "completed", - "failed", - "in_progress" - ], - "type": "string" + "additionalProperties": false }, - "error": { - "type": [ - "null", - "string" - ] + "components": { + "type": "array", + "items": {} }, - "incomplete_details": {}, - "instructions": {}, - "max_output_tokens": { - "type": [ - "null", - "integer" + "file": { + "type": "object", + "properties": { + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename" ] }, - "model": { + "payload_json": { "type": "string" }, - "output": { + "attachments": { + "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", "type": "array", "items": { "type": "object", "properties": { - "type": { - "type": "string", - "const": "message" - }, "id": { "type": "string" }, - "status": { - "type": "string" - }, - "role": { + "filename": { "type": "string" - }, - "content": { - "type": "array", - "items": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "output_text" - }, - "text": { - "type": "string" - }, - "annotations": { - "type": "array", - "items": {} - } - }, - "additionalProperties": false, - "required": [ - "annotations", - "text", - "type" - ] - } } }, "additionalProperties": false, "required": [ - "content", - "id", - "role", - "status", - "type" + "filename", + "id" ] } }, - "output_text": { - "type": "string" + "flags": { + "type": "integer" }, - "parallel_tool_calls": { - "type": "boolean" + "thread_name": { + "type": "string" }, - "previous_response_id": { - "type": [ - "null", - "string" - ] + "applied_tags": { + "type": "array", + "items": { + "type": "string" + } }, - "reasoning": { + "message_reference": { "type": "object", "properties": { - "effort": { - "type": [ - "null", - "string" - ] + "message_id": { + "type": "string" }, - "summary": { - "type": [ - "null", - "string" - ] + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "effort", - "summary" + "message_id" ] }, - "store": { - "type": "boolean" + "sticker_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "nonce": { + "type": "string" + }, + "enforce_nonce": { + "type": "boolean" + }, + "poll": { + "$ref": "#/definitions/PollCreationSchema" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WebhookUpdateSchema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "avatar": { + "type": "string" + }, + "channel_id": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WidgetModifySchema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "channel_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "channel_id", + "enabled" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MessageThreadCreationSchema": { + "type": "object", + "properties": { + "auto_archive_duration": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "location": { + "type": "string" }, - "temperature": { + "type": { "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ApplicationCommandCreateSchema": { + "type": "object", + "properties": { + "type": { + "enum": [ + 1, + 2, + 3, + 4 + ], + "type": "number" }, - "text": { + "name": { + "type": "string" + }, + "name_localizations": { "type": "object", - "properties": { - "format": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - } - }, - "additionalProperties": false, - "required": [ - "format" - ] + "additionalProperties": { + "type": "string" + } }, - "tool_choice": { + "description": { "type": "string" }, - "tools": { - "type": "array", - "items": {} + "description_localizations": { + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "top_p": { - "type": "integer" + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/ApplicationCommandOption" + } }, - "truncation": { + "default_member_permissions": { "type": "string" }, - "usage": { - "type": "object", - "properties": { - "input_tokens": { - "type": "integer" - }, - "input_tokens_details": { - "type": "object", - "properties": { - "cached_tokens": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "output_tokens": { - "type": "integer" - }, - "output_tokens_details": { - "type": "object", - "properties": { - "reasoning_tokens": { - "type": "integer" - } - }, - "additionalProperties": false - }, - "total_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "input_tokens", - "output_tokens", - "total_tokens" - ] + "dm_permission": { + "type": "boolean" }, - "user": { - "type": [ - "null", - "string" - ] + "nsfw": { + "type": "boolean" }, - "metadata": { - "$ref": "#/definitions/Record" + "integration_types": { + "type": "array", + "items": { + "$ref": "#/definitions/ApplicationIntegrationType" + } + }, + "contexts": { + "type": "array", + "items": { + "$ref": "#/definitions/InteractionContextType" + } + }, + "handler": { + "enum": [ + 1, + 2, + 3 + ], + "type": "number" } }, "additionalProperties": false, "required": [ - "created_at", - "error", - "id", - "incomplete_details", - "instructions", - "max_output_tokens", - "metadata", - "model", - "object", - "output", - "output_text", - "parallel_tool_calls", - "previous_response_id", - "reasoning", - "status", - "store", - "temperature", - "text", - "tool_choice", - "tools", - "top_p", - "truncation", - "usage", - "user" + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "OpenAICreateEmbeddingsObject": { + "BulkApplicationCommandCreateSchema": { + "type": "array", + "items": { + "$ref": "#/definitions/ApplicationCommandCreateSchema" + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ThreadCreationSchema": { "type": "object", "properties": { - "object": { - "type": "string", - "const": "list" + "auto_archive_duration": { + "type": "integer" }, - "data": { + "rate_limit_per_user": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "type": { + "enum": [ + 11, + 12 + ], + "type": "number" + }, + "invitable": { + "type": "boolean" + }, + "applied_tags": { "type": "array", "items": { - "$ref": "#/definitions/OpenAIEmbeddingsObject" + "type": "string" } }, - "model": { + "location": { "type": "string" }, - "usage": { + "message": { "type": "object", "properties": { - "prompt_tokens": { - "type": "integer" + "content": { + "type": "string" + }, + "embeds": { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } + }, + "allowed_mentions": { + "type": "object", + "properties": { + "parse": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "users": { + "type": "array", + "items": { + "type": "string" + } + }, + "replied_user": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "components": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/ActionRowComponent" + } + }, + { + "type": "null" + } + ] + }, + "sticker_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "activity": { + "$ref": "#/definitions/MessageActivity" + }, + "application_id": { + "type": "string" }, - "total_tokens": { + "flags": { "type": "integer" + }, + "attachments": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename", + "id" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "uploaded_filename": { + "type": "string" + }, + "original_content_type": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename", + "uploaded_filename" + ] + } + ] + } } }, - "additionalProperties": false, - "required": [ - "prompt_tokens", - "total_tokens" - ] + "additionalProperties": false } }, "additionalProperties": false, "required": [ - "data", - "model", - "object", - "usage" + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "OpenAIEmbeddingsObject": { + "PostDataSchema": { "type": "object", "properties": { - "object": { - "type": "string", - "const": "embedding" - }, - "embedding": { + "thread_ids": { "type": "array", "items": { - "type": "integer" + "type": "string" } - }, - "index": { - "type": "integer" } }, "additionalProperties": false, "required": [ - "embedding", - "index", - "object" + "thread_ids" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "OpenAIConversationObject": { - "description": "OpenAI Conversations API Conversation object", + "TagCreateSchema": { "type": "object", "properties": { - "id": { + "name": { "type": "string" }, - "object": { - "type": "string", - "const": "conversation" + "moderated": { + "type": [ + "null", + "boolean" + ] }, - "created_at": { - "type": "integer" + "emoji_id": { + "type": [ + "null", + "string" + ] }, - "metadata": { - "$ref": "#/definitions/Record" + "emoji_name": { + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, "required": [ - "created_at", - "id", - "object" + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Blob": { - "description": "`Blob` class is a global reference for `import { Blob } from 'node:buffer'`\nhttps://nodejs.org/api/buffer.html#class-blob", + "ChannelCreateSchema": { "type": "object", "properties": { - "size": { - "description": "The total size of the `Blob` in bytes.", - "type": "integer" + "name": { + "type": "string" }, "type": { - "description": "The content-type of the `Blob`.", + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 2, + 255, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "type": "number" + }, + "id": { + "type": "string" + }, + "flags": { + "type": "integer" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "parent_id": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "video_quality_mode": { + "type": "integer" + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "nsfw": { + "type": "boolean" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "topic": { + "type": "string" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + }, + "applied_tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "position": { + "type": "integer" + }, + "invitable": { + "type": "boolean" + }, + "rtc_region": { "type": "string" + }, + "default_reaction_emoji": { + "type": [ + "null", + "string" + ] + }, + "auto_archive_duration": { + "type": "integer" + }, + "archived": { + "type": "boolean" + }, + "locked": { + "type": "boolean" } }, "additionalProperties": false, - "required": [ - "size", - "type" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ArrayBufferLike": { - "anyOf": [ - { - "$ref": "#/definitions/ArrayBuffer" - }, - { - "$ref": "#/definitions/SharedArrayBuffer" + "ChannelPromoteSchema": { + "type": "object", + "properties": { + "position": { + "type": "integer" } - ], + }, + "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "ArrayBuffer": { + "TicketCreateSchema": { "type": "object", "properties": { - "byteLength": { - "type": "integer" - }, - "__@toStringTag@1293": { + "name": { "type": "string" } }, "additionalProperties": false, - "required": [ - "__@toStringTag@1293", - "byteLength" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SharedArrayBuffer": { + "TicketPatchSchema": { "type": "object", "properties": { - "byteLength": { - "type": "integer" + "owner_id": { + "type": [ + "null", + "string" + ] }, - "__@species@16534": { - "$ref": "#/definitions/SharedArrayBuffer" + "resolved": { + "type": [ + "null", + "boolean" + ] }, - "__@toStringTag@1293": { - "type": "string", - "const": "SharedArrayBuffer" + "public": { + "type": [ + "null", + "boolean" + ] + }, + "closed": { + "type": [ + "null", + "boolean" + ] } }, "additionalProperties": false, - "required": [ - "__@species@16534", - "__@toStringTag@1293", - "byteLength" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MessageName": { - "enum": [ - "authenticationCleartextPassword", - "authenticationMD5Password", - "authenticationOk", - "authenticationSASL", - "authenticationSASLContinue", - "authenticationSASLFinal", - "backendKeyData", - "bindComplete", - "closeComplete", - "commandComplete", - "copyData", - "copyDone", - "copyInResponse", - "copyOutResponse", - "dataRow", - "emptyQuery", - "error", - "noData", - "notice", - "notification", - "parameterDescription", - "parameterStatus", - "parseComplete", - "portalSuspended", - "readyForQuery", - "replicationStart", - "rowDescription" - ], - "type": "string", - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ErrorList": { - "type": "object", - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InteractionType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5 - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "InteractionData": { - "type": "object", - "properties": { - "application_command": { - "type": "object", - "properties": {}, - "additionalProperties": true + "VoiceIdentifySchema": { + "type": "object", + "properties": { + "server_id": { + "type": "string" }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/UploadAttachmentRequestSchema" - } + "user_id": { + "type": "string" }, - "id": { + "session_id": { "type": "string" }, - "name": { + "channel_id": { "type": "string" }, - "options": { + "token": { + "type": "string" + }, + "video": { + "type": "boolean" + }, + "streams": { "type": "array", "items": { - "$ref": "#/definitions/ApplicationCommandOption" + "type": "object", + "properties": { + "type": { + "enum": [ + "audio", + "screen", + "video" + ], + "type": "string" + }, + "rid": { + "type": "string" + }, + "quality": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "quality", + "rid", + "type" + ] } }, - "type": { + "max_secure_frames_version": { "type": "integer" }, - "version": { - "type": "string" + "max_dave_protocol_version": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "application_command", - "attachments", - "id", - "name", - "options", - "type", - "version" + "server_id", + "session_id", + "token", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UploadAttachmentRequest": { + "VoiceVideoSchema": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "filename": { - "type": "string" + "audio_ssrc": { + "type": "integer" }, - "file_size": { + "video_ssrc": { "type": "integer" }, - "is_clip": { - "type": "boolean" + "rtx_ssrc": { + "type": "integer" }, - "original_content_type": { + "user_id": { "type": "string" + }, + "streams": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "enum": [ + "audio", + "screen", + "video" + ], + "type": "string" + }, + "rid": { + "type": "string" + }, + "ssrc": { + "type": "integer" + }, + "active": { + "type": "boolean" + }, + "quality": { + "type": "integer" + }, + "rtx_ssrc": { + "type": "integer" + }, + "max_bitrate": { + "type": "integer" + }, + "max_framerate": { + "type": "integer" + }, + "max_resolution": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "height", + "type", + "width" + ] + } + }, + "additionalProperties": false, + "required": [ + "active", + "max_bitrate", + "max_framerate", + "max_resolution", + "quality", + "rid", + "rtx_ssrc", + "ssrc", + "type" + ] + } } }, "additionalProperties": false, "required": [ - "file_size", - "filename" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "InteractionCallbackType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12 + "audio_ssrc", + "video_ssrc" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Message": { + "CreateReportSchema": { "type": "object", "properties": { - "channel_id": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "thread_id": { - "type": "string" - }, - "thread": { - "$ref": "#/definitions/Channel" - }, - "guild_id": { - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "author_id": { - "type": "string" - }, - "author": { - "$ref": "#/definitions/User" - }, - "member_id": { - "type": "string" - }, - "member": { - "$ref": "#/definitions/Member" - }, - "webhook_id": { - "type": "string" - }, - "webhook": { - "$ref": "#/definitions/Webhook" - }, - "application_id": { - "type": "string" - }, - "application": { - "$ref": "#/definitions/Application" - }, - "content": { + "version": { "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "edited_timestamp": { - "type": "string", - "format": "date-time" - }, - "tts": { - "type": "boolean" - }, - "mention_everyone": { - "type": "boolean" - }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/definitions/User" - } - }, - "mention_roles": { - "type": "array", - "items": { - "$ref": "#/definitions/Role" - } - }, - "mention_channels": { - "type": "array", - "items": { - "$ref": "#/definitions/Channel" - } - }, - "sticker_items": { - "type": "array", - "items": { - "$ref": "#/definitions/Sticker" - } + "variant": { + "type": "string" }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/Attachment" - } + "name": { + "type": "string" }, - "embeds": { + "language": { + "type": "string" + }, + "breadcrumbs": { "type": "array", "items": { - "$ref": "#/definitions/Embed" + "type": "integer" } }, - "reactions": { - "type": "array", - "items": { - "$ref": "#/definitions/Reaction" + "elements": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } } }, - "nonce": { + "channel_id": { "type": "string" }, - "pinned": { - "type": "boolean" - }, - "pinned_at": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ] + "message_id": { + "type": "string" }, - "type": { - "$ref": "#/definitions/MessageType" + "guild_id": { + "type": "string" }, - "activity": { - "type": "object", - "properties": { - "type": { - "type": "integer" - }, - "party_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "party_id", - "type" - ] + "stage_instance_id": { + "type": "string" }, - "flags": { - "type": "integer" + "guild_scheduled_event_id": { + "type": "string" }, - "message_reference": { - "type": "object", - "properties": { - "message_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "type": { - "type": "integer" - } - }, - "additionalProperties": false + "reported_user_id": { + "type": "string" }, - "referenced_message": { - "$ref": "#/definitions/Message" + "application_id": { + "type": "string" }, - "interaction": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "name": { - "type": "string" - }, - "user_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "id", - "name", - "type", - "user_id" - ] + "user_id": { + "type": "string" }, - "components": { + "widget_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "breadcrumbs", + "language", + "name", + "variant", + "version" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "SessionsLogoutSchema": { + "type": "object", + "properties": { + "session_ids": { "type": "array", "items": { - "$ref": "#/definitions/ActionRowComponent" + "type": "string" } }, - "poll": { - "$ref": "#/definitions/Poll" - }, - "interaction_metadata": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/InteractionType" - }, - "user_id": { - "type": "string" + "session_id_hashes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GetSessionsResponse": { + "type": "object", + "properties": { + "user_sessions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "id_hash": { + "type": "string" + }, + "status": { + "type": "string" + }, + "activities": { + "type": "array", + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/Activity" + } + } + }, + "client_status": { + "$ref": "#/definitions/ClientStatus" + }, + "approx_last_used_time": { + "type": "string" + }, + "client_info": { + "type": "object", + "properties": { + "client": { + "type": "string" + }, + "os": { + "type": "string" + }, + "version": { + "type": "integer" + }, + "location": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "client", + "location", + "os", + "version" + ] + }, + "last_seen": { + "type": "string", + "format": "date-time" + }, + "last_seen_ip": { + "type": "string" + }, + "last_seen_location": { + "type": "string" + } }, - "authorizing_integration_owners": { + "additionalProperties": false, + "required": [ + "activities", + "approx_last_used_time", + "client_info", + "client_status", + "id", + "id_hash", + "status" + ] + } + } + }, + "additionalProperties": false, + "required": [ + "user_sessions" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LobbyMemberSchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { "$ref": "#/definitions/Record" }, - "original_response_message_id": { - "type": "string" - }, - "interacted_message_id": { - "type": "string" - }, - "name": { - "type": "string" + { + "type": "null" } - }, - "additionalProperties": false, - "required": [ - "id", - "type", - "user_id" ] }, - "message_snapshots": { - "type": "array", - "items": { - "$ref": "#/definitions/MessageSnapshot" - } - }, - "reply_ids": { - "type": "array", - "items": { - "type": "string" - } - }, - "username": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "id": { - "type": "string" + "flags": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "channel", - "embeds", - "flags", - "id", - "mention_channels", - "mention_roles", - "mentions", - "reactions", - "timestamp", - "type" + "id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Channel": { + "LobbyCreateSchema": { "type": "object", "properties": { - "created_at": { - "type": "string", - "format": "date-time" - }, - "name": { - "type": "string" - }, - "icon": { - "type": [ - "null", - "string" + "metadata": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "null" + } ] }, - "type": { - "$ref": "#/definitions/ChannelType" - }, - "recipients": { - "type": "array", - "items": { - "$ref": "#/definitions/Recipient" - } - }, - "thread_members": { + "members": { + "maxItems": 25, "type": "array", "items": { - "$ref": "#/definitions/ThreadMember" + "$ref": "#/definitions/LobbyMemberSchema" } }, - "last_message_id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "parent_id": { - "type": [ - "null", - "string" - ] - }, - "parent": { - "$ref": "#/definitions/Channel" - }, - "owner_id": { - "type": "string" - }, - "owner": { - "$ref": "#/definitions/User" - }, - "last_pin_timestamp": { - "type": "integer" - }, - "default_auto_archive_duration": { + "idle_timeout_seconds": { + "minimum": 5, + "maximum": 604800, "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "idle_timeout_seconds" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LobbyUpdateSchema": { + "type": "object", + "properties": { + "metadata": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "null" + } + ] }, - "permission_overwrites": { + "members": { + "maxItems": 25, "type": "array", "items": { - "$ref": "#/definitions/ChannelPermissionOverwrite" + "$ref": "#/definitions/LobbyMemberSchema" } }, - "video_quality_mode": { - "type": "integer" - }, - "bitrate": { + "idle_timeout_seconds": { + "minimum": 5, + "maximum": 604800, "type": "integer" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LobbyMemberUpdateSchema": { + "type": "object", + "properties": { + "metadata": { + "anyOf": [ + { + "$ref": "#/definitions/Record" + }, + { + "type": "null" + } + ] }, - "user_limit": { - "type": "integer" - }, - "nsfw": { - "type": "boolean", - "default": false - }, - "rate_limit_per_user": { + "flags": { "type": "integer" - }, - "topic": { + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "LobbyChannelLinkSchema": { + "type": "object", + "properties": { + "channel_id": { "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ApplicationCommandOption": { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/ApplicationCommandOptionType" }, - "invites": { - "type": "array", - "items": { - "$ref": "#/definitions/Invite" - } - }, - "retention_policy_id": { + "name": { "type": "string" }, - "messages": { - "type": "array", - "items": { - "$ref": "#/definitions/Message" - } - }, - "voice_states": { - "type": "array", - "items": { - "$ref": "#/definitions/VoiceState" - } - }, - "read_states": { - "type": "array", - "items": { - "$ref": "#/definitions/ReadState" - } - }, - "webhooks": { - "type": "array", - "items": { - "$ref": "#/definitions/Webhook" - } - }, - "flags": { - "type": "integer", - "default": 0 - }, - "default_thread_rate_limit_per_user": { - "type": "integer", - "default": 0 - }, - "thread_metadata": { - "$ref": "#/definitions/ThreadMetadata" - }, - "member_count": { - "type": "integer" - }, - "message_count": { - "type": "integer" + "description": { + "type": "string" }, - "total_message_sent": { - "type": "integer" + "required": { + "type": "boolean" }, - "available_tags": { + "choices": { "type": "array", "items": { - "$ref": "#/definitions/Tag" + "$ref": "#/definitions/ApplicationCommandOptionChoice" } }, - "applied_tags": { + "options": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/ApplicationCommandOption" } - }, - "position": { - "description": "Must be calculated Channel.calculatePosition", - "type": "integer" - }, - "id": { - "type": "string" } }, "additionalProperties": false, "required": [ - "created_at", - "flags", - "id", - "nsfw", - "owner", - "parent_id", - "position", + "description", + "name", "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ChannelType": { + "ApplicationCommandOptionType": { "type": "number", "enum": [ - 0, 1, 2, 3, @@ -11024,2045 +8068,2406 @@ 5, 6, 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 33, - 34, - 35, - 64, - 255 + 8 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Recipient": { + "ApplicationCommandOptionChoice": { "type": "object", "properties": { - "channel_id": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "user_id": { + "name": { "type": "string" }, - "user": { - "$ref": "#/definitions/User" - }, - "closed": { - "type": "boolean" - }, - "id": { - "type": "string" + "value": { + "type": [ + "string", + "integer" + ] } }, "additionalProperties": false, "required": [ - "channel", - "channel_id", - "closed", - "id", - "user", - "user_id" + "name", + "value" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "User": { + "ApplicationCommandIndexPermissions": { "type": "object", "properties": { - "username": { - "type": "string" - }, - "discriminator": { - "type": "string" - }, - "avatar": { - "type": "string" - }, - "accent_color": { - "type": "integer" - }, - "banner": { - "type": "string" - }, - "theme_colors": { - "type": "array", - "items": { - "type": "integer" - } - }, - "pronouns": { - "type": "string" - }, - "phone": { - "type": "string" - }, - "desktop": { - "type": "boolean", - "default": false - }, - "mobile": { - "type": "boolean", - "default": false - }, - "premium": { - "type": "boolean" - }, - "premium_type": { - "type": "integer" - }, - "bot": { - "type": "boolean", - "default": false - }, - "bio": { - "type": "string", - "default": "" - }, - "system": { - "type": "boolean", - "default": false - }, - "nsfw_allowed": { - "type": "boolean", - "default": true - }, - "mfa_enabled": { - "type": "boolean", - "default": false - }, - "webauthn_enabled": { - "type": "boolean", - "default": false - }, - "totp_secret": { - "type": "string", - "default": "" - }, - "totp_last_ticket": { - "type": "string", - "default": "" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "premium_since": { - "type": "string", - "format": "date-time" - }, - "verified": { + "user": { "type": "boolean" }, - "disabled": { - "type": "boolean", - "default": false - }, - "deleted": { - "type": "boolean", - "default": false - }, - "email": { - "type": "string" - }, - "flags": { - "type": "integer", - "default": 0 - }, - "public_flags": { - "type": "integer", - "default": 0 - }, - "purchased_flags": { - "type": "integer", - "default": 0 - }, - "premium_usage_flags": { - "type": "integer", - "default": 0 - }, - "rights": { - "type": "string" - }, - "sessions": { - "type": "array", - "items": { - "$ref": "#/definitions/Session" - } - }, - "relationships": { - "type": "array", - "items": { - "$ref": "#/definitions/Relationship" - } - }, - "connected_accounts": { - "type": "array", - "items": { - "$ref": "#/definitions/ConnectedAccount" - } + "roles": { + "$ref": "#/definitions/Record" }, - "data": { + "channels": { + "$ref": "#/definitions/Record" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ApplicationIntegrationType": { + "type": "number", + "enum": [ + 0, + 1 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InteractionContextType": { + "type": "number", + "enum": [ + 0, + 1, + 2 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ErrorList": { + "type": "object", + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InteractionType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InteractionData": { + "type": "object", + "properties": { + "application_command": { "type": "object", - "properties": { - "valid_tokens_since": { - "type": "string", - "format": "date-time" - }, - "hash": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "valid_tokens_since" - ] - }, - "fingerprints": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "settings": { - "$ref": "#/definitions/UserSettings" + "properties": {}, + "additionalProperties": true }, - "security_keys": { + "attachments": { "type": "array", "items": { - "$ref": "#/definitions/SecurityKey" + "$ref": "#/definitions/UploadAttachmentRequestSchema" } }, - "badge_ids": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "options": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/ApplicationCommandOption" } }, - "avatar_decoration_data": { - "$ref": "#/definitions/AvatarDecorationData" + "type": { + "type": "integer" }, - "display_name_styles": { - "$ref": "#/definitions/DisplayNameStyle" + "version": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "application_command", + "attachments", + "id", + "name", + "options", + "type", + "version" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "UploadAttachmentRequest": { + "type": "object", + "properties": { + "id": { + "type": "string" }, - "collectibles": { - "$ref": "#/definitions/Collectibles" + "filename": { + "type": "string" }, - "primary_guild": { - "$ref": "#/definitions/PrimaryGuild" + "file_size": { + "type": "integer" }, - "tag": { - "type": "string" + "is_clip": { + "type": "boolean" }, - "id": { + "original_content_type": { "type": "string" } }, "additionalProperties": false, "required": [ - "bio", - "bot", - "connected_accounts", - "created_at", - "data", - "deleted", - "desktop", - "disabled", - "discriminator", - "fingerprints", - "flags", - "id", - "mfa_enabled", - "mobile", - "nsfw_allowed", - "premium", - "premium_since", - "premium_type", - "premium_usage_flags", - "public_flags", - "purchased_flags", - "relationships", - "rights", - "security_keys", - "sessions", - "system", - "tag", - "username", - "verified", - "webauthn_enabled" + "file_size", + "filename" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Session": { + "InteractionCallbackType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "Message": { "type": "object", "properties": { - "session_id": { + "channel_id": { "type": "string" }, - "user_id": { + "channel": { + "$ref": "#/definitions/Channel" + }, + "thread_id": { "type": "string" }, - "user": { + "thread": { + "$ref": "#/definitions/Channel" + }, + "guild_id": { + "type": "string" + }, + "guild": { + "$ref": "#/definitions/Guild" + }, + "author_id": { + "type": "string" + }, + "author": { "$ref": "#/definitions/User" }, - "activities": { - "type": "array", - "items": { - "$ref": "#/definitions/Activity" - } + "member_id": { + "type": "string" }, - "client_info": { - "type": "object", - "properties": { - "platform": { - "type": "string" - }, - "os": { - "type": "string" - }, - "version": { - "type": "integer" - }, - "location": { - "type": "string" - } - }, - "additionalProperties": false + "member": { + "$ref": "#/definitions/Member" }, - "client_status": { - "$ref": "#/definitions/ClientStatus" + "webhook_id": { + "type": "string" }, - "status": { - "$ref": "#/definitions/Status" + "webhook": { + "$ref": "#/definitions/Webhook" }, - "is_admin_session": { - "type": "boolean" + "application_id": { + "type": "string" }, - "created_at": { + "application": { + "$ref": "#/definitions/Application" + }, + "content": { + "type": "string" + }, + "timestamp": { "type": "string", "format": "date-time" }, - "last_seen": { + "edited_timestamp": { "type": "string", "format": "date-time" }, - "last_seen_ip": { - "type": "string" + "tts": { + "type": "boolean" }, - "last_seen_location": { - "type": "string" + "mention_everyone": { + "type": "boolean" }, - "last_seen_location_info": { - "$ref": "#/definitions/ExtendedLocationInfo" + "mentions": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } }, - "session_nickname": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "activities", - "client_info", - "client_status", - "created_at", - "is_admin_session", - "session_id", - "status", - "user", - "user_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Activity": { - "type": "object", - "properties": { - "name": { + "mention_roles": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" + } + }, + "mention_channels": { + "type": "array", + "items": { + "$ref": "#/definitions/Channel" + } + }, + "sticker_items": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" + } + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment" + } + }, + "embeds": { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } + }, + "reactions": { + "type": "array", + "items": { + "$ref": "#/definitions/Reaction" + } + }, + "nonce": { "type": "string" }, - "type": { - "$ref": "#/definitions/ActivityType" + "pinned_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] }, - "url": { - "type": "string" + "pinned": { + "type": "boolean" }, - "created_at": { - "type": "integer" + "type": { + "$ref": "#/definitions/MessageType" }, - "timestamps": { + "activity": { "type": "object", "properties": { - "start": { + "type": { "type": "integer" }, - "end": { - "type": "integer" + "party_id": { + "type": "string" } }, "additionalProperties": false, "required": [ - "end", - "start" + "party_id", + "type" ] }, - "application_id": { - "type": "string" - }, - "details": { - "type": "string" - }, - "state": { - "type": "string" + "flags": { + "type": "integer" }, - "emoji": { + "message_reference": { "type": "object", "properties": { - "name": { + "message_id": { "type": "string" }, - "id": { + "channel_id": { "type": "string" }, - "animated": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "animated", - "name" - ] - }, - "party": { - "type": "object", - "properties": { - "id": { + "guild_id": { "type": "string" }, - "size": { - "type": "array", - "items": { - "type": "integer" - } + "type": { + "type": "integer" } }, "additionalProperties": false }, - "assets": { + "referenced_message": { + "anyOf": [ + { + "$ref": "#/definitions/Message" + }, + { + "type": "null" + } + ] + }, + "interaction": { "type": "object", "properties": { - "large_image": { - "type": "string" - }, - "large_text": { + "id": { "type": "string" }, - "small_image": { - "type": "string" + "type": { + "$ref": "#/definitions/InteractionType" }, - "small_text": { + "name": { "type": "string" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "id", + "name", + "type" + ] }, - "secrets": { + "interaction_metadata": { "type": "object", "properties": { - "join": { + "id": { "type": "string" }, - "spectate": { + "type": { + "$ref": "#/definitions/InteractionType" + }, + "user_id": { "type": "string" }, - "match": { + "authorizing_integration_owners": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "name": { "type": "string" + }, + "command_type": { + "$ref": "#/definitions/ApplicationCommandType" } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "authorizing_integration_owners", + "command_type", + "id", + "name", + "type", + "user_id" + ] }, - "instance": { - "type": "boolean" + "components": { + "type": "array", + "items": { + "$ref": "#/definitions/ActionRowComponent" + } }, - "flags": { + "poll": { + "$ref": "#/definitions/Poll" + }, + "username": { "type": "string" }, - "id": { + "avatar": { "type": "string" }, - "sync_id": { + "message_snapshots": { + "type": "array", + "items": { + "$ref": "#/definitions/MessageSnapshot" + } + }, + "reply_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { "type": "string" }, - "metadata": { + "__@annotationsKey@11707": { "type": "object", - "properties": { - "context_uri": { - "type": "string" - }, - "album_id": { + "additionalProperties": { + "type": "array", + "items": { "type": "string" - }, - "artist_ids": { - "type": "array", - "items": { - "type": "string" - } } - }, - "additionalProperties": false, - "required": [ - "album_id", - "artist_ids" - ] - }, - "session_id": { - "type": "string" + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", + "channel", + "embeds", "flags", - "name", - "session_id", + "id", + "mention_channels", + "mention_roles", + "mentions", + "message_snapshots", + "pinned", + "reactions", + "timestamp", "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ActivityType": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 4, - 5 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ClientStatus": { + "Channel": { "type": "object", "properties": { - "desktop": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time" }, - "mobile": { + "name": { "type": "string" }, - "web": { + "icon": { + "type": [ + "null", + "string" + ] + }, + "type": { + "$ref": "#/definitions/ChannelType" + }, + "recipients": { + "type": "array", + "items": { + "$ref": "#/definitions/Recipient" + } + }, + "thread_members": { + "type": "array", + "items": { + "$ref": "#/definitions/ThreadMember" + } + }, + "last_message_id": { "type": "string" }, - "embedded": { + "guild_id": { "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Status": { - "enum": [ - "dnd", - "idle", - "invisible", - "offline", - "online" - ], - "type": "string", - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ExtendedLocationInfo": { - "type": "object", - "properties": { - "is_eu": { - "type": "boolean" }, - "city": { - "type": "string" + "guild": { + "$ref": "#/definitions/Guild" + }, + "parent_id": { + "type": [ + "null", + "string" + ] + }, + "parent": { + "$ref": "#/definitions/Channel" }, - "region": { + "owner_id": { "type": "string" }, - "region_code": { - "type": "string" + "owner": { + "$ref": "#/definitions/User" }, - "country_name": { - "type": "string" + "last_pin_timestamp": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] }, - "country_code": { - "type": "string" + "default_auto_archive_duration": { + "type": "integer" }, - "continent_name": { - "type": "string" + "permission_overwrites": { + "type": "array", + "items": { + "$ref": "#/definitions/ChannelPermissionOverwrite" + } }, - "continent_code": { - "type": "string" + "video_quality_mode": { + "type": "integer" }, - "latitude": { + "bitrate": { "type": "integer" }, - "longitude": { + "user_limit": { "type": "integer" }, - "postal": { - "type": "string" + "nsfw": { + "type": "boolean", + "default": false }, - "calling_code": { - "type": "string" + "rate_limit_per_user": { + "type": "integer" }, - "flag": { + "topic": { "type": "string" }, - "emoji_flag": { - "type": "string" + "invites": { + "type": "array", + "items": { + "$ref": "#/definitions/Invite" + } }, - "emoji_unicode": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "calling_code", - "city", - "continent_code", - "continent_name", - "country_code", - "country_name", - "emoji_flag", - "emoji_unicode", - "flag", - "is_eu", - "latitude", - "longitude", - "postal", - "region", - "region_code" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Relationship": { - "type": "object", - "properties": { - "from_id": { + "retention_policy_id": { "type": "string" }, - "from": { - "$ref": "#/definitions/User" + "messages": { + "type": "array", + "items": { + "$ref": "#/definitions/Message" + } }, - "to_id": { - "type": "string" + "voice_states": { + "type": "array", + "items": { + "$ref": "#/definitions/VoiceState" + } }, - "to": { - "$ref": "#/definitions/User" + "read_states": { + "type": "array", + "items": { + "$ref": "#/definitions/ReadState" + } }, - "nickname": { - "type": "string" + "webhooks": { + "type": "array", + "items": { + "$ref": "#/definitions/Webhook" + } }, - "type": { - "$ref": "#/definitions/RelationshipType" + "flags": { + "type": "integer", + "default": 0 + }, + "default_thread_rate_limit_per_user": { + "type": "integer", + "default": 0 + }, + "thread_metadata": { + "$ref": "#/definitions/ThreadMetadata" + }, + "member_count": { + "type": "integer" + }, + "message_count": { + "type": "integer" + }, + "total_message_sent": { + "type": "integer" + }, + "available_tags": { + "type": "array", + "items": { + "$ref": "#/definitions/Tag" + } + }, + "applied_tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "position": { + "description": "Must be calculated Channel.calculatePosition", + "type": "integer" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "from", - "from_id", + "__@annotationsKey@11707", + "created_at", + "flags", "id", - "to", - "to_id", + "nsfw", + "owner", + "parent_id", + "position", "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RelationshipType": { + "ChannelType": { "type": "number", "enum": [ - 4, - 3, + 0, + 1, 2, - 1 + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 255 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ConnectedAccount": { + "Recipient": { "type": "object", "properties": { - "external_id": { + "channel_id": { "type": "string" }, + "channel": { + "$ref": "#/definitions/Channel" + }, "user_id": { "type": "string" }, "user": { "$ref": "#/definitions/User" }, - "friend_sync": { - "type": "boolean", - "default": false - }, - "name": { - "type": "string" - }, - "revoked": { - "type": "boolean", - "default": false - }, - "show_activity": { - "type": "integer", - "default": 0 - }, - "type": { - "type": "string" - }, - "verified": { - "type": "boolean", - "default": true - }, - "visibility": { - "type": "integer", - "default": 0 - }, - "integrations": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] - }, - "metadata_": {}, - "metadata_visibility": { - "type": "integer", - "default": 0 - }, - "two_way_link": { - "type": "boolean", - "default": false - }, - "token_data": { - "anyOf": [ - { - "$ref": "#/definitions/ConnectedAccountTokenData" - }, - { - "type": "null" - } - ] + "closed": { + "type": "boolean" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "external_id", + "__@annotationsKey@11707", + "channel", + "channel_id", + "closed", "id", - "name", - "type", "user", "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ConnectedAccountTokenData": { + "User": { "type": "object", "properties": { - "access_token": { - "type": "string" - }, - "token_type": { + "username": { "type": "string" }, - "scope": { + "discriminator": { "type": "string" }, - "refresh_token": { + "avatar": { "type": "string" }, - "expires_in": { + "accent_color": { "type": "integer" }, - "expires_at": { - "type": "integer" + "banner": { + "type": "string" }, - "fetched_at": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "access_token", - "fetched_at" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "UserSettings": { - "type": "object", - "properties": { - "index": { + "theme_colors": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pronouns": { "type": "string" }, - "afk_timeout": { - "type": "integer", - "default": 3600 + "phone": { + "type": "string" }, - "allow_accessibility_detection": { + "desktop": { "type": "boolean", - "default": true + "default": false }, - "animate_emoji": { + "mobile": { "type": "boolean", - "default": true + "default": false }, - "animate_stickers": { - "type": "integer", - "default": 0 + "premium": { + "type": "boolean" }, - "contact_sync_enabled": { + "premium_type": { + "type": "integer" + }, + "bot": { "type": "boolean", "default": false }, - "convert_emoticons": { + "bio": { + "type": "string", + "default": "" + }, + "system": { "type": "boolean", "default": false }, - "custom_status": { - "anyOf": [ - { - "$ref": "#/definitions/CustomStatus" - }, - { - "type": "null" - } - ], - "default": null + "nsfw_allowed": { + "type": "boolean", + "default": true }, - "default_guilds_restricted": { + "mfa_enabled": { "type": "boolean", "default": false }, - "detect_platform_accounts": { + "webauthn_enabled": { "type": "boolean", "default": false }, - "developer_mode": { - "type": "boolean", - "default": true + "totp_secret": { + "type": "string", + "default": "" }, - "disable_games_tab": { + "totp_last_ticket": { + "type": "string", + "default": "" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "premium_since": { + "type": "string", + "format": "date-time" + }, + "verified": { + "type": "boolean" + }, + "disabled": { "type": "boolean", - "default": true + "default": false }, - "enable_tts_command": { + "deleted": { "type": "boolean", "default": false }, - "explicit_content_filter": { + "email": { + "type": "string" + }, + "flags": { "type": "integer", "default": 0 }, - "friend_discovery_flags": { + "public_flags": { "type": "integer", "default": 0 }, - "friend_source_flags": { - "$ref": "#/definitions/FriendSourceFlags" + "purchased_flags": { + "type": "integer", + "default": 0 + }, + "premium_usage_flags": { + "type": "integer", + "default": 0 + }, + "rights": { + "type": "string" + }, + "sessions": { + "type": "array", + "items": { + "$ref": "#/definitions/Session" + } + }, + "relationships": { + "type": "array", + "items": { + "$ref": "#/definitions/Relationship" + } + }, + "connected_accounts": { + "type": "array", + "items": { + "$ref": "#/definitions/ConnectedAccount" + } + }, + "data": { + "type": "object", + "properties": { + "valid_tokens_since": { + "type": "string", + "format": "date-time" + }, + "hash": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "valid_tokens_since" + ] + }, + "fingerprints": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "settings": { + "$ref": "#/definitions/UserSettings" + }, + "security_keys": { + "type": "array", + "items": { + "$ref": "#/definitions/SecurityKey" + } + }, + "badge_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "avatar_decoration_data": { + "$ref": "#/definitions/AvatarDecorationData" + }, + "display_name_styles": { + "$ref": "#/definitions/DisplayNameStyle" + }, + "collectibles": { + "$ref": "#/definitions/Collectibles" + }, + "primary_guild": { + "$ref": "#/definitions/PrimaryGuild" + }, + "tag": { + "type": "string" + }, + "id": { + "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "additionalProperties": false, + "required": [ + "__@annotationsKey@11707", + "bio", + "bot", + "connected_accounts", + "created_at", + "data", + "deleted", + "desktop", + "disabled", + "discriminator", + "fingerprints", + "flags", + "id", + "mfa_enabled", + "mobile", + "nsfw_allowed", + "premium", + "premium_since", + "premium_type", + "premium_usage_flags", + "public_flags", + "purchased_flags", + "relationships", + "rights", + "security_keys", + "sessions", + "system", + "tag", + "username", + "verified", + "webauthn_enabled" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "Session": { + "type": "object", + "properties": { + "session_id": { + "type": "string" }, - "gateway_connected": { - "type": "boolean", - "default": false + "user_id": { + "type": "string" }, - "gif_auto_play": { - "type": "boolean", - "default": false + "user": { + "$ref": "#/definitions/User" }, - "guild_folders": { + "activities": { "type": "array", "items": { - "$ref": "#/definitions/GuildFolder" - }, - "default": [] + "$ref": "#/definitions/Activity" + } }, - "guild_positions": { - "type": "array", - "items": { - "type": "string" + "client_info": { + "type": "object", + "properties": { + "platform": { + "type": "string" + }, + "os": { + "type": "string" + }, + "version": { + "type": "integer" + }, + "location": { + "type": "string" + } }, - "default": [] - }, - "inline_attachment_media": { - "type": "boolean", - "default": true - }, - "inline_embed_media": { - "type": "boolean", - "default": true - }, - "locale": { - "type": "string", - "default": "en-US" - }, - "message_display_compact": { - "type": "boolean", - "default": false - }, - "native_phone_integration_enabled": { - "type": "boolean", - "default": true + "additionalProperties": false }, - "render_embeds": { - "type": "boolean", - "default": true + "client_status": { + "$ref": "#/definitions/ClientStatus" }, - "render_reactions": { - "type": "boolean", - "default": true + "status": { + "$ref": "#/definitions/Status" }, - "restricted_guilds": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] + "is_admin_session": { + "type": "boolean" }, - "show_current_game": { - "type": "boolean", - "default": true + "created_at": { + "type": "string", + "format": "date-time" }, - "status": { - "enum": [ - "dnd", - "idle", - "invisible", - "offline", - "online" - ], + "last_seen": { "type": "string", - "default": "online" + "format": "date-time" }, - "stream_notifications_enabled": { - "type": "boolean", - "default": false + "last_seen_ip": { + "type": "string" }, - "theme": { - "enum": [ - "dark", - "light" - ], - "type": "string", - "default": "dark" + "last_seen_location": { + "type": "string" }, - "timezone_offset": { - "type": "integer", - "default": 0 + "last_seen_location_info": { + "$ref": "#/definitions/ExtendedLocationInfo" }, - "view_nsfw_guilds": { - "type": "boolean", - "default": true + "session_nickname": { + "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "afk_timeout", - "allow_accessibility_detection", - "animate_emoji", - "animate_stickers", - "contact_sync_enabled", - "convert_emoticons", - "custom_status", - "default_guilds_restricted", - "detect_platform_accounts", - "developer_mode", - "disable_games_tab", - "enable_tts_command", - "explicit_content_filter", - "friend_discovery_flags", - "friend_source_flags", - "gateway_connected", - "gif_auto_play", - "guild_folders", - "guild_positions", - "index", - "inline_attachment_media", - "inline_embed_media", - "locale", - "message_display_compact", - "native_phone_integration_enabled", - "render_embeds", - "render_reactions", - "restricted_guilds", - "show_current_game", + "__@annotationsKey@11707", + "activities", + "client_info", + "client_status", + "created_at", + "is_admin_session", + "session_id", "status", - "stream_notifications_enabled", - "theme", - "timezone_offset", - "view_nsfw_guilds" + "user", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CustomStatus": { + "Activity": { "type": "object", "properties": { - "emoji_id": { + "name": { "type": "string" }, - "emoji_name": { + "type": { + "$ref": "#/definitions/ActivityType" + }, + "url": { "type": "string" }, - "expires_at": { + "created_at": { "type": "integer" }, - "text": { + "timestamps": { + "type": "object", + "properties": { + "start": { + "type": "integer" + }, + "end": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "end", + "start" + ] + }, + "application_id": { "type": "string" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "FriendSourceFlags": { - "type": "object", - "properties": { - "all": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "all" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildFolder": { - "type": "object", - "properties": { - "color": { - "type": [ - "null", - "integer" + }, + "details": { + "type": "string" + }, + "state": { + "type": "string" + }, + "emoji": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "animated", + "name" ] }, - "guild_ids": { - "type": "array", - "items": { - "type": "string" - } + "party": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "size": { + "type": "array", + "items": { + "type": "integer" + } + } + }, + "additionalProperties": false }, - "id": { - "type": [ - "null", - "integer" - ] + "assets": { + "type": "object", + "properties": { + "large_image": { + "type": "string" + }, + "large_text": { + "type": "string" + }, + "small_image": { + "type": "string" + }, + "small_text": { + "type": "string" + } + }, + "additionalProperties": false }, - "name": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "guild_ids" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SecurityKey": { - "type": "object", - "properties": { - "user_id": { - "type": "string" + "secrets": { + "type": "object", + "properties": { + "join": { + "type": "string" + }, + "spectate": { + "type": "string" + }, + "match": { + "type": "string" + } + }, + "additionalProperties": false }, - "user": { - "$ref": "#/definitions/User" + "instance": { + "type": "boolean" }, - "key_id": { + "flags": { "type": "string" }, - "public_key": { + "id": { "type": "string" }, - "counter": { - "type": "integer" - }, - "name": { + "sync_id": { "type": "string" }, - "id": { + "metadata": { + "type": "object", + "properties": { + "context_uri": { + "type": "string" + }, + "album_id": { + "type": "string" + }, + "artist_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "album_id", + "artist_ids" + ] + }, + "session_id": { "type": "string" } }, "additionalProperties": false, "required": [ - "counter", - "id", - "key_id", + "flags", "name", - "public_key", - "user", - "user_id" + "session_id", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AvatarDecorationData": { - "type": "object", - "properties": { - "asset": { - "type": "string" - }, - "sku_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "expires_at": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "asset", - "expires_at", - "sku_id" + "ActivityType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 4, + 5 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "DisplayNameStyle": { + "ClientStatus": { "type": "object", "properties": { - "font_id": { - "type": "integer" + "desktop": { + "type": "string" }, - "effect_id": { - "type": "integer" + "mobile": { + "type": "string" }, - "colors": { - "type": "array", - "items": { - "type": "integer" - } + "web": { + "type": "string" + }, + "embedded": { + "type": "string" } }, "additionalProperties": false, - "required": [ - "colors", - "effect_id", - "font_id" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Collectibles": { - "type": "object", - "properties": { - "nameplate": { - "anyOf": [ - { - "$ref": "#/definitions/NameplateData" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false, - "required": [ - "nameplate" + "Status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" ], + "type": "string", "$schema": "http://json-schema.org/draft-07/schema#" }, - "NameplateData": { + "ExtendedLocationInfo": { "type": "object", "properties": { - "asset": { + "is_eu": { + "type": "boolean" + }, + "city": { "type": "string" }, - "sku_id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "region": { "type": "string" }, - "label": { + "region_code": { "type": "string" }, - "palette": { + "country_name": { "type": "string" }, - "expires_at": { - "type": [ - "null", - "integer" - ] - } - }, - "additionalProperties": false, - "required": [ - "asset", - "expires_at", - "label", - "palette", - "sku_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PrimaryGuild": { - "type": "object", - "properties": { - "identity_enabled": { - "type": [ - "null", - "boolean" - ] + "country_code": { + "type": "string" + }, + "continent_name": { + "type": "string" + }, + "continent_code": { + "type": "string" + }, + "latitude": { + "type": "integer" + }, + "longitude": { + "type": "integer" + }, + "postal": { + "type": "string" }, - "identity_guild_id": { - "type": [ - "null", - "string" - ] + "calling_code": { + "type": "string" }, - "tag": { - "type": [ - "null", - "string" - ] + "flag": { + "type": "string" }, - "badge": { - "type": [ - "null", - "string" - ] + "emoji_flag": { + "type": "string" + }, + "emoji_unicode": { + "type": "string" } }, "additionalProperties": false, "required": [ - "badge", - "identity_enabled", - "identity_guild_id", - "tag" + "calling_code", + "city", + "continent_code", + "continent_name", + "country_code", + "country_name", + "emoji_flag", + "emoji_unicode", + "flag", + "is_eu", + "latitude", + "longitude", + "postal", + "region", + "region_code" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ThreadMember": { + "Relationship": { "type": "object", "properties": { - "index": { - "type": "string" - }, - "id": { + "from_id": { "type": "string" }, - "channel": { - "$ref": "#/definitions/Channel" + "from": { + "$ref": "#/definitions/User" }, - "member_idx": { + "to_id": { "type": "string" }, - "member": { - "$ref": "#/definitions/Member" + "to": { + "$ref": "#/definitions/User" }, - "join_timestamp": { - "type": "string", - "format": "date-time" + "nickname": { + "type": "string" }, - "muted": { - "type": "boolean" + "type": { + "$ref": "#/definitions/RelationshipType" }, - "mute_config": { - "$ref": "#/definitions/ThreadMemberMuteConfig" + "id": { + "type": "string" }, - "flags": { - "$ref": "#/definitions/ThreadMemberFlags" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "channel", - "flags", + "__@annotationsKey@11707", + "from", + "from_id", "id", - "index", - "join_timestamp", - "member", - "member_idx", - "muted" + "to", + "to_id", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Member": { + "RelationshipType": { + "type": "number", + "enum": [ + 4, + 3, + 2, + 1 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ConnectedAccount": { "type": "object", "properties": { - "index": { + "external_id": { "type": "string" }, - "id": { + "user_id": { "type": "string" }, "user": { "$ref": "#/definitions/User" }, - "guild_id": { - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" + "friend_sync": { + "type": "boolean", + "default": false }, - "nick": { + "name": { "type": "string" }, - "roles": { - "type": "array", - "items": { - "$ref": "#/definitions/Role" - } - }, - "joined_at": { - "type": "string", - "format": "date-time" - }, - "premium_since": { - "type": "integer" - }, - "deaf": { - "type": "boolean" - }, - "mute": { - "type": "boolean" - }, - "pending": { - "type": "boolean" - }, - "settings": { - "$ref": "#/definitions/UserGuildSettings" - }, - "last_message_id": { - "type": "string" + "revoked": { + "type": "boolean", + "default": false }, - "joined_by": { - "type": "string" + "show_activity": { + "type": "integer", + "default": 0 }, - "avatar": { + "type": { "type": "string" }, - "banner": { - "type": "string" + "verified": { + "type": "boolean", + "default": true }, - "bio": { - "type": "string" + "visibility": { + "type": "integer", + "default": 0 }, - "theme_colors": { + "integrations": { "type": "array", "items": { - "type": "integer" - } + "type": "string" + }, + "default": [] }, - "pronouns": { - "type": "string" + "metadata_": {}, + "metadata_visibility": { + "type": "integer", + "default": 0 }, - "communication_disabled_until": { + "two_way_link": { + "type": "boolean", + "default": false + }, + "token_data": { "anyOf": [ { - "type": "string", - "format": "date-time" + "$ref": "#/definitions/ConnectedAccountTokenData" }, { "type": "null" } ] }, - "avatar_decoration_data": { - "$ref": "#/definitions/AvatarDecorationData" - }, - "display_name_styles": { - "$ref": "#/definitions/DisplayNameStyle" + "id": { + "type": "string" }, - "collectibles": { - "$ref": "#/definitions/Collectibles" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "banner", - "bio", - "communication_disabled_until", - "deaf", - "guild", - "guild_id", + "__@annotationsKey@11707", + "external_id", "id", - "index", - "joined_at", - "joined_by", - "mute", - "pending", - "roles", - "settings", - "user" + "name", + "type", + "user", + "user_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ConnectedAccountTokenData": { + "type": "object", + "properties": { + "access_token": { + "type": "string" + }, + "token_type": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "refresh_token": { + "type": "string" + }, + "expires_in": { + "type": "integer" + }, + "expires_at": { + "type": "integer" + }, + "fetched_at": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "access_token", + "fetched_at" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Guild": { + "UserSettings": { "type": "object", "properties": { - "afk_channel_id": { - "type": [ - "null", - "string" - ] - }, - "afk_channel": { - "$ref": "#/definitions/Channel" + "index": { + "type": "string" }, "afk_timeout": { - "type": "integer" + "type": "integer", + "default": 3600 }, - "bans": { - "type": "array", - "items": { - "$ref": "#/definitions/Ban" - } + "allow_accessibility_detection": { + "type": "boolean", + "default": true }, - "banner": { - "type": "string" + "animate_emoji": { + "type": "boolean", + "default": true }, - "default_message_notifications": { - "type": "integer" + "animate_stickers": { + "type": "integer", + "default": 0 }, - "description": { - "type": "string" + "contact_sync_enabled": { + "type": "boolean", + "default": false }, - "discovery_splash": { - "type": "string" + "convert_emoticons": { + "type": "boolean", + "default": false }, - "explicit_content_filter": { - "type": "integer" + "custom_status": { + "anyOf": [ + { + "$ref": "#/definitions/CustomStatus" + }, + { + "type": "null" + } + ], + "default": null }, - "features": { - "type": "array", - "items": { - "type": "string" - }, - "default": [] + "default_guilds_restricted": { + "type": "boolean", + "default": false }, - "primary_category_id": { - "type": "string" + "detect_platform_accounts": { + "type": "boolean", + "default": false }, - "icon": { - "type": "string" + "developer_mode": { + "type": "boolean", + "default": true }, - "large": { + "disable_games_tab": { + "type": "boolean", + "default": true + }, + "enable_tts_command": { "type": "boolean", "default": false }, - "max_members": { - "type": "integer" + "explicit_content_filter": { + "type": "integer", + "default": 0 }, - "max_presences": { - "type": "integer" + "friend_discovery_flags": { + "type": "integer", + "default": 0 }, - "max_video_channel_users": { - "type": "integer" + "friend_source_flags": { + "$ref": "#/definitions/FriendSourceFlags" }, - "member_count": { - "type": "integer" + "gateway_connected": { + "type": "boolean", + "default": false }, - "presence_count": { - "type": "integer" + "gif_auto_play": { + "type": "boolean", + "default": false }, - "members": { + "guild_folders": { "type": "array", "items": { - "$ref": "#/definitions/Member" - } + "$ref": "#/definitions/GuildFolder" + }, + "default": [] }, - "roles": { + "guild_positions": { "type": "array", "items": { - "$ref": "#/definitions/Role" - } + "type": "string" + }, + "default": [] }, - "channels": { - "type": "array", - "items": { - "$ref": "#/definitions/Channel" - } + "inline_attachment_media": { + "type": "boolean", + "default": true }, - "template_id": { - "type": "string" + "inline_embed_media": { + "type": "boolean", + "default": true }, - "template": { - "$ref": "#/definitions/Template" + "locale": { + "type": "string", + "default": "en-US" }, - "emojis": { - "type": "array", - "items": { - "$ref": "#/definitions/Emoji" - } + "message_display_compact": { + "type": "boolean", + "default": false }, - "stickers": { - "type": "array", - "items": { - "$ref": "#/definitions/Sticker" - } + "native_phone_integration_enabled": { + "type": "boolean", + "default": true }, - "invites": { - "type": "array", - "items": { - "$ref": "#/definitions/Invite" - } + "render_embeds": { + "type": "boolean", + "default": true }, - "voice_states": { - "type": "array", - "items": { - "$ref": "#/definitions/VoiceState" - } + "render_reactions": { + "type": "boolean", + "default": true }, - "webhooks": { + "restricted_guilds": { "type": "array", "items": { - "$ref": "#/definitions/Webhook" - } + "type": "string" + }, + "default": [] }, - "mfa_level": { - "type": "integer" + "show_current_game": { + "type": "boolean", + "default": true }, - "name": { - "type": "string" + "status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" + ], + "type": "string", + "default": "online" }, - "owner_id": { + "stream_notifications_enabled": { + "type": "boolean", + "default": false + }, + "theme": { + "enum": [ + "dark", + "light" + ], + "type": "string", + "default": "dark" + }, + "timezone_offset": { + "type": "integer", + "default": 0 + }, + "view_nsfw_guilds": { + "type": "boolean", + "default": true + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "additionalProperties": false, + "required": [ + "__@annotationsKey@11707", + "afk_timeout", + "allow_accessibility_detection", + "animate_emoji", + "animate_stickers", + "contact_sync_enabled", + "convert_emoticons", + "custom_status", + "default_guilds_restricted", + "detect_platform_accounts", + "developer_mode", + "disable_games_tab", + "enable_tts_command", + "explicit_content_filter", + "friend_discovery_flags", + "friend_source_flags", + "gateway_connected", + "gif_auto_play", + "guild_folders", + "guild_positions", + "index", + "inline_attachment_media", + "inline_embed_media", + "locale", + "message_display_compact", + "native_phone_integration_enabled", + "render_embeds", + "render_reactions", + "restricted_guilds", + "show_current_game", + "status", + "stream_notifications_enabled", + "theme", + "timezone_offset", + "view_nsfw_guilds" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "CustomStatus": { + "type": "object", + "properties": { + "emoji_id": { "type": "string" }, - "owner": { - "$ref": "#/definitions/User" - }, - "preferred_locale": { + "emoji_name": { "type": "string" }, - "premium_subscription_count": { - "type": "integer" - }, - "premium_tier": { + "expires_at": { "type": "integer" }, - "public_updates_channel_id": { + "text": { + "type": "string" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "FriendSourceFlags": { + "type": "object", + "properties": { + "all": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "all" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildFolder": { + "type": "object", + "properties": { + "color": { "type": [ "null", - "string" + "integer" ] }, - "public_updates_channel": { - "$ref": "#/definitions/Channel" + "guild_ids": { + "type": "array", + "items": { + "type": "string" + } }, - "rules_channel_id": { + "id": { "type": [ "null", - "string" + "integer" ] }, - "rules_channel": { - "type": "string" - }, - "region": { - "type": "string" - }, - "splash": { - "type": "string" - }, - "system_channel_id": { + "name": { "type": [ "null", "string" ] + } + }, + "additionalProperties": false, + "required": [ + "guild_ids" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "SecurityKey": { + "type": "object", + "properties": { + "user_id": { + "type": "string" }, - "system_channel": { - "$ref": "#/definitions/Channel" + "user": { + "$ref": "#/definitions/User" }, - "system_channel_flags": { - "type": "integer" + "key_id": { + "type": "string" }, - "unavailable": { - "type": "boolean", - "default": false + "public_key": { + "type": "string" }, - "verification_level": { + "counter": { "type": "integer" }, - "welcome_screen": { - "$ref": "#/definitions/GuildWelcomeScreen", - "description": "DEPRECATED: Look at the new Guild onboarding screens." - }, - "widget_channel_id": { + "name": { "type": "string" }, - "widget_channel": { - "$ref": "#/definitions/Channel" - }, - "widget_enabled": { - "type": "boolean", - "default": true - }, - "nsfw_level": { - "type": "integer" + "id": { + "type": "string" }, - "nsfw": { - "type": "boolean", - "default": false + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "additionalProperties": false, + "required": [ + "__@annotationsKey@11707", + "counter", + "id", + "key_id", + "name", + "public_key", + "user", + "user_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AvatarDecorationData": { + "type": "object", + "properties": { + "asset": { + "type": "string" }, - "parent": { + "sku_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "permissions": { + "expires_at": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false, + "required": [ + "asset", + "expires_at", + "sku_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "DisplayNameStyle": { + "type": "object", + "properties": { + "font_id": { "type": "integer" }, - "premium_progress_bar_enabled": { - "type": "boolean", - "default": false + "effect_id": { + "type": "integer" }, - "channel_ordering": { + "colors": { "type": "array", "items": { - "type": "string" + "type": "integer" } + } + }, + "additionalProperties": false, + "required": [ + "colors", + "effect_id", + "font_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "Collectibles": { + "type": "object", + "properties": { + "nameplate": { + "anyOf": [ + { + "$ref": "#/definitions/NameplateData" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "required": [ + "nameplate" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "NameplateData": { + "type": "object", + "properties": { + "asset": { + "type": "string" }, - "discovery_weight": { - "type": "integer", - "default": 0 + "sku_id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" }, - "discovery_excluded": { - "type": "boolean", - "default": false + "label": { + "type": "string" + }, + "palette": { + "type": "string" + }, + "expires_at": { + "type": [ + "null", + "integer" + ] + } + }, + "additionalProperties": false, + "required": [ + "asset", + "expires_at", + "label", + "palette", + "sku_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "PrimaryGuild": { + "type": "object", + "properties": { + "identity_enabled": { + "type": [ + "null", + "boolean" + ] + }, + "identity_guild_id": { + "type": [ + "null", + "string" + ] + }, + "tag": { + "type": [ + "null", + "string" + ] }, - "id": { - "type": "string" + "badge": { + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, "required": [ - "bans", - "channel_ordering", - "channels", - "discovery_excluded", - "discovery_weight", - "emojis", - "features", - "id", - "invites", - "members", - "name", - "nsfw", - "premium_progress_bar_enabled", - "public_updates_channel_id", - "roles", - "stickers", - "template", - "unavailable", - "voice_states", - "webhooks", - "welcome_screen", - "widget_enabled" + "badge", + "identity_enabled", + "identity_guild_id", + "tag" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Ban": { + "ThreadMember": { "type": "object", "properties": { - "user_id": { + "index": { "type": "string" }, - "user": { - "$ref": "#/definitions/User" - }, - "guild_id": { + "id": { "type": "string" }, - "guild": { - "$ref": "#/definitions/Guild" + "channel": { + "$ref": "#/definitions/Channel" }, - "executor_id": { + "member_idx": { "type": "string" }, - "executor": { - "$ref": "#/definitions/User" + "member": { + "$ref": "#/definitions/Member" }, - "ip": { - "type": "string" + "join_timestamp": { + "type": "string", + "format": "date-time" }, - "reason": { - "type": "string" + "muted": { + "type": "boolean" }, - "id": { - "type": "string" + "mute_config": { + "$ref": "#/definitions/ThreadMemberMuteConfig" + }, + "flags": { + "$ref": "#/definitions/ThreadMemberFlags" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "executor", - "executor_id", - "guild", - "guild_id", + "__@annotationsKey@11707", + "channel", + "flags", "id", - "user", - "user_id" + "index", + "join_timestamp", + "member", + "member_idx", + "muted" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Role": { + "Member": { "type": "object", "properties": { + "index": { + "type": "string" + }, + "id": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/User" + }, "guild_id": { "type": "string" }, "guild": { "$ref": "#/definitions/Guild" }, - "color": { + "nick": { + "type": "string" + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" + } + }, + "joined_at": { + "type": "string", + "format": "date-time" + }, + "premium_since": { "type": "integer" }, - "hoist": { + "deaf": { "type": "boolean" }, - "managed": { + "mute": { "type": "boolean" }, - "mentionable": { + "pending": { "type": "boolean" }, - "name": { + "settings": { + "$ref": "#/definitions/UserGuildSettings" + }, + "last_message_id": { "type": "string" }, - "permissions": { + "joined_by": { "type": "string" }, - "position": { - "type": "integer" + "avatar": { + "type": "string" }, - "icon": { + "banner": { "type": "string" }, - "unicode_emoji": { + "bio": { "type": "string" }, - "tags": { - "type": "object", - "properties": { - "bot_id": { - "type": "string" - }, - "integration_id": { - "type": "string" + "theme_colors": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pronouns": { + "type": "string" + }, + "communication_disabled_until": { + "anyOf": [ + { + "type": "string", + "format": "date-time" }, - "premium_subscriber": { - "type": "boolean" + { + "type": "null" } - }, - "additionalProperties": false + ] }, - "flags": { - "type": "integer" + "avatar_decoration_data": { + "$ref": "#/definitions/AvatarDecorationData" }, - "colors": { - "$ref": "#/definitions/RoleColors" + "display_name_styles": { + "$ref": "#/definitions/DisplayNameStyle" }, - "id": { - "type": "string" + "collectibles": { + "$ref": "#/definitions/Collectibles" + }, + "flags": { + "type": "integer", + "default": 0 + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "color", - "colors", + "__@annotationsKey@11707", + "banner", + "bio", + "communication_disabled_until", + "deaf", "flags", "guild", "guild_id", - "hoist", "id", - "managed", - "mentionable", - "name", - "permissions", - "position" + "index", + "joined_at", + "joined_by", + "mute", + "pending", + "roles", + "settings", + "user" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "RoleColors": { + "Guild": { "type": "object", "properties": { - "primary_color": { - "type": "integer" + "afk_channel_id": { + "type": [ + "null", + "string" + ] }, - "secondary_color": { - "type": "integer" + "afk_channel": { + "$ref": "#/definitions/Channel" }, - "tertiary_color": { + "afk_timeout": { "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "primary_color" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Template": { - "type": "object", - "properties": { - "code": { - "type": "string" }, - "name": { + "bans": { + "type": "array", + "items": { + "$ref": "#/definitions/Ban" + } + }, + "banner": { "type": "string" }, + "default_message_notifications": { + "type": "integer" + }, "description": { "type": "string" }, - "usage_count": { + "discovery_splash": { + "type": "string" + }, + "explicit_content_filter": { "type": "integer" }, - "creator_id": { + "features": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "primary_category_id": { "type": "string" }, - "creator": { - "$ref": "#/definitions/User" + "icon": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time" + "large": { + "type": "boolean", + "default": false }, - "updated_at": { - "type": "string", - "format": "date-time" + "max_members": { + "type": "integer" }, - "source_guild_id": { - "type": "string" + "max_presences": { + "type": "integer" }, - "source_guild": { - "$ref": "#/definitions/Guild" + "max_video_channel_users": { + "type": "integer" }, - "serialized_source_guild": { - "$ref": "#/definitions/Guild" + "member_count": { + "type": "integer" }, - "id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "code", - "created_at", - "creator", - "creator_id", - "id", - "name", - "serialized_source_guild", - "source_guild", - "source_guild_id", - "updated_at" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Emoji": { - "type": "object", - "properties": { - "animated": { - "type": "boolean" + "presence_count": { + "type": "integer" }, - "available": { - "type": "boolean" + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/Member" + } }, - "guild_id": { - "type": "string" + "roles": { + "type": "array", + "items": { + "$ref": "#/definitions/Role" + } }, - "guild": { - "$ref": "#/definitions/Guild" + "channels": { + "type": "array", + "items": { + "$ref": "#/definitions/Channel" + } }, - "user_id": { + "template_id": { "type": "string" }, - "user": { - "$ref": "#/definitions/User" + "template": { + "$ref": "#/definitions/Template" }, - "managed": { - "type": "boolean" + "emojis": { + "type": "array", + "items": { + "$ref": "#/definitions/Emoji" + } }, - "name": { - "type": "string" + "stickers": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" + } }, - "require_colons": { - "type": "boolean" + "invites": { + "type": "array", + "items": { + "$ref": "#/definitions/Invite" + } }, - "roles": { + "voice_states": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/VoiceState" } }, - "groups": { + "webhooks": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/Webhook" } }, - "id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "animated", - "available", - "groups", - "guild", - "guild_id", - "id", - "managed", - "name", - "require_colons", - "roles", - "user", - "user_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Sticker": { - "type": "object", - "properties": { + "mfa_level": { + "type": "integer" + }, "name": { "type": "string" }, - "description": { + "owner_id": { "type": "string" }, - "available": { - "type": "boolean" + "owner": { + "$ref": "#/definitions/User" }, - "tags": { + "preferred_locale": { "type": "string" }, - "pack_id": { - "type": "string" + "premium_subscription_count": { + "type": "integer" }, - "pack": { - "$ref": "#/definitions/StickerPack" + "premium_tier": { + "type": "integer" }, - "guild_id": { + "public_updates_channel_id": { + "type": [ + "null", + "string" + ] + }, + "public_updates_channel": { + "$ref": "#/definitions/Channel" + }, + "rules_channel_id": { + "type": [ + "null", + "string" + ] + }, + "rules_channel": { "type": "string" }, - "guild": { - "$ref": "#/definitions/Guild" + "region": { + "type": "string" }, - "user_id": { + "splash": { "type": "string" }, - "user": { - "$ref": "#/definitions/User" + "system_channel_id": { + "type": [ + "null", + "string" + ] }, - "type": { - "$ref": "#/definitions/StickerType" + "system_channel": { + "$ref": "#/definitions/Channel" }, - "format_type": { - "$ref": "#/definitions/StickerFormatType" + "system_channel_flags": { + "type": "integer" }, - "id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "format_type", - "id", - "name", - "pack", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "StickerPack": { - "type": "object", - "properties": { - "name": { - "type": "string" + "unavailable": { + "type": "boolean", + "default": false }, - "description": { + "verification_level": { + "type": "integer" + }, + "welcome_screen": { + "$ref": "#/definitions/GuildWelcomeScreen", + "description": "DEPRECATED: Look at the new Guild onboarding screens." + }, + "widget_channel_id": { "type": "string" }, - "banner_asset_id": { + "widget_channel": { + "$ref": "#/definitions/Channel" + }, + "widget_enabled": { + "type": "boolean", + "default": true + }, + "nsfw_level": { + "type": "integer" + }, + "nsfw": { + "type": "boolean", + "default": false + }, + "parent": { "type": "string" }, - "stickers": { + "permissions": { + "type": "integer" + }, + "premium_progress_bar_enabled": { + "type": "boolean", + "default": false + }, + "channel_ordering": { "type": "array", "items": { - "$ref": "#/definitions/Sticker" + "type": "string" } }, - "cover_sticker_id": { - "type": "string" + "discovery_weight": { + "type": "integer", + "default": 0 }, - "cover_sticker": { - "$ref": "#/definitions/Sticker" + "discovery_excluded": { + "type": "boolean", + "default": false }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", + "bans", + "channel_ordering", + "channels", + "discovery_excluded", + "discovery_weight", + "emojis", + "features", "id", + "invites", + "members", "name", - "stickers" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "StickerType": { - "type": "number", - "enum": [ - 1, - 2 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "StickerFormatType": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4 + "nsfw", + "premium_progress_bar_enabled", + "public_updates_channel_id", + "roles", + "stickers", + "template", + "unavailable", + "voice_states", + "webhooks", + "welcome_screen", + "widget_enabled" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Invite": { + "Ban": { "type": "object", "properties": { - "code": { + "user_id": { "type": "string" }, - "temporary": { - "type": "boolean" - }, - "uses": { - "type": "integer" - }, - "max_uses": { - "type": "integer" - }, - "max_age": { - "type": "integer" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "expires_at": { - "type": "string", - "format": "date-time" + "user": { + "$ref": "#/definitions/User" }, "guild_id": { "type": "string" @@ -13070,53 +10475,45 @@ "guild": { "$ref": "#/definitions/Guild" }, - "channel_id": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" - }, - "inviter_id": { + "executor_id": { "type": "string" }, - "inviter": { + "executor": { "$ref": "#/definitions/User" }, - "target_user_id": { + "ip": { "type": "string" }, - "target_user": { + "reason": { "type": "string" }, - "target_user_type": { - "type": "integer" - }, - "vanity_url": { - "type": "boolean" + "id": { + "type": "string" }, - "flags": { - "type": "integer" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "channel", - "channel_id", - "code", - "created_at", - "flags", + "__@annotationsKey@11707", + "executor", + "executor_id", "guild", "guild_id", - "inviter", - "max_age", - "max_uses", - "target_user_id", - "temporary", - "uses" + "id", + "user", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceState": { + "Role": { "type": "object", "properties": { "guild_id": { @@ -13125,114 +10522,132 @@ "guild": { "$ref": "#/definitions/Guild" }, - "channel_id": { - "type": "string" - }, - "channel": { - "$ref": "#/definitions/Channel" + "color": { + "type": "integer" }, - "user_id": { - "type": "string" + "hoist": { + "type": "boolean" }, - "user": { - "$ref": "#/definitions/User" + "managed": { + "type": "boolean" }, - "member": { - "$ref": "#/definitions/Member" + "mentionable": { + "type": "boolean" }, - "session_id": { + "name": { "type": "string" }, - "token": { + "permissions": { "type": "string" }, - "deaf": { - "type": "boolean" - }, - "mute": { - "type": "boolean" - }, - "self_deaf": { - "type": "boolean" + "position": { + "type": "integer" }, - "self_mute": { - "type": "boolean" + "icon": { + "type": "string" }, - "self_stream": { - "type": "boolean" + "unicode_emoji": { + "type": "string" }, - "self_video": { - "type": "boolean" + "tags": { + "type": "object", + "properties": { + "bot_id": { + "type": "string" + }, + "integration_id": { + "type": "string" + }, + "premium_subscriber": { + "type": "boolean" + } + }, + "additionalProperties": false }, - "suppress": { - "type": "boolean" + "flags": { + "type": "integer" }, - "request_to_speak_timestamp": { - "type": "string", - "format": "date-time" + "colors": { + "$ref": "#/definitions/RoleColors" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "channel", - "channel_id", - "deaf", + "__@annotationsKey@11707", + "color", + "colors", + "flags", + "guild", "guild_id", + "hoist", "id", - "member", - "mute", - "self_deaf", - "self_mute", - "self_video", - "session_id", - "suppress", - "token", - "user", - "user_id" + "managed", + "mentionable", + "name", + "permissions", + "position" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Webhook": { + "RoleColors": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/WebhookType" - }, - "name": { - "type": "string" + "primary_color": { + "type": "integer" }, - "avatar": { - "type": "string" + "secondary_color": { + "type": "integer" }, - "token": { + "tertiary_color": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "primary_color" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "Template": { + "type": "object", + "properties": { + "code": { "type": "string" }, - "guild_id": { + "name": { "type": "string" }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "channel_id": { + "description": { "type": "string" }, - "channel": { - "$ref": "#/definitions/Channel" + "usage_count": { + "type": "integer" }, - "application_id": { + "creator_id": { "type": "string" }, - "application": { - "$ref": "#/definitions/Application" + "creator": { + "$ref": "#/definitions/User" }, - "user_id": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time" }, - "user": { - "$ref": "#/definitions/User" + "updated_at": { + "type": "string", + "format": "date-time" }, "source_guild_id": { "type": "string" @@ -13240,281 +10655,221 @@ "source_guild": { "$ref": "#/definitions/Guild" }, - "source_channel_id": { - "type": "string" - }, - "source_channel": { - "$ref": "#/definitions/Channel" - }, - "url": { - "type": "string" + "serialized_source_guild": { + "$ref": "#/definitions/Guild" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "application", - "application_id", - "avatar", - "channel", - "channel_id", + "__@annotationsKey@11707", + "code", + "created_at", + "creator", + "creator_id", "id", "name", - "source_channel", - "source_channel_id", - "type", - "url", - "user", - "user_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "WebhookType": { - "type": "number", - "enum": [ - 1, - 2, - 3 + "serialized_source_guild", + "source_guild", + "source_guild_id", + "updated_at" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Application": { + "Emoji": { "type": "object", "properties": { - "name": { - "type": "string" + "animated": { + "type": "boolean" }, - "icon": { - "type": "string" + "available": { + "type": "boolean" }, - "description": { + "guild_id": { "type": "string" }, - "summary": { - "type": "string", - "default": "" - }, - "type": { - "type": "object", - "properties": {}, - "additionalProperties": true + "guild": { + "$ref": "#/definitions/Guild" }, - "hook": { - "type": "boolean", - "default": true + "user_id": { + "type": "string" }, - "bot_public": { - "type": "boolean", - "default": true + "user": { + "$ref": "#/definitions/User" }, - "bot_require_code_grant": { - "type": "boolean", - "default": false + "managed": { + "type": "boolean" }, - "verify_key": { + "name": { "type": "string" }, - "owner": { - "$ref": "#/definitions/User" - }, - "flags": { - "type": "integer", - "default": 0 + "require_colons": { + "type": "boolean" }, - "redirect_uris": { + "roles": { "type": "array", "items": { "type": "string" - }, - "default": [] - }, - "rpc_application_state": { - "type": "integer", - "default": 0 - }, - "store_application_state": { - "type": "integer", - "default": 1 - }, - "verification_state": { - "type": "integer", - "default": 1 - }, - "interactions_endpoint_url": { - "type": "string" - }, - "integration_public": { - "type": "boolean", - "default": true - }, - "integration_require_code_grant": { - "type": "boolean", - "default": false - }, - "discoverability_state": { - "type": "integer", - "default": 1 - }, - "discovery_eligibility_flags": { - "type": "integer", - "default": 2240 - }, - "bot": { - "$ref": "#/definitions/User" + } }, - "tags": { + "groups": { "type": "array", "items": { "type": "string" } }, - "cover_image": { + "id": { "type": "string" }, - "install_params": { + "__@annotationsKey@11707": { "type": "object", - "properties": { - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "permissions": { + "additionalProperties": { + "type": "array", + "items": { "type": "string" } - }, - "additionalProperties": false, - "required": [ - "permissions", - "scopes" - ] - }, - "terms_of_service_url": { - "type": "string" - }, - "privacy_policy_url": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "guild": { - "$ref": "#/definitions/Guild" - }, - "custom_install_url": { - "type": "string" - }, - "team": { - "$ref": "#/definitions/Team" - }, - "id": { - "type": "string" + } } }, "additionalProperties": false, "required": [ - "description", - "discoverability_state", - "discovery_eligibility_flags", - "flags", - "hook", + "__@annotationsKey@11707", + "animated", + "available", + "groups", + "guild", + "guild_id", "id", - "integration_public", - "integration_require_code_grant", + "managed", "name", - "owner", - "redirect_uris", - "rpc_application_state", - "store_application_state", - "summary", - "verification_state", - "verify_key" + "require_colons", + "roles", + "user", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Team": { + "Sticker": { "type": "object", "properties": { - "icon": { + "name": { "type": "string" }, - "members": { - "type": "array", - "items": { - "$ref": "#/definitions/TeamMember" - } + "description": { + "type": "string" }, - "name": { + "available": { + "type": "boolean" + }, + "tags": { + "type": "string" + }, + "pack_id": { "type": "string" }, - "owner_user_id": { + "pack": { + "$ref": "#/definitions/StickerPack" + }, + "guild_id": { + "type": "string" + }, + "guild": { + "$ref": "#/definitions/Guild" + }, + "user_id": { "type": "string" }, - "owner_user": { + "user": { "$ref": "#/definitions/User" }, + "type": { + "$ref": "#/definitions/StickerType" + }, + "format_type": { + "$ref": "#/definitions/StickerFormatType" + }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", + "format_type", "id", - "members", "name", - "owner_user", - "owner_user_id" + "pack", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TeamMember": { + "StickerPack": { "type": "object", "properties": { - "membership_state": { - "$ref": "#/definitions/TeamMemberState" - }, - "permissions": { - "type": "array", - "items": { - "type": "string" - } + "name": { + "type": "string" }, - "role": { - "$ref": "#/definitions/TeamMemberRole" + "description": { + "type": "string" }, - "team_id": { + "banner_asset_id": { "type": "string" }, - "team": { - "$ref": "#/definitions/Team" + "stickers": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" + } }, - "user_id": { + "cover_sticker_id": { "type": "string" }, - "user": { - "$ref": "#/definitions/User" + "cover_sticker": { + "$ref": "#/definitions/Sticker" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", "id", - "membership_state", - "permissions", - "role", - "team", - "team_id", - "user", - "user_id" + "name", + "stickers" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TeamMemberState": { + "StickerType": { "type": "number", "enum": [ 1, @@ -13522,3259 +10877,3497 @@ ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TeamMemberRole": { - "type": "string", + "StickerFormatType": { + "type": "number", "enum": [ - "admin", - "developer", - "read_only" + 1, + 2, + 3, + 4 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildWelcomeScreen": { + "Invite": { "type": "object", "properties": { - "enabled": { + "code": { + "type": "string" + }, + "temporary": { "type": "boolean" }, - "description": { + "uses": { + "type": "integer" + }, + "max_uses": { + "type": "integer" + }, + "max_age": { + "type": "integer" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "guild_id": { "type": "string" }, - "welcome_channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "emoji_id": { - "type": "string" - }, - "emoji_name": { - "type": "string" - }, - "channel_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "channel_id", - "description" - ] + "guild": { + "$ref": "#/definitions/Guild" + }, + "channel_id": { + "type": "string" + }, + "channel": { + "$ref": "#/definitions/Channel" + }, + "inviter_id": { + "type": "string" + }, + "inviter": { + "$ref": "#/definitions/User" + }, + "target_user_id": { + "type": "string" + }, + "target_user": { + "type": "string" + }, + "target_user_type": { + "type": "integer" + }, + "vanity_url": { + "type": "boolean" + }, + "flags": { + "type": "integer" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } } } }, "additionalProperties": false, "required": [ - "description", - "enabled", - "welcome_channels" + "__@annotationsKey@11707", + "channel", + "channel_id", + "code", + "created_at", + "flags", + "guild", + "guild_id", + "inviter", + "max_age", + "max_uses", + "target_user_id", + "temporary", + "uses" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserGuildSettings": { + "VoiceState": { "type": "object", "properties": { - "channel_overrides": { - "anyOf": [ - { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/ChannelOverride" - } - }, - { - "type": "null" - } - ] + "guild_id": { + "type": "string" }, - "message_notifications": { - "type": "integer" + "guild": { + "$ref": "#/definitions/Guild" }, - "mobile_push": { - "type": "boolean" + "channel_id": { + "type": "string" }, - "mute_config": { - "anyOf": [ - { - "$ref": "#/definitions/MuteConfig" - }, - { - "type": "null" - } - ] + "channel": { + "$ref": "#/definitions/Channel" }, - "muted": { - "type": "boolean" + "user_id": { + "type": "string" }, - "suppress_everyone": { + "user": { + "$ref": "#/definitions/User" + }, + "member": { + "$ref": "#/definitions/Member" + }, + "session_id": { + "type": "string" + }, + "token": { + "type": "string" + }, + "deaf": { "type": "boolean" }, - "suppress_roles": { + "mute": { "type": "boolean" }, - "version": { - "type": "integer" + "self_deaf": { + "type": "boolean" }, - "guild_id": { - "type": [ - "null", - "string" - ] + "self_mute": { + "type": "boolean" }, - "flags": { - "type": "integer" + "self_stream": { + "type": "boolean" }, - "mute_scheduled_events": { + "self_video": { "type": "boolean" }, - "hide_muted_channels": { + "suppress": { "type": "boolean" }, - "notify_highlights": { - "type": "integer", - "const": 0 + "request_to_speak_timestamp": { + "type": "string", + "format": "date-time" + }, + "id": { + "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "channel_overrides", - "flags", + "__@annotationsKey@11707", + "channel", + "channel_id", + "deaf", "guild_id", - "hide_muted_channels", - "message_notifications", - "mobile_push", - "mute_config", - "mute_scheduled_events", - "muted", - "notify_highlights", - "suppress_everyone", - "suppress_roles", - "version" + "id", + "member", + "mute", + "self_deaf", + "self_mute", + "self_video", + "session_id", + "suppress", + "token", + "user", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ChannelOverride": { + "Webhook": { "type": "object", "properties": { - "message_notifications": { - "type": "integer" + "type": { + "$ref": "#/definitions/WebhookType" }, - "mute_config": { - "$ref": "#/definitions/MuteConfig" + "name": { + "type": "string" }, - "muted": { - "type": "boolean" + "avatar": { + "type": "string" + }, + "token": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "guild": { + "$ref": "#/definitions/Guild" }, "channel_id": { - "type": [ - "null", - "string" - ] - } - }, - "additionalProperties": false, - "required": [ - "channel_id", - "message_notifications", - "mute_config", - "muted" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MuteConfig": { - "type": "object", - "properties": { - "end_time": { - "type": "integer" + "type": "string" }, - "selected_time_window": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "end_time", - "selected_time_window" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ThreadMemberMuteConfig": { - "type": "object", - "properties": { - "end_time": { - "type": "string", - "format": "date-time" + "channel": { + "$ref": "#/definitions/Channel" }, - "selected_time_window": { - "type": "integer" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ThreadMemberFlags": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 4, - 8 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ChannelPermissionOverwrite": { - "type": "object", - "properties": { - "allow": { + "application_id": { "type": "string" }, - "deny": { + "application": { + "$ref": "#/definitions/Application" + }, + "user_id": { + "type": "string" + }, + "user": { + "$ref": "#/definitions/User" + }, + "source_guild_id": { + "type": "string" + }, + "source_guild": { + "$ref": "#/definitions/Guild" + }, + "source_channel_id": { + "type": "string" + }, + "source_channel": { + "$ref": "#/definitions/Channel" + }, + "url": { "type": "string" }, "id": { "type": "string" }, - "type": { - "$ref": "#/definitions/ChannelPermissionOverwriteType" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "allow", - "deny", + "__@annotationsKey@11707", + "application", + "application_id", + "avatar", + "channel", + "channel_id", "id", - "type" + "name", + "source_channel", + "source_channel_id", + "type", + "url", + "user", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ChannelPermissionOverwriteType": { + "WebhookType": { "type": "number", "enum": [ - 0, 1, - 2 + 2, + 3 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ReadState": { + "Application": { "type": "object", "properties": { - "channel_id": { + "name": { "type": "string" }, - "channel": { - "$ref": "#/definitions/Channel" + "icon": { + "type": "string" }, - "user_id": { + "description": { "type": "string" }, - "user": { - "$ref": "#/definitions/User" + "summary": { + "type": "string", + "default": "" }, - "last_message_id": { - "type": "string" + "type": { + "type": "object", + "properties": {}, + "additionalProperties": true }, - "public_ack": { + "hook": { + "type": "boolean", + "default": true + }, + "bot_public": { + "type": "boolean", + "default": true + }, + "bot_require_code_grant": { + "type": "boolean", + "default": false + }, + "verify_key": { "type": "string" }, - "notifications_cursor": { + "owner": { + "$ref": "#/definitions/User" + }, + "flags": { + "type": "integer", + "default": 0 + }, + "redirect_uris": { + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "rpc_application_state": { + "type": "integer", + "default": 0 + }, + "store_application_state": { + "type": "integer", + "default": 1 + }, + "verification_state": { + "type": "integer", + "default": 1 + }, + "interactions_endpoint_url": { "type": "string" }, - "last_pin_timestamp": { - "type": "string", - "format": "date-time" + "integration_public": { + "type": "boolean", + "default": true + }, + "integration_require_code_grant": { + "type": "boolean", + "default": false }, - "mention_count": { - "type": "integer" + "discoverability_state": { + "type": "integer", + "default": 1 }, - "manual": { - "type": "boolean" + "discovery_eligibility_flags": { + "type": "integer", + "default": 2240 }, - "id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "channel", - "channel_id", - "id", - "last_message_id", - "manual", - "mention_count", - "notifications_cursor", - "public_ack", - "user", - "user_id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ThreadMetadata": { - "type": "object", - "properties": { - "archived": { - "type": "boolean" + "bot": { + "$ref": "#/definitions/User" }, - "auto_archive_duration": { - "type": "integer" + "tags": { + "type": "array", + "items": { + "type": "string" + } }, - "archive_timestamp": { + "cover_image": { "type": "string" }, - "locked": { - "type": "boolean" - }, - "invitable": { - "type": "boolean" + "install_params": { + "type": "object", + "properties": { + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "permissions": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "permissions", + "scopes" + ] }, - "create_timestamp": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "archive_timestamp", - "archived", - "auto_archive_duration", - "create_timestamp", - "locked" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Tag": { - "type": "object", - "properties": { - "channel_id": { + "terms_of_service_url": { "type": "string" }, - "channel": { - "$ref": "#/definitions/Channel" + "privacy_policy_url": { + "type": "string" }, - "name": { + "guild_id": { "type": "string" }, - "moderated": { - "type": "boolean", - "default": false + "guild": { + "$ref": "#/definitions/Guild" }, - "emoji_id": { + "custom_install_url": { "type": "string" }, - "emoji_name": { - "type": "string" + "team": { + "$ref": "#/definitions/Team" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "channel", - "channel_id", + "__@annotationsKey@11707", + "description", + "discoverability_state", + "discovery_eligibility_flags", + "flags", + "hook", "id", - "moderated", - "name" + "integration_public", + "integration_require_code_grant", + "name", + "owner", + "redirect_uris", + "rpc_application_state", + "store_application_state", + "summary", + "verification_state", + "verify_key" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Attachment": { + "Team": { "type": "object", "properties": { - "filename": { - "type": "string" - }, - "size": { - "type": "integer" - }, - "url": { - "type": "string" - }, - "proxy_url": { + "icon": { "type": "string" }, - "height": { - "type": "integer" - }, - "width": { - "type": "integer" + "members": { + "type": "array", + "items": { + "$ref": "#/definitions/TeamMember" + } }, - "content_type": { + "name": { "type": "string" }, - "message_id": { + "owner_user_id": { "type": "string" }, - "message": { - "$ref": "#/definitions/Message" + "owner_user": { + "$ref": "#/definitions/User" }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "filename", + "__@annotationsKey@11707", "id", - "message", - "message_id", - "proxy_url", - "size", - "url" + "members", + "name", + "owner_user", + "owner_user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Embed": { + "TeamMember": { "type": "object", "properties": { - "title": { - "type": "string" + "membership_state": { + "$ref": "#/definitions/TeamMemberState" }, - "type": { - "enum": [ - "article", - "auto_moderation_message", - "gifv", - "image", - "link", - "rich", - "video" - ], - "type": "string" + "permissions": { + "type": "array", + "items": { + "type": "string" + } }, - "description": { - "type": "string" + "role": { + "$ref": "#/definitions/TeamMemberRole" }, - "url": { + "team_id": { "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" - }, - "color": { - "type": "integer" - }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" - ] - }, - "image": { - "$ref": "#/definitions/EmbedImage" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage" + "team": { + "$ref": "#/definitions/Team" }, - "video": { - "$ref": "#/definitions/EmbedImage" + "user_id": { + "type": "string" }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "additionalProperties": false + "user": { + "$ref": "#/definitions/User" }, - "author": { + "id": { + "type": "string" + }, + "__@annotationsKey@11707": { "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { + "additionalProperties": { + "type": "array", + "items": { "type": "string" } - }, - "additionalProperties": false + } + } + }, + "additionalProperties": false, + "required": [ + "__@annotationsKey@11707", + "id", + "membership_state", + "permissions", + "role", + "team", + "team_id", + "user", + "user_id" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TeamMemberState": { + "type": "number", + "enum": [ + 1, + 2 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TeamMemberRole": { + "type": "string", + "enum": [ + "admin", + "developer", + "read_only" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildWelcomeScreen": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" }, - "fields": { + "description": { + "type": "string" + }, + "welcome_channels": { "type": "array", "items": { "type": "object", "properties": { - "name": { + "description": { "type": "string" }, - "value": { + "emoji_id": { "type": "string" }, - "inline": { - "type": "boolean" + "emoji_name": { + "type": "string" + }, + "channel_id": { + "type": "string" } }, "additionalProperties": false, "required": [ - "name", - "value" + "channel_id", + "description" ] } } }, "additionalProperties": false, + "required": [ + "description", + "enabled", + "welcome_channels" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmbedImage": { + "UserGuildSettings": { "type": "object", "properties": { - "url": { - "type": "string" - }, - "proxy_url": { - "type": "string" + "channel_overrides": { + "anyOf": [ + { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/ChannelOverride" + } + }, + { + "type": "null" + } + ] }, - "height": { + "message_notifications": { "type": "integer" }, - "width": { + "mobile_push": { + "type": "boolean" + }, + "mute_config": { + "anyOf": [ + { + "$ref": "#/definitions/MuteConfig" + }, + { + "type": "null" + } + ] + }, + "muted": { + "type": "boolean" + }, + "suppress_everyone": { + "type": "boolean" + }, + "suppress_roles": { + "type": "boolean" + }, + "version": { "type": "integer" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Reaction": { - "type": "object", - "properties": { - "count": { + }, + "guild_id": { + "type": [ + "null", + "string" + ] + }, + "flags": { "type": "integer" }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" + "mute_scheduled_events": { + "type": "boolean" }, - "user_ids": { - "type": "array", - "items": { - "type": "string" - } + "hide_muted_channels": { + "type": "boolean" + }, + "notify_highlights": { + "type": "integer", + "const": 0 } }, "additionalProperties": false, "required": [ - "count", - "emoji", - "user_ids" + "channel_overrides", + "flags", + "guild_id", + "hide_muted_channels", + "message_notifications", + "mobile_push", + "mute_config", + "mute_scheduled_events", + "muted", + "notify_highlights", + "suppress_everyone", + "suppress_roles", + "version" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PartialEmoji": { + "ChannelOverride": { "type": "object", "properties": { - "id": { - "type": "string" + "message_notifications": { + "type": "integer" }, - "name": { - "type": "string" + "mute_config": { + "$ref": "#/definitions/MuteConfig" }, - "animated": { + "muted": { "type": "boolean" + }, + "channel_id": { + "type": [ + "null", + "string" + ] } }, "additionalProperties": false, "required": [ - "name" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MessageType": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 127, - 128, - 129, - 130, - 131, - 132, - 255 + "channel_id", + "message_notifications", + "mute_config", + "muted" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ActionRowComponent": { + "MuteConfig": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.ActionRow" - }, - "components": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/definitions/ButtonComponent" - }, - { - "$ref": "#/definitions/SelectMenuComponent" - }, - { - "$ref": "#/definitions/StringSelectMenuComponent" - }, - { - "$ref": "#/definitions/TextInputComponent" - } - ] - } + "end_time": { + "type": "integer" + }, + "selected_time_window": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "components", - "type" + "end_time", + "selected_time_window" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageComponentType.ActionRow": { + "ThreadMemberMuteConfig": { + "type": "object", + "properties": { + "end_time": { + "type": "string", + "format": "date-time" + }, + "selected_time_window": { + "type": "integer" + } + }, + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ThreadMemberFlags": { "type": "number", - "const": 1, + "enum": [ + 0, + 1, + 2, + 4, + 8 + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ButtonComponent": { + "ChannelPermissionOverwrite": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.Button" - }, - "style": { - "$ref": "#/definitions/ButtonStyle" - }, - "label": { - "type": "string" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - }, - "custom_id": { + "allow": { "type": "string" }, - "sku_id": { + "deny": { "type": "string" }, - "url": { + "id": { "type": "string" }, - "disabled": { - "type": "boolean" + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" } }, "additionalProperties": false, "required": [ - "style", + "allow", + "deny", + "id", "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageComponentType.Button": { - "type": "number", - "const": 2, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ButtonStyle": { + "ChannelPermissionOverwriteType": { "type": "number", "enum": [ + 0, 1, - 2, - 3, - 4, - 5, - 6 + 2 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SelectMenuComponent": { + "ReadState": { "type": "object", "properties": { - "type": { - "enum": [ - 3, - 5, - 6, - 7, - 8 - ], - "type": "number" + "channel_id": { + "type": "string" }, - "custom_id": { + "channel": { + "$ref": "#/definitions/Channel" + }, + "user_id": { "type": "string" }, - "channel_types": { - "type": "array", - "items": { - "type": "integer" - } + "user": { + "$ref": "#/definitions/User" }, - "placeholder": { + "last_message_id": { "type": "string" }, - "default_values": { - "type": "array", - "items": { - "$ref": "#/definitions/SelectMenuDefaultOption" - } + "last_acked_id": { + "type": "string" }, - "min_values": { + "notifications_cursor": { + "type": "string" + }, + "mention_count": { "type": "integer" }, - "max_values": { + "badge_count": { "type": "integer" }, - "disabled": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "custom_id", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SelectMenuDefaultOption": { - "type": "object", - "properties": { + "last_pin_timestamp": { + "type": "string", + "format": "date-time" + }, + "read_state_type": { + "$ref": "#/definitions/ReadStateType" + }, + "flags": { + "$ref": "#/definitions/ReadStateFlags" + }, "id": { "type": "string" }, - "type": { - "enum": [ - "channel", - "role", - "user" - ], - "type": "string" + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", + "badge_count", + "channel", + "channel_id", + "flags", "id", - "type" + "mention_count", + "notifications_cursor", + "read_state_type", + "user", + "user_id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "StringSelectMenuComponent": { + "ReadStateType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ReadStateFlags": { + "type": "number", + "enum": [ + 1, + 2, + 4 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ThreadMetadata": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.StringSelect" - }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/SelectMenuOption" - } - }, - "custom_id": { - "type": "string" + "archived": { + "type": "boolean" }, - "channel_types": { - "type": "array", - "items": { - "type": "integer" - } + "auto_archive_duration": { + "type": "integer" }, - "placeholder": { + "archive_timestamp": { "type": "string" }, - "default_values": { - "type": "array", - "items": { - "$ref": "#/definitions/SelectMenuDefaultOption" - } - }, - "min_values": { - "type": "integer" - }, - "max_values": { - "type": "integer" + "locked": { + "type": "boolean" }, - "disabled": { + "invitable": { "type": "boolean" + }, + "create_timestamp": { + "type": "string" } }, "additionalProperties": false, "required": [ - "custom_id", - "options", - "type" + "archive_timestamp", + "archived", + "auto_archive_duration", + "create_timestamp", + "locked" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageComponentType.StringSelect": { - "type": "number", - "const": 3, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SelectMenuOption": { + "Tag": { "type": "object", "properties": { - "label": { + "channel_id": { "type": "string" }, - "value": { + "channel": { + "$ref": "#/definitions/Channel" + }, + "name": { "type": "string" }, - "description": { + "moderated": { + "type": "boolean", + "default": false + }, + "emoji_id": { "type": "string" }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" + "emoji_name": { + "type": "string" }, - "default": { - "type": "boolean" + "id": { + "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "label", - "value" + "__@annotationsKey@11707", + "channel", + "channel_id", + "id", + "moderated", + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TextInputComponent": { + "Attachment": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.TextInput" - }, - "custom_id": { + "filename": { "type": "string" }, - "style": { - "$ref": "#/definitions/TextInputStyle" + "size": { + "type": "integer" }, - "label": { + "url": { "type": "string" }, - "min_length": { + "proxy_url": { + "type": "string" + }, + "height": { "type": "integer" }, - "max_length": { + "width": { "type": "integer" }, - "required": { - "type": "boolean" + "content_type": { + "type": "string" }, - "value": { + "message_id": { "type": "string" }, - "placeholder": { + "message": { + "$ref": "#/definitions/Message" + }, + "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ - "custom_id", - "label", - "style", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MessageComponentType.TextInput": { - "type": "number", - "const": 4, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TextInputStyle": { - "type": "number", - "enum": [ - 1, - 2 + "__@annotationsKey@11707", + "filename", + "id", + "message", + "message_id", + "proxy_url", + "size", + "url" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Poll": { + "Embed": { "type": "object", "properties": { - "question": { - "$ref": "#/definitions/PollMedia" + "title": { + "type": "string" }, - "answers": { - "type": "array", - "items": { - "$ref": "#/definitions/PollAnswer" - } + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" }, - "expiry": { + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { "type": "string", "format": "date-time" }, - "allow_multiselect": { - "type": "boolean" + "color": { + "type": "integer" }, - "results": { - "$ref": "#/definitions/PollResult" + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/definitions/EmbedImage" + }, + "thumbnail": { + "$ref": "#/definitions/EmbedImage" + }, + "video": { + "$ref": "#/definitions/EmbedImage" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name", + "value" + ] + } } }, "additionalProperties": false, - "required": [ - "allow_multiselect", - "answers", - "expiry", - "question" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PollMedia": { + "EmbedImage": { "type": "object", "properties": { - "text": { + "url": { "type": "string" }, - "emoji": { - "$ref": "#/definitions/PartialEmoji" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PollAnswer": { - "type": "object", - "properties": { - "answer_id": { + "proxy_url": { "type": "string" }, - "poll_media": { - "$ref": "#/definitions/PollMedia" - } - }, - "additionalProperties": false, - "required": [ - "poll_media" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PollResult": { - "type": "object", - "properties": { - "is_finalized": { - "type": "boolean" + "height": { + "type": "integer" }, - "answer_counts": { - "type": "array", - "items": { - "$ref": "#/definitions/PollAnswerCount" - } + "width": { + "type": "integer" } }, "additionalProperties": false, - "required": [ - "answer_counts", - "is_finalized" - ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PollAnswerCount": { + "Reaction": { "type": "object", "properties": { - "id": { - "type": "string" - }, "count": { "type": "integer" }, - "me_voted": { - "type": "boolean" + "emoji": { + "$ref": "#/definitions/PartialEmoji" + }, + "user_ids": { + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false, "required": [ "count", - "id", - "me_voted" + "emoji", + "user_ids" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageSnapshot": { - "type": "object", - "properties": { - "message": { + "PartialEmoji": { + "anyOf": [ + { "type": "object", "properties": { - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/Attachment" - } - }, - "components": { - "type": "array", - "items": { - "$ref": "#/definitions/ActionRowComponent" - } - }, - "content": { + "id": { "type": "string" }, - "edited_timestamp": { - "type": "string", - "format": "date-time" - }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } - }, - "flags": { - "type": "integer" - }, - "mention_roles": { - "type": "array", - "items": { - "type": "string" - } + "name": { + "type": "string" }, - "mentions": { - "type": "array", - "items": { - "type": "string" - } + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" + "name": { + "type": "string" }, - "type": { - "enum": [ - 0, - 1, - 10, - 11, - 12, - 127, - 128, - 129, - 13, - 130, - 131, - 132, - 14, - 15, - 16, - 17, - 18, - 19, - 2, - 20, - 21, - 22, - 23, - 24, - 255, - 3, - 4, - 5, - 6, - 7, - 8, - 9 - ], - "type": "number" + "animated": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "content", - "embeds", - "mention_roles", - "mentions" + "id" ] } - }, - "additionalProperties": false, - "required": [ - "message" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "InteractionGuild": { + "MessageType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 255 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ApplicationCommandType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ActionRowComponent": { "type": "object", "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" + "type": { + "$ref": "#/definitions/MessageComponentType.ActionRow" }, - "features": { + "components": { "type": "array", "items": { - "type": "string" + "anyOf": [ + { + "$ref": "#/definitions/ButtonComponent" + }, + { + "$ref": "#/definitions/SelectMenuComponent" + }, + { + "$ref": "#/definitions/StringSelectMenuComponent" + }, + { + "$ref": "#/definitions/TextInputComponent" + } + ] } - }, - "locale": { - "type": "string" } }, "additionalProperties": false, "required": [ - "features", - "id", - "locale" + "components", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PublicMember": { - "additionalProperties": false, + "MessageComponentType.ActionRow": { + "type": "number", + "const": 1, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ButtonComponent": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "guild_id": { - "type": "string" - }, - "mute": { - "type": "boolean" + "type": { + "$ref": "#/definitions/MessageComponentType.Button" }, - "deaf": { - "type": "boolean" + "style": { + "$ref": "#/definitions/ButtonStyle" }, - "nick": { + "label": { "type": "string" }, - "joined_at": { - "type": "string", - "format": "date-time" - }, - "pending": { - "type": "boolean" + "emoji": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + } + ] }, - "premium_since": { - "type": "integer" + "custom_id": { + "type": "string" }, - "avatar": { + "sku_id": { "type": "string" }, - "banner": { + "url": { "type": "string" }, - "bio": { + "disabled": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "style", + "type" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "MessageComponentType.Button": { + "type": "number", + "const": 2, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ButtonStyle": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "SelectMenuComponent": { + "type": "object", + "properties": { + "type": { + "enum": [ + 3, + 5, + 6, + 7, + 8 + ], + "type": "number" + }, + "custom_id": { "type": "string" }, - "theme_colors": { + "channel_types": { "type": "array", "items": { "type": "integer" } }, - "pronouns": { + "placeholder": { "type": "string" }, - "communication_disabled_until": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ] - }, - "user": { - "$ref": "#/definitions/PublicUser" - }, - "roles": { + "default_values": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/SelectMenuDefaultOption" } + }, + "min_values": { + "type": "integer" + }, + "max_values": { + "type": "integer" + }, + "disabled": { + "type": "boolean" } }, + "additionalProperties": false, "required": [ - "banner", - "bio", - "communication_disabled_until", - "deaf", - "guild_id", - "id", - "joined_at", - "mute", - "pending", - "roles", - "user" + "custom_id", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PublicUser": { + "SelectMenuDefaultOption": { "type": "object", "properties": { "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "premium_since": { - "type": "string", - "format": "date-time" - }, - "avatar": { + "type": { + "enum": [ + "channel", + "role", + "user" + ], "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "id", + "type" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "StringSelectMenuComponent": { + "type": "object", + "properties": { + "type": { + "$ref": "#/definitions/MessageComponentType.StringSelect" }, - "banner": { - "type": "string" + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/SelectMenuOption" + } }, - "bio": { + "custom_id": { "type": "string" }, - "theme_colors": { + "channel_types": { "type": "array", "items": { "type": "integer" } }, - "pronouns": { - "type": "string" - }, - "username": { - "type": "string" - }, - "discriminator": { + "placeholder": { "type": "string" }, - "public_flags": { - "type": "integer" - }, - "accent_color": { - "type": "integer" - }, - "bot": { - "type": "boolean" - }, - "premium_type": { - "type": "integer" - }, - "badge_ids": { + "default_values": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/SelectMenuDefaultOption" } }, - "avatar_decoration_data": { - "$ref": "#/definitions/AvatarDecorationData" - }, - "display_name_styles": { - "$ref": "#/definitions/DisplayNameStyle" - }, - "collectibles": { - "$ref": "#/definitions/Collectibles" - }, - "primary_guild": { - "$ref": "#/definitions/PrimaryGuild" - } - }, - "additionalProperties": false, - "required": [ - "bio", - "bot", - "discriminator", - "id", - "premium_since", - "premium_type", - "public_flags", - "username" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Snowflake": { - "description": "A container for useful snowflake-related methods.", - "type": "object", - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GameActivity": { - "type": "object", - "properties": { - "activity_level": { + "min_values": { "type": "integer" }, - "activity_score": { + "max_values": { "type": "integer" + }, + "disabled": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "activity_level", - "activity_score" + "custom_id", + "options", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildBadgeType": { + "MessageComponentType.StringSelect": { "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30 - ], + "const": 3, "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildTrait": { + "SelectMenuOption": { "type": "object", "properties": { - "emoji_id": { - "type": [ - "null", - "string" - ] + "label": { + "type": "string" }, - "emoji_name": { - "type": [ - "null", - "string" + "value": { + "type": "string" + }, + "description": { + "type": "string" + }, + "emoji": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + } ] }, - "emoji_animated": { + "default": { "type": "boolean" - }, - "label": { - "type": "string" - }, - "position": { - "type": "integer" } }, "additionalProperties": false, "required": [ - "emoji_animated", - "emoji_id", - "emoji_name", "label", - "position" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildVisibilityLevel": { - "type": "number", - "enum": [ - 1, - 2, - 3 + "value" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "InstanceUserDeleteSchemaContent": { - "type": "object", - "properties": { - "reason": { - "type": "string" - }, - "persistInstanceBan": { - "type": "boolean" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Classification": { + "TextInputComponent": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "classification_type": { - "$ref": "#/definitions/ClassificationType" - }, - "description": { - "type": "string" + "type": { + "$ref": "#/definitions/MessageComponentType.TextInput" }, - "explainer_link": { + "custom_id": { "type": "string" }, - "actions": { - "type": "array", - "items": { - "$ref": "#/definitions/ClassificationAction" - } + "style": { + "$ref": "#/definitions/TextInputStyle" }, - "max_expiration_time": { + "label": { "type": "string" }, - "flagged_content": { - "type": "array", - "items": {} + "min_length": { + "type": "integer" }, - "appeal_status": { - "$ref": "#/definitions/AppealStatus" + "max_length": { + "type": "integer" }, - "is_coppa": { + "required": { "type": "boolean" }, - "is_spam": { - "type": "boolean" + "value": { + "type": "string" }, - "appeal_ingestion_type": { - "$ref": "#/definitions/AppealIngestionType" + "placeholder": { + "type": "string" } }, "additionalProperties": false, "required": [ - "actions", - "appeal_ingestion_type", - "appeal_status", - "classification_type", - "description", - "explainer_link", - "flagged_content", - "id", - "is_coppa", - "is_spam", - "max_expiration_time" + "custom_id", + "label", + "style", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ClassificationType": { + "MessageComponentType.TextInput": { + "type": "number", + "const": 4, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "TextInputStyle": { "type": "number", "enum": [ 1, - 100, - 200, - 210, - 220, - 230, - 240, - 250, - 280, - 290, - 310, - 320, - 390, - 600, - 650, - 711, - 720, - 3010, - 3030, - 4000, - 4010, - 4130, - 4140, - 5010, - 5090, - 5305, - 5411, - 5440, - 5485 + 2 ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ClassificationAction": { + "Poll": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "action_type": { - "$ref": "#/definitions/ClassificationActionType" + "question": { + "$ref": "#/definitions/PollMedia" }, - "descriptions": { + "answers": { "type": "array", "items": { - "type": "string" + "$ref": "#/definitions/PollAnswer" } + }, + "expiry": { + "type": "string", + "format": "date-time" + }, + "allow_multiselect": { + "type": "boolean" + }, + "results": { + "$ref": "#/definitions/PollResult" } }, "additionalProperties": false, "required": [ - "action_type", - "descriptions", - "id" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ClassificationActionType": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 20, - 22 + "allow_multiselect", + "answers", + "expiry", + "question" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AppealStatus": { + "PollMedia": { "type": "object", "properties": { - "status": { - "$ref": "#/definitions/AppealStatusValue" + "text": { + "type": "string" + }, + "emoji": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "id" + ] + } + ] } - }, - "additionalProperties": false, - "required": [ - "status" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "AppealStatusValue": { - "type": "number", - "enum": [ - 1, - 2, - 3 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "AppealIngestionType": { - "type": "number", - "enum": [ - 0, - 1, - 2 - ], + }, + "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildClassification": { + "PollAnswer": { "type": "object", "properties": { - "guild_metadata": { - "$ref": "#/definitions/GuildMetadata" - }, - "id": { - "type": "string" - }, - "classification_type": { - "$ref": "#/definitions/ClassificationType" - }, - "description": { + "answer_id": { "type": "string" }, - "explainer_link": { - "type": "string" + "poll_media": { + "$ref": "#/definitions/PollMedia" + } + }, + "additionalProperties": false, + "required": [ + "poll_media" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "PollResult": { + "type": "object", + "properties": { + "is_finalized": { + "type": "boolean" }, - "actions": { + "answer_counts": { "type": "array", "items": { - "$ref": "#/definitions/ClassificationAction" + "$ref": "#/definitions/PollAnswerCount" } - }, - "max_expiration_time": { - "type": "string" - }, - "flagged_content": { - "type": "array", - "items": {} - }, - "appeal_status": { - "$ref": "#/definitions/AppealStatus" - }, - "is_coppa": { - "type": "boolean" - }, - "is_spam": { - "type": "boolean" - }, - "appeal_ingestion_type": { - "$ref": "#/definitions/AppealIngestionType" } }, "additionalProperties": false, "required": [ - "actions", - "appeal_ingestion_type", - "appeal_status", - "classification_type", - "description", - "explainer_link", - "flagged_content", - "guild_metadata", - "id", - "is_coppa", - "is_spam", - "max_expiration_time" + "answer_counts", + "is_finalized" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildMetadata": { + "PollAnswerCount": { "type": "object", "properties": { - "name": { + "id": { "type": "string" }, - "icon": { - "type": "string" + "count": { + "type": "integer" }, - "member_type": { - "$ref": "#/definitions/GuildMemberType" + "me_voted": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "member_type", - "name" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "GuildMemberType": { - "type": "number", - "enum": [ - 1, - 2 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "AccountStandingState": { - "type": "number", - "enum": [ - 100, - 200, - 300, - 400, - 500 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "AppealEligibility": { - "type": "number", - "enum": [ - 1, - 2, - 3 + "count", + "id", + "me_voted" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CollectiblesCategoryItem": { + "MessageSnapshot": { "type": "object", "properties": { - "sku_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "store_listing_id": { - "type": "string" - }, - "banner": { - "type": "string" - }, - "unpublished_at": { - "type": [ - "null", - "string" - ] - }, - "styles": { - "$ref": "#/definitions/CollectiblesCategoryStyle" - }, - "logo": { - "type": "string" - }, - "hero_ranking": { - "anyOf": [ - { + "message": { + "type": "object", + "properties": { + "content": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "edited_timestamp": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "mentions": { "type": "array", "items": { "type": "string" } }, - { - "type": "null" + "mention_roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment" + } + }, + "embeds": { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } + }, + "type": { + "$ref": "#/definitions/MessageType" + }, + "flags": { + "type": "integer" + }, + "components": { + "type": "array", + "items": { + "$ref": "#/definitions/MessageComponent" + } + }, + "resolved": { + "type": "array", + "items": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "sticker_items": { + "type": "array", + "items": { + "$ref": "#/definitions/Sticker" + } } + }, + "additionalProperties": false, + "required": [ + "content", + "embeds", + "flags", + "mention_roles", + "mentions", + "timestamp", + "type" ] - }, - "mobile_bg": { - "type": [ - "null", - "string" - ] - }, - "pdp_bg": { - "type": [ - "null", - "string" - ] - }, - "success_modal_bg": { - "type": [ - "null", - "string" - ] - }, - "mobile_banner": { - "type": [ - "null", - "string" - ] - }, - "featured_block": { - "type": [ - "null", - "string" - ] - }, - "hero_banner": { - "type": [ - "null", - "string" - ] - }, - "wide_banner": { - "type": [ - "null", - "string" - ] - }, - "hero_logo": { - "type": [ - "null", - "string" - ] - }, - "products": { - "type": "array", - "items": { - "$ref": "#/definitions/CollectiblesCategoryProductItem" - } - }, - "banner_asset": { - "$ref": "#/definitions/StaticAnimatedAsset" - }, - "hero_banner_asset": { - "$ref": "#/definitions/StaticAnimatedAsset" } }, "additionalProperties": false, "required": [ - "banner", - "featured_block", - "hero_banner", - "hero_logo", - "hero_ranking", - "logo", - "mobile_banner", - "mobile_bg", - "name", - "pdp_bg", - "products", - "sku_id", - "store_listing_id", - "styles", - "success_modal_bg", - "summary", - "unpublished_at", - "wide_banner" + "message" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CollectiblesCategoryStyle": { + "MessageComponent": { "type": "object", "properties": { - "background_colors": { - "type": "array", - "items": { - "type": "integer" - } - }, - "button_colors": { - "type": "array", - "items": { - "type": "integer" - } - }, - "confetti_colors": { - "type": "array", - "items": { - "type": "integer" - } + "type": { + "$ref": "#/definitions/MessageComponentType" } }, "additionalProperties": false, "required": [ - "background_colors", - "button_colors", - "confetti_colors" + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CollectiblesCategoryProductItem": { + "MessageComponentType": { + "type": "number", + "enum": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InteractionGuild": { "type": "object", "properties": { - "sku_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "store_listing_id": { - "type": "string" - }, - "banner": { + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "unpublished_at": { - "type": [ - "null", - "string" - ] - }, - "styles": { - "$ref": "#/definitions/CollectiblesCategoryStyle" - }, - "prices": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "country_prices": { - "$ref": "#/definitions/CountryPrice" - } - }, - "additionalProperties": false, - "required": [ - "country_prices" - ] - } - }, - "items": { + "features": { "type": "array", "items": { - "$ref": "#/definitions/ProductItem" - } - }, - "type": { - "type": "integer" - }, - "premium_type": { - "type": "integer" - }, - "category_sku_id": { - "type": "string" - }, - "google_sku_ids": { - "type": "object", - "additionalProperties": { "type": "string" } }, - "variants": { - "type": "array", - "items": { - "$ref": "#/definitions/ProductItemVariant" - } + "locale": { + "type": "string" } }, "additionalProperties": false, "required": [ - "banner", - "category_sku_id", - "google_sku_ids", - "items", - "name", - "premium_type", - "prices", - "sku_id", - "store_listing_id", - "styles", - "summary", - "type", - "unpublished_at" + "features", + "id", + "locale" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CountryPrice": { + "PublicMember": { + "additionalProperties": false, "type": "object", "properties": { - "country_code": { + "id": { "type": "string" }, - "prices": { - "type": "array", - "items": { - "$ref": "#/definitions/PriceEntry" - } - } - }, - "additionalProperties": false, - "required": [ - "country_code", - "prices" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PriceEntry": { - "type": "object", - "properties": { - "amount": { + "guild_id": { + "type": "string" + }, + "flags": { "type": "integer" }, - "currency": { + "mute": { + "type": "boolean" + }, + "deaf": { + "type": "boolean" + }, + "nick": { "type": "string" }, - "exponent": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "amount", - "currency", - "exponent" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ProductItem": { - "type": "object", - "properties": { - "type": { + "joined_at": { + "type": "string", + "format": "date-time" + }, + "pending": { + "type": "boolean" + }, + "premium_since": { "type": "integer" }, - "id": { + "avatar": { "type": "string" }, - "sku_id": { + "banner": { "type": "string" }, - "asset": { + "bio": { + "type": "string" + }, + "theme_colors": { + "type": "array", + "items": { + "type": "integer" + } + }, + "pronouns": { "type": "string" }, - "label": { - "type": "string" + "communication_disabled_until": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ] + }, + "user": { + "$ref": "#/definitions/PublicUser" }, - "palette": { - "type": "string" + "roles": { + "type": "array", + "items": { + "type": "string" + } } }, - "additionalProperties": false, "required": [ + "banner", + "bio", + "communication_disabled_until", + "deaf", + "flags", + "guild_id", "id", - "sku_id", - "type" + "joined_at", + "mute", + "pending", + "roles", + "user" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ProductItemVariant": { + "PublicUser": { "type": "object", "properties": { - "sku_id": { + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "name": { + "premium_since": { + "type": "string", + "format": "date-time" + }, + "avatar": { "type": "string" }, - "name_localizations": { - "type": "null" + "banner": { + "type": "string" }, - "summary": { + "bio": { "type": "string" }, - "summary_localizations": { - "type": "null" + "theme_colors": { + "type": "array", + "items": { + "type": "integer" + } }, - "store_listing_id": { + "pronouns": { "type": "string" }, - "banner": { + "username": { "type": "string" }, - "unpublished_at": { - "type": [ - "null", - "string" - ] - }, - "styles": { - "$ref": "#/definitions/CollectiblesCategoryStyle" - }, - "prices": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "country_prices": { - "$ref": "#/definitions/CountryPrice" - } - }, - "additionalProperties": false, - "required": [ - "country_prices" - ] - } + "discriminator": { + "type": "string" }, - "items": { - "type": "array", - "items": { - "$ref": "#/definitions/ProductItem" - } + "public_flags": { + "type": "integer" }, - "type": { + "accent_color": { "type": "integer" }, + "bot": { + "type": "boolean" + }, "premium_type": { "type": "integer" }, - "category_sku_id": { - "type": "string" - }, - "google_sku_ids": { - "type": "object", - "additionalProperties": { + "badge_ids": { + "type": "array", + "items": { "type": "string" } }, - "base_variant_sku_id": { - "type": "string" + "avatar_decoration_data": { + "$ref": "#/definitions/AvatarDecorationData" }, - "base_variant_name": { - "type": "string" + "display_name_styles": { + "$ref": "#/definitions/DisplayNameStyle" }, - "variant_label": { - "type": "string" + "collectibles": { + "$ref": "#/definitions/Collectibles" }, - "variant_value": { - "type": "string" + "primary_guild": { + "$ref": "#/definitions/PrimaryGuild" } }, "additionalProperties": false, "required": [ - "base_variant_name", - "base_variant_sku_id", - "category_sku_id", - "items", - "name", - "name_localizations", + "bio", + "bot", + "discriminator", + "id", + "premium_since", "premium_type", - "prices", - "sku_id", - "store_listing_id", - "summary", - "summary_localizations", - "type", - "variant_label", - "variant_value" + "public_flags", + "username" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "StaticAnimatedAsset": { + "Snowflake": { + "description": "A container for useful snowflake-related methods.", + "type": "object", + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GameActivity": { "type": "object", "properties": { - "animated": { - "type": [ - "null", - "string" - ] + "activity_level": { + "type": "integer" }, - "static": { - "type": "string" + "activity_score": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "animated", - "static" + "activity_level", + "activity_score" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "CollectiblesMarketingItem": { + "GuildBadgeType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildTrait": { "type": "object", "properties": { - "type": { - "type": "integer" + "emoji_id": { + "type": [ + "null", + "string" + ] }, - "version": { - "type": "integer" + "emoji_name": { + "type": [ + "null", + "string" + ] }, - "title": { - "type": "string" + "emoji_animated": { + "type": "boolean" }, - "body": { + "label": { "type": "string" + }, + "position": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "body", - "title", - "type", - "version" + "emoji_animated", + "emoji_id", + "emoji_name", + "label", + "position" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "AnyShopBlock": { - "anyOf": [ - { - "$ref": "#/definitions/ItemRowShopBlock" - }, - { - "$ref": "#/definitions/BundleTileRowShopBlock" + "GuildVisibilityLevel": { + "type": "number", + "enum": [ + 1, + 2, + 3 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "InstanceUserDeleteSchemaContent": { + "type": "object", + "properties": { + "reason": { + "type": "string" }, - { - "$ref": "#/definitions/ItemCollectionShopBlock" + "persistInstanceBan": { + "type": "boolean" } - ], + }, + "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, - "ItemRowShopBlock": { + "Classification": { "type": "object", "properties": { - "type": { - "type": "integer", - "const": 0 - }, - "category_sku_id": { + "id": { "type": "string" }, - "name": { - "type": "string" + "classification_type": { + "$ref": "#/definitions/ClassificationType" }, - "category_store_listing_id": { + "description": { "type": "string" }, - "banner_asset": { - "$ref": "#/definitions/StaticAnimatedAsset" - }, - "logo_url": { + "explainer_link": { "type": "string" }, - "unpublished_at": { - "type": [ - "null", - "string" - ] + "actions": { + "type": "array", + "items": { + "$ref": "#/definitions/ClassificationAction" + } }, - "summary": { + "max_expiration_time": { "type": "string" }, - "ranked_sku_ids": { + "flagged_content": { "type": "array", - "items": { - "type": "string" - } + "items": {} + }, + "appeal_status": { + "$ref": "#/definitions/AppealStatus" + }, + "is_coppa": { + "type": "boolean" + }, + "is_spam": { + "type": "boolean" + }, + "appeal_ingestion_type": { + "$ref": "#/definitions/AppealIngestionType" } }, "additionalProperties": false, "required": [ - "banner_asset", - "category_sku_id", - "category_store_listing_id", - "logo_url", - "name", - "ranked_sku_ids", - "summary", - "type", - "unpublished_at" + "actions", + "appeal_ingestion_type", + "appeal_status", + "classification_type", + "description", + "explainer_link", + "flagged_content", + "id", + "is_coppa", + "is_spam", + "max_expiration_time" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "BundleTileRowShopBlock": { + "ClassificationType": { + "type": "number", + "enum": [ + 1, + 100, + 200, + 210, + 220, + 230, + 240, + 250, + 280, + 290, + 310, + 320, + 390, + 600, + 650, + 711, + 720, + 3010, + 3030, + 4000, + 4010, + 4130, + 4140, + 5010, + 5090, + 5305, + 5411, + 5440, + 5485 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "ClassificationAction": { "type": "object", "properties": { - "type": { - "type": "integer", - "const": 1 + "id": { + "type": "string" }, - "subblocks": { + "action_type": { + "$ref": "#/definitions/ClassificationActionType" + }, + "descriptions": { "type": "array", "items": { - "$ref": "#/definitions/ShopBlockSubBlock" + "type": "string" } } }, "additionalProperties": false, "required": [ - "subblocks", - "type" + "action_type", + "descriptions", + "id" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ShopBlockSubBlock": { + "ClassificationActionType": { + "type": "number", + "enum": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 20, + 22 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AppealStatus": { "type": "object", "properties": { - "type": { - "type": "integer" - }, - "category_store_listing_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "unpublished_at": { - "type": [ - "null", - "string" - ] - }, - "banner_url": { - "type": "string" - }, - "body_text": { - "type": [ - "null", - "string" - ] - }, - "banner_text_color": { - "type": [ - "null", - "integer" - ] + "status": { + "$ref": "#/definitions/AppealStatusValue" } }, "additionalProperties": false, "required": [ - "banner_text_color", - "banner_url", - "body_text", - "category_store_listing_id", - "name", - "type", - "unpublished_at" + "status" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ItemCollectionShopBlock": { + "AppealStatusValue": { + "type": "number", + "enum": [ + 1, + 2, + 3 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AppealIngestionType": { + "type": "number", + "enum": [ + 0, + 1, + 2 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "GuildClassification": { "type": "object", "properties": { - "type": { - "type": "integer", - "const": 2 + "guild_metadata": { + "$ref": "#/definitions/GuildMetadata" }, - "ranked_sku_ids": { - "type": "array", - "items": { - "type": "string" - } + "id": { + "type": "string" }, - "sorted_sku_ids": { - "type": "object", - "properties": { - "recommended": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "null" - } - ] - }, - "popular": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": [ - "popular", - "recommended" - ] + "classification_type": { + "$ref": "#/definitions/ClassificationType" + }, + "description": { + "type": "string" + }, + "explainer_link": { + "type": "string" + }, + "actions": { + "type": "array", + "items": { + "$ref": "#/definitions/ClassificationAction" + } + }, + "max_expiration_time": { + "type": "string" + }, + "flagged_content": { + "type": "array", + "items": {} + }, + "appeal_status": { + "$ref": "#/definitions/AppealStatus" + }, + "is_coppa": { + "type": "boolean" + }, + "is_spam": { + "type": "boolean" + }, + "appeal_ingestion_type": { + "$ref": "#/definitions/AppealIngestionType" } }, "additionalProperties": false, "required": [ - "ranked_sku_ids", - "sorted_sku_ids", - "type" + "actions", + "appeal_ingestion_type", + "appeal_status", + "classification_type", + "description", + "explainer_link", + "flagged_content", + "guild_metadata", + "id", + "is_coppa", + "is_spam", + "max_expiration_time" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PartialMessage": { - "description": "https://docs.discord.food/resources/message#partial-message-structure", + "GuildMetadata": { "type": "object", "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", - "type": "string" - }, - "channel_id": { + "name": { "type": "string" }, - "type": { - "$ref": "#/definitions/MessageType" - }, - "content": { + "icon": { "type": "string" }, - "author": { - "$ref": "#/definitions/PartialUser" - }, - "flags": { - "type": "integer" - }, - "application_id": { - "type": "string" + "member_type": { + "$ref": "#/definitions/GuildMemberType" } }, "additionalProperties": false, "required": [ - "author", - "channel_id", - "content", - "id", - "type" + "member_type", + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "PartialUser": { + "GuildMemberType": { + "type": "number", + "enum": [ + 1, + 2 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AccountStandingState": { + "type": "number", + "enum": [ + 100, + 200, + 300, + 400, + 500 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "AppealEligibility": { + "type": "number", + "enum": [ + 1, + 2, + 3 + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "CollectiblesCategoryItem": { "type": "object", "properties": { - "id": { - "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "sku_id": { "type": "string" }, - "username": { + "name": { "type": "string" }, - "discriminator": { + "summary": { "type": "string" }, - "global_name": { - "type": [ - "null", - "string" - ] + "store_listing_id": { + "type": "string" }, - "avatar": { + "banner": { + "type": "string" + }, + "unpublished_at": { "type": [ "null", "string" ] }, - "avatar_decoration_data": { + "styles": { + "$ref": "#/definitions/CollectiblesCategoryStyle" + }, + "logo": { + "type": "string" + }, + "hero_ranking": { "anyOf": [ { - "$ref": "#/definitions/AvatarDecorationData" + "type": "array", + "items": { + "type": "string" + } }, { "type": "null" } ] }, - "collectibles": { - "anyOf": [ - { - "$ref": "#/definitions/Collectibles" - }, - { - "type": "null" - } + "mobile_bg": { + "type": [ + "null", + "string" ] }, - "display_name_styles": { - "anyOf": [ - { - "$ref": "#/definitions/DisplayNameStyle" - }, - { - "type": "null" - } + "pdp_bg": { + "type": [ + "null", + "string" ] }, - "primary_guild": { - "anyOf": [ - { - "$ref": "#/definitions/PrimaryGuild" - }, - { - "type": "null" - } + "success_modal_bg": { + "type": [ + "null", + "string" ] }, - "bot": { - "type": "boolean" + "mobile_banner": { + "type": [ + "null", + "string" + ] }, - "system": { - "type": "boolean" + "featured_block": { + "type": [ + "null", + "string" + ] }, - "banner": { + "hero_banner": { "type": [ "null", "string" ] }, - "accent_color": { + "wide_banner": { "type": [ "null", - "integer" + "string" ] }, - "public_flags": { - "type": "integer" + "hero_logo": { + "type": [ + "null", + "string" + ] + }, + "products": { + "type": "array", + "items": { + "$ref": "#/definitions/CollectiblesCategoryProductItem" + } + }, + "banner_asset": { + "$ref": "#/definitions/StaticAnimatedAsset" + }, + "hero_banner_asset": { + "$ref": "#/definitions/StaticAnimatedAsset" } }, "additionalProperties": false, "required": [ - "avatar", - "discriminator", - "id", - "username" + "banner", + "featured_block", + "hero_banner", + "hero_logo", + "hero_ranking", + "logo", + "mobile_banner", + "mobile_bg", + "name", + "pdp_bg", + "products", + "sku_id", + "store_listing_id", + "styles", + "success_modal_bg", + "summary", + "unpublished_at", + "wide_banner" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "HubGuild": { + "CollectiblesCategoryStyle": { "type": "object", "properties": { - "icon": { - "type": "string" + "background_colors": { + "type": "array", + "items": { + "type": "integer" + } }, - "id": { - "type": "string" + "button_colors": { + "type": "array", + "items": { + "type": "integer" + } }, - "name": { - "type": "string" + "confetti_colors": { + "type": "array", + "items": { + "type": "integer" + } } }, "additionalProperties": false, "required": [ - "icon", - "id", - "name" + "background_colors", + "button_colors", + "confetti_colors" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmojiGuild": { + "CollectiblesCategoryProductItem": { "type": "object", "properties": { - "id": { + "sku_id": { "type": "string" }, "name": { "type": "string" }, - "icon": { - "type": [ - "null", - "string" - ] + "summary": { + "type": "string" }, - "description": { + "store_listing_id": { + "type": "string" + }, + "banner": { + "type": "string" + }, + "unpublished_at": { "type": [ "null", "string" ] }, - "features": { - "type": "array", - "items": { - "type": "string" + "styles": { + "$ref": "#/definitions/CollectiblesCategoryStyle" + }, + "prices": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "country_prices": { + "$ref": "#/definitions/CountryPrice" + } + }, + "additionalProperties": false, + "required": [ + "country_prices" + ] } }, - "emojis": { + "items": { "type": "array", "items": { - "$ref": "#/definitions/Emoji" + "$ref": "#/definitions/ProductItem" } }, - "premium_tier": { + "type": { "type": "integer" }, - "premium_subscription_count": { + "premium_type": { "type": "integer" }, - "approximate_member_count": { + "category_sku_id": { + "type": "string" + }, + "google_sku_ids": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "variants": { + "type": "array", + "items": { + "$ref": "#/definitions/ProductItemVariant" + } + } + }, + "additionalProperties": false, + "required": [ + "banner", + "category_sku_id", + "google_sku_ids", + "items", + "name", + "premium_type", + "prices", + "sku_id", + "store_listing_id", + "styles", + "summary", + "type", + "unpublished_at" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "CountryPrice": { + "type": "object", + "properties": { + "country_code": { + "type": "string" + }, + "prices": { + "type": "array", + "items": { + "$ref": "#/definitions/PriceEntry" + } + } + }, + "additionalProperties": false, + "required": [ + "country_code", + "prices" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "PriceEntry": { + "type": "object", + "properties": { + "amount": { "type": "integer" }, - "approximate_presence_count": { + "currency": { + "type": "string" + }, + "exponent": { "type": "integer" } }, "additionalProperties": false, "required": [ - "emojis", - "features", - "id", - "name", - "premium_tier" + "amount", + "currency", + "exponent" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmojiApplication": { + "ProductItem": { "type": "object", "properties": { + "type": { + "type": "integer" + }, "id": { "type": "string" }, - "name": { + "sku_id": { + "type": "string" + }, + "asset": { + "type": "string" + }, + "label": { + "type": "string" + }, + "palette": { "type": "string" } }, "additionalProperties": false, "required": [ "id", - "name" + "sku_id", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "GuildMessagesSearchMessage": { + "ProductItemVariant": { "type": "object", "properties": { - "id": { + "sku_id": { "type": "string" }, - "type": { - "$ref": "#/definitions/MessageType" + "name": { + "type": "string" }, - "content": { + "name_localizations": { + "type": "null" + }, + "summary": { "type": "string" }, - "channel_id": { + "summary_localizations": { + "type": "null" + }, + "store_listing_id": { "type": "string" }, - "author": { - "$ref": "#/definitions/PublicUser" + "banner": { + "type": "string" }, - "attachments": { - "type": "array", - "items": { - "$ref": "#/definitions/Attachment" - } + "unpublished_at": { + "type": [ + "null", + "string" + ] }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed_1" - } + "styles": { + "$ref": "#/definitions/CollectiblesCategoryStyle" }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/definitions/PublicUser" + "prices": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "country_prices": { + "$ref": "#/definitions/CountryPrice" + } + }, + "additionalProperties": false, + "required": [ + "country_prices" + ] } }, - "mention_roles": { + "items": { "type": "array", "items": { - "$ref": "#/definitions/Role" + "$ref": "#/definitions/ProductItem" } }, - "pinned": { - "type": "boolean" + "type": { + "type": "integer" }, - "mention_everyone": { - "type": "boolean" + "premium_type": { + "type": "integer" }, - "tts": { - "type": "boolean" + "category_sku_id": { + "type": "string" }, - "timestamp": { + "google_sku_ids": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "base_variant_sku_id": { "type": "string" }, - "edited_timestamp": { + "base_variant_name": { + "type": "string" + }, + "variant_label": { + "type": "string" + }, + "variant_value": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "base_variant_name", + "base_variant_sku_id", + "category_sku_id", + "items", + "name", + "name_localizations", + "premium_type", + "prices", + "sku_id", + "store_listing_id", + "summary", + "summary_localizations", + "type", + "variant_label", + "variant_value" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "StaticAnimatedAsset": { + "type": "object", + "properties": { + "animated": { "type": [ "null", "string" ] }, - "flags": { + "static": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "animated", + "static" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "CollectiblesMarketingItem": { + "type": "object", + "properties": { + "type": { "type": "integer" }, - "components": { - "type": "array", - "items": { - "$ref": "#/definitions/ActionRowComponent_1" - } + "version": { + "type": "integer" }, - "poll": { - "$ref": "#/definitions/Poll_1" + "title": { + "type": "string" }, - "hit": { - "type": "boolean", - "const": true + "body": { + "type": "string" } }, "additionalProperties": false, "required": [ - "attachments", - "author", - "channel_id", - "components", - "edited_timestamp", - "embeds", - "flags", - "hit", - "id", - "mention_roles", - "mentions", - "pinned", - "poll", - "timestamp", - "tts", - "type" + "body", + "title", + "type", + "version" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageType_1": { - "type": "number", - "enum": [ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 127, - 128, - 129, - 130, - 131, - 132, - 255 + "AnyShopBlock": { + "anyOf": [ + { + "$ref": "#/definitions/ItemRowShopBlock" + }, + { + "$ref": "#/definitions/BundleTileRowShopBlock" + }, + { + "$ref": "#/definitions/ItemCollectionShopBlock" + } ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "Embed_1": { + "ItemRowShopBlock": { "type": "object", "properties": { - "title": { - "type": "string" - }, "type": { - "enum": [ - "article", - "gifv", - "image", - "link", - "rich", - "video" - ], + "type": "integer", + "const": 0 + }, + "category_sku_id": { "type": "string" }, - "description": { + "name": { "type": "string" }, - "url": { + "category_store_listing_id": { "type": "string" }, - "timestamp": { - "type": "string", - "format": "date-time" + "banner_asset": { + "$ref": "#/definitions/StaticAnimatedAsset" }, - "color": { - "type": "integer" + "logo_url": { + "type": "string" }, - "footer": { - "type": "object", - "properties": { - "text": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "text" + "unpublished_at": { + "type": [ + "null", + "string" ] }, - "image": { - "$ref": "#/definitions/EmbedImage_1" - }, - "thumbnail": { - "$ref": "#/definitions/EmbedImage_1" - }, - "video": { - "$ref": "#/definitions/EmbedImage_1" - }, - "provider": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "additionalProperties": false + "summary": { + "type": "string" }, - "author": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "icon_url": { - "type": "string" - }, - "proxy_icon_url": { - "type": "string" - } - }, - "additionalProperties": false + "ranked_sku_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "banner_asset", + "category_sku_id", + "category_store_listing_id", + "logo_url", + "name", + "ranked_sku_ids", + "summary", + "type", + "unpublished_at" + ], + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "BundleTileRowShopBlock": { + "type": "object", + "properties": { + "type": { + "type": "integer", + "const": 1 }, - "fields": { + "subblocks": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "value": { - "type": "string" - }, - "inline": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name", - "value" - ] + "$ref": "#/definitions/ShopBlockSubBlock" } } }, "additionalProperties": false, + "required": [ + "subblocks", + "type" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "EmbedImage_1": { + "ShopBlockSubBlock": { "type": "object", "properties": { - "url": { + "type": { + "type": "integer" + }, + "category_store_listing_id": { "type": "string" }, - "proxy_url": { + "name": { "type": "string" }, - "height": { - "type": "integer" + "unpublished_at": { + "type": [ + "null", + "string" + ] }, - "width": { - "type": "integer" + "banner_url": { + "type": "string" + }, + "body_text": { + "type": [ + "null", + "string" + ] + }, + "banner_text_color": { + "type": [ + "null", + "integer" + ] } }, "additionalProperties": false, + "required": [ + "banner_text_color", + "banner_url", + "body_text", + "category_store_listing_id", + "name", + "type", + "unpublished_at" + ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "ActionRowComponent_1": { + "ItemCollectionShopBlock": { "type": "object", "properties": { "type": { - "$ref": "#/definitions/MessageComponentType.ActionRow_1" + "type": "integer", + "const": 2 }, - "components": { + "ranked_sku_ids": { "type": "array", "items": { - "anyOf": [ - { - "$ref": "#/definitions/ButtonComponent_1" - }, - { - "$ref": "#/definitions/SelectMenuComponent_1" - }, - { - "$ref": "#/definitions/StringSelectMenuComponent_1" - }, - { - "$ref": "#/definitions/TextInputComponent_1" - } - ] + "type": "string" } + }, + "sorted_sku_ids": { + "type": "object", + "properties": { + "recommended": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ] + }, + "popular": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "popular", + "recommended" + ] } }, "additionalProperties": false, "required": [ - "components", + "ranked_sku_ids", + "sorted_sku_ids", "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageComponentType.ActionRow_1": { - "type": "number", - "const": 1, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ButtonComponent_1": { + "PartialMessage": { + "description": "https://docs.discord.food/resources/message#partial-message-structure", "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.Button_1" - }, - "style": { - "$ref": "#/definitions/ButtonStyle_1" + "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", + "type": "string" }, - "label": { + "channel_id": { "type": "string" }, - "emoji": { - "$ref": "#/definitions/PartialEmoji_1" + "type": { + "$ref": "#/definitions/MessageType" }, - "custom_id": { + "content": { "type": "string" }, - "sku_id": { - "type": "string" + "author": { + "$ref": "#/definitions/PartialUser" }, - "url": { - "type": "string" + "flags": { + "type": "integer" }, - "disabled": { - "type": "boolean" + "application_id": { + "type": "string" } }, "additionalProperties": false, "required": [ - "style", + "author", + "channel_id", + "content", + "id", "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageComponentType.Button_1": { - "type": "number", - "const": 2, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "ButtonStyle_1": { - "type": "number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PartialEmoji_1": { + "PartialUser": { "type": "object", "properties": { "id": { + "description": "A Twitter-like snowflake, except the epoch is 2015-01-01T00:00:00.000Z\n```\nIf we have a snowflake '266241948824764416' we can represent it as binary:\n\n64 22 17 12 0\n 000000111011000111100001101001000101000000 00001 00000 000000000000\n number of ms since Discord epoch worker pid increment\n```", "type": "string" }, - "name": { + "username": { "type": "string" }, - "animated": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "name" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SelectMenuComponent_1": { - "type": "object", - "properties": { - "type": { - "enum": [ - 3, - 5, - 6, - 7, - 8 - ], - "type": "number" - }, - "custom_id": { + "discriminator": { "type": "string" }, - "channel_types": { - "type": "array", - "items": { - "type": "integer" - } + "global_name": { + "type": [ + "null", + "string" + ] }, - "placeholder": { - "type": "string" + "avatar": { + "type": [ + "null", + "string" + ] }, - "default_values": { - "type": "array", - "items": { - "$ref": "#/definitions/SelectMenuDefaultOption_1" - } + "avatar_decoration_data": { + "anyOf": [ + { + "$ref": "#/definitions/AvatarDecorationData" + }, + { + "type": "null" + } + ] }, - "min_values": { - "type": "integer" + "collectibles": { + "anyOf": [ + { + "$ref": "#/definitions/Collectibles" + }, + { + "type": "null" + } + ] }, - "max_values": { - "type": "integer" + "display_name_styles": { + "anyOf": [ + { + "$ref": "#/definitions/DisplayNameStyle" + }, + { + "type": "null" + } + ] }, - "disabled": { + "primary_guild": { + "anyOf": [ + { + "$ref": "#/definitions/PrimaryGuild" + }, + { + "type": "null" + } + ] + }, + "bot": { + "type": "boolean" + }, + "system": { "type": "boolean" + }, + "banner": { + "type": [ + "null", + "string" + ] + }, + "accent_color": { + "type": [ + "null", + "integer" + ] + }, + "public_flags": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "custom_id", - "type" + "avatar", + "discriminator", + "id", + "username" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "SelectMenuDefaultOption_1": { + "HubGuild": { "type": "object", "properties": { + "icon": { + "type": "string" + }, "id": { "type": "string" }, - "type": { - "enum": [ - "channel", - "role", - "user" - ], + "name": { "type": "string" } }, "additionalProperties": false, "required": [ + "icon", "id", - "type" + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "StringSelectMenuComponent_1": { + "EmojiGuild": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.StringSelect_1" + "id": { + "type": "string" }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/SelectMenuOption_1" - } + "name": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] }, - "custom_id": { - "type": "string" + "description": { + "type": [ + "null", + "string" + ] }, - "channel_types": { + "features": { "type": "array", "items": { - "type": "integer" + "type": "string" } }, - "placeholder": { - "type": "string" - }, - "default_values": { + "emojis": { "type": "array", "items": { - "$ref": "#/definitions/SelectMenuDefaultOption_1" + "$ref": "#/definitions/Emoji" } }, - "min_values": { + "premium_tier": { "type": "integer" }, - "max_values": { + "premium_subscription_count": { "type": "integer" }, - "disabled": { - "type": "boolean" + "approximate_member_count": { + "type": "integer" + }, + "approximate_presence_count": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "custom_id", - "options", - "type" + "emojis", + "features", + "id", + "name", + "premium_tier" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageComponentType.StringSelect_1": { - "type": "number", - "const": 3, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "SelectMenuOption_1": { + "EmojiApplication": { "type": "object", "properties": { - "label": { - "type": "string" - }, - "value": { + "id": { "type": "string" }, - "description": { + "name": { "type": "string" - }, - "emoji": { - "$ref": "#/definitions/PartialEmoji_1" - }, - "default": { - "type": "boolean" } }, "additionalProperties": false, "required": [ - "label", - "value" + "id", + "name" ], "$schema": "http://json-schema.org/draft-07/schema#" }, - "TextInputComponent_1": { + "GuildMessagesSearchMessage": { "type": "object", "properties": { - "type": { - "$ref": "#/definitions/MessageComponentType.TextInput_1" - }, - "custom_id": { + "id": { "type": "string" }, - "style": { - "$ref": "#/definitions/TextInputStyle_1" + "type": { + "$ref": "#/definitions/MessageType" }, - "label": { + "content": { "type": "string" }, - "min_length": { - "type": "integer" + "channel_id": { + "type": "string" }, - "max_length": { - "type": "integer" + "author": { + "$ref": "#/definitions/PublicUser" }, - "required": { - "type": "boolean" + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment" + } }, - "value": { - "type": "string" + "embeds": { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } }, - "placeholder": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "custom_id", - "label", - "style", - "type" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "MessageComponentType.TextInput_1": { - "type": "number", - "const": 4, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "TextInputStyle_1": { - "type": "number", - "enum": [ - 1, - 2 - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "Poll_1": { - "type": "object", - "properties": { - "question": { - "$ref": "#/definitions/PollMedia_1" + "mentions": { + "type": "array", + "items": { + "$ref": "#/definitions/PublicUser" + } }, - "answers": { + "mention_roles": { "type": "array", "items": { - "$ref": "#/definitions/PollAnswer_1" + "$ref": "#/definitions/Role" } }, - "expiry": { - "type": "string", - "format": "date-time" + "pinned": { + "type": "boolean" }, - "allow_multiselect": { + "mention_everyone": { "type": "boolean" }, - "results": { - "$ref": "#/definitions/PollResult_1" - } - }, - "additionalProperties": false, - "required": [ - "allow_multiselect", - "answers", - "expiry", - "question" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PollMedia_1": { - "type": "object", - "properties": { - "text": { - "type": "string" + "tts": { + "type": "boolean" }, - "emoji": { - "$ref": "#/definitions/PartialEmoji_1" - } - }, - "additionalProperties": false, - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PollAnswer_1": { - "type": "object", - "properties": { - "answer_id": { + "timestamp": { "type": "string" }, - "poll_media": { - "$ref": "#/definitions/PollMedia_1" - } - }, - "additionalProperties": false, - "required": [ - "poll_media" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PollResult_1": { - "type": "object", - "properties": { - "is_finalized": { - "type": "boolean" + "edited_timestamp": { + "type": [ + "null", + "string" + ] }, - "answer_counts": { + "flags": { + "type": "integer" + }, + "components": { "type": "array", "items": { - "$ref": "#/definitions/PollAnswerCount_1" + "$ref": "#/definitions/ActionRowComponent" } - } - }, - "additionalProperties": false, - "required": [ - "answer_counts", - "is_finalized" - ], - "$schema": "http://json-schema.org/draft-07/schema#" - }, - "PollAnswerCount_1": { - "type": "object", - "properties": { - "id": { - "type": "string" }, - "count": { - "type": "integer" + "poll": { + "$ref": "#/definitions/Poll" }, - "me_voted": { - "type": "boolean" + "hit": { + "type": "boolean", + "const": true } }, "additionalProperties": false, "required": [ - "count", + "attachments", + "author", + "channel_id", + "components", + "edited_timestamp", + "embeds", + "flags", + "hit", "id", - "me_voted" + "mention_roles", + "mentions", + "pinned", + "poll", + "timestamp", + "tts", + "type" ], "$schema": "http://json-schema.org/draft-07/schema#" }, @@ -16884,7 +14477,7 @@ "length": { "type": "integer" }, - "__@unscopables@700": { + "__@unscopables@697": { "type": "object", "additionalProperties": false, "patternProperties": { @@ -17010,28 +14603,17 @@ "with": { "type": "boolean" }, - "remove": { - "type": "boolean" - }, - "distinct": { - "description": "Returns a new array with duplicate elements removed.", + "__@iterator@695": { "type": "boolean" }, - "single": { - "description": "Returns the only element matching the predicate, or undefined.\nThrows if more than one element matches.", - "type": "boolean" - }, - "__@iterator@698": { - "type": "boolean" - }, - "__@unscopables@700": { + "__@unscopables@697": { "type": "boolean" } } } }, "required": [ - "__@unscopables@700", + "__@unscopables@697", "length" ] }, @@ -17063,10 +14645,20 @@ }, "id": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", "code", "consumed", "expired", @@ -17191,10 +14783,20 @@ }, "icon": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", "id", "is_primary", "localizations", @@ -17600,10 +15202,20 @@ }, "link": { "type": "string" + }, + "__@annotationsKey@11707": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } } }, "additionalProperties": false, "required": [ + "__@annotationsKey@11707", "description", "icon", "id" @@ -17616,6 +15228,11 @@ "parse": { "type": "array", "items": { + "enum": [ + "everyone", + "roles", + "users" + ], "type": "string" } }, @@ -17638,6 +15255,12 @@ "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" }, + "User_DisplayNameEffect": { + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "User_DisplayNameFont": { + "$schema": "http://json-schema.org/draft-07/schema#" + }, "MessageActivity": { "type": "object", "properties": { diff --git a/default.nix b/default.nix index 691162a6b..2073757b9 100644 --- a/default.nix +++ b/default.nix @@ -14,7 +14,6 @@ let lib.fileset.unions [ ./src ./package.json - ./package-lock.json ./tsconfig.json ./assets ./patches @@ -23,47 +22,61 @@ let ) ); }; -in -pkgs.buildNpmPackage { - pname = "spacebar-server-ts"; - nodejs = pkgs.nodejs_24; - version = "1.0.0-" + rVersion; - meta = with lib; { - description = "Spacebar server, a FOSS reimplementation of the Discord backend."; - homepage = "https://github.com/spacebarchat/server"; - license = licenses.agpl3Plus; - platforms = platforms.all; - mainProgram = "start-bundle"; - maintainers = with maintainers; [ RorySys ]; # lol. - }; + revsFile = pkgs.writeText "spacebar-server-rev.json" ( + builtins.toJSON { + rev = self.sourceInfo.rev or self.sourceInfo.dirtyRev; + shortRev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; + lastModified = self.sourceInfo.lastModified; + } + ); - src = filteredSrc; - npmDeps = pkgs.importNpmLock { npmRoot = filteredSrc; }; - npmConfigHook = pkgs.importNpmLock.npmConfigHook; + srcHash = builtins.substring 11 32 (toString filteredSrc); + unwrapped = pkgs.stdenv.mkDerivation { + pname = "spacebar-server-ts-unwrapped"; + nodejs = pkgs.nodejs_24; + version = "1.0.0-" + srcHash; - npmBuildScript = "build:tsgo"; - makeCacheWritable = true; - nativeBuildInputs = with pkgs; [ - (pkgs.python3.withPackages (ps: with ps; [ setuptools ])) - ]; + meta = with lib; { + description = "Spacebar server, a FOSS reimplementation of the Discord backend."; + homepage = "https://github.com/spacebarchat/server"; + license = licenses.agpl3Plus; + platforms = platforms.all; + mainProgram = "start-bundle"; + maintainers = with maintainers; [ RorySys ]; + }; + + src = filteredSrc; + dontStrip = true; + + nativeBuildInputs = with pkgs; [ + nodejs + (pkgs.python3.withPackages (ps: with ps; [ setuptools ])) + ]; + + configurePhase = '' + cp -r --no-preserve=ownership,timestamps ${pkgs.callPackage ./node-modules.nix { }} node_modules + chown $USER:$GROUP node_modules -R + chmod +w node_modules -R + + # Apply pnpm-only patches (patch-package format patches are already applied by npm postinstall) + # discord-protos uses pnpm patch format (no version suffix) and needs explicit application + if [ -d node_modules/discord-protos ] && [ -f patches/discord-protos.patch ]; then + echo "Applying discord-protos patch (pnpm format)" + (cd node_modules/discord-protos && patch -p1 < ../../patches/discord-protos.patch) + fi + ''; + + buildPhase = '' + npm run build:tsgo + ''; - installPhase = - let - revsFile = pkgs.writeText "spacebar-server-rev.json" ( - builtins.toJSON { - rev = self.sourceInfo.rev or self.sourceInfo.dirtyRev; - shortRev = self.sourceInfo.shortRev or self.sourceInfo.dirtyShortRev; - lastModified = self.sourceInfo.lastModified; - } - ); - in - '' + installPhase = '' runHook preInstall # set -x # remove packages not needed for production, or at least try to... - npm prune --omit dev --no-save $npmInstallFlags "''${npmInstallFlagsArray[@]}" $npmFlags "''${npmFlagsArray[@]}" + npm prune --omit dev --no-save --offline rm -v dist/src.tsbuildinfo rm -rv scripts time ${./nix/trimNodeModules.sh} @@ -72,19 +85,49 @@ pkgs.buildNpmPackage { echo "Installing package into $out" mkdir -p $out cp -r assets dist node_modules package.json $out/ - cp ${revsFile} $out/.rev - - # Create wrappers for start scripts - echo "Creating wrappers for start scripts" - for i in dist/**/start.js - do - makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/start-`dirname ''${i/dist\//}` --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/$i - done - makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/apply-migrations --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/dist/apply-migrations.js # set +x runHook postInstall ''; + passthru.tests = pkgs.testers.runNixOSTest (import ./nix/tests/test-bundle-starts.nix self); + }; +in +pkgs.stdenv.mkDerivation { + pname = "spacebar-server-ts"; + nodejs = unwrapped.nodejs; + version = "1.0.0-" + rVersion; + meta = unwrapped.meta; + + nativeBuildInputs = with pkgs; [ + makeWrapper + ]; + + # this isnt a real builder, we dont need these at all + dontUnpack = true; + dontBuild = true; + dontPatch = true; + dontConfigure = true; + dontStrip = true; + dontFixup = true; + + installPhase = '' + # Copy outputs + echo "Installing package into $out" + mkdir -p $out + cp -r --no-preserve=ownership,timestamps ${unwrapped}/. $out/ + + # add version info + cp ${revsFile} $out/.rev + + # Create wrappers for start scripts + echo "Creating wrappers for start scripts" + for i in $out/dist/**/start.js + do + makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/start-`dirname ''${i#$out/dist/}` --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $i + done + makeWrapper ${pkgs.nodejs_24}/bin/node $out/bin/apply-migrations --prefix NODE_PATH : $out/node_modules --add-flags --enable-source-maps --add-flags $out/dist/apply-migrations.js + ''; + passthru.tests = pkgs.testers.runNixOSTest (import ./nix/tests/test-bundle-starts.nix self); } diff --git a/eslint.config.mjs b/eslint.config.mjs index eb865c2ac..3f609d7bb 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -17,7 +17,7 @@ const compat = new FlatCompat({ export default defineConfig([ { - ignores: ["**/node_modules", "**/dist", "**/README.md", "**/COPYING", "**/scripts/", "**/assets", "**/extra/"], + ignores: ["**/node_modules", "**/dist", "**/README.md", "**/COPYING", "**/scripts/", "**/assets", "**/extra/", "**/paper-recommender-extension/"], }, ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), { diff --git a/extra/admin-api/.idea/.idea.SpacebarAdminAPI/.idea/sqldialects.xml b/extra/admin-api/.idea/.idea.SpacebarAdminAPI/.idea/sqldialects.xml index 0f3b7c205..ba3f74620 100644 --- a/extra/admin-api/.idea/.idea.SpacebarAdminAPI/.idea/sqldialects.xml +++ b/extra/admin-api/.idea/.idea.SpacebarAdminAPI/.idea/sqldialects.xml @@ -1,6 +1,7 @@ + \ No newline at end of file diff --git a/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/deps.json b/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/deps.json index aead01783..79f44dd42 100644 --- a/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/deps.json +++ b/extra/admin-api/DataMappings/Spacebar.DataMappings.Generic/deps.json @@ -1,153 +1,58 @@ [ - { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" - }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.0", - "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.0", - "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.0", - "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" + "version": "10.0.4", + "hash": "sha256-/vLXWvT42HQAm/JLjWGeFo9AvLn/mu4nCNjabm+wABE=" }, { "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" + "version": "10.0.4", + "hash": "sha256-+0sK/vSyB4KFC9kliECROfK1WaiWwlRrhLpi03pT+3w=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" + "version": "10.0.4", + "hash": "sha256-bvEQLGSOpJHKdPD6kd59IIi4x57lKapVMgOORtcjJPs=" }, { "pname": "Microsoft.Extensions.DependencyInjection", - "version": "9.0.0", - "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + "version": "10.0.4", + "hash": "sha256-hbpKNzN0KIszhlpM5iAT/V3B7QRs3FGTZI9Wq/cYc1A=" }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "9.0.0", - "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" - }, - { - "pname": "Microsoft.Extensions.Logging", "version": "10.0.0", - "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" }, { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.4", + "hash": "sha256-0QhVYjk9Cxy6NFef9VKftGmscTZnvcD1bhBQoXz3mwA=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + "version": "10.0.4", + "hash": "sha256-eneXBu83dGBiWMFabheGFPYiZJQ+WMewG6bTs2oJ7RA=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", @@ -156,92 +61,27 @@ }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + "version": "10.0.4", + "hash": "sha256-dvBRBgEf4Bw9NW2J2XDlEeJw2TNhJ+6gV98dPE13/J0=" }, { "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.4", + "hash": "sha256-ybyUtpSs/irdarkVjsGhqDcj0w8TNUgh0Kp0j3EVfIA=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" - }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + "version": "10.0.4", + "hash": "sha256-ePpEFzrKQbEMY6Kh/xyxKHq6txNru62Vh4LVZO5Rrvg=" }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" } ] diff --git a/extra/admin-api/Interop/Spacebar.Interop.Authentication.AspNetCore/deps.json b/extra/admin-api/Interop/Spacebar.Interop.Authentication.AspNetCore/deps.json index 707a54648..0e58479d5 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Authentication.AspNetCore/deps.json +++ b/extra/admin-api/Interop/Spacebar.Interop.Authentication.AspNetCore/deps.json @@ -5,283 +5,63 @@ "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" }, { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" + "pname": "BCrypt.Net-Next", + "version": "4.1.0", + "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.0", - "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.0", - "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" - }, - { - "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.0", - "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" - }, - { - "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" - }, - { - "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" - }, - { - "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.0", - "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "9.0.0", - "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.0", - "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.0", - "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.IdentityModel.Abstractions", - "version": "8.15.0", - "hash": "sha256-LKTvERNUTMCEF7xs377tCMwOMRki93OS4kh6Yv0uXJ4=" + "version": "8.16.0", + "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" }, { "pname": "Microsoft.IdentityModel.JsonWebTokens", - "version": "8.15.0", - "hash": "sha256-LwzKiGjcnORvmQ9tim6lomXpfVlPpd/fE8FKTFWKlpM=" + "version": "8.16.0", + "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" }, { "pname": "Microsoft.IdentityModel.Logging", - "version": "8.15.0", - "hash": "sha256-mMXwsjGcrrmHR1mG7BLTKg/30mX+m93QVX17/ynOOd4=" + "version": "8.16.0", + "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" }, { "pname": "Microsoft.IdentityModel.Tokens", - "version": "8.15.0", - "hash": "sha256-7Lo/TsvqgNCEMyFssO3Om233521Pqgb9K9lUeHc5HMk=" - }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + "version": "8.16.0", + "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" }, { "pname": "System.IdentityModel.Tokens.Jwt", - "version": "8.15.0", - "hash": "sha256-5O0wbGp0gWnukK+0mWBjMnP1bZc6N0xuNcO2qmFiUX8=" + "version": "8.16.0", + "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" } -] \ No newline at end of file +] diff --git a/extra/admin-api/Interop/Spacebar.Interop.Authentication/Spacebar.Interop.Authentication.csproj b/extra/admin-api/Interop/Spacebar.Interop.Authentication/Spacebar.Interop.Authentication.csproj index f4f4e6289..eb0e47f7f 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Authentication/Spacebar.Interop.Authentication.csproj +++ b/extra/admin-api/Interop/Spacebar.Interop.Authentication/Spacebar.Interop.Authentication.csproj @@ -8,14 +8,15 @@ + - - - + + + diff --git a/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationService.cs b/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationService.cs index 1094a7146..46ae248f6 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationService.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Authentication/SpacebarAuthenticationService.cs @@ -12,22 +12,32 @@ public class SpacebarAuthenticationService(ILogger UserCache = new(); private static readonly ExpiringSemaphoreCache SessionCache = new(); - public async Task ValidateTokenAsync(string token) { - var handler = new JwtSecurityTokenHandler(); + private static bool _isInitialised; + private static readonly JwtSecurityTokenHandler Handler = new(); + + private static readonly TokenValidationParameters TokenValidationParameters = new() { + // IssuerSigningKey = new ECDsaSecurityKey(key), + ValidAlgorithms = ["ES512"], + LogValidationExceptions = true, + // These are required to be false for the token to be valid as they aren't provided by the token + ValidateIssuer = false, + ValidateLifetime = false, + ValidateAudience = false, + // TryAllIssuerSigningKeys = true + }; + + public async Task InitializeAsync() { + if (_isInitialised) return; var secretFile = await File.ReadAllTextAsync(config.PublicKeyPath); var key = ECDsa.Create(ECCurve.NamedCurves.nistP256); key.ImportFromPem(secretFile); + TokenValidationParameters.IssuerSigningKey = new ECDsaSecurityKey(key); + _isInitialised = true; + } - var res = await handler.ValidateTokenAsync(token, new TokenValidationParameters { - IssuerSigningKey = new ECDsaSecurityKey(key), - ValidAlgorithms = ["ES512"], - LogValidationExceptions = true, - // These are required to be false for the token to be valid as they aren't provided by the token - ValidateIssuer = false, - ValidateLifetime = false, - ValidateAudience = false, - // TryAllIssuerSigningKeys = true - }); + public async Task ValidateTokenAsync(string token) { + if (!_isInitialised) await InitializeAsync(); + var res = await Handler.ValidateTokenAsync(token, TokenValidationParameters); if ((!res.IsValid || res.Exception is not null) && !config.DisableAuthentication) { logger.LogInformation("Invalid token"); @@ -58,4 +68,10 @@ public async Task GetCurrentSessionAsync(string token) { }, config.AuthCacheExpiry); } + + // public async Task GenerateAccessTokenAsync(string userId) { + // // await db.Sessions.AddAsync(new() { + // + // // }) + // } } \ No newline at end of file diff --git a/extra/admin-api/Interop/Spacebar.Interop.Authentication/deps.json b/extra/admin-api/Interop/Spacebar.Interop.Authentication/deps.json index f436bccb9..a1b73ba77 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Authentication/deps.json +++ b/extra/admin-api/Interop/Spacebar.Interop.Authentication/deps.json @@ -5,129 +5,64 @@ "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" }, { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" + "pname": "BCrypt.Net-Next", + "version": "4.1.0", + "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.0", - "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.0", - "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" + "version": "10.0.4", + "hash": "sha256-/vLXWvT42HQAm/JLjWGeFo9AvLn/mu4nCNjabm+wABE=" }, { "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.0", - "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" + "version": "10.0.4", + "hash": "sha256-+0sK/vSyB4KFC9kliECROfK1WaiWwlRrhLpi03pT+3w=" }, { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" + "pname": "Microsoft.Extensions.Configuration", + "version": "10.0.5", + "hash": "sha256-6rOmJD7Jzq5MPLDd1aV+7gCQwIM9j4c+iT1pGea/daI=" }, { - "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.4", + "hash": "sha256-bvEQLGSOpJHKdPD6kd59IIi4x57lKapVMgOORtcjJPs=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" + "version": "10.0.5", + "hash": "sha256-DNK+lL2jeHFYyd43zfgVY32UskEfQ4YsTapztuQbYwo=" }, { "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" + "version": "10.0.5", + "hash": "sha256-cVG2NEW1rgLfeq/Gnh/XXqzDx2Tt8ecvgCAB4uFzcQo=" }, { "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" + "version": "10.0.5", + "hash": "sha256-ofDRirUV9XLSz4oksCqErwBJFtAieHACFfyZukHKFng=" }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", @@ -136,33 +71,18 @@ }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" + "version": "10.0.4", + "hash": "sha256-0QhVYjk9Cxy6NFef9VKftGmscTZnvcD1bhBQoXz3mwA=" }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "9.0.0", - "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.0", - "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" + "version": "10.0.5", + "hash": "sha256-KrP+hE3gk7pATbJYZsJ1LHiXjzLA+ntHW7G/VGgHk2g=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + "version": "10.0.4", + "hash": "sha256-eneXBu83dGBiWMFabheGFPYiZJQ+WMewG6bTs2oJ7RA=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", @@ -171,117 +91,57 @@ }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" + "version": "10.0.4", + "hash": "sha256-dvBRBgEf4Bw9NW2J2XDlEeJw2TNhJ+6gV98dPE13/J0=" }, { "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" + "version": "10.0.4", + "hash": "sha256-ybyUtpSs/irdarkVjsGhqDcj0w8TNUgh0Kp0j3EVfIA=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.4", + "hash": "sha256-ePpEFzrKQbEMY6Kh/xyxKHq6txNru62Vh4LVZO5Rrvg=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + "version": "10.0.5", + "hash": "sha256-uvrur+0dg4zAAQcpLkkhPA77ST0tA3+EpGdDlCckC+E=" }, { "pname": "Microsoft.IdentityModel.Abstractions", - "version": "8.15.0", - "hash": "sha256-LKTvERNUTMCEF7xs377tCMwOMRki93OS4kh6Yv0uXJ4=" + "version": "8.16.0", + "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" }, { "pname": "Microsoft.IdentityModel.JsonWebTokens", - "version": "8.15.0", - "hash": "sha256-LwzKiGjcnORvmQ9tim6lomXpfVlPpd/fE8FKTFWKlpM=" + "version": "8.16.0", + "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" }, { "pname": "Microsoft.IdentityModel.Logging", - "version": "8.15.0", - "hash": "sha256-mMXwsjGcrrmHR1mG7BLTKg/30mX+m93QVX17/ynOOd4=" + "version": "8.16.0", + "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" }, { "pname": "Microsoft.IdentityModel.Tokens", - "version": "8.15.0", - "hash": "sha256-7Lo/TsvqgNCEMyFssO3Om233521Pqgb9K9lUeHc5HMk=" - }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + "version": "8.16.0", + "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" }, { "pname": "System.IdentityModel.Tokens.Jwt", - "version": "8.15.0", - "hash": "sha256-5O0wbGp0gWnukK+0mWBjMnP1bZc6N0xuNcO2qmFiUX8=" + "version": "8.16.0", + "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" } ] diff --git a/extra/admin-api/Interop/Spacebar.Interop.Cdn.Abstractions/FilesystemFileSource.cs b/extra/admin-api/Interop/Spacebar.Interop.Cdn.Abstractions/FilesystemFileSource.cs index a5cee2c0b..a3f0a7d87 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Cdn.Abstractions/FilesystemFileSource.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Cdn.Abstractions/FilesystemFileSource.cs @@ -3,10 +3,6 @@ namespace Spacebar.Interop.Cdn.Abstractions; public class FilesystemFileSource(string baseUrl) : IFileSource { - private readonly StreamingHttpClient _httpClient = new() { - BaseAddress = new Uri(baseUrl) - }; - public string BaseUrl => baseUrl; public async Task Init(CancellationToken? cancellationToken = null) { @@ -46,4 +42,7 @@ public async Task WriteFile(string path, Stream stream) { // using var mic = new MagickImageCollection(stream); // return Mimes.GetMime(mic.First().Format); // } + public Task DirectoryExists(string path) { + return Task.FromResult(Directory.Exists(Path.Join(baseUrl, path))); + } } \ No newline at end of file diff --git a/extra/admin-api/Interop/Spacebar.Interop.Cdn.Signing/Spacebar.Interop.Cdn.Signing.csproj b/extra/admin-api/Interop/Spacebar.Interop.Cdn.Signing/Spacebar.Interop.Cdn.Signing.csproj index afa8afbaf..65f5d0caa 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Cdn.Signing/Spacebar.Interop.Cdn.Signing.csproj +++ b/extra/admin-api/Interop/Spacebar.Interop.Cdn.Signing/Spacebar.Interop.Cdn.Signing.csproj @@ -7,7 +7,7 @@ - + diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ISpacebarReplication.cs b/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ISpacebarReplication.cs index 95ee07f39..c568c98dc 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ISpacebarReplication.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ISpacebarReplication.cs @@ -2,5 +2,6 @@ public interface ISpacebarReplication { public Task InitializeAsync(); - public Task SendAsync(ReplicationMessage message); -} \ No newline at end of file + public Task SendAsync(ContentlessReplicationMessage message); + public Task SendAsync(ReplicationMessage message); +} diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ReplicationMessage.cs b/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ReplicationMessage.cs index 99b8a126c..630ef47e4 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ReplicationMessage.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.Abstractions/ReplicationMessage.cs @@ -2,7 +2,7 @@ namespace Spacebar.Interop.Replication.Abstractions; -public class ReplicationMessage { +public class ContentlessReplicationMessage { [JsonPropertyName("channel_id")] public string? ChannelId { get; set; } @@ -12,6 +12,9 @@ public class ReplicationMessage { [JsonPropertyName("user_id")] public string? UserId { get; set; } + [JsonPropertyName("session_id")] + public string? SessionId { get; set; } + [JsonPropertyName("created_at")] public DateTime? CreatedAt { get; set; } @@ -21,6 +24,11 @@ public class ReplicationMessage { [JsonPropertyName("origin")] public string? Origin { get; set; } + [JsonPropertyName("reconnect_delay")] + public int? ReconnectDelay { get; set; } +} + +public class ReplicationMessage : ContentlessReplicationMessage { [JsonPropertyName("data")] - public object Payload { get; set; } = null!; + public TPayload Payload { get; set; } = default!; } \ No newline at end of file diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/RabbitMqSpacebarReplication.cs b/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/RabbitMqSpacebarReplication.cs index f979d923a..b818b13ea 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/RabbitMqSpacebarReplication.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/RabbitMqSpacebarReplication.cs @@ -22,16 +22,17 @@ public async Task InitializeAsync() { _mqChannel = await _mqConnection.CreateChannelAsync(); } - public async Task SendAsync(ReplicationMessage message) { + // HACK: body is required in rabbitmq... + private async Task SendAsyncInternal(ContentlessReplicationMessage message, object? body = null) { var exchangeId = message.GuildId ?? message.ChannelId ?? message.UserId ?? "global"; await _mqChannel.ExchangeDeclareAsync(exchange: exchangeId, type: ExchangeType.Fanout, durable: false); var props = new BasicProperties() { Type = message.Event }; var publishSuccess = false; - var body = message.Payload.ToJson().AsBytes().ToArray(); // TODO: byte array payloads etc someday? + var encodedBody = body.ToJson().AsBytes().ToArray(); // TODO: byte array payloads etc someday? do { try { - await _mqChannel.BasicPublishAsync(exchange: exchangeId, routingKey: "", mandatory: true, basicProperties: props, body: body); + await _mqChannel.BasicPublishAsync(exchange: exchangeId, routingKey: "", mandatory: true, basicProperties: props, body: encodedBody); publishSuccess = true; } catch (Exception e) { @@ -40,4 +41,7 @@ public async Task SendAsync(ReplicationMessage message) { } } while (!publishSuccess); } + + public async Task SendAsync(ContentlessReplicationMessage message) => await SendAsyncInternal(message); + public async Task SendAsync(ReplicationMessage message) => await SendAsyncInternal(message, message.Payload); } \ No newline at end of file diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/Spacebar.Interop.Replication.RabbitMq.csproj b/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/Spacebar.Interop.Replication.RabbitMq.csproj index 7cc89b437..8a23201e4 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/Spacebar.Interop.Replication.RabbitMq.csproj +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/Spacebar.Interop.Replication.RabbitMq.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/deps.json b/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/deps.json index 68ec5c285..f2af5d4ed 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/deps.json +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.RabbitMq/deps.json @@ -6,28 +6,28 @@ }, { "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" + "version": "10.0.5", + "hash": "sha256-6rOmJD7Jzq5MPLDd1aV+7gCQwIM9j4c+iT1pGea/daI=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" + "version": "10.0.5", + "hash": "sha256-DNK+lL2jeHFYyd43zfgVY32UskEfQ4YsTapztuQbYwo=" }, { "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" + "version": "10.0.5", + "hash": "sha256-cVG2NEW1rgLfeq/Gnh/XXqzDx2Tt8ecvgCAB4uFzcQo=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.5", + "hash": "sha256-uvrur+0dg4zAAQcpLkkhPA77ST0tA3+EpGdDlCckC+E=" }, { "pname": "RabbitMQ.Client", - "version": "7.2.0", - "hash": "sha256-NkEb2ey0jo/OAeaVOUwIeSbeplqkOsgv1+ys8ZQgemQ=" + "version": "7.2.1", + "hash": "sha256-5sW2xJy8WxyN2sAh3iWvb0+QuJSBO5xXOTdFfk0dnpM=" }, { "pname": "System.Threading.RateLimiting", diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/Spacebar.Interop.Replication.UnixSocket.csproj b/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/Spacebar.Interop.Replication.UnixSocket.csproj index 7de7d0251..0d86aa534 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/Spacebar.Interop.Replication.UnixSocket.csproj +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/Spacebar.Interop.Replication.UnixSocket.csproj @@ -12,7 +12,7 @@ - + diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/UnixSocketSpacebarReplication.cs b/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/UnixSocketSpacebarReplication.cs index a817335dd..1c23c1c47 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/UnixSocketSpacebarReplication.cs +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/UnixSocketSpacebarReplication.cs @@ -20,7 +20,7 @@ public async Task InitializeAsync() { }; } - public async Task SendAsync(ReplicationMessage message) { + public async Task SendAsync(ContentlessReplicationMessage message) { // message format: [uint32be length][payload] var payload = JsonSerializer.SerializeToUtf8Bytes(message); byte[] formattedPayload = [..BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(payload.Length)), ..payload]; @@ -30,11 +30,13 @@ public async Task SendAsync(ReplicationMessage message) { skv.Value.SendAsync(formattedPayload); }); } + + async Task ISpacebarReplication.SendAsync(ReplicationMessage message) => await SendAsync(message); } public class UnixSocketConfiguration { public UnixSocketConfiguration(IConfiguration config) { - config.GetRequiredSection("UnixSocketReplication").Bind(this); + config.GetRequiredSection("Spacebar").GetRequiredSection("UnixSocketReplication").Bind(this); } public string SocketDir { get; set; } = null!; diff --git a/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/deps.json b/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/deps.json index 8e51ad6c2..f35bcf257 100644 --- a/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/deps.json +++ b/extra/admin-api/Interop/Spacebar.Interop.Replication.UnixSocket/deps.json @@ -1,22 +1,22 @@ [ { "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" + "version": "10.0.5", + "hash": "sha256-6rOmJD7Jzq5MPLDd1aV+7gCQwIM9j4c+iT1pGea/daI=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" + "version": "10.0.5", + "hash": "sha256-DNK+lL2jeHFYyd43zfgVY32UskEfQ4YsTapztuQbYwo=" }, { "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" + "version": "10.0.5", + "hash": "sha256-cVG2NEW1rgLfeq/Gnh/XXqzDx2Tt8ecvgCAB4uFzcQo=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.5", + "hash": "sha256-uvrur+0dg4zAAQcpLkkhPA77ST0tA3+EpGdDlCckC+E=" } ] diff --git a/extra/admin-api/Models/Spacebar.Models.AdminApi/DiscoverableGuildModel.cs b/extra/admin-api/Models/Spacebar.Models.AdminApi/DiscoverableGuildModel.cs new file mode 100644 index 000000000..f98d62b7e --- /dev/null +++ b/extra/admin-api/Models/Spacebar.Models.AdminApi/DiscoverableGuildModel.cs @@ -0,0 +1,139 @@ +using System.Text.Json.Serialization; + +namespace Spacebar.Models.AdminApi; + +public class DiscoverableGuildModel { + [JsonPropertyName("id")] + public string Id { get; set; } = null!; + + [JsonPropertyName("afk_channel_id")] + public string? AfkChannelId { get; set; } + + [JsonPropertyName("afk_timeout")] + public int? AfkTimeout { get; set; } + + [JsonPropertyName("banner")] + public string? Banner { get; set; } + + [JsonPropertyName("default_message_notifications")] + public int? DefaultMessageNotifications { get; set; } + + [JsonPropertyName("description")] + public string? Description { get; set; } + + [JsonPropertyName("discovery_splash")] + public string? DiscoverySplash { get; set; } + + [JsonPropertyName("explicit_content_filter")] + public int? ExplicitContentFilter { get; set; } + + [JsonPropertyName("features")] + public List Features { get; set; } + + [JsonPropertyName("primary_category_id")] + public string? PrimaryCategoryId { get; set; } + + [JsonPropertyName("icon")] + public string? Icon { get; set; } + + [JsonPropertyName("large")] + public bool Large { get; set; } + + [JsonPropertyName("max_members")] + public int? MaxMembers { get; set; } + + [JsonPropertyName("max_presences")] + public int? MaxPresences { get; set; } + + [JsonPropertyName("max_video_channel_users")] + public int? MaxVideoChannelUsers { get; set; } + + [JsonPropertyName("member_count")] + public int? MemberCount { get; set; } + + [JsonPropertyName("presence_count")] + public int? PresenceCount { get; set; } + + [JsonPropertyName("template_id")] + public string? TemplateId { get; set; } + + [JsonPropertyName("mfa_level")] + public int? MfaLevel { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } = null!; + + [JsonPropertyName("owner_id")] + public string? OwnerId { get; set; } + + [JsonPropertyName("preferred_locale")] + public string? PreferredLocale { get; set; } + + [JsonPropertyName("premium_subscription_count")] + public int? PremiumSubscriptionCount { get; set; } + + [JsonPropertyName("premium_tier")] + public int PremiumTier { get; set; } + + [JsonPropertyName("public_updates_channel_id")] + public string? PublicUpdatesChannelId { get; set; } + + [JsonPropertyName("rules_channel_id")] + public string? RulesChannelId { get; set; } + + [JsonPropertyName("region")] + public string? Region { get; set; } + + [JsonPropertyName("splash")] + public string? Splash { get; set; } + + [JsonPropertyName("system_channel_id")] + public string? SystemChannelId { get; set; } + + [JsonPropertyName("system_channel_flags")] + public int? SystemChannelFlags { get; set; } + + [JsonPropertyName("unavailable")] + public bool Unavailable { get; set; } + + [JsonPropertyName("verification_level")] + public int? VerificationLevel { get; set; } + + [JsonPropertyName("welcome_screen")] + public string WelcomeScreen { get; set; } = null!; + + [JsonPropertyName("widget_channel_id")] + public string? WidgetChannelId { get; set; } + + [JsonPropertyName("widget_enabled")] + public bool WidgetEnabled { get; set; } + + [JsonPropertyName("nsfw_level")] + public int? NsfwLevel { get; set; } + + [JsonPropertyName("nsfw")] + public bool Nsfw { get; set; } + + [JsonPropertyName("parent")] + public string? Parent { get; set; } + + [JsonPropertyName("premium_progress_bar_enabled")] + public bool? PremiumProgressBarEnabled { get; set; } + + [JsonPropertyName("channel_ordering")] + public List ChannelOrdering { get; set; } + + [JsonPropertyName("discovery_weight")] + public int DiscoveryWeight { get; set; } + + [JsonPropertyName("discovery_excluded")] + public bool DiscoveryExcluded { get; set; } +} + +public class DiscoverableGuildUpdateModel { + [JsonPropertyName("discovery_weight")] + public int? DiscoveryWeight { get; set; } + + [JsonPropertyName("discovery_excluded")] + public bool? DiscoveryExcluded { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Models/Spacebar.Models.AdminApi/ForceJoinRequest.cs b/extra/admin-api/Models/Spacebar.Models.AdminApi/ForceJoinRequest.cs index 495e51762..10c6eecfa 100644 --- a/extra/admin-api/Models/Spacebar.Models.AdminApi/ForceJoinRequest.cs +++ b/extra/admin-api/Models/Spacebar.Models.AdminApi/ForceJoinRequest.cs @@ -1,7 +1,12 @@ +using System.Text.Json.Serialization; + namespace Spacebar.Models.AdminApi; public class ForceJoinRequest { + [JsonPropertyName("user_id")] public string? UserId { get; set; } = null!; + [JsonPropertyName("make_admin")] public bool MakeAdmin { get; set; } = false; + [JsonPropertyName("make_owner")] public bool MakeOwner { get; set; } = false; } \ No newline at end of file diff --git a/extra/admin-api/Models/Spacebar.Models.Config/SecurityConfiguration.cs b/extra/admin-api/Models/Spacebar.Models.Config/SecurityConfiguration.cs index e1592c8d4..88681d2df 100644 --- a/extra/admin-api/Models/Spacebar.Models.Config/SecurityConfiguration.cs +++ b/extra/admin-api/Models/Spacebar.Models.Config/SecurityConfiguration.cs @@ -9,9 +9,6 @@ public class SecurityConfiguration { [JsonPropertyName("twoFactor")] public TwoFactorConfiguration TwoFactor { get; set; } = new(); - [JsonPropertyName("autoUpdate")] - public bool AutoUpdate { get; set; } = true; - [JsonPropertyName("requestSignature")] public string RequestSignature; // {get;set;}=crypto.randomBytes(32).toString("base64"); [JsonPropertyName("jwtSecret")] diff --git a/extra/admin-api/Models/Spacebar.Models.Db/Contexts/SpacebarDbContext.cs b/extra/admin-api/Models/Spacebar.Models.Db/Contexts/SpacebarDbContext.cs index 2a456964a..f8e58a57f 100644 --- a/extra/admin-api/Models/Spacebar.Models.Db/Contexts/SpacebarDbContext.cs +++ b/extra/admin-api/Models/Spacebar.Models.Db/Contexts/SpacebarDbContext.cs @@ -83,6 +83,8 @@ public SpacebarDbContext(DbContextOptions options) public virtual DbSet StreamSessions { get; set; } + public virtual DbSet Tags { get; set; } + public virtual DbSet Teams { get; set; } public virtual DbSet TeamMembers { get; set; } @@ -593,6 +595,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasOne(d => d.User).WithMany(p => p.StreamSessions).HasConstraintName("FK_13ae5c29aff4d0890c54179511a"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK_e7dc17249a1148a1970748eda99"); + + entity.HasOne(d => d.Channel).WithMany(p => p.Tags).HasConstraintName("FK_2e2df07f6dacc12e1932b361fe4"); + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PK_7e5523774a38b08a6236d322403"); @@ -694,7 +703,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasOne(d => d.SourceChannel).WithMany(p => p.WebhookSourceChannels) .OnDelete(DeleteBehavior.Cascade) - .HasConstraintName("FK_4495b7032a33c6b8b605d030398"); + .HasConstraintName("fk_d64f38834fa676f6caa4786ddd6"); entity.HasOne(d => d.SourceGuild).WithMany(p => p.WebhookSourceGuilds) .OnDelete(DeleteBehavior.Cascade) diff --git a/extra/admin-api/Models/Spacebar.Models.Db/Models/Channel.cs b/extra/admin-api/Models/Spacebar.Models.Db/Models/Channel.cs index 536e73c33..6e70aaccd 100644 --- a/extra/admin-api/Models/Spacebar.Models.Db/Models/Channel.cs +++ b/extra/admin-api/Models/Spacebar.Models.Db/Models/Channel.cs @@ -85,6 +85,9 @@ public partial class Channel [Column("total_message_sent")] public int? TotalMessageSent { get; set; } + [Column("applied_tags")] + public List? AppliedTags { get; set; } + [InverseProperty("Channel")] public virtual ICollection CloudAttachments { get; set; } = new List(); @@ -136,6 +139,9 @@ public partial class Channel [InverseProperty("Channel")] public virtual ICollection Streams { get; set; } = new List(); + [InverseProperty("Channel")] + public virtual ICollection Tags { get; set; } = new List(); + [InverseProperty("IdNavigation")] public virtual ICollection ThreadMembers { get; set; } = new List(); diff --git a/extra/admin-api/Models/Spacebar.Models.Db/Models/Guild.cs b/extra/admin-api/Models/Spacebar.Models.Db/Models/Guild.cs index f997daf5a..9dbba150d 100644 --- a/extra/admin-api/Models/Spacebar.Models.Db/Models/Guild.cs +++ b/extra/admin-api/Models/Spacebar.Models.Db/Models/Guild.cs @@ -130,6 +130,12 @@ public partial class Guild [Column("channel_ordering")] public string ChannelOrdering { get; set; } = null!; + [Column("discovery_weight")] + public int DiscoveryWeight { get; set; } + + [Column("discovery_excluded")] + public bool DiscoveryExcluded { get; set; } + [ForeignKey("AfkChannelId")] [InverseProperty("GuildAfkChannels")] public virtual Channel? AfkChannel { get; set; } diff --git a/extra/admin-api/Models/Spacebar.Models.Db/Models/Tag.cs b/extra/admin-api/Models/Spacebar.Models.Db/Models/Tag.cs new file mode 100644 index 000000000..e128b4d81 --- /dev/null +++ b/extra/admin-api/Models/Spacebar.Models.Db/Models/Tag.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Spacebar.Models.Db.Models; + +[Table("tags")] +public partial class Tag +{ + [Key] + [Column("id", TypeName = "character varying")] + public string Id { get; set; } = null!; + + [Column("channel_id", TypeName = "character varying")] + public string ChannelId { get; set; } = null!; + + [Column("name", TypeName = "character varying")] + public string Name { get; set; } = null!; + + [Column("moderated")] + public bool Moderated { get; set; } + + [Column("emoji_id", TypeName = "character varying")] + public string? EmojiId { get; set; } + + [Column("emoji_name", TypeName = "character varying")] + public string? EmojiName { get; set; } + + [ForeignKey("ChannelId")] + [InverseProperty("Tags")] + public virtual Channel Channel { get; set; } = null!; +} diff --git a/extra/admin-api/Models/Spacebar.Models.Db/Models/User.cs b/extra/admin-api/Models/Spacebar.Models.Db/Models/User.cs index 70fe581d0..f0aa08790 100644 --- a/extra/admin-api/Models/Spacebar.Models.Db/Models/User.cs +++ b/extra/admin-api/Models/Spacebar.Models.Db/Models/User.cs @@ -89,7 +89,7 @@ public partial class User [Column("email", TypeName = "character varying")] public string? Email { get; set; } - [Column("flags", TypeName = "character varying")] + [Column("flags")] public ulong Flags { get; set; } [Column("public_flags")] diff --git a/extra/admin-api/Models/Spacebar.Models.Db/Spacebar.Models.Db.csproj b/extra/admin-api/Models/Spacebar.Models.Db/Spacebar.Models.Db.csproj index 05faafe83..523201b66 100644 --- a/extra/admin-api/Models/Spacebar.Models.Db/Spacebar.Models.Db.csproj +++ b/extra/admin-api/Models/Spacebar.Models.Db/Spacebar.Models.Db.csproj @@ -7,8 +7,11 @@ - - + + + + + diff --git a/extra/admin-api/Models/Spacebar.Models.Db/deps.json b/extra/admin-api/Models/Spacebar.Models.Db/deps.json index aead01783..79f44dd42 100644 --- a/extra/admin-api/Models/Spacebar.Models.Db/deps.json +++ b/extra/admin-api/Models/Spacebar.Models.Db/deps.json @@ -1,153 +1,58 @@ [ - { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" - }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.0", - "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.0", - "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.0", - "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" + "version": "10.0.4", + "hash": "sha256-/vLXWvT42HQAm/JLjWGeFo9AvLn/mu4nCNjabm+wABE=" }, { "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" + "version": "10.0.4", + "hash": "sha256-+0sK/vSyB4KFC9kliECROfK1WaiWwlRrhLpi03pT+3w=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" + "version": "10.0.4", + "hash": "sha256-bvEQLGSOpJHKdPD6kd59IIi4x57lKapVMgOORtcjJPs=" }, { "pname": "Microsoft.Extensions.DependencyInjection", - "version": "9.0.0", - "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + "version": "10.0.4", + "hash": "sha256-hbpKNzN0KIszhlpM5iAT/V3B7QRs3FGTZI9Wq/cYc1A=" }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "9.0.0", - "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" - }, - { - "pname": "Microsoft.Extensions.Logging", "version": "10.0.0", - "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" + "hash": "sha256-9iodXP39YqgxomnOPOxd/mzbG0JfOSXzFoNU3omT2Ps=" }, { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.4", + "hash": "sha256-0QhVYjk9Cxy6NFef9VKftGmscTZnvcD1bhBQoXz3mwA=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + "version": "10.0.4", + "hash": "sha256-eneXBu83dGBiWMFabheGFPYiZJQ+WMewG6bTs2oJ7RA=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", @@ -156,92 +61,27 @@ }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + "version": "10.0.4", + "hash": "sha256-dvBRBgEf4Bw9NW2J2XDlEeJw2TNhJ+6gV98dPE13/J0=" }, { "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.4", + "hash": "sha256-ybyUtpSs/irdarkVjsGhqDcj0w8TNUgh0Kp0j3EVfIA=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" - }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + "version": "10.0.4", + "hash": "sha256-ePpEFzrKQbEMY6Kh/xyxKHq6txNru62Vh4LVZO5Rrvg=" }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" } ] diff --git a/extra/admin-api/Models/Spacebar.Models.Gateway/BulkMessageDeleteResponse.cs b/extra/admin-api/Models/Spacebar.Models.Gateway/BulkMessageDeleteResponse.cs new file mode 100644 index 000000000..f4cb669a5 --- /dev/null +++ b/extra/admin-api/Models/Spacebar.Models.Gateway/BulkMessageDeleteResponse.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Spacebar.Models.Gateway; + +public class BulkMessageDeleteResponse { + [JsonPropertyName("guild_id")] + public string? GuildId { get; set; } + + [JsonPropertyName("channel_id")] + public required string ChannelId { get; set; } + + [JsonPropertyName("ids")] + public required List MessageIds { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Models/Spacebar.Models.Gateway/ChannelStatusesRequest.cs b/extra/admin-api/Models/Spacebar.Models.Gateway/ChannelStatusesRequest.cs new file mode 100644 index 000000000..94737ffe4 --- /dev/null +++ b/extra/admin-api/Models/Spacebar.Models.Gateway/ChannelStatusesRequest.cs @@ -0,0 +1,64 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Spacebar.Models.Gateway; + +public class ChannelStatusesRequest { + [JsonPropertyName("guild_id")] + [JsonRequired] + public JsonValue GuildIdRawValue { get; set; } = null!; + + [JsonIgnore] + public string? GuildId { + get => GuildIdRawValue.GetValueKind() == JsonValueKind.String ? GuildIdRawValue.GetValue() : null; + [MemberNotNull] set => GuildIdRawValue = JsonValue.Create(value!); + } + + [JsonIgnore] + public List? GuildIds { + get => GuildIdRawValue.GetValueKind() == JsonValueKind.Array ? GuildIdRawValue.AsArray().Deserialize>() : null; + [MemberNotNull] set => GuildIdRawValue = JsonValue.Create(value!)!; + } +} + +public class ChannelInfoRequest : ChannelStatusesRequest { + [JsonPropertyName("fields")] + public required List Fields { get; set; } +} + +public class ChannelStatus { + [JsonPropertyName("id")] + public string ChannelId { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } +} + +public class ChannelStatusesResponse { + [JsonPropertyName("guild_id")] + public string GuildId { get; set; } + + [JsonPropertyName("channels")] + public List Channels { get; set; } +} + +public class ChannelInfo { + [JsonPropertyName("id")] + public required string ChannelId { get; set; } + + [JsonPropertyName("status")] + public string? Status { get; set; } + + [JsonPropertyName("voice_start_time")] + public DateTimeOffset? VoiceStartTime { get; set; } +} + +public class ChannelInfoResponse { + [JsonPropertyName("guild_id")] + public string GuildId { get; set; } + + [JsonPropertyName("channels")] + public List Channels { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Models/Spacebar.Models.Gateway/Spacebar.Models.Gateway.csproj b/extra/admin-api/Models/Spacebar.Models.Gateway/Spacebar.Models.Gateway.csproj index b104e193c..ac6bad9f0 100644 --- a/extra/admin-api/Models/Spacebar.Models.Gateway/Spacebar.Models.Gateway.csproj +++ b/extra/admin-api/Models/Spacebar.Models.Gateway/Spacebar.Models.Gateway.csproj @@ -7,7 +7,7 @@ - + diff --git a/extra/admin-api/Models/Spacebar.Models.Generic/Constants/Permissions.cs b/extra/admin-api/Models/Spacebar.Models.Generic/Constants/Permissions.cs new file mode 100644 index 000000000..03b4112a1 --- /dev/null +++ b/extra/admin-api/Models/Spacebar.Models.Generic/Constants/Permissions.cs @@ -0,0 +1,60 @@ +namespace Spacebar.Models.Generic.Constants; + +[Flags] +public enum Permissions : ulong { + CreateInstantInvite = 1UL << 0, + KickMembers = 1UL << 1, + BanMembers = 1UL << 2, + Administrator = 1UL << 3, + ManageChannels = 1UL << 4, + ManageGuild = 1UL << 5, + AddReactions = 1UL << 6, + ViewAuditLog = 1UL << 7, + PrioritySpeaker = 1UL << 8, + Stream = 1UL << 9, + ViewChannel = 1UL << 10, + SendMessages = 1UL << 11, + SendTtsMessages = 1UL << 12, + ManageMessages = 1UL << 13, + EmbedLinks = 1UL << 14, + AttachFiles = 1UL << 15, + ReadMessageHistory = 1UL << 16, + MentionEveryone = 1UL << 17, + UseExternalEmojis = 1UL << 18, + ViewGuildInsights = 1UL << 19, + Connect = 1UL << 20, + Speak = 1UL << 21, + MuteMembers = 1UL << 22, + DeafenMembers = 1UL << 23, + MoveMembers = 1UL << 24, + UseVad = 1UL << 25, + ChangeNickname = 1UL << 26, + ManageNicknames = 1UL << 27, + ManageRoles = 1UL << 28, + ManageWebhooks = 1UL << 29, + ManageExpressions = 1UL << 30, + UseApplicationCommands = 1UL << 31, + RequestToSpeak = 1UL << 32, + ManageEvents = 1UL << 33, + ManageThreads = 1UL << 34, + CreatePublicThreads = 1UL << 35, + CreatePrivateThreads = 1UL << 36, + UseExternalStickers = 1UL << 37, + SendMessagesInThreads = 1UL << 38, + UseEmbeddedActivities = 1UL << 39, + ModerateMembers = 1UL << 40, + ViewCreatorMonetizationAnalytics = 1UL << 41, + UseSoundboard = 1UL << 42, + CreateExpressions = 1UL << 43, + CreateEvents = 1UL << 44, + UseExternalSounds = 1UL << 45, + SendVoiceMessages = 1UL << 46, + + [Obsolete("Clyde is no longer available")] + UseClydeAi = 1UL << 47, + SetVoiceChannelStatus = 1UL << 48, + SendPolls = 1UL << 49, + UseExternalApps = 1UL << 50, + PinMessages = 1UL << 51, + BypassSlowmode = 1UL << 52 +} \ No newline at end of file diff --git a/extra/admin-api/Models/Spacebar.Models.Generic/Member.cs b/extra/admin-api/Models/Spacebar.Models.Generic/Member.cs index 9ead95595..a42cfe948 100644 --- a/extra/admin-api/Models/Spacebar.Models.Generic/Member.cs +++ b/extra/admin-api/Models/Spacebar.Models.Generic/Member.cs @@ -1,9 +1,12 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; namespace Spacebar.Models.Generic; [DebuggerDisplay("{User.Id} ({User.Username}#{User.Discriminator})")] +[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] +[SuppressMessage("ReSharper", "PropertyCanBeMadeInitOnly.Global")] public class Member { [JsonPropertyName("user")] public required PartialUser User { get; set; } diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/ChannelController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/ChannelController.cs new file mode 100644 index 000000000..cccc47609 --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/ChannelController.cs @@ -0,0 +1,135 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.AdminApi.Extensions; +using Spacebar.Models.AdminApi; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; +using Spacebar.Models.Gateway; + +namespace Spacebar.AdminApi.Controllers; + +[ApiController] +[Route("/channels")] +public class ChannelController( + ILogger logger, + SpacebarDbContext db, + IServiceProvider sp, + SpacebarAspNetAuthenticationService auth, + ISpacebarReplication replication +) : ControllerBase { + [HttpDelete("{id}")] + public async Task DeleteById(string id) { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + // TODO: proper type + await replication.SendAsync(new() { + Origin = "AdminApi/DeleteChannelById", + ChannelId = id, + Event = "CHANNEL_DELETE", + Payload = await db.Channels.SingleAsync(x => x.Id == id) + }); + + await db.Channels.Where(x => x.Id == id).ExecuteDeleteAsync(); + } + + private async IAsyncEnumerable DeleteMessagesForChannel( + // context + string? guildId, string channelId, string authorId, + // options + int messageDeleteChunkSize = 100 + ) { + { + await using var ctx = sp.CreateAsyncScope(); + await using var _db = ctx.ServiceProvider.GetRequiredService(); + var messagesInChannel = _db.Messages.AsNoTracking().Count(m => m.AuthorId == authorId && m.ChannelId == channelId && m.GuildId == guildId); + var remaining = messagesInChannel; + while (true) { + var messageIds = _db.Database.SqlQuery($""" + DELETE FROM messages + WHERE id IN ( + SELECT id FROM messages + WHERE author_id = {authorId} + AND channel_id = {channelId} + AND guild_id = {guildId} + LIMIT {messageDeleteChunkSize} + ) RETURNING id; + """).ToList(); + if (messageIds.Count == 0) { + break; + } + + await replication.SendAsync(new() { + ChannelId = channelId, + Event = "MESSAGE_BULK_DELETE", + Payload = new BulkMessageDeleteResponse() { + GuildId = guildId, + ChannelId = channelId, + MessageIds = messageIds, + }, + Origin = "Admin API (GuildController.DeleteUser)", + }); + + yield return new("BULK_DELETED", new { + channel_id = channelId, + total = messagesInChannel, + deleted = messageIds.Count, + remaining = remaining -= messageIds.Count, + }); + await Task.Yield(); + } + } + } + + private async IAsyncEnumerable AggregateAsyncEnumerablesWithoutOrder(params IEnumerable> enumerables) { + var enumerators = enumerables.Select(e => e.GetAsyncEnumerator()).ToList(); + var tasks = enumerators.Select(e => e.MoveNextAsync().AsTask()).ToList(); + + try { + while (tasks.Count > 0) { + var completedTask = await Task.WhenAny(tasks); + var completedTaskIndex = tasks.IndexOf(completedTask); + + if (completedTask.IsCanceled) { + try { + await enumerators[completedTaskIndex].DisposeAsync(); + } + catch { + // ignored + } + + enumerators.RemoveAt(completedTaskIndex); + tasks.RemoveAt(completedTaskIndex); + continue; + } + + if (await completedTask) { + var enumerator = enumerators[completedTaskIndex]; + yield return enumerator.Current; + tasks[completedTaskIndex] = enumerator.MoveNextAsync().AsTask(); + } + else { + try { + await enumerators[completedTaskIndex].DisposeAsync(); + } + catch { + // ignored + } + + enumerators.RemoveAt(completedTaskIndex); + tasks.RemoveAt(completedTaskIndex); + } + } + } + finally { + foreach (var enumerator in enumerators) { + try { + await enumerator.DisposeAsync(); + } + catch { + // ignored + } + } + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs new file mode 100644 index 000000000..b914abefd --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/DiscoveryController.cs @@ -0,0 +1,198 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.AdminApi.Extensions; +using Spacebar.Models.AdminApi; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; + +namespace Spacebar.AdminApi.Controllers; + +[ApiController] +[Route("/discovery/guilds")] +public class GuildDiscoveryController( + ILogger logger, + SpacebarDbContext db, + IServiceProvider sp, + SpacebarAspNetAuthenticationService auth, + ISpacebarReplication replication +) : ControllerBase { + [HttpGet] + public async IAsyncEnumerable GetDiscoverableGuilds(bool includeExcluded = false) { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + var discoverableGuilds = db.Guilds + .AsNoTracking() + .Where(x => (!x.DiscoveryExcluded || includeExcluded) && x.Features.Contains("DISCOVERABLE")) + .OrderByDescending(x => x.DiscoveryWeight) + .ThenByDescending(x => x.MemberCount); + await foreach (var guild in discoverableGuilds.AsAsyncEnumerable()) { + yield return new DiscoverableGuildModel() { + Id = guild.Id, + Features = guild.Features.Split(",").ToList(), + Banner = guild.Banner, + DiscoveryExcluded = guild.DiscoveryExcluded, + DiscoveryWeight = guild.DiscoveryWeight, + MemberCount = guild.MemberCount, + Name = guild.Name, + SystemChannelFlags = guild.SystemChannelFlags, + AfkChannelId = guild.AfkChannelId, + AfkTimeout = guild.AfkTimeout, + ChannelOrdering = guild.ChannelOrdering.Split(",").ToList(), + DefaultMessageNotifications = guild.DefaultMessageNotifications, + Description = guild.Description, + DiscoverySplash = guild.DiscoverySplash, + ExplicitContentFilter = guild.ExplicitContentFilter, + Icon = guild.Icon, + Large = guild.Large, + MaxMembers = guild.MaxMembers, + MaxPresences = guild.MaxPresences, + MaxVideoChannelUsers = guild.MaxVideoChannelUsers, + MfaLevel = guild.MfaLevel, + Nsfw = guild.Nsfw, + NsfwLevel = guild.NsfwLevel, + OwnerId = guild.OwnerId, + Parent = guild.Parent, + PreferredLocale = guild.PreferredLocale, + PremiumProgressBarEnabled = guild.PremiumProgressBarEnabled, + PremiumTier = guild.PremiumTier, + PremiumSubscriptionCount = guild.PremiumSubscriptionCount, + PresenceCount = guild.PresenceCount, + PrimaryCategoryId = guild.PrimaryCategoryId, + PublicUpdatesChannelId = guild.PublicUpdatesChannelId, + Region = guild.Region, + RulesChannelId = guild.RulesChannelId, + Splash = guild.Splash, + SystemChannelId = guild.SystemChannelId, + TemplateId = guild.TemplateId, + Unavailable = guild.Unavailable, + VerificationLevel = guild.VerificationLevel, + WelcomeScreen = guild.WelcomeScreen, + WidgetChannelId = guild.WidgetChannelId, + WidgetEnabled = guild.WidgetEnabled + }; + } + } + + [HttpGet("{guildId}")] + public async Task GetDiscoverableGuild(string guildId, bool includeExcluded = false) { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + var discoverableGuilds = db.Guilds + .AsNoTracking() + .Where(x => x.Id == guildId) + .Where(x => (!x.DiscoveryExcluded || includeExcluded) && x.Features.Contains("DISCOVERABLE")) + .OrderByDescending(x => x.DiscoveryWeight) + .ThenByDescending(x => x.MemberCount); + var guild = await discoverableGuilds.SingleAsync(); + return new DiscoverableGuildModel() { + Id = guild.Id, + Features = guild.Features.Split(",").ToList(), + Banner = guild.Banner, + DiscoveryExcluded = guild.DiscoveryExcluded, + DiscoveryWeight = guild.DiscoveryWeight, + MemberCount = guild.MemberCount, + Name = guild.Name, + SystemChannelFlags = guild.SystemChannelFlags, + AfkChannelId = guild.AfkChannelId, + AfkTimeout = guild.AfkTimeout, + ChannelOrdering = guild.ChannelOrdering.Split(",").ToList(), + DefaultMessageNotifications = guild.DefaultMessageNotifications, + Description = guild.Description, + DiscoverySplash = guild.DiscoverySplash, + ExplicitContentFilter = guild.ExplicitContentFilter, + Icon = guild.Icon, + Large = guild.Large, + MaxMembers = guild.MaxMembers, + MaxPresences = guild.MaxPresences, + MaxVideoChannelUsers = guild.MaxVideoChannelUsers, + MfaLevel = guild.MfaLevel, + Nsfw = guild.Nsfw, + NsfwLevel = guild.NsfwLevel, + OwnerId = guild.OwnerId, + Parent = guild.Parent, + PreferredLocale = guild.PreferredLocale, + PremiumProgressBarEnabled = guild.PremiumProgressBarEnabled, + PremiumTier = guild.PremiumTier, + PremiumSubscriptionCount = guild.PremiumSubscriptionCount, + PresenceCount = guild.PresenceCount, + PrimaryCategoryId = guild.PrimaryCategoryId, + PublicUpdatesChannelId = guild.PublicUpdatesChannelId, + Region = guild.Region, + RulesChannelId = guild.RulesChannelId, + Splash = guild.Splash, + SystemChannelId = guild.SystemChannelId, + TemplateId = guild.TemplateId, + Unavailable = guild.Unavailable, + VerificationLevel = guild.VerificationLevel, + WelcomeScreen = guild.WelcomeScreen, + WidgetChannelId = guild.WidgetChannelId, + WidgetEnabled = guild.WidgetEnabled + }; + } + + [HttpPatch("{guildId}")] + public async Task UpdateDiscoverableGuild(string guildId, [FromBody] DiscoverableGuildUpdateModel guildUpdateModel, bool includeExcluded = false) { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + var guild = await db.Guilds + .AsNoTracking() + .Where(x => x.Id == guildId) + .Where(x => (!x.DiscoveryExcluded || includeExcluded) && x.Features.Contains("DISCOVERABLE")) + .OrderByDescending(x => x.DiscoveryWeight) + .ThenByDescending(x => x.MemberCount) + .SingleAsync(); + + if (guildUpdateModel.DiscoveryExcluded != null) + guild.DiscoveryExcluded = guildUpdateModel.DiscoveryExcluded.Value; + + if (guildUpdateModel.DiscoveryWeight != null) + guild.DiscoveryWeight = guildUpdateModel.DiscoveryWeight.Value; + + db.Guilds.Update(guild); + await db.SaveChangesAsync(); + + return new DiscoverableGuildModel() { + Id = guild.Id, + Features = guild.Features.Split(",").ToList(), + Banner = guild.Banner, + DiscoveryExcluded = guild.DiscoveryExcluded, + DiscoveryWeight = guild.DiscoveryWeight, + MemberCount = guild.MemberCount, + Name = guild.Name, + SystemChannelFlags = guild.SystemChannelFlags, + AfkChannelId = guild.AfkChannelId, + AfkTimeout = guild.AfkTimeout, + ChannelOrdering = guild.ChannelOrdering.Split(",").ToList(), + DefaultMessageNotifications = guild.DefaultMessageNotifications, + Description = guild.Description, + DiscoverySplash = guild.DiscoverySplash, + ExplicitContentFilter = guild.ExplicitContentFilter, + Icon = guild.Icon, + Large = guild.Large, + MaxMembers = guild.MaxMembers, + MaxPresences = guild.MaxPresences, + MaxVideoChannelUsers = guild.MaxVideoChannelUsers, + MfaLevel = guild.MfaLevel, + Nsfw = guild.Nsfw, + NsfwLevel = guild.NsfwLevel, + OwnerId = guild.OwnerId, + Parent = guild.Parent, + PreferredLocale = guild.PreferredLocale, + PremiumProgressBarEnabled = guild.PremiumProgressBarEnabled, + PremiumTier = guild.PremiumTier, + PremiumSubscriptionCount = guild.PremiumSubscriptionCount, + PresenceCount = guild.PresenceCount, + PrimaryCategoryId = guild.PrimaryCategoryId, + PublicUpdatesChannelId = guild.PublicUpdatesChannelId, + Region = guild.Region, + RulesChannelId = guild.RulesChannelId, + Splash = guild.Splash, + SystemChannelId = guild.SystemChannelId, + TemplateId = guild.TemplateId, + Unavailable = guild.Unavailable, + VerificationLevel = guild.VerificationLevel, + WelcomeScreen = guild.WelcomeScreen, + WidgetChannelId = guild.WidgetChannelId, + WidgetEnabled = guild.WidgetEnabled + }; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs index f140cccd9..38d8b3c2c 100644 --- a/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/GuildController.cs @@ -6,6 +6,7 @@ using Spacebar.Interop.Authentication.AspNetCore; using Spacebar.Models.Db.Contexts; using Spacebar.Models.Db.Models; +using Spacebar.Models.Gateway; namespace Spacebar.AdminApi.Controllers; @@ -98,10 +99,15 @@ public async Task ForceJoinGuild([FromBody] ForceJoinRequest requ member = new Member { Id = userId, GuildId = id, - JoinedAt = DateTime.UtcNow, + JoinedAt = DateTime.Now, PremiumSince = 0, Roles = [await db.Roles.SingleAsync(r => r.Id == id)], - Pending = false + Pending = false, + Settings = "{}", + Bio = "", + Mute = false, + Deaf = false, + }; await db.Members.AddAsync(member); guild.MemberCount++; @@ -213,13 +219,13 @@ SELECT id FROM messages break; } - await replication.SendAsync(new() { + await replication.SendAsync(new() { ChannelId = channelId, Event = "MESSAGE_BULK_DELETE", - Payload = new { - ids = messageIds, - channel_id = channelId, - guild_id = guildId, + Payload = new() { + GuildId = guildId, + ChannelId = channelId, + MessageIds = messageIds, }, Origin = "Admin API (GuildController.DeleteUser)", }); diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/EmptyDmsController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/EmptyDmsController.cs new file mode 100644 index 000000000..862585b33 --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/EmptyDmsController.cs @@ -0,0 +1,54 @@ +using System.Diagnostics; +using ArcaneLibs; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.AdminApi.Extensions; +using Spacebar.Interop.Authentication; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.AdminApi; +using Spacebar.Models.Db.Contexts; + +namespace Spacebar.AdminApi.Controllers.TestControllers; + +[ApiController] +public class EmptyDmsController( + ILogger logger, + SpacebarAuthenticationConfiguration config, + SpacebarDbContext db, + IServiceProvider sp, + SpacebarAspNetAuthenticationService auth, + ISpacebarReplication replication +) : ControllerBase { + [HttpGet("emptydms")] + public async IAsyncEnumerable GetEmptyDms() { + (await auth.GetCurrentUserAsync(Request)).GetRights().AssertHasAllRights(SpacebarRights.Rights.OPERATOR); + + // TODO channel type enum + var channels = db.Channels + .Include(x=>x.Recipients) + .Include(x=>x.Messages) + .Where(x => x.Type == 1) + .Where(x => !x.Messages.Any() && x.Recipients.Count == 1) + ; + + await using var db2Scope = sp.CreateAsyncScope(); + await using var db3Scope = sp.CreateAsyncScope(); + var db2 = db2Scope.ServiceProvider.GetRequiredService(); + var db3 = db3Scope.ServiceProvider.GetRequiredService(); + int count = 0; + await foreach (var channel in channels.AsAsyncEnumerable()) { + count++; + yield return new { + id = channel.Id, + msgs = await db2.Messages.Where(x => x.ChannelId == channel.Id).CountAsync(), + recips = await db3.Recipients.Where(x => x.ChannelId == channel.Id).CountAsync() + }; + } + + logger.LogInformation("Got {count} empty DM channels", count); + + yield break; + + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs index 228606f79..6881e1e34 100644 --- a/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/TestControllers/IpcTestController.cs @@ -40,7 +40,8 @@ public async IAsyncEnumerable Test() { while (true) { var clr = re.Next(); color = clr.r << 16 | clr.g << 8 | clr.b; - await replication.SendAsync(new() { + // TODO: create type + await replication.SendAsync(new() { Event = "GUILD_ROLE_UPDATE", GuildId = guildId, Origin = "Admin API (GET /users/test)", diff --git a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs index 7b76aabde..f1f861d54 100644 --- a/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs +++ b/extra/admin-api/Spacebar.AdminApi/Controllers/UserController.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; -using ArcaneLibs; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Spacebar.AdminApi.Extensions; @@ -8,7 +6,7 @@ using Spacebar.Interop.Replication.Abstractions; using Spacebar.Models.AdminApi; using Spacebar.Models.Db.Contexts; -using Spacebar.Models.Db.Models; +using Spacebar.Models.Gateway; namespace Spacebar.AdminApi.Controllers; @@ -155,6 +153,12 @@ public async IAsyncEnumerable DeleteUser(string id, [FromQuer user.Rights = 0; db.Users.Update(user); await db.SaveChangesAsync(); + await replication.SendAsync(new() { + UserId = user.Id, + Event = "SB_SESSION_REMOVE", + Origin = "AdminAPI/User Delete", + CreatedAt = DateTime.UtcNow + }); var messages = db.Messages .AsNoTracking() @@ -184,6 +188,11 @@ public async IAsyncEnumerable DeleteUser(string id, [FromQuer } } + // [HttpGet("{id}/Dms")] + // public async IEnumerable GetDmsAsync(string userId) { + // yield break; // TODO + // } + private async IAsyncEnumerable DeleteMessagesForChannel( // context string? guildId, string channelId, string authorId, @@ -210,13 +219,13 @@ SELECT id FROM messages break; } - await replication.SendAsync(new() { + await replication.SendAsync(new() { Event = "MESSAGE_BULK_DELETE", ChannelId = channelId, - Payload = new { - channel_id = channelId, - guild_id = guildId, - ids = messageIds, + Payload = new() { + GuildId = guildId, + ChannelId = channelId, + MessageIds = messageIds, }, Origin = "AdminApi/DeleteMessagesForChannel" }); diff --git a/extra/admin-api/Spacebar.AdminApi/Program.cs b/extra/admin-api/Spacebar.AdminApi/Program.cs index b0f8f2090..3b3a9ef1d 100644 --- a/extra/admin-api/Spacebar.AdminApi/Program.cs +++ b/extra/admin-api/Spacebar.AdminApi/Program.cs @@ -36,6 +36,7 @@ // builder.Services.AddSingleton(); // builder.Services.AddSingleton(); // builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddRequestTimeouts(x => { @@ -59,7 +60,7 @@ var app = builder.Build(); app.Use((context, next) => { context.Response.Headers["Access-Control-Allow-Origin"] = "*"; - context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"; + context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS, PATCH"; context.Response.Headers["Access-Control-Allow-Headers"] = "*, Authorization"; if (context.Request.Method == "OPTIONS") { context.Response.StatusCode = 200; diff --git a/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj index 65824f818..6632d6d32 100644 --- a/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj +++ b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.csproj @@ -10,7 +10,7 @@ - + @@ -24,6 +24,8 @@ + + diff --git a/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http new file mode 100644 index 000000000..bdb509e9b --- /dev/null +++ b/extra/admin-api/Spacebar.AdminApi/Spacebar.AdminApi.http @@ -0,0 +1,19 @@ +@SpacebarAdminApi_HostAddress = http://localhost:5112 + +POST {{Spacebar.AdminApi_HostAddress}}/_spacebar/admin/guilds/1473141782615941382/force_join +Content-Type: application/json +Accept: application/json + +{ + "MakeOwner": true, + "UserId": "1006598230156341276" +} + +### + +GET {{SpacebarAdminApi_HostAddress}}/_spacebar/admin/discovery +Content-Type: application/json +Accept: application/json +Authorization: null null + +### diff --git a/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json b/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json index 032457733..0ee920fff 100644 --- a/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json +++ b/extra/admin-api/Spacebar.AdminApi/appsettings.Development.json @@ -2,16 +2,17 @@ "Logging": { "LogLevel": { "Default": "Information", - "Microsoft.AspNetCore": "Trace", //Warning - "Microsoft.AspNetCore.Mvc": "Warning", //Warning - "Microsoft.AspNetCore.HostFiltering": "Warning", //Warning - "Microsoft.AspNetCore.Cors": "Warning", //Warning - // "Microsoft.EntityFrameworkCore": "Warning" - "Microsoft.EntityFrameworkCore.Database.Command": "Debug" + "Microsoft.AspNetCore": "Trace", + "Microsoft.AspNetCore.Mvc": "Warning", + "Microsoft.AspNetCore.HostFiltering": "Warning", + "Microsoft.AspNetCore.Cors": "Warning", + // "Microsoft.EntityFrameworkCore": "Warning" + "Microsoft.EntityFrameworkCore.Database.Command": "Debug", + "Microsoft.AspNetCore.Server.Kestrel.Connections": "Information" } }, "ConnectionStrings": { - "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;", + "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;" }, "RabbitMQ": { "Host": "127.0.0.1", @@ -19,9 +20,16 @@ "Username": "guest", "Password": "guest" }, - "SpacebarAdminApi": { - "Enforce2FA": true, - "OverrideUid": null, - "DisableAuthentication": false + "Spacebar": { + "Authentication": { + "Enforce2FA": false, + "OverrideUid": "1006598230156341276", + "DisableAuthentication": true, + "PublicKeyPath": "../../../jwt.key.pub", + "PrivateKeyPath": "../../../jwt.key" + }, + "UnixSocketReplication": { + "SocketDir": "../../.." + } } } diff --git a/extra/admin-api/Spacebar.AdminApi/deps.json b/extra/admin-api/Spacebar.AdminApi/deps.json index 73b811294..6fd848627 100644 --- a/extra/admin-api/Spacebar.AdminApi/deps.json +++ b/extra/admin-api/Spacebar.AdminApi/deps.json @@ -10,294 +10,74 @@ "hash": "sha256-XcwbLG9w4OFzMVA54pvTvRJ9692tvE8D0tMVcteSQL0=" }, { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" + "pname": "BCrypt.Net-Next", + "version": "4.1.0", + "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" }, { "pname": "Microsoft.AspNetCore.OpenApi", - "version": "10.0.2", - "hash": "sha256-FU57fPXL4NUDRqi+rLresi4yKttv1KcAnLuEdPCyTos=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" + "version": "10.0.5", + "hash": "sha256-CQXAu6Tm8nOy/rrZksIKGaLW7USEP/N1kwKBMLoh7js=" }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.0", - "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.0", - "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" - }, - { - "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.0", - "hash": "sha256-AMgDSm1k6q0s17spGtyR5q8nAqUFDOxl/Fe38f9M+d4=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" - }, - { - "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" - }, - { - "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" - }, - { - "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "9.0.0", - "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "9.0.0", - "hash": "sha256-CncVwkKZ5CsIG2O0+OM9qXuYXh3p6UGyueTHSLDVL+c=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.0", - "hash": "sha256-P+zPAadLL63k/GqK34/qChqQjY9aIRxZfxlB9lqsSrs=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.0", - "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.IdentityModel.Abstractions", - "version": "8.15.0", - "hash": "sha256-LKTvERNUTMCEF7xs377tCMwOMRki93OS4kh6Yv0uXJ4=" + "version": "8.16.0", + "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" }, { "pname": "Microsoft.IdentityModel.JsonWebTokens", - "version": "8.15.0", - "hash": "sha256-LwzKiGjcnORvmQ9tim6lomXpfVlPpd/fE8FKTFWKlpM=" + "version": "8.16.0", + "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" }, { "pname": "Microsoft.IdentityModel.Logging", - "version": "8.15.0", - "hash": "sha256-mMXwsjGcrrmHR1mG7BLTKg/30mX+m93QVX17/ynOOd4=" + "version": "8.16.0", + "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" }, { "pname": "Microsoft.IdentityModel.Tokens", - "version": "8.15.0", - "hash": "sha256-7Lo/TsvqgNCEMyFssO3Om233521Pqgb9K9lUeHc5HMk=" + "version": "8.16.0", + "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" }, { "pname": "Microsoft.OpenApi", "version": "2.0.0", "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" - }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" }, { "pname": "System.IdentityModel.Tokens.Jwt", - "version": "8.15.0", - "hash": "sha256-5O0wbGp0gWnukK+0mWBjMnP1bZc6N0xuNcO2qmFiUX8=" + "version": "8.16.0", + "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" }, { "pname": "Unidecode.NET", diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs index 9a7ddf23f..8e64100a0 100644 --- a/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs +++ b/extra/admin-api/Spacebar.Cdn/Controllers/GetImageController.cs @@ -7,25 +7,17 @@ namespace Spacebar.Cdn.Controllers; [ApiController] -public class GetImageController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ControllerBase { - [HttpGet("/avatars/{_:required}")] +public class GetImageController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ImageController { + // [HttpGet("/avatars/{_:required}")] [HttpGet("/emojis/{emoji_id:required}.{ext:required}")] [HttpGet("/stickers/{sticker_id:required}.{ext:required}")] - [HttpGet("/avatars/{user_id:required}/{avatar_hash:required}.{ext:required}")] + // [HttpGet("/avatars/{user_id:required}/{avatar_hash:required}.{ext:required}")] [HttpGet("/banners/{user_id:required}/{user_banner:required}.{ext:required}")] public async Task GetImage(string? ext) { var originalKey = fs.BaseUrl + Request.Path; var cacheKey = Request.Path + Request.QueryString; - - DiscordImageResizeParams resizeParams = new() { - Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null, - Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) ? quality : DiscordImageResizeQuality.High, - KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar, - Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt, - Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an, - SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au, - SpacebarOptimiseGif = Request.Query.ContainsKey("optimiseGif") && bool.TryParse(Request.Query["optimiseGif"], out bool og) && og - }; + + DiscordImageResizeParams resizeParams = GetResizeParams(); var entry = await lfc.GetOrAdd(cacheKey, async () => { var original = await fs.GetFile(Request.Path); diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/Internal/GetImageController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/Internal/GetImageController.cs index 404e6e294..f595fba97 100644 --- a/extra/admin-api/Spacebar.Cdn/Controllers/Internal/GetImageController.cs +++ b/extra/admin-api/Spacebar.Cdn/Controllers/Internal/GetImageController.cs @@ -1,3 +1,4 @@ +using ArcaneLibs; using ArcaneLibs.Collections; using ImageMagick; using Microsoft.AspNetCore.Mvc; @@ -6,7 +7,7 @@ using Spacebar.Interop.Cdn.Abstractions; namespace Spacebar.Cdn.Controllers.Internal; -/* + [ApiController] public class IsPixelArtController(LruFileCache lfc, IFileSource fs, PixelArtDetectionService pads, DiscordImageResizeService dirs) : ControllerBase { private static readonly LruCache _isPixelArtCache = new(100_000); @@ -20,7 +21,7 @@ public async Task IsPixelArt() { return pads.IsPixelArt(img); }); } - + [HttpGet("/isCartoonArt/{*_:required}")] public async Task IsCartoonArt() { return await _isPixelArtCache.GetOrAddAsync(Request.Path.ToString(), async () => { @@ -34,18 +35,19 @@ public async Task IsCartoonArt() { public async Task GetEdges([FromQuery] string applyMode = "pre") { return await _edgeCache.GetOrAdd(Request.Path.ToString() + Request.QueryString, async () => { var original = await fs.GetFile(Request.Path.ToString().Replace("/edges", "")); - + DiscordImageResizeParams resizeParams = new() { Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null, - Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) ? quality : DiscordImageResizeQuality.High, + Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) + ? quality + : DiscordImageResizeQuality.High, KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar, Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt, Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an, SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au, SpacebarOptimiseGif = Request.Query.ContainsKey("optimiseGif") && bool.TryParse(Request.Query["optimiseGif"], out bool og) && og }; - - + double radius = 1; if (Request.Query.ContainsKey("radius")) double.TryParse(Request.Query["radius"], out radius); @@ -65,27 +67,27 @@ public async Task GetEdges([FromQuery] string applyMode = "pr }; }).ContinueWith(t => File(t.Result.Data, t.Result.MimeType)); } - - [HttpGet("/posterize/{*_:required}")] + + [HttpGet("/posterize/{*_:required}")] public async Task Posterize() { return await _edgeCache.GetOrAdd(Request.Path.ToString() + Request.QueryString, async () => { var original = await fs.GetFile(Request.Path.ToString().Replace("/posterize", "")); DiscordImageResizeParams resizeParams = new() { Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null, - Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) ? quality : DiscordImageResizeQuality.High, + Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) + ? quality + : DiscordImageResizeQuality.High, KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar, Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt, Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an, SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au }; - - + double radius = 1; if (Request.Query.ContainsKey("radius")) double.TryParse(Request.Query["radius"], out radius); var img = await original.ToMagickImageCollectionAsync(); - int inFrames = img.Count; // using var edged = pads.RenderEdges(img, radius); foreach (var frame in img) { @@ -94,9 +96,9 @@ public async Task Posterize() { frame.Posterize(16, DitherMethod.No); frame.ColorFuzz = new Percentage(0); } - - if(resizeParams.Size.HasValue) - img = dirs.Apply(img,resizeParams); + + if (resizeParams.Size.HasValue) + img = dirs.Apply(img, resizeParams); Console.WriteLine($"Generated edges for {Request.Path}, radius={radius}, inFrames={inFrames}, outFrames={img.Count}"); return new LruFileCache.Entry { @@ -106,36 +108,38 @@ public async Task Posterize() { }; }).ContinueWith(t => File(t.Result.Data, t.Result.MimeType)); } - + [HttpGet("/colorFuzz/{*_:required}")] public async Task ColorFuzz() { return await _edgeCache.GetOrAdd(Request.Path.ToString() + Request.QueryString, async () => { var original = await fs.GetFile(Request.Path.ToString().Replace("/colorFuzz", "")); DiscordImageResizeParams resizeParams = new() { - Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null, - Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) ? quality : DiscordImageResizeQuality.High, + Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) + ? size + : null, + Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) + ? quality + : DiscordImageResizeQuality.High, KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar, Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt, Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an, - SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au + SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au, }; - - + double colorFuzz = 1; if (Request.Query.ContainsKey("colorFuzz")) double.TryParse(Request.Query["colorFuzz"], out colorFuzz); var img = await original.ToMagickImageCollectionAsync(); - int inFrames = img.Count; // using var edged = pads.RenderEdges(img, radius); foreach (var frame in img) { // get major color count frame.ColorFuzz = new Percentage(colorFuzz); } - - if(resizeParams.Size.HasValue) - img = dirs.Apply(img,resizeParams); + + if (resizeParams.Size.HasValue) + img = dirs.Apply(img, resizeParams); Console.WriteLine($"Generated colorFuzz for {Request.Path}, fuzz={colorFuzz}, inFrames={inFrames}, outFrames={img.Count}"); return new LruFileCache.Entry { @@ -145,5 +149,14 @@ public async Task ColorFuzz() { }; }).ContinueWith(t => File(t.Result.Data, t.Result.MimeType)); } -} -*/ \ No newline at end of file + + [HttpGet("/defaultAvatar")] + public async Task DefaultAvatar() { + var re = new RainbowEnumerator(); + var img = new MagickImageCollection(); + + var ms = new MemoryStream(); + + return new FileContentResult(ms.ToArray(), "image/gif"); + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/StaticAssetController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/StaticAssetController.cs new file mode 100644 index 000000000..5d9d0d781 --- /dev/null +++ b/extra/admin-api/Spacebar.Cdn/Controllers/StaticAssetController.cs @@ -0,0 +1,66 @@ +using ArcaneLibs.Extensions.Streams; +using Microsoft.AspNetCore.Mvc; +using Microsoft.OpenApi; +using Spacebar.AdminApi.TestClient.Services.Services; +using Spacebar.Cdn.Extensions; +using Spacebar.Interop.Cdn.Abstractions; + +namespace Spacebar.Cdn.Controllers; + +[ApiController] +public class StaticAssetController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ImageController { + private static readonly Dictionary defaultAvatarHashMap = new() { + { "0", "4a8562cf00887030c416d3ec2d46385a" }, + { "1", "9b0bb198936784c45c72833cc426cc55" }, + { "2", "22341bdb500c7b63a93bbce957d1601e" }, + { "3", "d9977836b82058bf2f74eebd50edc095" }, + { "4", "9d6ddb4e4d899a533a8cc617011351c9" }, + { "5", "7213ab6677377974697dfdfbaf5f6a6f" }, + }; + + private static readonly Dictionary defaultGroupDMAvatarHashMap = new() { + { "0", "3b70bb66089c60f8be5e214bf8574c9d" }, + { "1", "9581acd31832465bdeaa5385b0e919a3" }, + { "2", "a8a4727cf2dc2939bd3c657fad4463fa" }, + { "3", "2e46fe14586f8e95471c0917f56726b5" }, + { "4", "fac7e78de9753d4a37083bba74c1d9ef" }, + { "5", "4ab900144b0865430dc9be825c838faa" }, + { "6", "1276374a404452756f3c9cc2601508a5" }, + { "7", "904bf9f1b61f53ef4a3b7a893afeabe3" }, + }; + + // png only + [HttpGet("/embed/avatars/{userIndex}.{ext}")] + public async Task GetDefaultUserAvatar(string userIndex, string ext) { + + var cacheKey = Request.Path + Request.QueryString; + + DiscordImageResizeParams resizeParams = GetResizeParams(); + + var entry = await lfc.GetOrAdd(cacheKey, async () => { + var original = await fs.GetFile(Request.Path); + + if (Request.Query.Any()) { + using var img = await original.ToMagickImageCollectionAsync(); + dirs.Apply(img, resizeParams); + + var outStream = new MemoryStream(); + await img.WriteAsync(outStream, img.First().Format); + outStream.Position = 0; + + return new LruFileCache.Entry() { + Data = outStream.ReadToEnd().ToArray(), + MimeType = original.MimeType + }; + } + + return new LruFileCache.Entry() { + Data = original.Stream.ReadToEnd().ToArray(), + MimeType = original.MimeType + }; + }); + + // byte array with mime type result + return new FileContentResult(entry.Data, entry.MimeType); + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Cdn/Controllers/UserController.cs b/extra/admin-api/Spacebar.Cdn/Controllers/UserController.cs new file mode 100644 index 000000000..976266098 --- /dev/null +++ b/extra/admin-api/Spacebar.Cdn/Controllers/UserController.cs @@ -0,0 +1,77 @@ +using ArcaneLibs.Extensions.Streams; +using Microsoft.AspNetCore.Mvc; +using Spacebar.AdminApi.TestClient.Services.Services; +using Spacebar.Cdn.Extensions; +using Spacebar.Interop.Cdn.Abstractions; + +namespace Spacebar.Cdn.Controllers; + +[ApiController] +public class UserController(LruFileCache lfc, IFileSource fs, DiscordImageResizeService dirs) : ImageController { + [HttpGet("/avatars/{userId}/{hash}.{ext}")] + public async Task GetUserAvatar(string userId, string hash, string ext) { + var originalKey = fs.BaseUrl + Request.Path; + var cacheKey = Request.Path + Request.QueryString; + + DiscordImageResizeParams resizeParams = GetResizeParams(); + + var entry = await lfc.GetOrAdd(cacheKey, async () => { + var original = await fs.GetFile(Request.Path); + + if (Request.Query.Any()) { + using var img = await original.ToMagickImageCollectionAsync(); + dirs.Apply(img, resizeParams); + + var outStream = new MemoryStream(); + await img.WriteAsync(outStream, img.First().Format); + outStream.Position = 0; + + return new LruFileCache.Entry() { + Data = outStream.ReadToEnd().ToArray(), + MimeType = original.MimeType + }; + } + + return new LruFileCache.Entry() { + Data = original.Stream.ReadToEnd().ToArray(), + MimeType = original.MimeType + }; + }); + + // byte array with mime type result + return new FileContentResult(entry.Data, entry.MimeType); + } + [HttpGet("/banners/{userId}/{hash}.{ext}")] + public async Task GetUserBanner(string userId, string hash, string ext) { + var originalKey = fs.BaseUrl + Request.Path; + var cacheKey = Request.Path + Request.QueryString; + + DiscordImageResizeParams resizeParams = GetResizeParams(); + + var entry = await lfc.GetOrAdd(cacheKey, async () => { + var original = await fs.GetFile(Request.Path); + + if (Request.Query.Any()) { + using var img = await original.ToMagickImageCollectionAsync(); + dirs.Apply(img, resizeParams); + + var outStream = new MemoryStream(); + await img.WriteAsync(outStream, img.First().Format); + outStream.Position = 0; + + return new LruFileCache.Entry() { + Data = outStream.ReadToEnd().ToArray(), + MimeType = original.MimeType + }; + } + + return new LruFileCache.Entry() { + Data = original.Stream.ReadToEnd().ToArray(), + MimeType = original.MimeType + }; + }); + + // byte array with mime type result + return new FileContentResult(entry.Data, entry.MimeType); + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Cdn/Extensions/ImageController.cs b/extra/admin-api/Spacebar.Cdn/Extensions/ImageController.cs new file mode 100644 index 000000000..133eb8e1a --- /dev/null +++ b/extra/admin-api/Spacebar.Cdn/Extensions/ImageController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Spacebar.AdminApi.TestClient.Services.Services; + +namespace Spacebar.Cdn.Extensions; + +public class ImageController : ControllerBase { + protected DiscordImageResizeParams GetResizeParams() { + return new() { + Size = Request.Query.ContainsKey("size") && uint.TryParse(Request.Query["size"], out uint size) ? size : null, + Quality = Request.Query.ContainsKey("quality") && Enum.TryParse(Request.Query["quality"], true, out var quality) + ? quality + : DiscordImageResizeQuality.High, + KeepAspectRatio = !Request.Query.ContainsKey("keepAspectRatio") || !bool.TryParse(Request.Query["keepAspectRatio"], out bool kar) || kar, + Passthrough = Request.Query.ContainsKey("passthrough") && bool.TryParse(Request.Query["passthrough"], out bool pt) && pt, + Animated = Request.Query.ContainsKey("animated") && bool.TryParse(Request.Query["animated"], out bool an) && an, + SpacebarAllowUpscale = Request.Query.ContainsKey("allowUpscale") && bool.TryParse(Request.Query["allowUpscale"], out bool au) && au, + SpacebarOptimiseGif = Request.Query.ContainsKey("optimiseGif") && bool.TryParse(Request.Query["optimiseGif"], out bool og) && og, + }; + } + + protected void SetSuccessCacheHeader() { + int cacheDuration = (int)TimeSpan.FromHours(6).TotalSeconds; + Response.Headers.CacheControl = $"public, max-age={cacheDuration}, s-maxage={cacheDuration}, immutable"; + } + + protected void SetFailureCacheHeader() { + int cacheDuration = (int)TimeSpan.FromMinutes(5).TotalSeconds; + Response.Headers.CacheControl = $"public, max-age={cacheDuration}, s-maxage={cacheDuration}, immutable"; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Cdn/Helpers/DefaultAvatarRenderer.cs b/extra/admin-api/Spacebar.Cdn/Helpers/DefaultAvatarRenderer.cs new file mode 100644 index 000000000..35b478f0d --- /dev/null +++ b/extra/admin-api/Spacebar.Cdn/Helpers/DefaultAvatarRenderer.cs @@ -0,0 +1,24 @@ +using ArcaneLibs.Extensions; +using ImageMagick; + +namespace Spacebar.AdminApi.TestClient.Services.Helpers; + +public class DefaultAvatarRenderer { + // Slower at runtime, but doesnt depend on filesystem + private static string GetDefaultAvatarSvg(byte r, byte g, byte b) { + return $""" + + + + + """; + } + + public static async Task GetDefaultAvatar(byte r, byte g, byte b, MagickFormat format = MagickFormat.Png, int size = 4096) { + var img = new MagickImageCollection(GetDefaultAvatarSvg(r, g, b).AsBytes().ToArray()); + var ms = new MemoryStream(); + await img.WriteAsync(ms); + ms.Position = 0; + return ms; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.Cdn/Mimes.cs b/extra/admin-api/Spacebar.Cdn/Mimes.cs index 508e9a3b6..5a2814321 100644 --- a/extra/admin-api/Spacebar.Cdn/Mimes.cs +++ b/extra/admin-api/Spacebar.Cdn/Mimes.cs @@ -1,3 +1,4 @@ +using System.ComponentModel; using ImageMagick; namespace Spacebar.AdminApi.TestClient.Services; @@ -8,6 +9,20 @@ private static string PrintLogged(string msg, string mime) { return mime; } + public static MagickFormat GetFormatForExtension(string extension) { + extension = extension.ToLower(); + // ban some values... + if (extension == "screenshot") throw new AccessViolationException("Disallowed extension: " + extension); + + var matchingFormat = Enum.GetNames(typeof(MagickFormat)).FirstOrDefault(f => f.ToLower() == extension); + if (string.IsNullOrWhiteSpace(matchingFormat)) throw new InvalidEnumArgumentException("Unknown format: " + extension); + if (Enum.TryParse(matchingFormat, out MagickFormat fmt)) { + return fmt; + } + + throw new InvalidEnumArgumentException("Unknown format: " + extension); + } + public static string GetMime(MagickFormat fmt) => fmt switch { MagickFormat.Png => "image/png", MagickFormat.Jpeg => "image/jpeg", diff --git a/extra/admin-api/Spacebar.Cdn/Program.cs b/extra/admin-api/Spacebar.Cdn/Program.cs index 47b518331..61a5c07bd 100644 --- a/extra/admin-api/Spacebar.Cdn/Program.cs +++ b/extra/admin-api/Spacebar.Cdn/Program.cs @@ -10,7 +10,8 @@ builder.Configuration.AddJsonFile(Environment.GetEnvironmentVariable("APPSETTINGS_PATH")!); // Add services to the container. -builder.Services.AddSingleton(new ProxyFileSource("http://cdn.old.server.spacebar.chat")); +// builder.Services.AddSingleton(new ProxyFileSource("http://cdn.old.server.spacebar.chat")); +builder.Services.AddSingleton(new FilesystemFileSource(Environment.GetEnvironmentVariable("STORAGE_PATH") ?? throw new InvalidOperationException("STORAGE_PATH not set!"))); builder.Services.AddSingleton(new LruFileCache(1 * 1024 * 1024 * 1024)); builder.Services.AddSingleton(); builder.Services.AddSingleton(); diff --git a/extra/admin-api/Spacebar.Cdn/Properties/launchSettings.json b/extra/admin-api/Spacebar.Cdn/Properties/launchSettings.json index b9fe4cc30..f3ffeac96 100644 --- a/extra/admin-api/Spacebar.Cdn/Properties/launchSettings.json +++ b/extra/admin-api/Spacebar.Cdn/Properties/launchSettings.json @@ -18,7 +18,8 @@ "applicationUrl": "http://localhost:5114", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Local", - "LD_LIBRARY_PATH": "/home/Rory/git/spacebar/server-master/extra/admin-api/Spacebar.Cdn/result-lib/lib/" +// "LD_LIBRARY_PATH": "/home/Rory/git/spacebar/server-master/extra/admin-api/Spacebar.Cdn/result-lib/lib/", + "STORAGE_PATH": "./files" } } } diff --git a/extra/admin-api/Spacebar.Cdn/Services/DiscordImageResizeService.cs b/extra/admin-api/Spacebar.Cdn/Services/DiscordImageResizeService.cs index 2a35c4049..382845e27 100644 --- a/extra/admin-api/Spacebar.Cdn/Services/DiscordImageResizeService.cs +++ b/extra/admin-api/Spacebar.Cdn/Services/DiscordImageResizeService.cs @@ -13,6 +13,10 @@ public class DiscordImageResizeParams { public bool SpacebarAllowUpscale { get; set; } = false; public bool SpacebarOptimiseGif { get; set; } = true; + + public string ToSerializedName() { + return $"{(Animated ? "a_" : "")}{Size}px_{Quality.ToString()}_u.{SpacebarAllowUpscale}_o.{SpacebarOptimiseGif}"; + } } public enum DiscordImageResizeQuality { diff --git a/extra/admin-api/Spacebar.Cdn/Spacebar.Cdn.csproj b/extra/admin-api/Spacebar.Cdn/Spacebar.Cdn.csproj index ed5f963df..7bc855fd8 100644 --- a/extra/admin-api/Spacebar.Cdn/Spacebar.Cdn.csproj +++ b/extra/admin-api/Spacebar.Cdn/Spacebar.Cdn.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/extra/admin-api/Spacebar.Cdn/deps.json b/extra/admin-api/Spacebar.Cdn/deps.json index c2b3e94c9..f0d20fb9e 100644 --- a/extra/admin-api/Spacebar.Cdn/deps.json +++ b/extra/admin-api/Spacebar.Cdn/deps.json @@ -4,244 +4,54 @@ "version": "1.0.1-preview.20260126-091403", "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" }, - { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, { "pname": "Magick.NET-Q16-HDRI-OpenMP-x64", - "version": "14.10.2", - "hash": "sha256-3RprUC1TLydXkbtC2ILjJGqYdiCCc3mm6/LoTfGwW9s=" + "version": "14.10.4", + "hash": "sha256-2L2o7K3CvULSqyWEhJNhY/JA4+7IaC8WiRXWKmsdNiM=" }, { "pname": "Magick.NET.Core", - "version": "14.10.2", - "hash": "sha256-Dbx2XVhb0oe0zz1I9XAT+d41fY3hqwQIxt53zKlU+6k=" + "version": "14.10.4", + "hash": "sha256-qadZhuOIB3uNWz+nAEx+57oAYRQtPZ+1jk67sutzcKo=" }, { "pname": "Microsoft.AspNetCore.OpenApi", - "version": "10.0.2", - "hash": "sha256-FU57fPXL4NUDRqi+rLresi4yKttv1KcAnLuEdPCyTos=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" + "version": "10.0.5", + "hash": "sha256-CQXAu6Tm8nOy/rrZksIKGaLW7USEP/N1kwKBMLoh7js=" }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" - }, - { - "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" - }, - { - "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" - }, - { - "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "9.0.0", - "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" - }, - { - "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.0", - "hash": "sha256-BnhgGZc01HwTSxogavq7Ueq4V7iMA3wPnbfRwQ4RhGk=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" - }, - { - "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" - }, - { - "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" - }, - { - "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.OpenApi", "version": "2.0.0", "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" - }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" } ] diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/ChannelStatusController.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/ChannelStatusController.cs new file mode 100644 index 000000000..0843218ed --- /dev/null +++ b/extra/admin-api/Spacebar.GatewayOffload/Controllers/ChannelStatusController.cs @@ -0,0 +1,68 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Gateway; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway")] +public class ChannelStatusController(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) + : ControllerBase { + [HttpPost("ChannelStatuses")] + public async IAsyncEnumerable> GetChannelStatuses([FromBody] ChannelStatusesRequest req) { + await foreach (var res in GetChannelInfos(new() { + Fields = ["status"], + GuildIdRawValue = req.GuildIdRawValue, + })) { + yield return new() { + Payload = new() { + GuildId = res.Payload.GuildId, + Channels = res.Payload.Channels.Select(c => new ChannelStatus { + ChannelId = c.ChannelId, + Status = c.Status!, + }).ToList(), + } + }; + } + } + + [HttpPost("ChannelInfo")] + public async IAsyncEnumerable> GetChannelInfos([FromBody] ChannelInfoRequest req) { + var user = await authService.GetCurrentUserAsync(Request); + string[] statusOptions = [ + "Vibing ✨", + "Hanging out: 12%...", + "Communicating...", + // idk, i cant come up with more stuff, maybe suggestions welcome, or actually storing some data? + ]; + + foreach (var guildId in req.GuildIds ?? [req.GuildId!]) { + var channels = (await db.Channels.Include(x => x.VoiceStates).Where(x => x.Type == 2 && x.GuildId == guildId && x.VoiceStates.Count > 0) + .Select(x => x.Id) + .ToListAsync()) + .Select(x => new { + id = x, + status = statusOptions[new Random().Next(statusOptions.Length)], // TODO: We don't currently store channel statuses, so make some stuff up + voiceStartTime = DateTime.Now.Subtract(TimeSpan.FromMinutes(new Random().Next(1, 120))), // TODO: We also don't store voice start times, so make some stuff up + }).ToList(); + + yield return new() { + Payload = new() { + GuildId = guildId, + Channels = channels.Select(c => new ChannelInfo { + ChannelId = c.id, + Status = req.Fields.Contains("status") ? c.status : null, + VoiceStartTime = req.Fields.Contains("voice_start_time") ? c.voiceStartTime : null, + }).ToList(), + }, + }; + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs index d49790a98..02bad394c 100644 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs +++ b/extra/admin-api/Spacebar.GatewayOffload/Controllers/IdentifyController.cs @@ -11,7 +11,7 @@ namespace Spacebar.GatewayOffload.Controllers; [Route("/_spacebar/offload/gateway/Identify")] public class IdentifyController(ILogger logger, SpacebarAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { [HttpPost("")] - public async IAsyncEnumerable DoIdentify(IdentifyRequest payload) { + public async IAsyncEnumerable DoIdentify(IdentifyRequest payload) { var user = await TraceResult.TraceAsync("getAuthUser", () => authService.GetCurrentUserAsync(payload.Token)); var session = await TraceResult.TraceAsync("getAuthSession", () => authService.GetCurrentSessionAsync(payload.Token)); @@ -37,12 +37,13 @@ public async IAsyncEnumerable DoIdentify(IdentifyRequest pay } } - yield return new() { - Payload = new ReadyResponse { }, + yield return new ReplicationMessage() { + Payload = new() { }, }; } - private ReplicationMessage Close(CloseCode closeCode) => new() { + // TODO: type? also, implement this in gateway lol + private ReplicationMessage Close(CloseCode closeCode) => new() { Origin = "IdentifyController", Event = "SB_GW_CLOSE", Payload = new { diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs index db45a8952..884a9de85 100644 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs +++ b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op12Controller.cs @@ -20,7 +20,7 @@ namespace Spacebar.GatewayOffload.Controllers; public class Op12Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { [HttpPost("")] - public async IAsyncEnumerable DoGuildSync(List guildIds) + public async IAsyncEnumerable> DoGuildSync(List guildIds) { var user = await authService.GetCurrentUserAsync(Request); guildIds = (await db.Members.AsNoTracking().Where(x => x.Id == user.Id).Select(x => x.GuildId).ToListAsync()) diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs index 9f32eef2f..57f1390ce 100644 --- a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs +++ b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op14Controller.cs @@ -14,7 +14,8 @@ namespace Spacebar.GatewayOffload.Controllers; [Route("/_spacebar/offload/gateway/LazyRequest")] public class Op14Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase { [HttpPost] - public async IAsyncEnumerable DoLazyRequest([FromBody] LazyRequest payload) { + // TODO: actually return something? + public async IAsyncEnumerable DoLazyRequest([FromBody] LazyRequest payload) { var user = await TraceResult.TraceAsync("getAuthUser", () => authService.GetCurrentUserAsync(Request)); var session = await TraceResult.TraceAsync("getAuthSession", () => authService.GetCurrentSessionAsync(Request)); diff --git a/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op8Controller.cs b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op8Controller.cs new file mode 100644 index 000000000..ac68bb22e --- /dev/null +++ b/extra/admin-api/Spacebar.GatewayOffload/Controllers/Op8Controller.cs @@ -0,0 +1,93 @@ +using System.Collections.Frozen; +using System.Linq.Expressions; +using System.Text.Json; +using System.Text.Json.Nodes; +using ArcaneLibs.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Spacebar.DataMappings.Generic; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Interop.Replication.Abstractions; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; +using Spacebar.Models.Gateway; +using Spacebar.Models.Generic; + +namespace Spacebar.GatewayOffload.Controllers; + +[ApiController] +[Route("/_spacebar/offload/gateway/GuildMembers")] +public class Op8Controller(ILogger logger, SpacebarAspNetAuthenticationService authService, SpacebarDbContext db, IServiceProvider sp) : ControllerBase +{ + [HttpPost("")] + public async IAsyncEnumerable> DoGuildSync(List guildIds) + { + var user = await authService.GetCurrentUserAsync(Request); + guildIds = (await db.Members.AsNoTracking().Where(x => x.Id == user.Id).Select(x => x.GuildId).ToListAsync()) + .Intersect(guildIds) + .OrderByDescending(gi => db.Members.Count(m => m.GuildId == gi)) + .ToList(); + + var syncs = guildIds.Select(GetGuildSyncAsync).ToList().ToAsyncResultEnumerable(); + await foreach (var res in syncs) + { + yield return new() + { + Origin = "OFFLOAD_GUILD_SYNC", + UserId = user.Id, + Event = "GUILD_SYNC", + CreatedAt = DateTime.Now, + Payload = res + }; + } + } + + // TODO: figure out how to abstract this to a function without EFCore complaining about not being translatable... + private static Expression> IsOnline = (Session session) => session.Status != "offline" && session.Status != "invisible" && session.Status != "unknown"; + + private async Task GetGuildSyncAsync(string guildId) + { + await using var sc = sp.CreateAsyncScope(); + var _db = sc.ServiceProvider.GetRequiredService(); + var memberCount = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId).CountAsync(); + + var offlineTreshold = DateTime.Now.Subtract(TimeSpan.FromDays(14)); + var isLargeGuild = memberCount > 10000; + + var members = await _db.Members.AsNoTracking().Where(x => x.GuildId == guildId) + .Include(x => x.IdNavigation) + .ThenInclude(x => x.Sessions.Where(s => + !s.IsAdminSession && ( + // see TODO on IsOnline - somehow need to replicate `IsOnline(s)` + s.Status != "offline" && s.Status != "invisible" && s.Status != "unknown" + ) && (!isLargeGuild || s.LastSeen >= offlineTreshold))) + .Where(x => x.IdNavigation.Sessions.Count > 0) // ignore members without sessions + .ToListAsync(); + + var mappedPartialUsers = members.Select(x => x.IdNavigation).ToFrozenDictionary(x => x.Id, x => x.ToPartialUser()); + var mappedMembers = members.ToFrozenDictionary(m => m.Id, m => m.ToPublicMember(mappedPartialUsers[m.Id])); + + var presences = members.Select(x => x.IdNavigation).Where(x => x.Sessions.Count > 0).ToFrozenDictionary(x => x.Id, x => + { + var sortedSessions = x.Sessions.OrderByDescending(s => s.LastSeen).ToList(); + return new Presence() + { + GuildId = guildId, + User = mappedPartialUsers[x.Id], + Activities = x.Sessions.Where(s => s.Status is not ("offline" or "invisible" or "unknown")) + .SelectMany(s => JsonSerializer.Deserialize(s.Activities) ?? []).ToList(), + Status = sortedSessions.FirstOrDefault(s => !string.IsNullOrWhiteSpace(s.Status))?.Status ?? "offline", + ClientStatus = JsonSerializer.Deserialize(sortedSessions.First(s => !string.IsNullOrWhiteSpace(s.ClientStatus)).ClientStatus) ?? + new() + }; + }).Where(x => x.Value.Activities.Count > 0).ToFrozenDictionary(); + + var r = new GuildSyncResponse() + { + GuildId = guildId, + Members = mappedMembers.Values.ToList(), + Presences = presences.Values.ToList() + }; + return r; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj b/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj index 423b7b440..8923cb36f 100644 --- a/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj +++ b/extra/admin-api/Spacebar.GatewayOffload/Spacebar.GatewayOffload.csproj @@ -7,7 +7,7 @@ - + diff --git a/extra/admin-api/Spacebar.GatewayOffload/deps.json b/extra/admin-api/Spacebar.GatewayOffload/deps.json index a2da77924..b03c4aaa9 100644 --- a/extra/admin-api/Spacebar.GatewayOffload/deps.json +++ b/extra/admin-api/Spacebar.GatewayOffload/deps.json @@ -5,193 +5,73 @@ "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" }, { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" + "pname": "BCrypt.Net-Next", + "version": "4.1.0", + "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" }, { "pname": "Microsoft.AspNetCore.OpenApi", - "version": "10.0.2", - "hash": "sha256-FU57fPXL4NUDRqi+rLresi4yKttv1KcAnLuEdPCyTos=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" + "version": "10.0.5", + "hash": "sha256-CQXAu6Tm8nOy/rrZksIKGaLW7USEP/N1kwKBMLoh7js=" }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.0", - "hash": "sha256-UDgZbRQcGPaKsE53EH6bvJiv+Q4KSxAbnsVhTVFGG4Q=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.0", - "hash": "sha256-7Q0jYJO50cqGI+u6gLpootbB8GZvgsgtg0F9FZI1jig=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" - }, - { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.IdentityModel.Abstractions", - "version": "8.15.0", - "hash": "sha256-LKTvERNUTMCEF7xs377tCMwOMRki93OS4kh6Yv0uXJ4=" + "version": "8.16.0", + "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" }, { "pname": "Microsoft.IdentityModel.JsonWebTokens", - "version": "8.15.0", - "hash": "sha256-LwzKiGjcnORvmQ9tim6lomXpfVlPpd/fE8FKTFWKlpM=" + "version": "8.16.0", + "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" }, { "pname": "Microsoft.IdentityModel.Logging", - "version": "8.15.0", - "hash": "sha256-mMXwsjGcrrmHR1mG7BLTKg/30mX+m93QVX17/ynOOd4=" + "version": "8.16.0", + "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" }, { "pname": "Microsoft.IdentityModel.Tokens", - "version": "8.15.0", - "hash": "sha256-7Lo/TsvqgNCEMyFssO3Om233521Pqgb9K9lUeHc5HMk=" + "version": "8.16.0", + "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" }, { "pname": "Microsoft.OpenApi", "version": "2.0.0", "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" - }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" }, { "pname": "System.IdentityModel.Tokens.Jwt", - "version": "8.15.0", - "hash": "sha256-5O0wbGp0gWnukK+0mWBjMnP1bZc6N0xuNcO2qmFiUX8=" + "version": "8.16.0", + "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" } ] diff --git a/extra/admin-api/Spacebar.UApi/Controllers/Applications/ApplicationRpcController.cs b/extra/admin-api/Spacebar.UApi/Controllers/Applications/ApplicationRpcController.cs new file mode 100644 index 000000000..08f45c62b --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Controllers/Applications/ApplicationRpcController.cs @@ -0,0 +1,84 @@ +using System.Text.Json.Serialization; +using ArcaneLibs.Collections; +using Microsoft.AspNetCore.Mvc; + +namespace Spacebar.UApi.Controllers.Applications; + +[ApiController] +[Route("/api/v{_}/applications/{applicationId}/")] +public class ApplicationRpcController : ControllerBase { + private static LruCache _rpcInfoCache = new(10000); + // [HttpGet] + // public async Task GetApplicationRpcInfo(string applicationId) { + // + // } + + [HttpGet("disclosures")] + public ApplicationDisclosures GetApplicationDisclosures(string applicationId) { + return new ApplicationDisclosures { + Disclosures = [], + AckedDisclosures = [], + AllAcked = true + }; + } + + [HttpPost("disclosures")] + public ApplicationDisclosures AckApplicationDisclosures(string applicationId) { + // TODO: type is wrong, normally only `disclosures` is returned + return new ApplicationDisclosures { + Disclosures = [], + AckedDisclosures = [], + AllAcked = true + }; + } +} + +public class ApplicationDisclosures { + [JsonPropertyName("disclosures")] + public List Disclosures { get; set; } + + [JsonPropertyName("acked_disclosures")] + public List AckedDisclosures { get; set; } + + [JsonPropertyName("all_acked")] + public bool AllAcked { get; set; } + + public enum ApplicationDisclosureType { + UnspecifiedDisclosure = 0, + IpLocation = 1, + DisplaysAdvertisements = 2, + PartnerSdkDataSharingMessage = 3 + } +} + +public class RpcApplicationInfo { + [JsonPropertyName("id")] + public string Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("icon")] + public string Icon { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("summary")] + public string Summary { get; set; } + + [JsonPropertyName("type")] + public ApplicationType? Type { get; set; } // what is this? + + [JsonPropertyName("verify_key")] + public string VerifyKey { get; set; } +} + +[Flags] +public enum ApplicationType { + DeprecatedGame, + Music, + TicketedEvents, + CreatorMonetization, + Game +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Controllers/GuildMembersController.cs b/extra/admin-api/Spacebar.UApi/Controllers/GuildMembersController.cs index ac8c1d40c..9f18624ee 100644 --- a/extra/admin-api/Spacebar.UApi/Controllers/GuildMembersController.cs +++ b/extra/admin-api/Spacebar.UApi/Controllers/GuildMembersController.cs @@ -8,6 +8,7 @@ namespace Spacebar.UApi.Controllers; [Route("/api/v{_}/guilds/{guildId}/members/")] +[ApiController] public class GuildMembersController(ILogger logger, SpacebarDbContext db, SpacebarAspNetAuthenticationService authService) : ControllerBase { /// /// Get a guild member by ID diff --git a/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs b/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs new file mode 100644 index 000000000..6ce1fc313 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Controllers/GuildStickerController.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; +using Spacebar.UApi.Controllers.Messages; +using Spacebar.UApi.Services; +using Config = Spacebar.ConfigModel.Config; + +namespace Spacebar.UApi.Controllers; + +[ApiController] +[Route("/api/v{_}/guilds/{guildId}/stickers/")] +public class GuildStickerController(ILogger logger, SpacebarDbContext db, SpacebarAspNetAuthenticationService authService, UApiConfiguration cfg, PermissionService permService, Config sbCfg) : ControllerBase { + // TODO proper response type + // [HttpPost] + // public async Task UploadGuildSticker(string guildId, MultipartFormDataContent content) { + // + // var sticker = new Sticker() { + // GuildId = guildId + // }; + // + // foreach (var item in content) { + // switch (item.Headers.ContentDisposition.Name.Trim('"')) { + // case "name": + // sticker.Name = await item.ReadAsStringAsync(); + // break; + // case "description": + // sticker.Description = await item.ReadAsStringAsync(); + // break; + // case "tags": + // sticker.Tags = await item.ReadAsStringAsync(); + // break; + // case "file": + // var fileContent = await item.ReadAsStreamAsync(); + // + // break; + // } + // } + // + // return sticker; + // } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Controllers/GuildTemplatesController.cs b/extra/admin-api/Spacebar.UApi/Controllers/GuildTemplatesController.cs new file mode 100644 index 000000000..270b55436 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Controllers/GuildTemplatesController.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Mvc; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.UApi.Models; +using Spacebar.UApi.Services; + +namespace Spacebar.UApi.Controllers; + +[ApiController] +public class GuildTemplatesController(ILogger logger, SpacebarDbContext db, SpacebarAspNetAuthenticationService authService, TemplateImportService importService) : ControllerBase { + // [HttpPost("/api/v10/guilds/templates/{templateId}")] + // public async Task UseTemplate(string templateId, UseGuildTemplateRequest request) { + // var user = await authService.GetCurrentUserAsync(Request); + // await importService.CreateGuildFromTemplateById(templateId, request, user); + // } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs b/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs new file mode 100644 index 000000000..c76ef7ae8 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Controllers/Messages/MessagesController.cs @@ -0,0 +1,58 @@ +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using ArcaneLibs; +using Microsoft.AspNetCore.Mvc; +using Spacebar.Interop.Authentication.AspNetCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.UApi.Services; + +namespace Spacebar.UApi.Controllers.Messages; + +[ApiController] +[Route("/api/v{_}/channels/{channelId}/messages")] +public partial class MessagesController(ILogger logger, SpacebarDbContext db, SpacebarAspNetAuthenticationService authService, UApiConfiguration cfg) : ControllerBase { + // [Consumes("multipart/form-data")] + // [HttpPost] + // public async Task CreateMessageWithAttachments(string channelId, MultipartFormDataContent formData) { + // // Generic proxy doesnt work with multipart/form-data for some reason, so handle them specially + // JsonObject jsonPayload = null!; + // + // foreach (var content in formData) + // { + // if (content.Headers.ContentDisposition?.Name == "payload_json") { + // jsonPayload = await content.ReadFromJsonAsync(); + // break; + // } + // if (FileNameRegex().IsMatch(content.Headers.ContentDisposition?.Name ?? "")) { + // + // break; + // } + // throw new InvalidOperationException("Invalid multipart/form-data payload: missing payload_json or file attachments"); + // } + // + // var client = new StreamingHttpClient(); + // var requestMessage = new HttpRequestMessage( + // new HttpMethod(Request.Method), + // cfg.FallbackApiEndpoint + Request.Path + Request.QueryString + // ) { + // Content = new StreamContent(Request.Body) + // }; + // Console.WriteLine(requestMessage.RequestUri); + // + // var responseMessage = await client.SendUnhandledAsync(requestMessage, CancellationToken.None); + // Response.StatusCode = (int)responseMessage.StatusCode; + // + // foreach (var header in responseMessage.Headers) Response.Headers[header.Key] = header.Value.ToArray(); + // foreach (var header in responseMessage.Content.Headers) Response.Headers[header.Key] = header.Value.ToArray(); + // + // await responseMessage.Content.CopyToAsync(Response.Body); + // } + + [GeneratedRegex(@"files\[\d+\]")] + private static partial Regex FileNameRegex(); + + private struct CloudUploadTask { + public int index; + public Task task; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Models/GuildTemplate.cs b/extra/admin-api/Spacebar.UApi/Models/GuildTemplate.cs new file mode 100644 index 000000000..d53035692 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Models/GuildTemplate.cs @@ -0,0 +1,73 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Spacebar.UApi.Models; + +// TODO: real schemas +public class GuildTemplate { + [JsonPropertyName("code")] + public string Code { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("usage_count")] + public int UsageCount { get; set; } + + [JsonPropertyName("creator_id")] + public string CreatorId { get; set; } + + [JsonPropertyName("creator")] + public JsonObject Creator { get; set; } + + [JsonPropertyName("created_at")] + public DateTimeOffset CreatedAt { get; set; } + + [JsonPropertyName("updated_at")] + public DateTimeOffset UpdatedAt { get; set; } + + [JsonPropertyName("source_guild_id")] + public string SourceGuildId { get; set; } + + [JsonPropertyName("serialized_source_guild")] + public SerializedSourceGuild SerializedSourceGuild { get; set; } +} + +public class SerializedSourceGuild { } + +public class GuildTemplateRole { + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("permissions")] + public string Permissions { get; set; } + + // "color" ignored for now - is deprecated anyhow + [JsonPropertyName("colors")] + public RoleColors Colors { get; set; } + + [JsonPropertyName("hoist")] + public bool Hoist { get; set; } + + [JsonPropertyName("mentionable")] + public bool Mentionable { get; set; } + + // [JsonPropertyName("icon")] +} + +public class RoleColors { + [JsonPropertyName("primary_color")] + public int PrimaryColor { get; set; } + + [JsonPropertyName("secondary_color")] + public int? SecondaryColor { get; set; } + + [JsonPropertyName("tertiary_color")] + public int TertiaryColor { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Models/UseGuildTemplateRequest.cs b/extra/admin-api/Spacebar.UApi/Models/UseGuildTemplateRequest.cs new file mode 100644 index 000000000..47a516a04 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Models/UseGuildTemplateRequest.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; + +namespace Spacebar.UApi.Models; + +public class UseGuildTemplateRequest { + /// + /// Data URI encoded image + /// + [JsonPropertyName("icon")] + public string? Icon { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Program.cs b/extra/admin-api/Spacebar.UApi/Program.cs index 4695b7abc..afdb42ae8 100644 --- a/extra/admin-api/Spacebar.UApi/Program.cs +++ b/extra/admin-api/Spacebar.UApi/Program.cs @@ -1,13 +1,21 @@ +using System.Text.Json; using System.Text.Json.Serialization; using ArcaneLibs; +using ArcaneLibs.Extensions; using Microsoft.EntityFrameworkCore; +using Spacebar.ConfigModel; +using Spacebar.ConfigModel.Extensions; using Spacebar.Interop.Authentication; using Spacebar.Interop.Authentication.AspNetCore; using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Generic.Constants; +using Spacebar.UApi.Services; var builder = WebApplication.CreateBuilder(args); -if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("APPSETTINGS_PATH"))) +if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("APPSETTINGS_PATH"))) { + Console.WriteLine("Loading appsettings from path: " + Environment.GetEnvironmentVariable("APPSETTINGS_PATH")); builder.Configuration.AddJsonFile(Environment.GetEnvironmentVariable("APPSETTINGS_PATH")!); +} // Add services to the container. @@ -29,9 +37,30 @@ .EnableDetailedErrors(); }); +if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CONFIG_PATH"))) + builder.Services.AddSingleton(sp => { + var cfgPath = Environment.GetEnvironmentVariable("CONFIG_PATH"); + sp.GetService>().LogInformation("Using config from path: {path}", cfgPath); + return JsonSerializer.Deserialize(File.ReadAllText(cfgPath)); + }); +else + builder.Services.AddSingleton(sp => { + sp.GetService>().LogInformation("Using config from database..."); + var db = sp.GetService(); + var config = db.Configs + .OrderBy(x => x.Key) + .ToDictionary(x => x.Key, x => x.Value); + + var readConfig = config.ToNestedJsonObject(); + return readConfig.Deserialize(); + }); + +builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); var app = builder.Build(); @@ -43,10 +72,25 @@ app.UseAuthorization(); app.MapControllers(); +StreamingHttpClient.LogRequests = false; app.Use((context, next) => { - context.Response.Headers["Access-Control-Allow-Origin"] = "*"; - context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"; - context.Response.Headers["Access-Control-Allow-Headers"] = "*, Authorization"; + context.Response.Headers["Access-Control-Allow-Origin"] = + string.IsNullOrWhiteSpace(context.Request.Headers.Origin) + ? "*" + : context.Request.Headers.Origin; + + context.Response.Headers["Access-Control-Allow-Methods"] = + string.IsNullOrWhiteSpace(context.Request.Headers.AccessControlRequestMethod) + ? "GET, POST, PUT, DELETE, OPTIONS" + : context.Request.Headers.AccessControlRequestMethod.ToString(); + + context.Response.Headers["Access-Control-Allow-Headers"] = + string.IsNullOrWhiteSpace(context.Request.Headers.AccessControlRequestHeaders) + ? "*, Authorization" + : context.Request.Headers.AccessControlRequestHeaders.ToString(); + + context.Response.Headers.AccessControlAllowCredentials = "true"; + if (context.Request.Method == "OPTIONS") { context.Response.StatusCode = 200; return Task.CompletedTask; @@ -55,13 +99,39 @@ return next(); }); +// add some special sauce +app.Map("/", async context => { + var client = new StreamingHttpClient(); + var cfg = context.RequestServices.GetService(); + var requestMessage = new HttpRequestMessage(new HttpMethod(context.Request.Method), cfg.FallbackApiEndpoint); + + foreach (var header in context.Request.Headers) + if (header.Key is not ("Accept-Encoding" or "Host")) + requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray()); + + var responseMessage = await client.SendUnhandledAsync(requestMessage, CancellationToken.None); + context.Response.StatusCode = (int)responseMessage.StatusCode; + + foreach (var header in responseMessage.Headers) context.Response.Headers[header.Key] = header.Value.ToArray(); + foreach (var header in responseMessage.Content.Headers) context.Response.Headers[header.Key] = header.Value.ToArray(); + context.Response.Headers["X-SB-UApi-Status"] = "MISSING"; + + // await responseMessage.Content.CopyToAsync(context.Response.Body); + var txt = await responseMessage.Content.ReadAsStringAsync(); + txt = txt.Replace("your very own Spacebar instance", "your very own Spacebar instance with μAPI"); + var data = txt.AsBytes().ToArray(); + context.Response.Headers.ContentLength = data.Length; + await context.Response.Body.WriteAsync(data); +}); + // fallback to proxy in case we dont have a specific endpoint... // TODO config app.MapFallback("{*_}", async context => { var client = new StreamingHttpClient(); + var cfg = context.RequestServices.GetService(); var requestMessage = new HttpRequestMessage( new HttpMethod(context.Request.Method), - "http://api.old.server.spacebar.chat" + context.Request.Path + context.Request.QueryString + cfg.FallbackApiEndpoint + context.Request.Path + context.Request.QueryString ) { Content = new StreamContent(context.Request.Body) }; diff --git a/extra/admin-api/Spacebar.UApi/Services/PermissionService.cs b/extra/admin-api/Spacebar.UApi/Services/PermissionService.cs new file mode 100644 index 000000000..033dbb205 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Services/PermissionService.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Generic.Constants; + +namespace Spacebar.UApi.Services; + +public class PermissionService(SpacebarDbContext db) { + /// + /// Asserts that user has all the relevant guild permissions + /// + /// Permissions to require + /// Guild ID + /// Member ID + /// Has one or more missing permissions + public async Task AssertUserHasGuildPermission(Permissions permission, string guildId, string userId) { + var member = await db.Members + .Include(x => x.Roles) + .SingleAsync(x => x.Id == userId && x.GuildId == guildId); + + if (member is null) + throw new InvalidOperationException("You are not a member of this guild."); + + var permissions = member.Roles.Aggregate((Permissions)0UL, (current, role) => current | (Permissions)ulong.Parse(role.Permissions)); + + if (member.CommunicationDisabledUntil is not null && member.CommunicationDisabledUntil > DateTime.UtcNow) { + permissions &= Permissions.ViewChannel | Permissions.ReadMessageHistory; + } + + if (!permissions.HasFlag(permission)) + throw new PermissionException(Enum.GetValues().Where(p => !permissions.HasFlag(p) && permission.HasFlag(p))); + } +} + +public class PermissionException : Exception { + public IEnumerable MissingPermissions { get; } + + public PermissionException(IEnumerable missingPermissions) : base( + $"You do not have the required permissions to perform this action: {string.Join(", ", missingPermissions)}") { + MissingPermissions = missingPermissions; + } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Services/TemplateImportService.cs b/extra/admin-api/Spacebar.UApi/Services/TemplateImportService.cs new file mode 100644 index 000000000..89c2bfbc6 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Services/TemplateImportService.cs @@ -0,0 +1,13 @@ +using Spacebar.Models.Db.Contexts; +using Spacebar.Models.Db.Models; +using Spacebar.UApi.Models; + +namespace Spacebar.UApi.Services; + +public class TemplateImportService(SpacebarDbContext db) { + public async Task CreateGuildFromTemplateById(string templateId, UseGuildTemplateRequest request, User user) { + return ""; + } + + // public async Task<> +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Services/UApiConfiguration.cs b/extra/admin-api/Spacebar.UApi/Services/UApiConfiguration.cs new file mode 100644 index 000000000..84b014905 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/Services/UApiConfiguration.cs @@ -0,0 +1,10 @@ +namespace Spacebar.UApi.Services; + +public class UApiConfiguration { + public UApiConfiguration(IConfiguration config) { + config.GetRequiredSection("Spacebar").GetRequiredSection("UApi").Bind(this); + } + + // ... for unhandled routes + public string? FallbackApiEndpoint { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Spacebar.UApi/Spacebar.UApi.csproj b/extra/admin-api/Spacebar.UApi/Spacebar.UApi.csproj index 1fe9c7999..82d19a1c9 100644 --- a/extra/admin-api/Spacebar.UApi/Spacebar.UApi.csproj +++ b/extra/admin-api/Spacebar.UApi/Spacebar.UApi.csproj @@ -10,23 +10,25 @@ - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/extra/admin-api/Spacebar.UApi/appsettings.Development.json b/extra/admin-api/Spacebar.UApi/appsettings.Development.json index 0c208ae91..524b10da7 100644 --- a/extra/admin-api/Spacebar.UApi/appsettings.Development.json +++ b/extra/admin-api/Spacebar.UApi/appsettings.Development.json @@ -4,5 +4,17 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "ConnectionStrings": { + "Spacebar": "Host=127.0.0.1; Username=postgres; Database=spacebar; Port=5433; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;" + }, + "Spacebar": { + "Authentication": { + "PublicKeyPath": "../../../jwt.key.pub", + "PrivateKeyPath": "../../../jwt.key" + }, + "UApi":{ + "FallbackApiEndpoint": "http://localhost:3113" + } } } diff --git a/extra/admin-api/Spacebar.UApi/deps.json b/extra/admin-api/Spacebar.UApi/deps.json new file mode 100644 index 000000000..6fd848627 --- /dev/null +++ b/extra/admin-api/Spacebar.UApi/deps.json @@ -0,0 +1,87 @@ +[ + { + "pname": "ArcaneLibs", + "version": "1.0.1-preview.20260126-091403", + "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" + }, + { + "pname": "ArcaneLibs.StringNormalisation", + "version": "1.0.1-preview.20260126-091403", + "hash": "sha256-XcwbLG9w4OFzMVA54pvTvRJ9692tvE8D0tMVcteSQL0=" + }, + { + "pname": "BCrypt.Net-Next", + "version": "4.1.0", + "hash": "sha256-Efjrsw4FXgVKA2vQV6ztc40pLsonnV3JxwxW7eOyfDk=" + }, + { + "pname": "Microsoft.AspNetCore.OpenApi", + "version": "10.0.5", + "hash": "sha256-CQXAu6Tm8nOy/rrZksIKGaLW7USEP/N1kwKBMLoh7js=" + }, + { + "pname": "Microsoft.EntityFrameworkCore", + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Abstractions", + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Analyzers", + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" + }, + { + "pname": "Microsoft.EntityFrameworkCore.Relational", + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" + }, + { + "pname": "Microsoft.IdentityModel.Abstractions", + "version": "8.16.0", + "hash": "sha256-OpTFQpTtg1A8I1bBIOqv/n9pwYXTqzMI8ZLXLZDti5w=" + }, + { + "pname": "Microsoft.IdentityModel.JsonWebTokens", + "version": "8.16.0", + "hash": "sha256-Cctf2iuIXLMklTuCvzWv721v2mHs0HEBA47BqAKhp9I=" + }, + { + "pname": "Microsoft.IdentityModel.Logging", + "version": "8.16.0", + "hash": "sha256-355u+3LIn/QfiCHFMXD+3ipdRTnbXLAQNzC4sWEFapQ=" + }, + { + "pname": "Microsoft.IdentityModel.Tokens", + "version": "8.16.0", + "hash": "sha256-6s8ZLnKw32W6+KbnahCVe1v9YzpoemnpHNQ3VbFSV4M=" + }, + { + "pname": "Microsoft.OpenApi", + "version": "2.0.0", + "hash": "sha256-8eiM3Mx4Hx1etx52RlczoHG2z9XIpWgu2LQtWSt086k=" + }, + { + "pname": "Npgsql", + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" + }, + { + "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" + }, + { + "pname": "System.IdentityModel.Tokens.Jwt", + "version": "8.16.0", + "hash": "sha256-wCEkUPnKDjO7Kpfr1vpr5Icvk69gFHgEWcSLbFtD6pg=" + }, + { + "pname": "Unidecode.NET", + "version": "2.2.1", + "hash": "sha256-erDK7cXaIyuwntBhRpop3OlwwnpFJREYQBwE7xqpCGk=" + } +] diff --git a/extra/admin-api/Utilities/ConfigTest/ConfigTest.csproj b/extra/admin-api/Utilities/ConfigTest/ConfigTest.csproj index 98895a19c..64543262e 100644 --- a/extra/admin-api/Utilities/ConfigTest/ConfigTest.csproj +++ b/extra/admin-api/Utilities/ConfigTest/ConfigTest.csproj @@ -7,7 +7,7 @@ - + diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor new file mode 100644 index 000000000..ff233731e --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor @@ -0,0 +1,83 @@ +@page "/GuildDiscovery" +@using System.Net.Http.Headers +@using System.Text.Json.Nodes +@using ArcaneLibs +@using ArcaneLibs.Blazor.Components +@using Spacebar.AdminApi.TestClient.Services +@using Spacebar.Models.AdminApi +@inject Config Config +

GuildDiscovery

+Save + +
+ @foreach (var guild in _guilds) { + if (guild == null) continue; + +
+ @guild.Name + @if (guild.Banner is not null) { + @guild.Name + } +

@guild.Name

+

@guild.Description

+ +
+
+ @guild.PresenceCount Online + @guild.MemberCount Members +
+ + + + +
+
+ } +
+ +@code { + + private List _guilds = []; + + protected override async Task OnInitializedAsync() { + var hc = new StreamingHttpClient(); + hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken); + var response = await hc.GetAsync(Config.AdminUrl + $"/_spacebar/admin/discovery/guilds?includeExcluded=true"); + if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync()); + var content = response.Content.ReadFromJsonAsAsyncEnumerable(); + await foreach (var guild in content) { + _guilds.Add(guild); + // var d = Task.Delay(25); + await StateHasChangedAsync(); + // await d; + } + } + + private async Task StateHasChangedAsync() { + StateHasChanged(); + await Task.Delay(1); + } + + protected async Task SaveChangesAsync() { + var hc = new StreamingHttpClient(); + hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Config.AccessToken); + var response = await hc.GetAsync(Config.AdminUrl + $"/_spacebar/admin/discovery/guilds?includeExcluded=true"); + if (!response.IsSuccessStatusCode) throw new Exception(await response.Content.ReadAsStringAsync()); + var content = response.Content.ReadFromJsonAsAsyncEnumerable(); + await foreach (var guild in content) { + var matchingGuild = _guilds.FirstOrDefault(g => g!.Id == guild!.Id); + if (matchingGuild == null) continue; + JsonObject update = new(); + + if (matchingGuild.DiscoveryExcluded != guild.DiscoveryExcluded) + update["discovery_excluded"] = matchingGuild.DiscoveryExcluded; + + if (matchingGuild.DiscoveryWeight != guild.DiscoveryWeight) + update["discovery_weight"] = matchingGuild.DiscoveryWeight; + + if (update.Count == 0) continue; + await hc.PatchAsJsonAsync(Config.AdminUrl + $"/_spacebar/admin/discovery/guilds/{guild.Id}", update); + } + } + +} \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css new file mode 100644 index 000000000..b1873d108 --- /dev/null +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Pages/GuildDiscovery.razor.css @@ -0,0 +1,59 @@ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(20px); } + to { transform: translateY(0); } +} + +.guild-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 3rem 1rem; + padding: 2rem 1rem 1rem; +} + +.guild-card { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem; + padding-top: 40px; + border: 1px solid #ccc; + border-radius: 8px; + width: 100%; + height: 360px; + background-color: var(--bs-card-bg); + margin-top: 32px; + animation: fadeIn 0.5s ease-out forwards; +} + +.guild-icon { + position: absolute; + top: -32px; + width: 64px; + height: 64px; + border-radius: 50%; + margin-bottom: 0; + border: 4px solid var(--bs-body-bg); + z-index: 10; +} + +.guild-banner { + width: 100%; + height: 150px; + object-fit: cover; + margin-bottom: 0.5rem; + position: absolute; + top: 0; + left: 0; + z-index: -1; + mask-image: linear-gradient(to bottom, black 0%, transparent 100%); +} + +.guild-details { + margin-top: auto; + text-align: center; +} + +.guild-card.excluded { + opacity: 0.2; +} \ No newline at end of file diff --git a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj index d1656c633..205891b6a 100644 --- a/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj +++ b/extra/admin-api/Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj @@ -21,9 +21,11 @@ - - - + + + + +
diff --git a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs index c0b0bbc3e..add630e5d 100644 --- a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs +++ b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/FsckService.cs @@ -29,10 +29,11 @@ public async Task StopAsync(CancellationToken cancellationToken) { } public struct FsckItem { public string Path; public string ItemId; + public bool IsSingleSubDirFile; } private async Task RunFsckAsync(string name, string path, IQueryable items) { - int i = 0, count = await items.CountAsync(); + int i = 0, count = await items.CountAsync(), missing = 0; List tasks = []; await foreach (var item in items.AsAsyncEnumerable()) { @@ -44,15 +45,28 @@ private async Task RunFsckAsync(string name, string path, IQueryable i } i++; - if (!await fs.FileExists(item.Path)) - logger.LogWarning("{itemType} {itemId} is missing at {path}", name, item.ItemId, item.Path); + if (!item.IsSingleSubDirFile) { + if (!await fs.FileExists(item.Path)) { + logger.LogWarning("{itemType} {itemId} is missing at {path}", name, item.ItemId, item.Path); + missing++; + } + } + else if (item.IsSingleSubDirFile && fs is FilesystemFileSource ffs) { + if (!await ffs.DirectoryExists(Path.GetDirectoryName(item.Path))) { + logger.LogWarning("{itemType} {itemId} is missing at {path} (directory missing)", name, item.ItemId, item.Path); + missing++; + } + } + else { + logger.LogWarning("Unhandled case: {itemType} {itemId} -> {path} (IsSingleSubDirFile: {isSingleSubDirFile}, fstype: {fsType})", name, item.ItemId, item.Path, item.IsSingleSubDirFile, fs.GetType().Name); + } _fsckSemaphore.Release(); })); } await Task.WhenAll(tasks); - logger.LogInformation("Validated {count} items for {path}.", i, path); + logger.LogInformation("Validated {count} items for {path}, {missing} missing.", i, path, missing); } #region User Assets @@ -87,7 +101,7 @@ public IQueryable EnumerateGuildIconPathsAsync() => Path = $"/icons/{x.Id}/{x.Icon}", ItemId = x.Id }); - + public IQueryable EnumerateRoleIconPathsAsync() => _db.Roles .Where(x => !string.IsNullOrWhiteSpace(x.Icon)) @@ -101,7 +115,7 @@ public IQueryable EnumerateStickerPathsAsync() => _db.Stickers .OrderBy(x => x.Id) .Select(x => new FsckItem { - Path = $"/stickers/{x.Id}.png", + Path = $"/stickers/{x.Id}", ItemId = x.Id }); diff --git a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/Spacebar.Cdn.Fsck.csproj b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/Spacebar.Cdn.Fsck.csproj index b6204556c..752c1aeec 100644 --- a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/Spacebar.Cdn.Fsck.csproj +++ b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/Spacebar.Cdn.Fsck.csproj @@ -7,7 +7,7 @@ - + diff --git a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/deps.json b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/deps.json index 516b87b20..0902c088a 100644 --- a/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/deps.json +++ b/extra/admin-api/Utilities/Spacebar.Cdn.Fsck/deps.json @@ -4,200 +4,140 @@ "version": "1.0.1-preview.20260126-091403", "hash": "sha256-CSmNE16nDi05qyDAcJR+8SqQQ2ReAeX0+/dRP3WpNsg=" }, - { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" + "version": "10.0.4", + "hash": "sha256-/vLXWvT42HQAm/JLjWGeFo9AvLn/mu4nCNjabm+wABE=" }, { "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" + "version": "10.0.4", + "hash": "sha256-+0sK/vSyB4KFC9kliECROfK1WaiWwlRrhLpi03pT+3w=" }, { "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" + "version": "10.0.5", + "hash": "sha256-6rOmJD7Jzq5MPLDd1aV+7gCQwIM9j4c+iT1pGea/daI=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" + "version": "10.0.4", + "hash": "sha256-bvEQLGSOpJHKdPD6kd59IIi4x57lKapVMgOORtcjJPs=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.5", + "hash": "sha256-DNK+lL2jeHFYyd43zfgVY32UskEfQ4YsTapztuQbYwo=" }, { "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" + "version": "10.0.5", + "hash": "sha256-cVG2NEW1rgLfeq/Gnh/XXqzDx2Tt8ecvgCAB4uFzcQo=" }, { "pname": "Microsoft.Extensions.Configuration.CommandLine", - "version": "10.0.2", - "hash": "sha256-2+bwt7N721EO3/uy2rnungaFguA2WNJ7pO1XKVClPKw=" + "version": "10.0.5", + "hash": "sha256-q5KS8ZtQTkc4WSjqczVIWbFZzdGNxbannVocNSi+coI=" }, { "pname": "Microsoft.Extensions.Configuration.EnvironmentVariables", - "version": "10.0.2", - "hash": "sha256-tjqCocpGVX4jg6z8VXjjooOqegcoqJA0sjIIslQPLyU=" + "version": "10.0.5", + "hash": "sha256-ngWw/kwYWNmK67ErLFoJDKULd4GOXv6MlD6P+eZ4q6A=" }, { "pname": "Microsoft.Extensions.Configuration.FileExtensions", - "version": "10.0.2", - "hash": "sha256-oOHg4UShuRyaflHJL1YLbkHSlwQtpZW2wONnsTKKiE4=" + "version": "10.0.5", + "hash": "sha256-PUIRpSxnT41HnF/6ZPMe7oAAAJ8gm6mNGRuPhw5Zy6k=" }, { "pname": "Microsoft.Extensions.Configuration.Json", - "version": "10.0.2", - "hash": "sha256-RDfVyrOP9E5kVax3VswqJzPFTHSm3MZ45gSRLO0qFWs=" + "version": "10.0.5", + "hash": "sha256-jxcGaQpr8RRQVtCobxvCOyn+8k/U2m5H5/ltFYhdKAk=" }, { "pname": "Microsoft.Extensions.Configuration.UserSecrets", - "version": "10.0.2", - "hash": "sha256-O7yda5ebH81nOAOVOrW+CaqtPcsJmWscLV41T40hHHk=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" + "version": "10.0.5", + "hash": "sha256-ROkQPXDOFU3hOrKtdwvRNR3so5eFLaJxJDxqhDX8IpU=" }, { "pname": "Microsoft.Extensions.DependencyInjection", - "version": "9.0.0", - "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + "version": "10.0.5", + "hash": "sha256-ofDRirUV9XLSz4oksCqErwBJFtAieHACFfyZukHKFng=" }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" + "version": "10.0.4", + "hash": "sha256-0QhVYjk9Cxy6NFef9VKftGmscTZnvcD1bhBQoXz3mwA=" }, { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.5", + "hash": "sha256-KrP+hE3gk7pATbJYZsJ1LHiXjzLA+ntHW7G/VGgHk2g=" }, { "pname": "Microsoft.Extensions.Diagnostics", - "version": "10.0.2", - "hash": "sha256-Zb/qJA0cZYiPQO7I3AegZCcNT0aJlKTycUP11Mqbm6o=" + "version": "10.0.5", + "hash": "sha256-LlFT3ZzFH9QfymvP9DY4NteJKTdT+mqSGpzUDpsLNhM=" }, { "pname": "Microsoft.Extensions.Diagnostics.Abstractions", - "version": "10.0.2", - "hash": "sha256-Aob6wq51LdquE7SkkxtCzcuHBKWrJcb3Ebi/dU3aqA4=" + "version": "10.0.5", + "hash": "sha256-pwQltVfaqx0jRpO0d9k/dYtyOpnGixK2MB3aHVVbo0E=" }, { "pname": "Microsoft.Extensions.FileProviders.Abstractions", - "version": "10.0.2", - "hash": "sha256-tibCkkT9WliU2E/i0ufx7/Va6H6QZX4hR/1oUp8ecgQ=" + "version": "10.0.5", + "hash": "sha256-6RwvzBQTJzbypUQAM9WNRkpn3gON63DLxY3gH0ZcrH4=" }, { "pname": "Microsoft.Extensions.FileProviders.Physical", - "version": "10.0.2", - "hash": "sha256-HtzaL9KEFkyIyG31RIjj57yFBw6Ja3U6UYLP0LLesvw=" + "version": "10.0.5", + "hash": "sha256-36irJIKuzhCmA28CC4U8+3W8EJ5U9PwMMdCSXRCp4J0=" }, { "pname": "Microsoft.Extensions.FileSystemGlobbing", - "version": "10.0.2", - "hash": "sha256-WHqwQLBlb4w+fw1KZVPvunir5cM/4jXExWRofgfVAxs=" + "version": "10.0.5", + "hash": "sha256-FdwPwpyNejk8YEw4doaouas0oe+8d7K2m49WH+7k0RE=" }, { "pname": "Microsoft.Extensions.Hosting", - "version": "10.0.2", - "hash": "sha256-q+GiN081aRi0690nHVhWmAynfokQINPuvGzKiXh6oIM=" + "version": "10.0.5", + "hash": "sha256-NJFH/l2wutMISKLZzMqFZfY1/QuMskSAhgWelo9V6Nc=" }, { "pname": "Microsoft.Extensions.Hosting.Abstractions", - "version": "10.0.2", - "hash": "sha256-mkeKUXepn4bfEdZFXdURmNEFdGiHQdpcxnm6joG+pUA=" + "version": "10.0.5", + "hash": "sha256-mqxu+PvqYP/J/9UWEbLIAfSNnsABhdl6m5wkZFf1DDM=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" + "version": "10.0.4", + "hash": "sha256-eneXBu83dGBiWMFabheGFPYiZJQ+WMewG6bTs2oJ7RA=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + "version": "10.0.5", + "hash": "sha256-4gVrKZfo/YHZKgKNsgGZZYqa79XWK9wDUuiVfguUV6U=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", @@ -206,127 +146,77 @@ }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" + "version": "10.0.4", + "hash": "sha256-dvBRBgEf4Bw9NW2J2XDlEeJw2TNhJ+6gV98dPE13/J0=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + "version": "10.0.5", + "hash": "sha256-e3A/l+II+n+D7/OPwjdyQM1IBtKHfHeIdlkJmuRw77w=" }, { "pname": "Microsoft.Extensions.Logging.Configuration", - "version": "10.0.2", - "hash": "sha256-eSfmagvrcdRVmTXEdzfvWEDNBewB/YClOGSDq4gThk0=" + "version": "10.0.5", + "hash": "sha256-yp0WZcCm+SAkMP9U/B3Dg/v282pFMXbZHVzAp2GLGwA=" }, { "pname": "Microsoft.Extensions.Logging.Console", - "version": "10.0.2", - "hash": "sha256-mfaypUwMZxEIKBfnmzhnCScC/uPpZBRYN+/2SNcxcu4=" + "version": "10.0.5", + "hash": "sha256-Epf70cMofVjqKQxTTgsmNNRW8YcpHj5c80nX8cJTv40=" }, { "pname": "Microsoft.Extensions.Logging.Debug", - "version": "10.0.2", - "hash": "sha256-hcHfYTWyQzRHEkuo9ejkUZxfJC0+JMLOgq51jpDJwOI=" + "version": "10.0.5", + "hash": "sha256-g6jXBWDCXXUpmygKkVChZq8vUqJgG7qFTJPI0BD32oo=" }, { "pname": "Microsoft.Extensions.Logging.EventLog", - "version": "10.0.2", - "hash": "sha256-LFbcqm2JfDowycOiVWNzWrF0vAsP3ug6c1njt373nE4=" + "version": "10.0.5", + "hash": "sha256-RWePIK3PZcc6ULwQa+zZMx+LCJf3jlVERnyPMVJl1ks=" }, { "pname": "Microsoft.Extensions.Logging.EventSource", - "version": "10.0.2", - "hash": "sha256-1vp78PlJpE+zyWknU7imDTq0mC8Fk0wU11DqDRuuEF4=" + "version": "10.0.5", + "hash": "sha256-YV2ff+tl3fpFkYZ3xOqosugYR5ai4oWKovK/HqDGdTc=" }, { "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" + "version": "10.0.4", + "hash": "sha256-ybyUtpSs/irdarkVjsGhqDcj0w8TNUgh0Kp0j3EVfIA=" }, { "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" + "version": "10.0.5", + "hash": "sha256-nw+m6VWXjmaBqZ1aH/l9SR9Oy62N9dmiMKloJ78kxv8=" }, { "pname": "Microsoft.Extensions.Options.ConfigurationExtensions", - "version": "10.0.2", - "hash": "sha256-WJahsWyT5wYdLPEJufHKpb3l/dl7D2iw2SnMK0Jr53U=" + "version": "10.0.5", + "hash": "sha256-VQPPvrvYWY/QpmilerCyTNLVejWeBE9mHtGTMOxXUlg=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.4", + "hash": "sha256-ePpEFzrKQbEMY6Kh/xyxKHq6txNru62Vh4LVZO5Rrvg=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" - }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + "version": "10.0.5", + "hash": "sha256-uvrur+0dg4zAAQcpLkkhPA77ST0tA3+EpGdDlCckC+E=" }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" }, { "pname": "System.Diagnostics.EventLog", - "version": "10.0.2", - "hash": "sha256-rezZk0M4+MWRxwGSFzXfvekrhL8qLTp7Pc8YsSy/4nE=" + "version": "10.0.5", + "hash": "sha256-SwmQif5X/T4cIPPNkVf6QsvA3oBLb85iJXNkb3GT+sU=" } ] diff --git a/extra/admin-api/Utilities/Spacebar.Cdn.Migration/Spacebar.Cdn.Migration.csproj b/extra/admin-api/Utilities/Spacebar.Cdn.Migration/Spacebar.Cdn.Migration.csproj index 0d076ae96..e13ece9f2 100644 --- a/extra/admin-api/Utilities/Spacebar.Cdn.Migration/Spacebar.Cdn.Migration.csproj +++ b/extra/admin-api/Utilities/Spacebar.Cdn.Migration/Spacebar.Cdn.Migration.csproj @@ -9,7 +9,7 @@ - + diff --git a/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/Spacebar.CleanSettingsRows.csproj b/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/Spacebar.CleanSettingsRows.csproj index 3e2412713..f4dfc6037 100644 --- a/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/Spacebar.CleanSettingsRows.csproj +++ b/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/Spacebar.CleanSettingsRows.csproj @@ -7,7 +7,7 @@ - + diff --git a/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/deps.json b/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/deps.json index 269c9e391..3b3685068 100644 --- a/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/deps.json +++ b/extra/admin-api/Utilities/Spacebar.CleanSettingsRows/deps.json @@ -1,198 +1,138 @@ [ - { - "pname": "Humanizer.Core", - "version": "2.14.1", - "hash": "sha256-EXvojddPu+9JKgOG9NSQgUTfWq1RpOYw7adxDPKDJ6o=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "17.11.31", - "hash": "sha256-YS4oASrmC5dmZrx5JPS7SfKmUpIJErlUpVDsU3VrfFE=" - }, - { - "pname": "Microsoft.Build.Framework", - "version": "18.0.2", - "hash": "sha256-fO31KAdDs2J0RUYD1ov9UB3ucsbALan7K0YdWW+yg7A=" - }, - { - "pname": "Microsoft.CodeAnalysis.Analyzers", - "version": "3.11.0", - "hash": "sha256-hQ2l6E6PO4m7i+ZsfFlEx+93UsLPo4IY3wDkNG11/Sw=" - }, - { - "pname": "Microsoft.CodeAnalysis.Common", - "version": "5.0.0", - "hash": "sha256-g4ALvBSNyHEmSb1l5TFtWW7zEkiRmhqLx4XWZu9sr2U=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp", - "version": "5.0.0", - "hash": "sha256-ctBCkQGFpH/xT5rRE3xibu9YxPD108RuC4a4Z25koG8=" - }, - { - "pname": "Microsoft.CodeAnalysis.CSharp.Workspaces", - "version": "5.0.0", - "hash": "sha256-yWVcLt/f2CouOfFy966glGdtSFy+RcgrU1dd9UtlL/Q=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.Common", - "version": "5.0.0", - "hash": "sha256-Bir5e1gEhgQQ6upQmVKQHAKLRfenAu60DAzNupNnZsQ=" - }, - { - "pname": "Microsoft.CodeAnalysis.Workspaces.MSBuild", - "version": "5.0.0", - "hash": "sha256-+58+iqTayTiE0pDaog1U8mjaDA8bNNDLA8gjCQZZudo=" - }, { "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.0", - "hash": "sha256-xfgrlxhtOkQwF5Q7j8gSm41URJiH8IuJ/T/Dh88++hE=" - }, - { - "pname": "Microsoft.EntityFrameworkCore", - "version": "10.0.2", - "hash": "sha256-FS6T8EnaWCMtj4PnZhh+oF8mcM44VlM3wkTSMlpte9A=" + "version": "10.0.4", + "hash": "sha256-V3Vwl1MtVMRTPo7a9lAgs6UaeMnFV3eEsKnLyPaPMHA=" }, { "pname": "Microsoft.EntityFrameworkCore.Abstractions", - "version": "10.0.2", - "hash": "sha256-qkDfIJpcPO2kk4n5OE/13hI/0mUygpTofInn95XjRZI=" + "version": "10.0.4", + "hash": "sha256-so4y7Wrp/oWhQ7wd1rK9ha9GtPme1l5H8SrQz7sf8MQ=" }, { "pname": "Microsoft.EntityFrameworkCore.Analyzers", - "version": "10.0.2", - "hash": "sha256-yOv78rgAACBz1zjitpcZbQQ3zx8huJongZTHkhN4PQ0=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Design", - "version": "10.0.2", - "hash": "sha256-bTShsGux0y/49PIIMb/4ZX3x5+rPacvT5/NcooNCI1Y=" - }, - { - "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.0", - "hash": "sha256-vOP2CE5YA551BlpbOuIy6RuAiAEPEpCVS1cEE33/zN4=" + "version": "10.0.4", + "hash": "sha256-Wed75M4RdQ7bCUWSc+q/0YqL6R5CrIZ2X0t/LpU7ZZA=" }, { "pname": "Microsoft.EntityFrameworkCore.Relational", - "version": "10.0.2", - "hash": "sha256-Y4jPpoYhKizg5wF6QfkBX4sYlE2FU1bYhfoDN3xkhKM=" + "version": "10.0.4", + "hash": "sha256-WwGoCwNxDXyqfBvUX9fGa/6X+yiDBuE7hf3csU78+Os=" }, { "pname": "Microsoft.Extensions.Caching.Abstractions", - "version": "10.0.2", - "hash": "sha256-nKmQuZTt1g5/8gBajo7wdCV64kdCucdiQR8JTt7ZZb0=" + "version": "10.0.4", + "hash": "sha256-/vLXWvT42HQAm/JLjWGeFo9AvLn/mu4nCNjabm+wABE=" }, { "pname": "Microsoft.Extensions.Caching.Memory", - "version": "10.0.2", - "hash": "sha256-sRUF7DM0s1yzZnfjM/hF9A/IysE6Er23gZ6jST+RWh0=" + "version": "10.0.4", + "hash": "sha256-+0sK/vSyB4KFC9kliECROfK1WaiWwlRrhLpi03pT+3w=" }, { "pname": "Microsoft.Extensions.Configuration", - "version": "10.0.2", - "hash": "sha256-dBJAKDyp/sm+ZSMQfH0+4OH8Jnv1s20aHlWS6HNnH+c=" + "version": "10.0.5", + "hash": "sha256-6rOmJD7Jzq5MPLDd1aV+7gCQwIM9j4c+iT1pGea/daI=" }, { "pname": "Microsoft.Extensions.Configuration.Abstractions", - "version": "10.0.2", - "hash": "sha256-P+0kaDGO+xB9KxF9eWHDJ4hzi05sUGM/uMNEX5NdBTE=" + "version": "10.0.4", + "hash": "sha256-bvEQLGSOpJHKdPD6kd59IIi4x57lKapVMgOORtcjJPs=" + }, + { + "pname": "Microsoft.Extensions.Configuration.Abstractions", + "version": "10.0.5", + "hash": "sha256-DNK+lL2jeHFYyd43zfgVY32UskEfQ4YsTapztuQbYwo=" }, { "pname": "Microsoft.Extensions.Configuration.Binder", - "version": "10.0.2", - "hash": "sha256-resI9gIxHh2cc+258/i+TjW8xxzKf4ZBTLIcWAMEYz0=" + "version": "10.0.5", + "hash": "sha256-cVG2NEW1rgLfeq/Gnh/XXqzDx2Tt8ecvgCAB4uFzcQo=" }, { "pname": "Microsoft.Extensions.Configuration.CommandLine", - "version": "10.0.2", - "hash": "sha256-2+bwt7N721EO3/uy2rnungaFguA2WNJ7pO1XKVClPKw=" + "version": "10.0.5", + "hash": "sha256-q5KS8ZtQTkc4WSjqczVIWbFZzdGNxbannVocNSi+coI=" }, { "pname": "Microsoft.Extensions.Configuration.EnvironmentVariables", - "version": "10.0.2", - "hash": "sha256-tjqCocpGVX4jg6z8VXjjooOqegcoqJA0sjIIslQPLyU=" + "version": "10.0.5", + "hash": "sha256-ngWw/kwYWNmK67ErLFoJDKULd4GOXv6MlD6P+eZ4q6A=" }, { "pname": "Microsoft.Extensions.Configuration.FileExtensions", - "version": "10.0.2", - "hash": "sha256-oOHg4UShuRyaflHJL1YLbkHSlwQtpZW2wONnsTKKiE4=" + "version": "10.0.5", + "hash": "sha256-PUIRpSxnT41HnF/6ZPMe7oAAAJ8gm6mNGRuPhw5Zy6k=" }, { "pname": "Microsoft.Extensions.Configuration.Json", - "version": "10.0.2", - "hash": "sha256-RDfVyrOP9E5kVax3VswqJzPFTHSm3MZ45gSRLO0qFWs=" + "version": "10.0.5", + "hash": "sha256-jxcGaQpr8RRQVtCobxvCOyn+8k/U2m5H5/ltFYhdKAk=" }, { "pname": "Microsoft.Extensions.Configuration.UserSecrets", - "version": "10.0.2", - "hash": "sha256-O7yda5ebH81nOAOVOrW+CaqtPcsJmWscLV41T40hHHk=" - }, - { - "pname": "Microsoft.Extensions.DependencyInjection", - "version": "10.0.2", - "hash": "sha256-/9UWQRAI2eoocnJWWf1ktnAx/1Gt65c16fc0Xqr9+CQ=" + "version": "10.0.5", + "hash": "sha256-ROkQPXDOFU3hOrKtdwvRNR3so5eFLaJxJDxqhDX8IpU=" }, { "pname": "Microsoft.Extensions.DependencyInjection", - "version": "9.0.0", - "hash": "sha256-dAH52PPlTLn7X+1aI/7npdrDzMEFPMXRv4isV1a+14k=" + "version": "10.0.5", + "hash": "sha256-ofDRirUV9XLSz4oksCqErwBJFtAieHACFfyZukHKFng=" }, { "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", - "version": "10.0.2", - "hash": "sha256-UF9T13V5SQxJy2msfLmyovLmitZrjJayf8gHH+uK2eg=" + "version": "10.0.4", + "hash": "sha256-0QhVYjk9Cxy6NFef9VKftGmscTZnvcD1bhBQoXz3mwA=" }, { - "pname": "Microsoft.Extensions.DependencyModel", - "version": "10.0.2", - "hash": "sha256-w/dGIjtZiGH+KW3969BPOdQpQEV+WB7RPTa2MK2DavE=" + "pname": "Microsoft.Extensions.DependencyInjection.Abstractions", + "version": "10.0.5", + "hash": "sha256-KrP+hE3gk7pATbJYZsJ1LHiXjzLA+ntHW7G/VGgHk2g=" }, { "pname": "Microsoft.Extensions.Diagnostics", - "version": "10.0.2", - "hash": "sha256-Zb/qJA0cZYiPQO7I3AegZCcNT0aJlKTycUP11Mqbm6o=" + "version": "10.0.5", + "hash": "sha256-LlFT3ZzFH9QfymvP9DY4NteJKTdT+mqSGpzUDpsLNhM=" }, { "pname": "Microsoft.Extensions.Diagnostics.Abstractions", - "version": "10.0.2", - "hash": "sha256-Aob6wq51LdquE7SkkxtCzcuHBKWrJcb3Ebi/dU3aqA4=" + "version": "10.0.5", + "hash": "sha256-pwQltVfaqx0jRpO0d9k/dYtyOpnGixK2MB3aHVVbo0E=" }, { "pname": "Microsoft.Extensions.FileProviders.Abstractions", - "version": "10.0.2", - "hash": "sha256-tibCkkT9WliU2E/i0ufx7/Va6H6QZX4hR/1oUp8ecgQ=" + "version": "10.0.5", + "hash": "sha256-6RwvzBQTJzbypUQAM9WNRkpn3gON63DLxY3gH0ZcrH4=" }, { "pname": "Microsoft.Extensions.FileProviders.Physical", - "version": "10.0.2", - "hash": "sha256-HtzaL9KEFkyIyG31RIjj57yFBw6Ja3U6UYLP0LLesvw=" + "version": "10.0.5", + "hash": "sha256-36irJIKuzhCmA28CC4U8+3W8EJ5U9PwMMdCSXRCp4J0=" }, { "pname": "Microsoft.Extensions.FileSystemGlobbing", - "version": "10.0.2", - "hash": "sha256-WHqwQLBlb4w+fw1KZVPvunir5cM/4jXExWRofgfVAxs=" + "version": "10.0.5", + "hash": "sha256-FdwPwpyNejk8YEw4doaouas0oe+8d7K2m49WH+7k0RE=" }, { "pname": "Microsoft.Extensions.Hosting", - "version": "10.0.2", - "hash": "sha256-q+GiN081aRi0690nHVhWmAynfokQINPuvGzKiXh6oIM=" + "version": "10.0.5", + "hash": "sha256-NJFH/l2wutMISKLZzMqFZfY1/QuMskSAhgWelo9V6Nc=" }, { "pname": "Microsoft.Extensions.Hosting.Abstractions", - "version": "10.0.2", - "hash": "sha256-mkeKUXepn4bfEdZFXdURmNEFdGiHQdpcxnm6joG+pUA=" + "version": "10.0.5", + "hash": "sha256-mqxu+PvqYP/J/9UWEbLIAfSNnsABhdl6m5wkZFf1DDM=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "10.0.2", - "hash": "sha256-9+gfQwK32JMYscW1YvyCWEzc9mRZOjCACoD9U1vVaJI=" + "version": "10.0.4", + "hash": "sha256-eneXBu83dGBiWMFabheGFPYiZJQ+WMewG6bTs2oJ7RA=" }, { "pname": "Microsoft.Extensions.Logging", - "version": "9.0.0", - "hash": "sha256-kR16c+N8nQrWeYLajqnXPg7RiXjZMSFLnKLEs4VfjcM=" + "version": "10.0.5", + "hash": "sha256-4gVrKZfo/YHZKgKNsgGZZYqa79XWK9wDUuiVfguUV6U=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", @@ -201,127 +141,77 @@ }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "10.0.2", - "hash": "sha256-ndKGzq8+2J/hvaIULwBui0L/jDyMQTAY24j+ohX5VX8=" + "version": "10.0.4", + "hash": "sha256-dvBRBgEf4Bw9NW2J2XDlEeJw2TNhJ+6gV98dPE13/J0=" }, { "pname": "Microsoft.Extensions.Logging.Abstractions", - "version": "9.0.0", - "hash": "sha256-iBTs9twjWXFeERt4CErkIIcoJZU1jrd1RWCI8V5j7KU=" + "version": "10.0.5", + "hash": "sha256-e3A/l+II+n+D7/OPwjdyQM1IBtKHfHeIdlkJmuRw77w=" }, { "pname": "Microsoft.Extensions.Logging.Configuration", - "version": "10.0.2", - "hash": "sha256-eSfmagvrcdRVmTXEdzfvWEDNBewB/YClOGSDq4gThk0=" + "version": "10.0.5", + "hash": "sha256-yp0WZcCm+SAkMP9U/B3Dg/v282pFMXbZHVzAp2GLGwA=" }, { "pname": "Microsoft.Extensions.Logging.Console", - "version": "10.0.2", - "hash": "sha256-mfaypUwMZxEIKBfnmzhnCScC/uPpZBRYN+/2SNcxcu4=" + "version": "10.0.5", + "hash": "sha256-Epf70cMofVjqKQxTTgsmNNRW8YcpHj5c80nX8cJTv40=" }, { "pname": "Microsoft.Extensions.Logging.Debug", - "version": "10.0.2", - "hash": "sha256-hcHfYTWyQzRHEkuo9ejkUZxfJC0+JMLOgq51jpDJwOI=" + "version": "10.0.5", + "hash": "sha256-g6jXBWDCXXUpmygKkVChZq8vUqJgG7qFTJPI0BD32oo=" }, { "pname": "Microsoft.Extensions.Logging.EventLog", - "version": "10.0.2", - "hash": "sha256-LFbcqm2JfDowycOiVWNzWrF0vAsP3ug6c1njt373nE4=" + "version": "10.0.5", + "hash": "sha256-RWePIK3PZcc6ULwQa+zZMx+LCJf3jlVERnyPMVJl1ks=" }, { "pname": "Microsoft.Extensions.Logging.EventSource", - "version": "10.0.2", - "hash": "sha256-1vp78PlJpE+zyWknU7imDTq0mC8Fk0wU11DqDRuuEF4=" + "version": "10.0.5", + "hash": "sha256-YV2ff+tl3fpFkYZ3xOqosugYR5ai4oWKovK/HqDGdTc=" }, { "pname": "Microsoft.Extensions.Options", - "version": "10.0.2", - "hash": "sha256-12AfUEDdta/pmZUyEyqSUfOk0YoA7JOfGmIYnZQ//qk=" + "version": "10.0.4", + "hash": "sha256-ybyUtpSs/irdarkVjsGhqDcj0w8TNUgh0Kp0j3EVfIA=" }, { "pname": "Microsoft.Extensions.Options", - "version": "9.0.0", - "hash": "sha256-DT5euAQY/ItB5LPI8WIp6Dnd0lSvBRP35vFkOXC68ck=" + "version": "10.0.5", + "hash": "sha256-nw+m6VWXjmaBqZ1aH/l9SR9Oy62N9dmiMKloJ78kxv8=" }, { "pname": "Microsoft.Extensions.Options.ConfigurationExtensions", - "version": "10.0.2", - "hash": "sha256-WJahsWyT5wYdLPEJufHKpb3l/dl7D2iw2SnMK0Jr53U=" + "version": "10.0.5", + "hash": "sha256-VQPPvrvYWY/QpmilerCyTNLVejWeBE9mHtGTMOxXUlg=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "10.0.2", - "hash": "sha256-8Ccrjjv9cFVf9RyCc7GS/Byt8+DXdSNea0UX3A5BEdA=" + "version": "10.0.4", + "hash": "sha256-ePpEFzrKQbEMY6Kh/xyxKHq6txNru62Vh4LVZO5Rrvg=" }, { "pname": "Microsoft.Extensions.Primitives", - "version": "9.0.0", - "hash": "sha256-ZNLusK1CRuq5BZYZMDqaz04PIKScE2Z7sS2tehU7EJs=" - }, - { - "pname": "Microsoft.VisualStudio.SolutionPersistence", - "version": "1.0.52", - "hash": "sha256-KZGPtOXe6Hv8RrkcsgoLKTRyaCScIpQEa2NhNB3iOXw=" - }, - { - "pname": "Mono.TextTemplating", - "version": "3.0.0", - "hash": "sha256-VlgGDvgNZb7MeBbIZ4DE2Nn/j2aD9k6XqNHnASUSDr0=" - }, - { - "pname": "Newtonsoft.Json", - "version": "13.0.3", - "hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=" + "version": "10.0.5", + "hash": "sha256-uvrur+0dg4zAAQcpLkkhPA77ST0tA3+EpGdDlCckC+E=" }, { "pname": "Npgsql", - "version": "10.0.0", - "hash": "sha256-UVKz9dH/rVCCbMyFdqA31RYpht1XgDRLIqUy0Dp9ACQ=" + "version": "10.0.2", + "hash": "sha256-hW03ZWoW7y16vvScwsjZNyqFybGy+s6hQYQXebo78yQ=" }, { "pname": "Npgsql.EntityFrameworkCore.PostgreSQL", - "version": "10.0.0", - "hash": "sha256-XIJxnTMektQVP1qtslEIGbmBGrIQsvjQjCMRTs9UIbg=" - }, - { - "pname": "System.CodeDom", - "version": "6.0.0", - "hash": "sha256-uPetUFZyHfxjScu5x4agjk9pIhbCkt5rG4Axj25npcQ=" - }, - { - "pname": "System.Composition", - "version": "9.0.0", - "hash": "sha256-FehOkQ2u1p8mQ0/wn3cZ+24HjhTLdck8VZYWA1CcgbM=" - }, - { - "pname": "System.Composition.AttributedModel", - "version": "9.0.0", - "hash": "sha256-a7y7H6zj+kmYkllNHA402DoVfY9IaqC3Ooys8Vzl24M=" - }, - { - "pname": "System.Composition.Convention", - "version": "9.0.0", - "hash": "sha256-tw4vE5JRQ60ubTZBbxoMPhtjOQCC3XoDFUH7NHO7o8U=" - }, - { - "pname": "System.Composition.Hosting", - "version": "9.0.0", - "hash": "sha256-oOxU+DPEEfMCuNLgW6wSkZp0JY5gYt44FJNnWt+967s=" - }, - { - "pname": "System.Composition.Runtime", - "version": "9.0.0", - "hash": "sha256-AyIe+di1TqwUBbSJ/sJ8Q8tzsnTN+VBdJw4K8xZz43s=" - }, - { - "pname": "System.Composition.TypedParts", - "version": "9.0.0", - "hash": "sha256-F5fpTUs3Rr7yP/NyIzr+Xn5NdTXXp8rrjBnF9UBBUog=" + "version": "10.0.1", + "hash": "sha256-G5WmWoc02gHTsdBLXESFQ5eMV+liwiO8YjzFKg4NDEk=" }, { "pname": "System.Diagnostics.EventLog", - "version": "10.0.2", - "hash": "sha256-rezZk0M4+MWRxwGSFzXfvekrhL8qLTp7Pc8YsSy/4nE=" + "version": "10.0.5", + "hash": "sha256-SwmQif5X/T4cIPPNkVf6QsvA3oBLb85iJXNkb3GT+sU=" } ] diff --git a/extra/admin-api/outputs.nix b/extra/admin-api/outputs.nix index 434301009..6f47df05d 100644 --- a/extra/admin-api/outputs.nix +++ b/extra/admin-api/outputs.nix @@ -47,7 +47,7 @@ flake-utils.lib.eachSystem flake-utils.lib.allSystems ( "--include-source" "--version-suffix ${rVersion}" ]; - dotnetFlags = [ "-v:n" ]; # diag + # dotnetFlags = [ "-v:n" ]; # diag dotnet-sdk = pkgs.dotnet-sdk_10; dotnet-runtime = pkgs.dotnet-aspnetcore_10; src = pkgs.lib.cleanSource srcRoot; @@ -57,6 +57,7 @@ flake-utils.lib.eachSystem flake-utils.lib.allSystems ( homepage = "https://github.com/spacebarchat/server"; license = licenses.agpl3Plus; maintainers = with maintainers; [ RorySys ]; + mainProgram = name; }; }; in @@ -142,14 +143,12 @@ flake-utils.lib.eachSystem flake-utils.lib.allSystems ( Spacebar-Models-Gateway = makeNupkg { name = "Spacebar.Models.Gateway"; projectFile = "Spacebar.Models.Gateway.csproj"; - # nugetDeps = Models/Spacebar.Models.Gateway/deps.json; srcRoot = Models/Spacebar.Models.Gateway; projectReferences = [ proj.Spacebar-Models-Generic ]; }; Spacebar-Models-Generic = makeNupkg { name = "Spacebar.Models.Generic"; projectFile = "Spacebar.Models.Generic.csproj"; - # nugetDeps = Models/Spacebar.Models.Generic/deps.json; srcRoot = Models/Spacebar.Models.Generic; }; @@ -189,6 +188,8 @@ flake-utils.lib.eachSystem flake-utils.lib.allSystems ( proj.Spacebar-Models-AdminApi proj.Spacebar-Models-Config proj.Spacebar-Models-Db + proj.Spacebar-Models-Gateway + proj.Spacebar-Models-Generic ]; }; Spacebar-Cdn = makeNupkg { @@ -218,6 +219,24 @@ flake-utils.lib.eachSystem flake-utils.lib.allSystems ( proj.Spacebar-Models-Generic ]; }; + Spacebar-UApi = makeNupkg { + name = "Spacebar.UApi"; + nugetDeps = Spacebar.UApi/deps.json; + projectFile = "Spacebar.UApi.csproj"; + srcRoot = ./Spacebar.UApi; + packNupkg = false; + projectReferences = [ + proj.Spacebar-DataMappings-Generic + proj.Spacebar-Interop-Authentication + proj.Spacebar-Interop-Authentication-AspNetCore + proj.Spacebar-Interop-Replication-Abstractions + proj.Spacebar-Interop-Replication-UnixSocket + proj.Spacebar-Models-Config + proj.Spacebar-Models-Db + proj.Spacebar-Models-Gateway + proj.Spacebar-Models-Generic + ]; + }; # Spacebar-AdminApi-TestClient = makeNupkg { # name = "Spacebar.AdminApi.TestClient"; # projectFile = "Utilities/Spacebar.AdminApi.TestClient/Spacebar.AdminApi.TestClient.csproj"; @@ -257,6 +276,15 @@ flake-utils.lib.eachSystem flake-utils.lib.allSystems ( Expose = [ "5000" ]; }; }; + containers.docker.uapi = pkgs.dockerTools.buildLayeredImage { + name = "spacebar-server-ts-cdn-cs"; + tag = builtins.replaceStrings [ "+" ] [ "_" ] self.packages.${system}.Spacebar-AdminApi.version; + contents = [ self.packages.${system}.Spacebar-AdminApi ]; + config = { + Cmd = [ "${lib.getExe self.outputs.packages.${system}.Spacebar-AdminApi}" ]; + Expose = [ "5000" ]; + }; + }; } ) // { diff --git a/extra/admin-api/update-deps.cs b/extra/admin-api/update-deps.cs index bf0516f8a..9aa1b0fe3 100755 --- a/extra/admin-api/update-deps.cs +++ b/extra/admin-api/update-deps.cs @@ -16,10 +16,12 @@ Console.WriteLine($"==> Updating dependencies for {outs.Length} projects..."); -foreach (var outp in outs) { +var ss = new SemaphoreSlim(1, 1); +var idx = 0; +var tasks = outs.Select(outp => Task.Run(async () => { Console.WriteLine(ConsoleUtils.ColoredString($" ==> Updating {outp}...", 0x80, 0x80, 0xff)); Console.Write(ConsoleUtils.ColoredString($" ==> Getting project root directory... ", 0x80, 0xff, 0xff)); - var rootDir = JsonSerializer.Deserialize(Util.GetCommandOutputSync("nix", $"eval --json .#packages.x86_64-linux.{outp}.srcRoot", silent: true, stderr: false)).Split("/extra/admin-api/",2)[1]; + var rootDir = JsonSerializer.Deserialize(Util.GetCommandOutputSync("nix", $"eval --json .#packages.x86_64-linux.{outp}.srcRoot", silent: true, stderr: false)).Split("/extra/admin-api/", 2)[1]; Console.WriteLine(ConsoleUtils.ColoredString($"{rootDir}", 0x80, 0xff, 0xff)); if (rootDir.Length <= 1) throw new Exception("Invalid project file count?"); @@ -27,18 +29,25 @@ Console.WriteLine(ConsoleUtils.ColoredString($" ==> {nugetDepsFilePath} exists: {File.Exists(nugetDepsFilePath)}", 0x80, 0xff, 0xff)); if (!File.Exists(nugetDepsFilePath)) { Console.WriteLine(ConsoleUtils.ColoredString($" ==> No NuGet deps file, skipping!", 0xff, 0x80, 0x80)); - continue; + return; } - Console.WriteLine(ConsoleUtils.ColoredString($" ==> Building fetch-deps script...", 0x80, 0xff, 0x80)); - Util.RunCommandSync("nix", $"build .#{outp}.passthru.fetch-deps"); + if (idx == 1) await Task.Delay(3000); // give the first one a bit of time to eval... + await Task.Delay(idx++ * 1500); + + var fname = $"./update-deps-{outp}"; + Console.WriteLine(ConsoleUtils.ColoredString($" ==> Building fetch-deps script {fname}...", 0x80, 0xff, 0x80)); + Util.RunCommandSync("nix", $"build .#{outp}.passthru.fetch-deps --out-link {fname}"); - Console.WriteLine(ConsoleUtils.ColoredString($" ==> Running fetch-deps script...", 0x80, 0xff, 0x80)); - Util.RunCommandSync("./result", nugetDepsFilePath); + Console.WriteLine(ConsoleUtils.ColoredString($" ==> Running fetch-deps script, writing into {nugetDepsFilePath}...", 0x80, 0xff, 0x80)); + Util.RunCommandSync(fname, nugetDepsFilePath); - var deps = JsonSerializer.Deserialize(File.ReadAllText(nugetDepsFilePath)); - Console.WriteLine(ConsoleUtils.ColoredString($" ==> Locked {deps.Length} dependencies...", (byte)(deps.Length == 0 ? 0xff : 0x80), (byte)(deps.Length == 0 ? 0x80 : 0xff), 0x80)); + var deps = JsonSerializer.Deserialize(await File.ReadAllTextAsync(nugetDepsFilePath)); + Console.WriteLine(ConsoleUtils.ColoredString($" ==> Locked {deps.Length} dependencies for {outp}...", (byte)(deps.Length == 0 ? 0xff : 0x80), (byte)(deps.Length == 0 ? 0x80 : 0xff), 0x80)); + File.Delete(fname); // await Task.Delay(250); -} \ No newline at end of file +})).ToList(); + +await Task.WhenAll(tasks); diff --git a/flake.lock b/flake.lock index 923e9f168..8e683b7e7 100644 --- a/flake.lock +++ b/flake.lock @@ -20,24 +20,48 @@ }, "nixpkgs": { "locked": { - "lastModified": 1770562336, - "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", + "lastModified": 1773436484, + "narHash": "sha256-SeGjOJJBrsTSa13z/Cu5ucQUo1qlIPwXfR7AeeOBoew=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", + "rev": "97715a1ceaf42795d7eb751872f7f8e1d317e6e1", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "master", "repo": "nixpkgs", "type": "github" } }, + "pion-webrtc": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773432540, + "narHash": "sha256-fQXP/5GDW1Qm6l6cEox3xHeg24RWif73QBdHIuRSHy8=", + "owner": "spacebarchat", + "repo": "pion-webrtc", + "rev": "c2fc00a80ff5ba5c1f9a9ff0111e97b6addc4ef6", + "type": "github" + }, + "original": { + "owner": "spacebarchat", + "repo": "pion-webrtc", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "pion-webrtc": "pion-webrtc" } }, "systems": { diff --git a/flake.nix b/flake.nix index 3a1f34cc9..84936b15b 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,13 @@ description = "Spacebar server, written in Typescript."; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/master"; # temp hack because unstable is frozen flake-utils.url = "github:numtide/flake-utils"; + pion-webrtc = { + url = "github:spacebarchat/pion-webrtc"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "flake-utils"; + }; }; outputs = @@ -11,6 +16,7 @@ self, nixpkgs, flake-utils, + pion-webrtc, }: nixpkgs.lib.recursiveUpdate ( @@ -34,6 +40,8 @@ { packages = { default = (pkgs.callPackage (import ./default.nix { inherit self rVersion; })) { }; + nodeModules = (pkgs.callPackage ./node-modules.nix) { }; + pion-sfu = pion-webrtc.packages.${system}.default; }; containers = { @@ -57,7 +65,7 @@ }; }; } - // lib.genAttrs [ "api" "cdn" "gateway" ] ( + // lib.genAttrs [ "api" "cdn" "gateway" "webrtc" ] ( mod: pkgs.dockerTools.buildLayeredImage { name = "spacebar-server-ts-${mod}"; @@ -93,7 +101,7 @@ ) // { nixosModules.default = import ./nix/modules/default self; - testVm = import ./nix/testVm/default.nix { inherit self nixpkgs; }; + nixosConfigurations.testVm = import ./nix/testVm/default.nix { inherit self nixpkgs; }; checks = let pkgs = import nixpkgs { system = "x86_64-linux"; }; diff --git a/nix/modules/default/config-file.nix b/nix/modules/default/config-file.nix new file mode 100644 index 000000000..55a558f04 --- /dev/null +++ b/nix/modules/default/config-file.nix @@ -0,0 +1,38 @@ +{ + config, + lib, + pkgs +}: + +let + cfg = config.services.spacebarchat-server; + jsonFormat = pkgs.formats.json { }; +in +let + endpointSettings = { + api = { + endpointPublic = "http${if cfg.apiEndpoint.useSsl then "s" else ""}://${cfg.apiEndpoint.host}:${toString cfg.apiEndpoint.publicPort}"; + }; + cdn = { + endpointPublic = "http${if cfg.cdnEndpoint.useSsl then "s" else ""}://${cfg.cdnEndpoint.host}:${toString cfg.cdnEndpoint.publicPort}"; + endpointPrivate = "http://127.0.0.1:${toString cfg.cdnEndpoint.localPort}"; + }; + gateway = { + endpointPublic = "ws${if cfg.gatewayEndpoint.useSsl then "s" else ""}://${cfg.gatewayEndpoint.host}:${toString cfg.gatewayEndpoint.publicPort}"; + }; + general = { + serverName = cfg.serverName; + }; + } + // ( + if cfg.adminApi.enable then + { + adminApi = { + endpointPublic = "http${if cfg.adminApiEndpoint.useSsl then "s" else ""}://${cfg.adminApiEndpoint.host}:${toString cfg.adminApiEndpoint.publicPort}"; + }; + } + else + { } + ); +in +jsonFormat.generate "spacebarchat-server.json" (lib.recursiveUpdate endpointSettings cfg.settings) diff --git a/nix/modules/default/cs/admin-api.nix b/nix/modules/default/cs/admin-api.nix new file mode 100644 index 000000000..531b50aa3 --- /dev/null +++ b/nix/modules/default/cs/admin-api.nix @@ -0,0 +1,70 @@ +self: +{ + config, + lib, + pkgs, + spacebar, + ... +}: + +let + secrets = import ../secrets.nix { inherit lib config; }; + cfg = config.services.spacebarchat-server; + jsonFormat = pkgs.formats.json { }; +in +{ + imports = [ ]; + options.services.spacebarchat-server.adminApi = lib.mkOption { + default = { }; + description = "Configuration for admin api."; + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "Enable admin api."; + extraConfiguration = lib.mkOption { + type = jsonFormat.type; + default = import ./default-appsettings-json.nix; + description = "Extra appsettings.json configuration for the gateway offload daemon."; + }; + }; + }; + }; + + config = lib.mkIf cfg.adminApi.enable ( + let + makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; }; + in + { + assertions = [ + (import ./assert-has-connection-string.nix "Admin API" cfg.adminApi.extraConfiguration) + ]; + + services.spacebarchat-server.settings.admin = { + endpointPublic = "http${if cfg.adminApiEndpoint.useSsl then "s" else ""}://${cfg.adminApiEndpoint.host}:${toString cfg.adminApiEndpoint.publicPort}"; + endpointPrivate = "http://127.0.0.1:${builtins.toString cfg.adminApiEndpoint.localPort}"; + }; + + systemd.services.spacebar-admin-api = makeServerTsService { + description = "Spacebar Server - Admin API"; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + { + # things we set by default... + EVENT_TRANSMISSION = "unix"; + EVENT_SOCKET_PATH = "/run/spacebar/"; + } + // cfg.extraEnvironment + // { + # things we force... + # CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + ASPNETCORE_URLS = "http://0.0.0.0:${toString cfg.adminApiEndpoint.localPort}"; + STORAGE_LOCATION = cfg.cdnPath; + APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-adminapi.json" (lib.recursiveUpdate (import ./default-appsettings-json.nix) cfg.adminApi.extraConfiguration); + } + ); + serviceConfig = { + ExecStart = "${self.packages.${pkgs.stdenv.hostPlatform.system}.Spacebar-AdminApi}/bin/Spacebar.AdminApi"; + }; + }; + } + ); +} diff --git a/nix/modules/default/cs/assert-has-connection-string.nix b/nix/modules/default/cs/assert-has-connection-string.nix new file mode 100644 index 000000000..4066c8938 --- /dev/null +++ b/nix/modules/default/cs/assert-has-connection-string.nix @@ -0,0 +1,7 @@ +name: extraConfig: { + assertion = extraConfig ? ConnectionStrings && extraConfig.ConnectionStrings ? Spacebar && extraConfig.ConnectionStrings.Spacebar != null; + message = '' + ${name}: Setting a database connection string in extraConfiguration (`extraConfiguration.ConnectionStrings.Spacebar`) is required when using C# services. + Example: Host=127.0.0.1; Username=Spacebar; Password=SuperSecurePassword12; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600; + ''; +} diff --git a/nix/modules/default/cs/cdn-cs.nix b/nix/modules/default/cs/cdn-cs.nix new file mode 100644 index 000000000..42432dab8 --- /dev/null +++ b/nix/modules/default/cs/cdn-cs.nix @@ -0,0 +1,65 @@ +self: +{ + config, + lib, + pkgs, + spacebar, + ... +}: + +let + secrets = import ../secrets.nix { inherit lib config; }; + cfg = config.services.spacebarchat-server; + jsonFormat = pkgs.formats.json { }; +in +{ + imports = [ ]; + options.services.spacebarchat-server.cdnCs = lib.mkOption { + default = { }; + description = "Configuration for C# cdn."; + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "Enable experimental C# CDN."; + extraConfiguration = lib.mkOption { + type = jsonFormat.type; + default = import ./default-appsettings-json.nix; + description = "Extra appsettings.json configuration for the gateway offload daemon."; + }; + }; + }; + }; + + config = lib.mkIf cfg.cdnCs.enable ( + let + makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; }; + in + { + assertions = [ + (import ./assert-has-connection-string.nix "Admin API" cfg.adminApi.extraConfiguration) + ]; + + systemd.services.spacebar-cdn = makeServerTsService { + description = "Spacebar Server - CDN (C#)"; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + { + # things we set by default... + EVENT_TRANSMISSION = "unix"; + EVENT_SOCKET_PATH = "/run/spacebar/"; + } + // cfg.extraEnvironment + // { + # things we force... + # CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + ASPNETCORE_URLS = "http://0.0.0.0:${toString cfg.cdnEndpoint.localPort}"; + STORAGE_LOCATION = cfg.cdnPath; + APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-cdn.json" (lib.recursiveUpdate (import ./default-appsettings-json.nix) cfg.cdnCs.extraConfiguration); + } + ); + serviceConfig = { + ExecStart = "${self.packages.${pkgs.stdenv.hostPlatform.system}.Spacebar-AdminApi}/bin/Spacebar.AdminApi"; + }; + }; + } + ); +} diff --git a/nix/modules/default/cs/default-appsettings-json.nix b/nix/modules/default/cs/default-appsettings-json.nix index e6bb6323f..1ed1c28c0 100644 --- a/nix/modules/default/cs/default-appsettings-json.nix +++ b/nix/modules/default/cs/default-appsettings-json.nix @@ -12,5 +12,8 @@ PrivateKeyPath = "./jwt.key"; PublicKeyPath = "./jwt.key.pub"; }; + UnixSocketReplication = { + SocketDir = "/run/spacebar"; + }; }; } diff --git a/nix/modules/default/cs/gateway-offload-cs.nix b/nix/modules/default/cs/gateway-offload-cs.nix index 54e84bc37..6187f0757 100644 --- a/nix/modules/default/cs/gateway-offload-cs.nix +++ b/nix/modules/default/cs/gateway-offload-cs.nix @@ -31,119 +31,32 @@ in description = "Extra appsettings.json configuration for the gateway offload daemon."; }; enableIdentify = lib.mkEnableOption "Enable offloading gateway opcode 2 (IDENTIFY)."; + enableGuildMembers = lib.mkEnableOption "Enable offloading gateway opcode 8 (REQUEST_GUILD_MEMBERS)."; enableGuildSync = lib.mkEnableOption "Enable offloading gateway opcode 12 (GUILD_SYNC)."; - enableLazyRequest = lib.mkEnableOption "Enable offloading gateway opcode 12 (LAZY_REQUEST)."; + enableLazyRequest = lib.mkEnableOption "Enable offloading gateway opcode 14 (LAZY_REQUEST)."; + enableChannelStatuses = lib.mkEnableOption "Enable offloading gateway opcode 36 (CHANNEL_STATUSES)."; + enableChannelInfo = lib.mkEnableOption "Enable offloading gateway opcode 43 (CHANNEL_INFO)."; }; }; }; config = lib.mkIf cfg.gatewayOffload.enable ( let - makeServerTsService = ( - conf: - lib.recursiveUpdate - (lib.recursiveUpdate { - documentation = [ "https://docs.spacebar.chat/" ]; - wantedBy = [ "multi-user.target" ]; - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; - environment = secrets.systemdEnvironment; - serviceConfig = { - LoadCredential = secrets.systemdLoadCredentials; - - User = "spacebarchat"; - Group = "spacebarchat"; - DynamicUser = false; - - LockPersonality = true; - NoNewPrivileges = true; - - ProtectClock = true; - ProtectControlGroups = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - PrivateDevices = true; - PrivateMounts = true; - PrivateUsers = true; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ - "@system-service" - "~@privileged" - "@chown" # Required for copying files with FICLONE, apparently. - ]; - CapabilityBoundingSet = [ - "~CAP_SYS_ADMIN" - "~CAP_AUDIT_*" - "~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)" - "~CAP_NET_ADMIN" # No use for this as we don't currently use iptables for enforcing instance bans - "~CAP_SYS_TIME" - "~CAP_KILL" - "~CAP_(DAC_*|FOWNER|IPC_OWNER)" - "~CAP_LINUX_IMMUTABLE" - "~CAP_IPC_LOCK" - "~CAP_BPF" - "~CAP_SYS_TTY_CONFIG" - "~CAP_SYS_BOOT" - "~CAP_SYS_CHROOT" - "~CAP_BLOCK_SUSPEND" - "~CAP_LEASE" - "~CAP_(CHOWN|FSETID|FSETFCAP)" # Check if we need CAP_CHOWN for `fchown()` (FICLONE)? - "~CAP_SET(UID|GID|PCAP)" - "~CAP_MAC_*" - "~CAP_SYS_PTRACE" - "~CAP_SYS_(NICE|RESOURCE)" - "~CAP_SYS_RAWIO" - "~CAP_SYSLOG" - ]; - RestrictSUIDSGID = true; - - WorkingDirectory = "/var/lib/spacebar"; - StateDirectory = "spacebar"; - StateDirectoryMode = "0750"; - RuntimeDirectory = "spacebar"; - RuntimeDirectoryMode = "0750"; - ReadWritePaths = [ cfg.cdnPath ]; - NoExecPaths = [ cfg.cdnPath ]; - - Restart = "on-failure"; - RestartSec = 10; - StartLimitBurst = 5; - UMask = "077"; - } - // lib.optionalAttrs (cfg.databaseFile != null) { EnvironmentFile = cfg.databaseFile; }; - } conf) - { - } - ); + makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; }; in { assertions = [ - { - assertion = - cfg.gatewayOffload.extraConfiguration ? ConnectionStrings - && cfg.gatewayOffload.extraConfiguration.ConnectionStrings ? Spacebar - && cfg.gatewayOffload.extraConfiguration.ConnectionStrings.Spacebar != null; - message = '' - Setting a database connection string in extraConfiguration (`extraConfiguration.ConnectionStrings.Spacebar`) is required when using C# services. - Example: Host=127.0.0.1; Username=Spacebar; Password=SuperSecurePassword12; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600; - ''; - } + (import ./assert-has-connection-string.nix "Gateway Offload" cfg.gatewayOffload.extraConfiguration) ]; services.spacebarchat-server.settings.offload = { gateway = { - op2BaseUrl = lib.mkIf cfg.gatewayOffload.enableIdentify "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}"; - op12BaseUrl = lib.mkIf cfg.gatewayOffload.enableGuildSync "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}"; - op14BaseUrl = lib.mkIf cfg.gatewayOffload.enableLazyRequest "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}"; + identifyUrl = lib.mkIf cfg.gatewayOffload.enableIdentify "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/Identify"; + guildMembersUrl = lib.mkIf cfg.gatewayOffload.enableGuildMembers "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/GuildMembers"; + guildSyncUrlUrl = lib.mkIf cfg.gatewayOffload.enableGuildSync "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/GuildSync"; + lazyRequestUrl = lib.mkIf cfg.gatewayOffload.enableLazyRequest "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/LazyRequest"; + channelStatusesUrl = lib.mkIf cfg.gatewayOffload.enableChannelStatuses "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/ChannelStatuses"; + channelInfoUrl = lib.mkIf cfg.gatewayOffload.enableChannelInfo "http://127.0.0.1:${builtins.toString cfg.gatewayOffload.listenPort}/_spacebar/offload/gateway/ChannelInfo"; }; }; @@ -160,8 +73,11 @@ in # things we force... # CONFIG_PATH = configFile; CONFIG_READONLY = 1; - ASPNETCORE_URLS = "http://127.0.0.1:${toString cfg.gatewayOffload.listenPort}"; + ASPNETCORE_URLS = "http://0.0.0.0:${toString cfg.gatewayOffload.listenPort}"; STORAGE_LOCATION = cfg.cdnPath; + APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-gateway-offload.json" ( + lib.recursiveUpdate (import ./default-appsettings-json.nix) cfg.gatewayOffload.extraConfiguration + ); } ); serviceConfig = { diff --git a/nix/modules/default/cs/uapi.nix b/nix/modules/default/cs/uapi.nix new file mode 100644 index 000000000..00caa214f --- /dev/null +++ b/nix/modules/default/cs/uapi.nix @@ -0,0 +1,71 @@ +self: +{ + config, + lib, + pkgs, + spacebar, + ... +}: + +let + secrets = import ../secrets.nix { inherit lib config; }; + cfg = config.services.spacebarchat-server; + jsonFormat = pkgs.formats.json { }; +in +{ + imports = [ ]; + options.services.spacebarchat-server.uApi = lib.mkOption { + default = { }; + description = "Configuration for C# API overlay."; + type = lib.types.submodule { + options = { + enable = lib.mkEnableOption "Enable C# API overlay."; + listenPort = lib.mkOption { + type = lib.types.port; + default = 3012; + description = "Port for the gateway offload daemon to listen on."; + }; + extraConfiguration = lib.mkOption { + type = jsonFormat.type; + default = import ./default-appsettings-json.nix; + description = "Extra appsettings.json configuration for the C# API overlay."; + }; + }; + }; + }; + + config = lib.mkIf cfg.uApi.enable ( + let + makeServerTsService = import ../makeServerTsService.nix { inherit cfg lib secrets; }; + in + { + assertions = [ + (import ./assert-has-connection-string.nix "uAPI" cfg.uApi.extraConfiguration) + ]; + + systemd.services.spacebar-uapi = makeServerTsService { + description = "Spacebar Server - C# API overlay"; + # after = [ "spacebar-api.service" ]; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + { + # things we set by default... + EVENT_TRANSMISSION = "unix"; + EVENT_SOCKET_PATH = "/run/spacebar/"; + } + // cfg.extraEnvironment + // { + # things we force... + # CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + ASPNETCORE_URLS = "http://0.0.0.0:${toString cfg.uApi.listenPort}"; + STORAGE_LOCATION = cfg.cdnPath; + APPSETTINGS_PATH = jsonFormat.generate "appsettings.spacebar-uapi.json" (lib.recursiveUpdate (import ./default-appsettings-json.nix) cfg.uApi.extraConfiguration); + } + ); + serviceConfig = { + ExecStart = "${self.packages.${pkgs.stdenv.hostPlatform.system}.Spacebar-UApi}/bin/Spacebar.UApi"; + }; + }; + } + ); +} diff --git a/nix/modules/default/default.nix b/nix/modules/default/default.nix index c4f9e4cd8..001cdc748 100644 --- a/nix/modules/default/default.nix +++ b/nix/modules/default/default.nix @@ -11,41 +11,18 @@ let secrets = import ./secrets.nix { inherit lib config; }; cfg = config.services.spacebarchat-server; jsonFormat = pkgs.formats.json { }; - configFile = - let - endpointSettings = { - api = { - endpointPublic = "http${if cfg.apiEndpoint.useSsl then "s" else ""}://${cfg.apiEndpoint.host}:${toString cfg.apiEndpoint.publicPort}"; - }; - cdn = { - endpointPublic = "http${if cfg.cdnEndpoint.useSsl then "s" else ""}://${cfg.cdnEndpoint.host}:${toString cfg.cdnEndpoint.publicPort}"; - endpointPrivate = "http://127.0.0.1:${toString cfg.cdnEndpoint.localPort}"; - }; - gateway = { - endpointPublic = "ws${if cfg.gatewayEndpoint.useSsl then "s" else ""}://${cfg.gatewayEndpoint.host}:${toString cfg.gatewayEndpoint.publicPort}"; - }; - general = { - serverName = cfg.serverName; - }; - } - // ( - if cfg.enableAdminApi then - { - adminApi = { - endpointPublic = "http${if cfg.adminApiEndpoint.useSsl then "s" else ""}://${cfg.adminApiEndpoint.host}:${toString cfg.adminApiEndpoint.publicPort}"; - }; - } - else - { } - ); - in - jsonFormat.generate "spacebarchat-server.json" (lib.recursiveUpdate endpointSettings cfg.settings); + configFile = (import ./config-file.nix { inherit config lib pkgs; }); in { imports = [ ./integration-nginx.nix ./users.nix + (import ./gw-sharding.nix self) + (import ./pion-sfu.nix self) + (import ./cs/cdn-cs.nix self) (import ./cs/gateway-offload-cs.nix self) + (import ./cs/admin-api.nix self) + (import ./cs/uapi.nix self) ]; options.services.spacebarchat-server = let @@ -53,8 +30,6 @@ in in { enable = lib.mkEnableOption "Spacebar server"; - enableAdminApi = lib.mkEnableOption "Spacebar server Admin API"; - enableCdnCs = lib.mkEnableOption "Spacebar's experimental CDN rewrite"; package = lib.mkPackageOption self.packages.${pkgs.stdenv.hostPlatform.system} "spacebar-server" { default = "default"; }; databaseFile = lib.mkOption { type = lib.types.nullOr lib.types.path; @@ -70,10 +45,12 @@ in type = lib.types.str; description = "The server name for this Spacebar instance (aka. common name, usually the domain where your well known is hosted)."; }; - adminApiEndpoint = mkEndpointOptions "admin-api.sb.localhost" 3004; apiEndpoint = mkEndpointOptions "api.sb.localhost" 3001; gatewayEndpoint = mkEndpointOptions "gateway.sb.localhost" 3003; cdnEndpoint = mkEndpointOptions "cdn.sb.localhost" 3003; + adminApiEndpoint = mkEndpointOptions "admin-api.sb.localhost" 3004; + webrtcEndpoint = mkEndpointOptions "voice.sb.localhost" 3005; + cdnPath = lib.mkOption { type = lib.types.str; default = "./files"; @@ -117,91 +94,7 @@ in config = lib.mkIf cfg.enable ( let - makeServerTsService = ( - conf: - lib.recursiveUpdate - (lib.recursiveUpdate { - documentation = [ "https://docs.spacebar.chat/" ]; - wantedBy = [ "multi-user.target" ]; - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; - environment = secrets.systemdEnvironment; - serviceConfig = { - LoadCredential = secrets.systemdLoadCredentials; - - User = "spacebarchat"; - Group = "spacebarchat"; - DynamicUser = false; - - LockPersonality = true; - NoNewPrivileges = true; - - ProtectClock = true; - ProtectControlGroups = true; - ProtectHostname = true; - ProtectKernelLogs = true; - ProtectKernelModules = true; - ProtectKernelTunables = true; - PrivateDevices = true; - PrivateMounts = true; - PrivateUsers = true; - RestrictAddressFamilies = [ - "AF_INET" - "AF_INET6" - "AF_UNIX" - ]; - RestrictNamespaces = true; - RestrictRealtime = true; - SystemCallArchitectures = "native"; - SystemCallFilter = [ - "@system-service" - "~@privileged" - "@chown" # Required for copying files with FICLONE, apparently. - ]; - CapabilityBoundingSet = [ - "~CAP_SYS_ADMIN" - "~CAP_AUDIT_*" - "~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)" - "~CAP_NET_ADMIN" # No use for this as we don't currently use iptables for enforcing instance bans - "~CAP_SYS_TIME" - "~CAP_KILL" - "~CAP_(DAC_*|FOWNER|IPC_OWNER)" - "~CAP_LINUX_IMMUTABLE" - "~CAP_IPC_LOCK" - "~CAP_BPF" - "~CAP_SYS_TTY_CONFIG" - "~CAP_SYS_BOOT" - "~CAP_SYS_CHROOT" - "~CAP_BLOCK_SUSPEND" - "~CAP_LEASE" - "~CAP_(CHOWN|FSETID|FSETFCAP)" # Check if we need CAP_CHOWN for `fchown()` (FICLONE)? - "~CAP_SET(UID|GID|PCAP)" - "~CAP_MAC_*" - "~CAP_SYS_PTRACE" - "~CAP_SYS_(NICE|RESOURCE)" - "~CAP_SYS_RAWIO" - "~CAP_SYSLOG" - ]; - RestrictSUIDSGID = true; - - WorkingDirectory = "/var/lib/spacebar"; - StateDirectory = "spacebar"; - StateDirectoryMode = "0750"; - RuntimeDirectory = "spacebar"; - RuntimeDirectoryMode = "0750"; - ReadWritePaths = [ cfg.cdnPath ]; - NoExecPaths = [ cfg.cdnPath ]; - - Restart = "on-failure"; - RestartSec = 10; - StartLimitBurst = 5; - UMask = "077"; - } - // lib.optionalAttrs (cfg.databaseFile != null) { EnvironmentFile = cfg.databaseFile; }; - } conf) - { - } - ); + makeServerTsService = import ./makeServerTsService.nix { inherit cfg lib secrets; }; in { assertions = [ @@ -210,6 +103,7 @@ in # message = "You cannot set CONFIG_PATH, CONFIG_READONLY, PORT or STORAGE_LOCATION in extraEnvironment, these are managed by the NixOS module."; # } ]; + services.spacebarchat-server.uApi.extraConfiguration.Spacebar.UApi.FallbackApiEndpoint = "http://127.0.0.1:${toString cfg.apiEndpoint.localPort}"; systemd.services.spacebar-api = makeServerTsService { description = "Spacebar Server - API"; @@ -235,6 +129,7 @@ in systemd.services.spacebar-gateway = makeServerTsService { description = "Spacebar Server - Gateway"; + # after = [ "spacebar-api.service" ]; environment = builtins.mapAttrs (_: val: builtins.toString val) ( { # things we set by default... @@ -256,7 +151,7 @@ in }; }; - systemd.services.spacebar-cdn = lib.mkIf (!cfg.enableCdnCs) (makeServerTsService { + systemd.services.spacebar-cdn = lib.mkIf (!cfg.cdnCs.enable) (makeServerTsService { description = "Spacebar Server - CDN"; environment = builtins.mapAttrs (_: val: builtins.toString val) ( { diff --git a/nix/modules/default/gw-sharding.nix b/nix/modules/default/gw-sharding.nix new file mode 100644 index 000000000..9358aca92 --- /dev/null +++ b/nix/modules/default/gw-sharding.nix @@ -0,0 +1,60 @@ +self: +{ + config, + lib, + pkgs, + spacebar, + ... +}: + +let + secrets = import ./secrets.nix { inherit lib config; }; + cfg = config.services.spacebarchat-server; + jsonFormat = pkgs.formats.json { }; + configFile = (import ./config-file.nix { inherit config lib pkgs; }); +in +{ + options.services.spacebarchat-server = { + extraGatewayPorts = lib.mkOption { + type = lib.types.listOf lib.types.port; + description = "Extra gateway ports"; + default = []; + }; + }; + + config = lib.mkIf cfg.enable ( + let + makeServerTsService = import ./makeServerTsService.nix { inherit cfg lib secrets; }; + in + { + systemd.services = builtins.listToAttrs ( + map (port: { + name = "spacebar-gateway-${toString port}"; + value = makeServerTsService { + description = "Spacebar Server - Gateway (extra port ${toString port})"; + # after = [ "spacebar-api.service" ]; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + { + # things we set by default... + EVENT_TRANSMISSION = "unix"; + EVENT_SOCKET_PATH = "/run/spacebar/"; + } + // cfg.extraEnvironment + // { + # things we force... + CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + PORT = toString port; + STORAGE_LOCATION = cfg.cdnPath; + APPLY_DB_MIGRATIONS = "false"; + } + ); + serviceConfig = { + ExecStart = "${cfg.package}/bin/start-gateway"; + }; + }; + }) cfg.extraGatewayPorts + ); + } + ); +} diff --git a/nix/modules/default/integration-nginx.nix b/nix/modules/default/integration-nginx.nix index 70e4b2e13..1dfa1faa9 100644 --- a/nix/modules/default/integration-nginx.nix +++ b/nix/modules/default/integration-nginx.nix @@ -15,12 +15,41 @@ in config = lib.mkIf (cfg.enable && cfg.nginx.enable) { services.nginx = { + recommendedProxySettings = true; + upstreams = { + "spacebar-api" = { + servers = { + "127.0.0.1:${if cfg.uApi.enable then toString cfg.uApi.listenPort else toString cfg.apiEndpoint.localPort}" = { }; + }; + }; + "spacebar-gateway" = { + servers = { + "127.0.0.1:${toString cfg.gatewayEndpoint.localPort}" = { }; + } + // builtins.listToAttrs ( + map (port: { + name = "127.0.0.1:${toString port}"; + value = { }; + }) cfg.extraGatewayPorts + ); + }; + "spacebar-cdn" = { + servers = { + "127.0.0.1:${toString cfg.cdnEndpoint.localPort}" = { }; + }; + }; + "spacebar-admin" = lib.mkIf cfg.adminApi.enable { + servers = { + "127.0.0.1:${toString cfg.adminApiEndpoint.localPort}" = { }; + }; + }; + }; virtualHosts = lib.mkIf cfg.enable { "${cfg.apiEndpoint.host}" = { enableACME = cfg.apiEndpoint.useSsl; forceSSL = cfg.apiEndpoint.useSsl; locations."/" = { - proxyPass = "http://127.0.0.1:${toString cfg.apiEndpoint.localPort}/"; + proxyPass = "http://spacebar-api/"; }; }; "${cfg.gatewayEndpoint.host}" = { @@ -28,17 +57,23 @@ in forceSSL = cfg.gatewayEndpoint.useSsl; locations."/" = { proxyWebsockets = true; - proxyPass = "http://127.0.0.1:${toString cfg.gatewayEndpoint.localPort}/"; + proxyPass = "http://spacebar-gateway/"; }; }; "${cfg.cdnEndpoint.host}" = { enableACME = cfg.cdnEndpoint.useSsl; forceSSL = cfg.cdnEndpoint.useSsl; locations."/" = { - proxyPass = "http://127.0.0.1:${toString cfg.cdnEndpoint.localPort}/"; + proxyPass = "http://spacebar-cdn/"; + }; + }; + "${cfg.adminApiEndpoint.host}" = lib.mkIf cfg.adminApi.enable { + enableACME = cfg.adminApiEndpoint.useSsl; + forceSSL = cfg.adminApiEndpoint.useSsl; + locations."/" = { + proxyPass = "http://spacebar-admin/"; }; }; - }; }; }; diff --git a/nix/modules/default/lib.nix b/nix/modules/default/lib.nix index 886b78fc8..cd7a7fb4d 100644 --- a/nix/modules/default/lib.nix +++ b/nix/modules/default/lib.nix @@ -11,4 +11,11 @@ else 80; }; + + mkEndpointRaw = domain: port: publicPort: ssl: { + host = domain; + localPort = port; + useSsl = ssl; + publicPort = publicPort; + }; } diff --git a/nix/modules/default/makeServerTsService.nix b/nix/modules/default/makeServerTsService.nix new file mode 100644 index 000000000..7a6112510 --- /dev/null +++ b/nix/modules/default/makeServerTsService.nix @@ -0,0 +1,90 @@ +{ + lib, + secrets, + cfg, +}: +conf: +lib.recursiveUpdate + (lib.recursiveUpdate { + documentation = [ "https://docs.spacebar.chat/" ]; + wantedBy = [ "multi-user.target" ]; + wants = [ "network-online.target" ]; + after = [ "network-online.target" ]; + environment = secrets.systemdEnvironment; + serviceConfig = { + LoadCredential = secrets.systemdLoadCredentials; + + User = "spacebarchat"; + Group = "spacebarchat"; + DynamicUser = false; + + LockPersonality = true; + NoNewPrivileges = true; + + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateUsers = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_UNIX" + "AF_NETLINK" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@privileged" + "@chown" # Required for copying files with FICLONE, apparently. + ]; + CapabilityBoundingSet = [ + "~CAP_SYS_ADMIN" + "~CAP_AUDIT_*" + "~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)" + "~CAP_NET_ADMIN" # No use for this as we don't currently use iptables for enforcing instance bans + "~CAP_SYS_TIME" + "~CAP_KILL" + "~CAP_(DAC_*|FOWNER|IPC_OWNER)" + "~CAP_LINUX_IMMUTABLE" + "~CAP_IPC_LOCK" + "~CAP_BPF" + "~CAP_SYS_TTY_CONFIG" + "~CAP_SYS_BOOT" + "~CAP_SYS_CHROOT" + "~CAP_BLOCK_SUSPEND" + "~CAP_LEASE" + "~CAP_(CHOWN|FSETID|FSETFCAP)" # Check if we need CAP_CHOWN for `fchown()` (FICLONE)? + "~CAP_SET(UID|GID|PCAP)" + "~CAP_MAC_*" + "~CAP_SYS_PTRACE" + "~CAP_SYS_(NICE|RESOURCE)" + "~CAP_SYS_RAWIO" + "~CAP_SYSLOG" + ]; + RestrictSUIDSGID = true; + + WorkingDirectory = "/var/lib/spacebar"; + StateDirectory = "spacebar"; + StateDirectoryMode = "0750"; + RuntimeDirectory = "spacebar"; + RuntimeDirectoryMode = "0750"; + RuntimeDirectoryPreserve = "yes"; + ReadWritePaths = [ cfg.cdnPath ]; + NoExecPaths = [ cfg.cdnPath ]; + + Restart = "on-failure"; + RestartSec = 10; + StartLimitBurst = 5; + UMask = "077"; + } + // lib.optionalAttrs (cfg.databaseFile != null) { EnvironmentFile = cfg.databaseFile; }; + } conf) + { + } diff --git a/nix/modules/default/pion-sfu.nix b/nix/modules/default/pion-sfu.nix new file mode 100644 index 000000000..aa2fbf240 --- /dev/null +++ b/nix/modules/default/pion-sfu.nix @@ -0,0 +1,91 @@ +self: +{ + config, + lib, + pkgs, + spacebar, + ... +}: + +let + cfg = config.services.spacebarchat-server; + secrets = import ./secrets.nix { inherit lib config; }; + configFile = (import ./config-file.nix { inherit config lib pkgs; }); +in +{ + options.services.spacebarchat-server.pion-sfu = + let + mkEndpointOptions = import ./options-subtypes/mkEndpointOptions.nix { inherit lib; }; + in + { + enable = lib.mkEnableOption "Enable Spacebar Pion SFU"; + openFirewall = lib.mkEnableOption "Allow SFU port in firewall"; + package = lib.mkPackageOption self.packages.${pkgs.stdenv.hostPlatform.system} "Pion SFU" { default = "pion-sfu"; }; + + publicIp = lib.mkOption { + type = lib.types.str; + description = "Public IP address of the server."; + }; + listenPort = lib.mkOption { + type = lib.types.port; + default = 6000; + description = "UDP port the SFU will listen on."; + }; + }; + + config = lib.mkIf cfg.pion-sfu.enable ( + let + makeServerTsService = import ./makeServerTsService.nix { inherit cfg lib secrets; }; + in + { + networking.firewall.allowedUDPPorts = lib.mkIf cfg.pion-sfu.openFirewall [ cfg.pion-sfu.listenPort ]; + services.spacebarchat-server.settings.regions = { + default = "default"; + available = [ + { + id = "default"; + name = "Default Region"; + endpoint = cfg.webrtcEndpoint.host + ":" + toString cfg.webrtcEndpoint.publicPort; + vip = false; + custom = false; + deprecated = false; + } + ]; + }; + + systemd.services.spacebar-webrtc = makeServerTsService { + description = "Spacebar Server - WebRTC"; + requires = [ "spacebar-sfu.service" ]; + environment = builtins.mapAttrs (_: val: builtins.toString val) ( + { + # things we set by default... + EVENT_TRANSMISSION = "unix"; + EVENT_SOCKET_PATH = "/run/spacebar/"; + } + // cfg.extraEnvironment + // { + # things we force... + CONFIG_PATH = configFile; + CONFIG_READONLY = 1; + PORT = toString cfg.webrtcEndpoint.localPort; + APPLY_DB_MIGRATIONS = "false"; + WRTC_LIBRARY = "@spacebarchat/pion-webrtc"; + WRTC_PUBLIC_IP = cfg.pion-sfu.publicIp; + WRTC_PORT_MIN = toString cfg.pion-sfu.listenPort; + WRTC_PORT_MAX = toString cfg.pion-sfu.listenPort; + } + ); + serviceConfig = { + ExecStart = "${cfg.package}/bin/start-webrtc"; + }; + }; + + systemd.services.spacebar-sfu = makeServerTsService { + description = "Spacebar Server - Pion SFU"; + serviceConfig = { + ExecStart = "${lib.getExe cfg.pion-sfu.package} -ip ${cfg.pion-sfu.publicIp} -port ${toString cfg.pion-sfu.listenPort}"; + }; + }; + } + ); +} diff --git a/nix/testVm/configuration.nix b/nix/testVm/configuration.nix index 2b1c19a5e..10bee8ec1 100644 --- a/nix/testVm/configuration.nix +++ b/nix/testVm/configuration.nix @@ -1,4 +1,9 @@ -{ pkgs, lib, ... }: +{ + config, + pkgs, + lib, + ... +}: let nginxTestSigning = { addSSL = true; @@ -11,57 +16,78 @@ let in { networking.hostName = "sbtest"; - services.nginx.virtualHosts."sb.localhost" = nginxTestSigning; - services.nginx.virtualHosts."api.sb.localhost" = nginxTestSigning; - services.nginx.virtualHosts."gw.sb.localhost" = nginxTestSigning; - services.nginx.virtualHosts."cdn.sb.localhost" = nginxTestSigning; + services.nginx.virtualHosts.${config.services.spacebarchat-server.serverName} = nginxTestSigning; + services.nginx.virtualHosts.${config.services.spacebarchat-server.adminApiEndpoint.host} = nginxTestSigning; + services.nginx.virtualHosts.${config.services.spacebarchat-server.apiEndpoint.host} = nginxTestSigning; + services.nginx.virtualHosts.${config.services.spacebarchat-server.cdnEndpoint.host} = nginxTestSigning; + services.nginx.virtualHosts.${config.services.spacebarchat-server.gatewayEndpoint.host} = nginxTestSigning; + services.nginx.virtualHosts.${config.services.spacebarchat-server.webrtcEndpoint.host} = nginxTestSigning; services.spacebarchat-server = let + sbLib = import ../modules/default/lib.nix; + csConnectionString = "Host=127.0.0.1; Username=postgres; Password=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;"; cfg = { enable = true; - apiEndpoint = { - useSsl = false; - host = "api.sb.localhost"; - localPort = 3001; - publicPort = 8080; - }; - gatewayEndpoint = { - useSsl = false; - host = "gw.sb.localhost"; - localPort = 3002; - publicPort = 8080; - }; - cdnEndpoint = { - useSsl = false; - host = "cdn.sb.localhost"; - localPort = 3003; - publicPort = 8080; - }; + apiEndpoint = sbLib.mkEndpointRaw "api.sb.localhost" 3001 8080 false; + gatewayEndpoint = sbLib.mkEndpointRaw "gw.sb.localhost" 3002 8080 false; + extraGatewayPorts = lib.range 3100 3116; + cdnEndpoint = sbLib.mkEndpointRaw "cdn.sb.localhost" 3003 8080 false; + adminApiEndpoint = sbLib.mkEndpointRaw "admin.sb.localhost" 3004 8080 false; + webrtcEndpoint = sbLib.mkEndpointRaw "voice.sb.localhost" 3005 8080 false; nginx.enable = true; serverName = "sb.localhost"; + settings = { + security = { + requestSignature = "meow"; + cdnSignatureDuration = "5m"; + cdnSignatureIncludeIp = true; + cdnSignatureIncludeUserAgent = false; + cdnSignatureKey = "meow"; + }; + }; + gatewayOffload = { enable = true; + enableIdentify = true; + enableGuildMembers = true; enableGuildSync = true; - extraConfiguration.ConnectionStrings.Spacebar = "Host=127.0.0.1; Username=Spacebar; Password=postgres; Database=spacebar; Port=5432; Include Error Detail=true; Maximum Pool Size=1000; Command Timeout=6000; Timeout=600;"; + enableLazyRequest = true; + enableChannelStatuses = true; + enableChannelInfo = true; + extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; + }; + + adminApi = { + enable = true; + extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; }; + + cdnCs = { + enable = false; + extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; + }; + + uApi = { + enable = true; + extraConfiguration.ConnectionStrings.Spacebar = csConnectionString; + }; + + pion-sfu = { + enable = true; + publicIp = "127.0.0.1"; + }; + extraEnvironment = { DATABASE = "postgres://postgres:postgres@127.0.0.1/spacebar"; - #WEBRTC_PORT_RANGE=60000-61000; - #PUBLIC_IP=216.230.228.60; - LOG_REQUESTS = "-200,204,304"; +# LOG_REQUESTS = "-200,204,304"; + LOG_REQUESTS = "-"; LOG_VALIDATION_ERRORS = true; #DB_LOGGING=true; #LOG_GATEWAY_TRACES=true; #LOG_PROTO_UPDATES=true; #LOG_PROTO_FRECENCY_UPDATES=true; #LOG_PROTO_SETTINGS_UPDATES=true; - #WRTC_PUBLIC_IP=webrtc.old.server.spacebar.chat; - WRTC_PUBLIC_IP = "216.230.228.19"; - WRTC_PORT_MIN = 60000; - WRTC_PORT_MAX = 65000; - WRTC_LIBRARY = "@spacebarchat/medooze-webrtc"; - #WRTC_LIBRARY=mediasoup-spacebar-wrtc; }; }; in diff --git a/nix/testVm/perlless.nix b/nix/testVm/perlless.nix index 6d391569f..669be83c5 100644 --- a/nix/testVm/perlless.nix +++ b/nix/testVm/perlless.nix @@ -1,7 +1,7 @@ -{ pkgs, lib, ... }: +{ lib, ... }: { #perlless profile -# system.switch.enable = lib.mkForce false; + #system.switch.enable = lib.mkForce false; # Remove perl from activation #system.etc.overlay.enable = lib.mkForce true; diff --git a/nix/testVm/postgres.nix b/nix/testVm/postgres.nix index 015f6d96b..0190c2756 100644 --- a/nix/testVm/postgres.nix +++ b/nix/testVm/postgres.nix @@ -1,4 +1,4 @@ -{ config, pkgs, ... }: +{ pkgs, ... }: { services.postgresql = { diff --git a/nix/testVm/vm.nix b/nix/testVm/vm.nix index 8a2821354..eccd12b24 100644 --- a/nix/testVm/vm.nix +++ b/nix/testVm/vm.nix @@ -1,14 +1,14 @@ { - nixpkgs, - modulesPath, pkgs, lib, + modulesPath, ... }: { imports = [ - # (modulesPath + "/virtualisation/qemu-vm.nix") + (modulesPath + "/virtualisation/qemu-vm.nix") ]; + virtualisation.vmVariant = { services.xserver.videoDrivers = [ "qxl" ]; services.spice-vdagentd.enable = true; @@ -19,11 +19,15 @@ "-display gtk,zoom-to-fit=off,show-cursor=on" "-device virtio-balloon" ]; - virtualisation.memorySize = 4096; - virtualisation.cores = 6; + virtualisation.memorySize = 8192; + virtualisation.cores = 10; virtualisation.forwardPorts = [ - # { hostPort = 2222; guestPort = 22; } # Probably shouldn't do this with root:root lol - { from = "host"; host.port = 8080; guest.port = 80; } + # { hostPort = 2222; guestPort = 22; } # Probably shouldn't do this with root:root lol + { + from = "host"; + host.port = 8080; + guest.port = 80; + } ]; networking.useDHCP = lib.mkForce true; }; @@ -55,14 +59,4 @@ font = "${pkgs.cozette}/share/consolefonts/cozette6x13.psfu"; packages = with pkgs; [ cozette ]; }; - - system = { - #activatable = false; - copySystemConfiguration = false; - includeBuildDependencies = false; - disableInstallerTools = lib.mkForce true; - build = { - separateActivationScript = true; - }; - }; } diff --git a/node-modules.nix b/node-modules.nix new file mode 100644 index 000000000..b064ee0a0 --- /dev/null +++ b/node-modules.nix @@ -0,0 +1,56 @@ +{ + pkgs, + lib, + ... +}: + +let + filteredSrc = lib.fileset.toSource { + root = ./.; + fileset = ( + lib.fileset.intersection ./. ( + lib.fileset.unions [ + ./package.json + ./package-lock.json + ./patches + ] + ) + ); + }; +in +pkgs.buildNpmPackage { + pname = "spacebar-server-ts-node_modules"; + nodejs = pkgs.nodejs_24; + version = builtins.hashFile "sha256" ./package.json; + + meta = with lib; { + description = "Node modules for the spacebar-server-ts package."; + homepage = "https://github.com/spacebarchat/server"; + license = licenses.agpl3Plus; + platforms = platforms.all; + maintainers = with maintainers; [ RorySys ]; + }; + + src = filteredSrc; + npmDeps = pkgs.importNpmLock { npmRoot = filteredSrc; }; + npmConfigHook = pkgs.importNpmLock.npmConfigHook; + + dontNpmBuild = true; + makeCacheWritable = true; + + nativeBuildInputs = with pkgs; [ + (pkgs.python3.withPackages (ps: with ps; [ setuptools ])) + ]; + + installPhase = '' + runHook preInstall + + # Copy outputs + echo "Copying node_modules as $out" + cp -r node_modules $out + echo -n 'Disk usage: ' + du -sh node_modules/ + + runHook postInstall + ''; +} diff --git a/package-lock.json b/package-lock.json index fbbbacf9a..5ddd08047 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,9 @@ "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { - "@protobuf-ts/runtime": "^2.11.1", - "@sentry/node": "^10.38.0", + "@spacebarchat/pion-webrtc": "^0.0.1", "@toondepauw/node-zstd": "^1.2.0", - "ajv": "^8.17.1", + "ajv": "^8.18.0", "ajv-formats": "^3.0.1", "amqplib": "^0.10.9", "badge-maker": "^5.0.2", @@ -23,15 +22,15 @@ "cheerio": "^1.2.0", "cookie-parser": "^1.4.7", "discord-protos": "^1.2.102", - "dotenv": "^17.2.4", - "email-providers": "^2.21.0", + "dotenv": "^17.3.1", + "email-providers": "^2.22.0", "exif-be-gone": "^1.5.1", "express": "^5.2.1", "fast-zlib": "^2.0.1", - "fido2-lib": "^3.5.6", - "file-type": "^21.3.0", + "fido2-lib": "^3.5.9", + "file-type": "^21.3.2", "form-data": "^4.0.5", - "i18next": "^25.8.6", + "i18next": "^25.8.18", "i18next-fs-backend": "^2.6.1", "i18next-http-middleware": "^3.9.2", "image-size": "^2.0.2", @@ -40,11 +39,11 @@ "module-alias": "^2.3.4", "morgan": "^1.10.1", "ms": "^2.1.3", - "multer": "^2.0.2", + "multer": "^2.1.1", "murmurhash-js": "^1.0.0", "node-2fa": "^2.0.3", - "pg": "^8.18.0", - "pg-query-stream": "^4.12.0", + "pg": "^8.20.0", + "pg-query-stream": "^4.14.0", "picocolors": "^1.1.1", "probe-image-size": "^7.2.3", "reflect-metadata": "^0.2.2", @@ -54,7 +53,7 @@ "ws": "^8.19.0" }, "devDependencies": { - "@eslint/eslintrc": "^3.3.3", + "@eslint/eslintrc": "^3.3.5", "@eslint/js": "^9.14.0", "@spacebarchat/spacebar-webrtc-types": "^1.0.1", "@types/amqplib": "^0.10.8", @@ -67,17 +66,17 @@ "@types/jsonwebtoken": "^9.0.10", "@types/module-alias": "^2.0.4", "@types/morgan": "^1.9.10", - "@types/multer": "^2.0.0", + "@types/multer": "^2.1.0", "@types/murmurhash-js": "^1.0.6", - "@types/node": "^24.10.13", - "@types/nodemailer": "^7.0.9", + "@types/node": "^24.12.0", + "@types/nodemailer": "^7.0.11", "@types/probe-image-size": "^7.2.5", "@types/sharp": "^0.31.1", "@types/ws": "^8.18.1", - "@typescript-eslint/eslint-plugin": "^8.55.0", - "@typescript-eslint/parser": "^8.55.0", + "@typescript-eslint/eslint-plugin": "^8.57.0", + "@typescript-eslint/parser": "^8.57.0", "@typescript/native-preview": "^7.0.0-dev.20260119.1", - "eslint": "^9.39.2", + "eslint": "^9.39.4", "globals": "^16.5.0", "husky": "^9.1.7", "patch-package": "^8.0.1", @@ -91,28 +90,11 @@ "@sendgrid/mail": "^8.1.6", "@yukikaze-bot/erlpack": "^1.0.1", "jimp": "^1.6.0", - "mailgun.js": "^12.7.0", + "mailgun.js": "^12.7.1", "node-mailjet": "^6.0.11", "nodemailer": "^7.0.13" } }, - "node_modules/@apm-js-collab/code-transformer": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz", - "integrity": "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==", - "license": "Apache-2.0" - }, - "node_modules/@apm-js-collab/tracing-hooks": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.3.1.tgz", - "integrity": "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==", - "license": "Apache-2.0", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.8.0", - "debug": "^4.4.1", - "module-details-from-path": "^1.0.4" - } - }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", @@ -123,9 +105,9 @@ } }, "node_modules/@borewit/text-codec": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", - "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "license": "MIT", "funding": { "type": "github", @@ -133,9 +115,9 @@ } }, "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", - "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.2.tgz", + "integrity": "sha512-ZKZ/F8US7JR92J4DMct6cLW/Y66o2K576+zjlEN/MevH70bFIsB10wkZEQPLzl2oNh2SMGy55xpJ9JoBRl5DOA==", "cpu": [ "arm64" ], @@ -146,9 +128,9 @@ ] }, "node_modules/@cbor-extract/cbor-extract-darwin-x64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", - "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.2.tgz", + "integrity": "sha512-32b1mgc+P61Js+KW9VZv/c+xRw5EfmOcPx990JbCBSkYJFY0l25VinvyyWfl+3KjibQmAcYwmyzKF9J4DyKP/Q==", "cpu": [ "x64" ], @@ -159,9 +141,9 @@ ] }, "node_modules/@cbor-extract/cbor-extract-linux-arm": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", - "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.2.tgz", + "integrity": "sha512-tNg0za41TpQfkhWjptD+0gSD2fggMiDCSacuIeELyb2xZhr7PrhPe5h66Jc67B/5dmpIhI2QOUtv4SBsricyYQ==", "cpu": [ "arm" ], @@ -172,9 +154,9 @@ ] }, "node_modules/@cbor-extract/cbor-extract-linux-arm64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", - "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.2.tgz", + "integrity": "sha512-wfqgzqCAy/Vn8i6WVIh7qZd0DdBFaWBjPdB6ma+Wihcjv0gHqD/mw3ouVv7kbbUNrab6dKEx/w3xQZEdeXIlzg==", "cpu": [ "arm64" ], @@ -185,9 +167,9 @@ ] }, "node_modules/@cbor-extract/cbor-extract-linux-x64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", - "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.2.tgz", + "integrity": "sha512-rpiLnVEsqtPJ+mXTdx1rfz4RtUGYIUg2rUAZgd1KjiC1SehYUSkJN7Yh+aVfSjvCGtVP0/bfkQkXpPXKbmSUaA==", "cpu": [ "x64" ], @@ -197,19 +179,6 @@ "linux" ] }, - "node_modules/@cbor-extract/cbor-extract-win32-x64": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", - "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -253,15 +222,15 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -294,20 +263,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -318,9 +287,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -355,9 +324,9 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { @@ -514,12 +483,12 @@ } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -1100,504 +1069,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.0.tgz", - "integrity": "sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", - "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.58.0.tgz", - "integrity": "sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.54.0.tgz", - "integrity": "sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/connect": "3.4.38" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.28.0.tgz", - "integrity": "sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.59.0.tgz", - "integrity": "sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.30.0.tgz", - "integrity": "sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.54.0.tgz", - "integrity": "sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.58.0.tgz", - "integrity": "sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.57.0.tgz", - "integrity": "sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.211.0.tgz", - "integrity": "sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/instrumentation": "0.211.0", - "@opentelemetry/semantic-conventions": "^1.29.0", - "forwarded-parse": "2.1.2" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.59.0.tgz", - "integrity": "sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/redis-common": "^0.38.2", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.20.0.tgz", - "integrity": "sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.30.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.55.0.tgz", - "integrity": "sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.59.0.tgz", - "integrity": "sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.36.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.55.0.tgz", - "integrity": "sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.64.0.tgz", - "integrity": "sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.57.0.tgz", - "integrity": "sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.57.0.tgz", - "integrity": "sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0", - "@types/mysql": "2.15.27" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.57.0.tgz", - "integrity": "sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0", - "@opentelemetry/sql-common": "^0.41.2" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.63.0.tgz", - "integrity": "sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@opentelemetry/sql-common": "^0.41.2", - "@types/pg": "8.15.6", - "@types/pg-pool": "2.0.7" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.59.0.tgz", - "integrity": "sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/redis-common": "^0.38.2", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.30.0.tgz", - "integrity": "sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0", - "@types/tedious": "^4.0.14" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.21.0.tgz", - "integrity": "sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.24.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.7.0" - } - }, - "node_modules/@opentelemetry/redis-common": { - "version": "0.38.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz", - "integrity": "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz", - "integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz", - "integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/resources": "2.5.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", - "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/sql-common": { - "version": "0.41.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", - "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0" - } - }, "node_modules/@peculiar/asn1-schema": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", @@ -1660,47 +1131,6 @@ "url": "https://opencollective.com/pkgr" } }, - "node_modules/@prisma/instrumentation": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.2.0.tgz", - "integrity": "sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.207.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.8" - } - }, - "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/api-logs": { - "version": "0.207.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz", - "integrity": "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/instrumentation": { - "version": "0.207.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.207.0.tgz", - "integrity": "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.207.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, "node_modules/@protobuf-ts/runtime": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.11.1.tgz", @@ -1748,134 +1178,24 @@ "node": ">=12.*" } }, - "node_modules/@sentry/core": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.38.0.tgz", - "integrity": "sha512-1pubWDZE5y5HZEPMAZERP4fVl2NH3Ihp1A+vMoVkb3Qc66Diqj1WierAnStlZP7tCx0TBa0dK85GTW/ZFYyB9g==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.38.0.tgz", - "integrity": "sha512-wriyDtWDAoatn8EhOj0U4PJR1WufiijTsCGALqakOHbFiadtBJANLe6aSkXoXT4tegw59cz1wY4NlzHjYksaPw==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-amqplib": "0.58.0", - "@opentelemetry/instrumentation-connect": "0.54.0", - "@opentelemetry/instrumentation-dataloader": "0.28.0", - "@opentelemetry/instrumentation-express": "0.59.0", - "@opentelemetry/instrumentation-fs": "0.30.0", - "@opentelemetry/instrumentation-generic-pool": "0.54.0", - "@opentelemetry/instrumentation-graphql": "0.58.0", - "@opentelemetry/instrumentation-hapi": "0.57.0", - "@opentelemetry/instrumentation-http": "0.211.0", - "@opentelemetry/instrumentation-ioredis": "0.59.0", - "@opentelemetry/instrumentation-kafkajs": "0.20.0", - "@opentelemetry/instrumentation-knex": "0.55.0", - "@opentelemetry/instrumentation-koa": "0.59.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.55.0", - "@opentelemetry/instrumentation-mongodb": "0.64.0", - "@opentelemetry/instrumentation-mongoose": "0.57.0", - "@opentelemetry/instrumentation-mysql": "0.57.0", - "@opentelemetry/instrumentation-mysql2": "0.57.0", - "@opentelemetry/instrumentation-pg": "0.63.0", - "@opentelemetry/instrumentation-redis": "0.59.0", - "@opentelemetry/instrumentation-tedious": "0.30.0", - "@opentelemetry/instrumentation-undici": "0.21.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", - "@opentelemetry/semantic-conventions": "^1.39.0", - "@prisma/instrumentation": "7.2.0", - "@sentry/core": "10.38.0", - "@sentry/node-core": "10.38.0", - "@sentry/opentelemetry": "10.38.0", - "import-in-the-middle": "^2.0.6", - "minimatch": "^9.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sentry/node-core": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.38.0.tgz", - "integrity": "sha512-ErXtpedrY1HghgwM6AliilZPcUCoNNP1NThdO4YpeMq04wMX9/GMmFCu46TnCcg6b7IFIOSr2S4yD086PxLlHQ==", - "license": "MIT", - "dependencies": { - "@apm-js-collab/tracing-hooks": "^0.3.1", - "@sentry/core": "10.38.0", - "@sentry/opentelemetry": "10.38.0", - "import-in-the-middle": "^2.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/resources": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", - "@opentelemetry/semantic-conventions": "^1.39.0" - } - }, - "node_modules/@sentry/node/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@sentry/node/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@sentry/opentelemetry": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.38.0.tgz", - "integrity": "sha512-YPVhWfYmC7nD3EJqEHGtjp4fp5LwtAbE5rt9egQ4hqJlYFvr8YEz9sdoqSZxO0cZzgs2v97HFl/nmWAXe52G2Q==", - "license": "MIT", + "node_modules/@spacebarchat/pion-webrtc": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@spacebarchat/pion-webrtc/-/pion-webrtc-0.0.1.tgz", + "integrity": "sha512-xhDa21hxe8OnCavTxnEDSnBJwFQsesx6NekiQeco4Rl3f2tG3IPx0DhmgXPnj/T1N70OEygSn96XpBaMg7lFgg==", + "license": "AGPL-3.0-only", "dependencies": { - "@sentry/core": "10.38.0" - }, - "engines": { - "node": ">=18" + "semantic-sdp": "^3.31.1" }, "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", - "@opentelemetry/semantic-conventions": "^1.39.0" + "@spacebarchat/spacebar-webrtc-types": "^1.0.1" } }, "node_modules/@spacebarchat/spacebar-webrtc-types": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@spacebarchat/spacebar-webrtc-types/-/spacebar-webrtc-types-1.0.1.tgz", "integrity": "sha512-WfBRUN2520w7o5vU9HNDug9alNvydQP7H/jwAy8LeHTHwlMMUw/60A54FQDIAtsahw787fR3QZ83UGjhKDzDTg==", - "dev": true, - "license": "AGPL-3.0-only" + "license": "AGPL-3.0-only", + "peer": true }, "node_modules/@sqltools/formatter": { "version": "1.2.5", @@ -2116,6 +1436,7 @@ "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -2144,6 +1465,7 @@ "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", @@ -2230,9 +1552,9 @@ "license": "MIT" }, "node_modules/@types/multer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", - "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.1.0.tgz", + "integrity": "sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==", "dev": true, "license": "MIT", "dependencies": { @@ -2246,15 +1568,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mysql": { - "version": "2.15.27", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", - "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/needle": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@types/needle/-/needle-3.3.0.tgz", @@ -2266,18 +1579,19 @@ } }, "node_modules/@types/node": { - "version": "24.10.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", - "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", + "version": "24.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", + "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@types/nodemailer": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.9.tgz", - "integrity": "sha512-vI8oF1M+8JvQhsId0Pc38BdUP2evenIIys7c7p+9OZXSPOH5c1dyINP1jT8xQ2xPuBUXmIC87s+91IZMDjH8Ow==", + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.11.tgz", + "integrity": "sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==", "dev": true, "license": "MIT", "dependencies": { @@ -2293,26 +1607,6 @@ "@types/node": "*" } }, - "node_modules/@types/pg": { - "version": "8.15.6", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", - "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/pg-pool": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", - "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", - "license": "MIT", - "dependencies": { - "@types/pg": "*" - } - }, "node_modules/@types/probe-image-size": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/@types/probe-image-size/-/probe-image-size-7.2.5.tgz", @@ -2325,9 +1619,9 @@ } }, "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", "dev": true, "license": "MIT" }, @@ -2369,15 +1663,6 @@ "@types/node": "*" } }, - "node_modules/@types/tedious": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", - "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -2389,17 +1674,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", - "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", + "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/type-utils": "8.55.0", - "@typescript-eslint/utils": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/type-utils": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -2412,8 +1697,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.55.0", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.57.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, @@ -2428,16 +1713,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", - "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "engines": { @@ -2448,19 +1734,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", - "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.55.0", - "@typescript-eslint/types": "^8.55.0", + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { @@ -2475,14 +1761,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", - "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0" + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2493,9 +1779,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", - "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { @@ -2510,15 +1796,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", - "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0", - "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -2530,14 +1816,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", - "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { @@ -2549,18 +1835,18 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", - "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.55.0", - "@typescript-eslint/tsconfig-utils": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/visitor-keys": "8.55.0", + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" @@ -2576,43 +1862,56 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", - "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.55.0", - "@typescript-eslint/types": "8.55.0", - "@typescript-eslint/typescript-estree": "8.55.0" + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2622,19 +1921,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", - "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.55.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.57.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2645,41 +1944,41 @@ } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-VHAVbp8d2VGm90EK//brKIYvT3iPrLXMq4/LApCdkKww/Hfn33zPRVmig4rswNaJiVu8XhcdHld5yfMw6d5A9Q==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-x+ZrFAEq+c7bF4Ml8+abYZ9vW6mzu22fmcPbDcBmUl/4uGFCYXXww0FS3+me9MfdSOCAPtqcZtwApx1RQO2X/w==", "dev": true, "license": "Apache-2.0", "bin": { "tsgo": "bin/tsgo.js" }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260212.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260212.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20260212.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260212.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20260212.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260212.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20260212.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260313.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260313.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20260313.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260313.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20260313.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260313.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20260313.1" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-HH4bOVbNW6ITv00VSaE3aZjCuU2d+amgFZKdhbq7NpcJDxFvxyy9GT9gkKV0D1DXz5qoxZIcyBEIbwrhABb9vg==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-/fU2IvlRQWOy63xSzkejW7tTQpsL5dQ/ATIsJFlK75vS941CnNJY8dAx3iQYLkHMhS45hhCIR+bbJPRaacq/fw==", "cpu": [ "arm64" ], @@ -2691,9 +1990,9 @@ ] }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-vnQ2xRJscbtyS/jHO5QY2xAZ3c11Yn1ZAor/XODDrxd7N7jIrm0Vtc2CIwsi51oncLS1SZtUd9cHZmJg5zUJrQ==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-oy7Ew1J3+YtO9QsqVGkncQ8bCwVPxNk8nSO2q1sHLccyYq0f4eDaZTlJ+u9Ynry548NwNucLh9wE+DWfWhzU3Q==", "cpu": [ "x64" ], @@ -2705,9 +2004,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-T8sF3YtYtODhWnFNhVuL/GABCHpKJs6ZxTtSC1LtXoM/CE0Ai06k5WKOxJG5rJrBtLIW+Dempk7qKPfhNliDTA==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-IAx0ajfEiL1tJg1N6+/nHXJKebNe72yanY2N5bicwIB3t2BmydnrEPG+/OFVqc+prfJngxSx/61mvkXScZePzg==", "cpu": [ "arm" ], @@ -2719,9 +2018,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-suA5OryrhL/tE7AiQXiNNV88XwKEOfO0sypJQj+cfg/fpQ2trFyDZcsdMLYVZ7J0zirDai6H3TDETYYoNFE1/g==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-KkbAweTnBpmQ8wCGHjrLzPX+FuwhSrVERNqyGPaq/267Sxt0UwbIO3rZduXlq5UUln1+/z7uT/BNJiuoFW3iLw==", "cpu": [ "arm64" ], @@ -2733,9 +2032,9 @@ ] }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-w687rpZKJM0Lev0ya0GYJlnFCITTUmN8jDpwLXn60jrNEZzL2J4F7biA6papr2sMdKRfWvRklhjB1TKHbJ6FKA==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-9LCNgXVNoArHlMuL6yFKJxSdshiiadTfW/pU4tz4Vbg+Dg9La1VE9mLlBdijy5ZIg4nsOFpR8JTDURcA1RoHXw==", "cpu": [ "x64" ], @@ -2747,9 +2046,9 @@ ] }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-NhCXPQF6OTNEZl8iwRE1ef/zHiqit5p3m7hdT2vfAOi1iA2eoazX0zTSdhgjX83o9cLjen3V1R7nbSYehFHaqw==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-cP2y5hb2xhfEDIgxdhxhPXa/D5Lq3yj6zxVuhh9ZkUariF+ZAmF4pySlIA+7NdprgTQqvNY5Mp70cPUiYD3yUg==", "cpu": [ "arm64" ], @@ -2761,9 +2060,9 @@ ] }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20260212.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260212.1.tgz", - "integrity": "sha512-0yqSBlASRx9rqM12QvaWc227w+bIsuI2EwAiNsoB1ybRbCXoXMah1RQlfjjTpD02eWCe/029vwrNhq+FLn7Z8A==", + "version": "7.0.0-dev.20260313.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20260313.1.tgz", + "integrity": "sha512-8KDfi7U1enFo4z6F0qe4Rd5QzBhk+4cwpZtOGAT9lgyR4pF/mo8zQd0t+Hlkj6d87W057RP8lgCGTGfclGWxUg==", "cpu": [ "x64" ], @@ -2827,10 +2126,12 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "devOptional": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2838,15 +2139,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -2858,9 +2150,9 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2884,9 +2176,9 @@ } }, "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -3087,9 +2379,9 @@ } }, "node_modules/axios": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", - "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", "optional": true, "dependencies": { @@ -3180,9 +2472,9 @@ } }, "node_modules/bcrypt/node_modules/node-addon-api": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.6.0.tgz", + "integrity": "sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==", "license": "MIT", "engines": { "node": "^18 || ^20 || >= 21" @@ -3393,9 +2685,9 @@ } }, "node_modules/cbor-extract": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", - "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.2.tgz", + "integrity": "sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3406,21 +2698,24 @@ "download-cbor-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", - "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", - "@cbor-extract/cbor-extract-linux-arm": "2.2.0", - "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", - "@cbor-extract/cbor-extract-linux-x64": "2.2.0", - "@cbor-extract/cbor-extract-win32-x64": "2.2.0" + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.2", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.2", + "@cbor-extract/cbor-extract-linux-arm": "2.2.2", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.2", + "@cbor-extract/cbor-extract-linux-x64": "2.2.2", + "@cbor-extract/cbor-extract-win32-x64": "2.2.2" } }, + "node_modules/cbor-extract/node_modules/@cbor-extract/cbor-extract-win32-x64": { + "optional": true + }, "node_modules/cbor-x": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.0.tgz", - "integrity": "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.4.tgz", + "integrity": "sha512-UGKHjp6RHC6QuZ2yy5LCKm7MojM4716DwoSaqwQpaH4DvZvbBTGcoDNTiG9Y2lByXZYFEs9WRkS5tLl96IrF1Q==", "license": "MIT", "optionalDependencies": { - "cbor-extract": "^2.2.0" + "cbor-extract": "^2.2.2" } }, "node_modules/chalk": { @@ -3512,12 +2807,6 @@ "node": ">=8" } }, - "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3711,9 +3000,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", "license": "MIT" }, "node_modules/debug": { @@ -3734,9 +3023,9 @@ } }, "node_modules/dedent": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -3827,9 +3116,9 @@ } }, "node_modules/discord-protos": { - "version": "1.2.102", - "resolved": "https://registry.npmjs.org/discord-protos/-/discord-protos-1.2.102.tgz", - "integrity": "sha512-L5KRaTVdixpWXhpABY4McOgBidSID3tPwtVvi5/pQYhBUpasSYEJsuKjwRz3PoqWxJR4PGq8NxIoEUQ7qjbTRQ==", + "version": "1.2.121", + "resolved": "https://registry.npmjs.org/discord-protos/-/discord-protos-1.2.121.tgz", + "integrity": "sha512-SDQhV3wU/sH//5B1e1s039q6VexAOzN3DBhMkXmDYzTBlmKT8d2N6YCzfZRIkbUd3mLzyu18M+toqaFGtjKqTw==", "license": "MIT", "dependencies": { "@protobuf-ts/runtime": "^2.9.6" @@ -3891,9 +3180,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz", - "integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==", + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3938,9 +3227,9 @@ "license": "MIT" }, "node_modules/email-providers": { - "version": "2.21.0", - "resolved": "https://registry.npmjs.org/email-providers/-/email-providers-2.21.0.tgz", - "integrity": "sha512-c31t0e+U4D15pBwEapk+Ft04O9Zk/T/KrYBglRMen2GQd9W41n7Ev1ShxhLyUWQwtFRnRDcjg8o3JLFAhR+qlg==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/email-providers/-/email-providers-2.22.0.tgz", + "integrity": "sha512-ucFcMgALX9EJhffm8aOFoogQInbG3A8eK3XCJyx3jwGQtiK0ZkLw2CdXaJyf4xTJfCS3JdgJSJjwUlSyuqf0ZA==", "license": "ISC", "engines": { "node": ">=16" @@ -3998,6 +3287,12 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/equals-ignore-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/equals-ignore-case/-/equals-ignore-case-1.0.1.tgz", + "integrity": "sha512-krgK/Px09jhcc7wK5/lxApRv7XmIT/fSgrMwdaW/V1FmPJEIJMNGEMhe0U9tJ/97rPe75MHKPRqi7/8Tqz6NMA==", + "license": "ISC" + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -4072,25 +3367,26 @@ } }, "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", + "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", @@ -4109,7 +3405,7 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -4162,9 +3458,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -4435,19 +3731,19 @@ } }, "node_modules/fido2-lib": { - "version": "3.5.6", - "resolved": "https://registry.npmjs.org/fido2-lib/-/fido2-lib-3.5.6.tgz", - "integrity": "sha512-uYJjMxO/odAelhdm2SMtiGJWZMRmbccev10c+Jw9FM2yYMS+3IOZfnGItrQiBiMoCt2t3lYqSF8DA85lBzVylg==", + "version": "3.5.9", + "resolved": "https://registry.npmjs.org/fido2-lib/-/fido2-lib-3.5.9.tgz", + "integrity": "sha512-OfiX3VetTaoiuA9Bk8QnIdDVEOyZyR8m6JE++9zLQ3sbpsQN8thaJoLHcDi770cOKGtzvSH49mp2njtXUm6S7w==", "license": "MIT", "dependencies": { "@hexagon/base64": "^2.0.4", "@peculiar/webcrypto": "~1.5.0", - "asn1js": "~3.0.6", - "cbor-x": "~1.6.0", - "jose": "^6.1.0", - "pkijs": "~3.3.2", + "asn1js": "~3.0.7", + "cbor-x": "~1.6.3", + "jose": "^6.2.1", + "pkijs": "~3.3.3", "punycode.js": "^2.3.1", - "tldts": "~7.0.17" + "tldts": "~7.0.25" }, "engines": { "node": ">=10" @@ -4467,9 +3763,9 @@ } }, "node_modules/file-type": { - "version": "21.3.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", - "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", + "version": "21.3.2", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.2.tgz", + "integrity": "sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==", "license": "MIT", "dependencies": { "@tokenizer/inflate": "^0.4.1", @@ -4560,9 +3856,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", + "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", "dev": true, "license": "ISC" }, @@ -4676,12 +3972,6 @@ "node": ">= 0.6" } }, - "node_modules/forwarded-parse": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", - "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "license": "MIT" - }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -5044,9 +4334,9 @@ } }, "node_modules/i18next": { - "version": "25.8.6", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.6.tgz", - "integrity": "sha512-HsS6p2yr/Vo5EPljWuBJ9OxKVFok2Q/Oa6PvFTpv2bMcDt2sQMOnKDQ7FTDDdME+3d1YULQjKj7aVSZP1bCouQ==", + "version": "25.8.18", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.18.tgz", + "integrity": "sha512-lzY5X83BiL5AP77+9DydbrqkQHFN9hUzWGjqjLpPcp5ZOzuu1aSoKaU3xbBLSjWx9dAzW431y+d+aogxOZaKRA==", "funding": [ { "type": "individual", @@ -5063,7 +4353,7 @@ ], "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.4" + "@babel/runtime": "^7.28.6" }, "peerDependencies": { "typescript": "^5" @@ -5178,18 +4468,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5398,9 +4676,9 @@ } }, "node_modules/jose": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", - "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.1.tgz", + "integrity": "sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -5646,9 +4924,9 @@ "license": "ISC" }, "node_modules/mailgun.js": { - "version": "12.7.0", - "resolved": "https://registry.npmjs.org/mailgun.js/-/mailgun.js-12.7.0.tgz", - "integrity": "sha512-TKuGxSGMdGKQDR+Ciocs0zzKNsy+ip+BwLEVatVLlJls30OigFcK61LiBBWoPrVg5D5nmKN/nDR9yKLJCERCsA==", + "version": "12.7.1", + "resolved": "https://registry.npmjs.org/mailgun.js/-/mailgun.js-12.7.1.tgz", + "integrity": "sha512-VG2zRx4hKVoLGdMDpF5Bt+lkhS6g+eWb547FR4/iozoGEszcT+uf8/0EsPBwnpfI2gYlui3aaPnQCzcFDYvGXQ==", "license": "MIT", "optional": true, "dependencies": { @@ -5776,9 +5054,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "devOptional": true, "license": "ISC", "dependencies": { @@ -5792,6 +5070,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5834,15 +5113,16 @@ } }, "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, + "optional": true, "bin": { "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/module-alias": { @@ -5851,12 +5131,6 @@ "integrity": "sha512-bOclZt8hkpuGgSSoG07PKmvzTizROilUTvLNyrMqvlC9snhs7y7GzjNWAVbISIOlhCP1T14rH1PDAV9iNyBq/w==", "license": "MIT" }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "license": "MIT" - }, "node_modules/morgan": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", @@ -5917,21 +5191,22 @@ "license": "MIT" }, "node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" + "type-is": "^1.6.18" }, "engines": { "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/multer/node_modules/media-typer": { @@ -6184,6 +5459,7 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", + "optional": true, "engines": { "node": ">=0.10.0" } @@ -6547,14 +5823,15 @@ } }, "node_modules/pg": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", - "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", "license": "MIT", + "peer": true, "dependencies": { - "pg-connection-string": "^2.11.0", - "pg-pool": "^3.11.0", - "pg-protocol": "^1.11.0", + "pg-connection-string": "^2.12.0", + "pg-pool": "^3.13.0", + "pg-protocol": "^1.13.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -6581,15 +5858,15 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz", - "integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", + "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", "license": "MIT" }, "node_modules/pg-cursor": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.17.0.tgz", - "integrity": "sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.19.0.tgz", + "integrity": "sha512-J5cF1MUz7LRJ9emOqF/06QjabMHMZy587rSPF0UuA8rCwKeeYl2co8Pp+6k5UU9YrAYHMzWkLxilfZB0hqsWWw==", "license": "MIT", "peerDependencies": { "pg": "^8" @@ -6605,27 +5882,28 @@ } }, "node_modules/pg-pool": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz", - "integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", + "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", - "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", + "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", "license": "MIT" }, "node_modules/pg-query-stream": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/pg-query-stream/-/pg-query-stream-4.12.0.tgz", - "integrity": "sha512-H97oiVPQ0+eRqIFOeYMUnjDcv9od7vHHMjiVDAhg2SEzAUr3M/dT83UEV1B+fm+tcVnymI8j2LSp57/+yjF6Fg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/pg-query-stream/-/pg-query-stream-4.14.0.tgz", + "integrity": "sha512-B1LLxgqngAATPciOPYYKyaQfsw5wyP6BZq6nHqQOC5QaaEBsfW/0OBwWUga+knCAqENMeoow9I8Zgi2m3P9rWw==", "license": "MIT", + "peer": true, "dependencies": { - "pg-cursor": "^2.17.0" + "pg-cursor": "^2.19.0" }, "peerDependencies": { "pg": "^8" @@ -6789,6 +6067,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -6929,9 +6208,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -6949,6 +6228,15 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "license": "MIT" }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -7045,19 +6333,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -7144,14 +6419,34 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", - "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", + "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" } }, + "node_modules/sdp-transform": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.15.0.tgz", + "integrity": "sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==", + "license": "MIT", + "bin": { + "sdp-verify": "checker.js" + } + }, + "node_modules/semantic-sdp": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/semantic-sdp/-/semantic-sdp-3.31.1.tgz", + "integrity": "sha512-esBGP11uWeHyvGwqDT1sf1p45H+kb7m5D49TIBHwYXXepzDFD24ar91eIwBQ8kwWWO8MNZfizsZ7CvBgYV9GuA==", + "license": "MIT", + "dependencies": { + "equals-ignore-case": "^1.0.0", + "randombytes": "^2.0.3", + "sdp-transform": "^2" + } + }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -7569,19 +6864,6 @@ "node": ">=10" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/thirty-two": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", @@ -7645,6 +6927,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -7653,21 +6936,21 @@ } }, "node_modules/tldts": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.23.tgz", - "integrity": "sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==", + "version": "7.0.25", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.25.tgz", + "integrity": "sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.23" + "tldts-core": "^7.0.25" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.23.tgz", - "integrity": "sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==", + "version": "7.0.25", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.25.tgz", + "integrity": "sha512-ZjCZK0rppSBu7rjHYDYsEaMOIbbT+nWF57hKkv4IUmZWBNrBWBOjIElc0mKRgLM8bm7x/BBlof6t2gi/Oq/Asw==", "license": "MIT" }, "node_modules/tmp": { @@ -7760,6 +7043,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -7996,12 +7280,12 @@ } }, "node_modules/typeorm/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -8011,10 +7295,10 @@ } }, "node_modules/typeorm/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", "engines": { "node": ">=16 || 14 >=14.17" } @@ -8025,6 +7309,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8097,9 +7382,9 @@ } }, "node_modules/undici": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", - "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.1.tgz", + "integrity": "sha512-5xoBibbmnjlcR3jdqtY2Lnx7WbrD/tHlT01TmvqZUFVc9Q1w4+j5hbnapTqbcXITMH1ovjq/W7BkqBilHiVAaA==", "license": "MIT", "engines": { "node": ">=20.18.1" diff --git a/package.json b/package.json index 86d1aa126..b24d20d3c 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ }, "homepage": "https://spacebar.chat", "devDependencies": { - "@eslint/eslintrc": "^3.3.3", + "@eslint/eslintrc": "^3.3.5", "@eslint/js": "^9.14.0", "@spacebarchat/spacebar-webrtc-types": "^1.0.1", "@types/amqplib": "^0.10.8", @@ -58,18 +58,17 @@ "@types/jsonwebtoken": "^9.0.10", "@types/module-alias": "^2.0.4", "@types/morgan": "^1.9.10", - "@types/ms": "^2.1.0", - "@types/multer": "^2.0.0", + "@types/multer": "^2.1.0", "@types/murmurhash-js": "^1.0.6", - "@types/node": "^24.10.13", - "@types/nodemailer": "^7.0.9", + "@types/node": "^24.12.0", + "@types/nodemailer": "^7.0.11", "@types/probe-image-size": "^7.2.5", "@types/sharp": "^0.31.1", "@types/ws": "^8.18.1", - "@typescript-eslint/eslint-plugin": "^8.55.0", - "@typescript-eslint/parser": "^8.55.0", + "@typescript-eslint/eslint-plugin": "^8.57.0", + "@typescript-eslint/parser": "^8.57.0", "@typescript/native-preview": "^7.0.0-dev.20260119.1", - "eslint": "^9.39.2", + "eslint": "^9.39.4", "globals": "^16.5.0", "husky": "^9.1.7", "patch-package": "^8.0.1", @@ -81,9 +80,10 @@ }, "dependencies": { "@protobuf-ts/runtime": "^2.11.1", - "@sentry/node": "^10.38.0", + "@spacebarchat/pion-webrtc": "^0.0.1", "@toondepauw/node-zstd": "^1.2.0", - "ajv": "^8.17.1", + "@types/ms": "^2.1.0", + "ajv": "^8.18.0", "ajv-formats": "^3.0.1", "amqplib": "^0.10.9", "badge-maker": "^5.0.2", @@ -92,16 +92,16 @@ "chalk": "^5.6.2", "cheerio": "^1.2.0", "cookie-parser": "^1.4.7", - "discord-protos": "^1.2.102", - "dotenv": "^17.2.4", - "email-providers": "^2.21.0", + "discord-protos": "1.2.121", + "dotenv": "^17.3.1", + "email-providers": "^2.22.0", "exif-be-gone": "^1.5.1", "express": "^5.2.1", "fast-zlib": "^2.0.1", - "fido2-lib": "^3.5.6", - "file-type": "^21.3.0", + "fido2-lib": "^3.5.9", + "file-type": "^21.3.2", "form-data": "^4.0.5", - "i18next": "^25.8.6", + "i18next": "^25.8.18", "i18next-fs-backend": "^2.6.1", "i18next-http-middleware": "^3.9.2", "image-size": "^2.0.2", @@ -110,11 +110,11 @@ "module-alias": "^2.3.4", "morgan": "^1.10.1", "ms": "^2.1.3", - "multer": "^2.0.2", + "multer": "^2.1.1", "murmurhash-js": "^1.0.0", "node-2fa": "^2.0.3", - "pg": "^8.18.0", - "pg-query-stream": "^4.12.0", + "pg": "^8.20.0", + "pg-query-stream": "^4.14.0", "picocolors": "^1.1.1", "probe-image-size": "^7.2.3", "reflect-metadata": "^0.2.2", @@ -136,7 +136,7 @@ "@sendgrid/mail": "^8.1.6", "@yukikaze-bot/erlpack": "^1.0.1", "jimp": "^1.6.0", - "mailgun.js": "^12.7.0", + "mailgun.js": "^12.7.1", "node-mailjet": "^6.0.11", "nodemailer": "^7.0.13" }, @@ -144,5 +144,10 @@ "typeorm": { "sqlite3": "../_EXCLUDED_" } + }, + "pnpm": { + "patchedDependencies": { + "discord-protos": "patches/discord-protos.patch" + } } } diff --git a/paper-recommender-extension/background.js b/paper-recommender-extension/background.js index 17dba051e..c7c87a7a5 100644 --- a/paper-recommender-extension/background.js +++ b/paper-recommender-extension/background.js @@ -1,4 +1,4 @@ -importScripts('lib/omrc_extractor.js', 'lib/recommender.js'); +importScripts("lib/omrc_extractor.js", "lib/recommender.js"); // Listen for messages from popup chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { @@ -25,10 +25,10 @@ async function handleAnalysis(sendResponse) { const result = await chrome.scripting.executeScript({ target: { tabId: activeTab.id }, func: () => { - const title = document.querySelector('h1.title')?.innerText.replace('Title:', '').trim(); - const abstract = document.querySelector('blockquote.abstract')?.innerText.replace('Abstract:', '').trim(); + const title = document.querySelector("h1.title")?.innerText.replace("Title:", "").trim(); + const abstract = document.querySelector("blockquote.abstract")?.innerText.replace("Abstract:", "").trim(); return { title, abstract }; - } + }, }); if (result && result[0] && result[0].result) { paperData = result[0].result; @@ -37,7 +37,8 @@ async function handleAnalysis(sendResponse) { // Mock data if not on a supported page, just to show UI works paperData = { title: "Discourse-Aware Scientific Paper Recommendation via QA-Style Summarization", - abstract: "The rapid growth of open-access (OA) publications has intensified the challenge of identifying relevant scientific papers. We propose OMRC-MR, a hierarchical framework that integrates QA-style OMRC summarization." + abstract: + "The rapid growth of open-access (OA) publications has intensified the challenge of identifying relevant scientific papers. We propose OMRC-MR, a hierarchical framework that integrates QA-style OMRC summarization.", }; } } @@ -54,7 +55,6 @@ async function handleAnalysis(sendResponse) { const recommendations = await fetchRecommendations(paperData.title, omrc); sendResponse({ omrc, recommendations }); - } catch (error) { console.error(error); sendResponse({ error: error.message }); diff --git a/paper-recommender-extension/lib/omrc_extractor.js b/paper-recommender-extension/lib/omrc_extractor.js index 3465d9589..b3505395a 100644 --- a/paper-recommender-extension/lib/omrc_extractor.js +++ b/paper-recommender-extension/lib/omrc_extractor.js @@ -4,10 +4,10 @@ */ const OMRC_KEYWORDS = { - OBJECTIVE: ['objective', 'aim', 'goal', 'purpose', 'propose', 'introduce', 'present', 'address', 'problem'], - METHOD: ['method', 'approach', 'framework', 'algorithm', 'model', 'technique', 'utilize', 'use', 'based on', 'via'], - RESULT: ['result', 'finding', 'show', 'demonstrate', 'achieve', 'perform', 'outperform', 'improvement', 'accuracy'], - CONCLUSION: ['conclusion', 'conclude', 'summary', 'future', 'implication', 'suggest', 'overall'] + OBJECTIVE: ["objective", "aim", "goal", "purpose", "propose", "introduce", "present", "address", "problem"], + METHOD: ["method", "approach", "framework", "algorithm", "model", "technique", "utilize", "use", "based on", "via"], + RESULT: ["result", "finding", "show", "demonstrate", "achieve", "perform", "outperform", "improvement", "accuracy"], + CONCLUSION: ["conclusion", "conclude", "summary", "future", "implication", "suggest", "overall"], }; function extractOMRC(abstract) { @@ -18,26 +18,26 @@ function extractOMRC(abstract) { OBJECTIVE: [], METHOD: [], RESULT: [], - CONCLUSION: [] + CONCLUSION: [], }; // Simple sentence classification based on keyword density // This is a naive approximation of the paper's deep learning model - sentences.forEach(sentence => { + sentences.forEach((sentence) => { const lowerSent = sentence.toLowerCase(); let maxScore = 0; let bestCategory = null; for (const [category, keywords] of Object.entries(OMRC_KEYWORDS)) { let score = 0; - keywords.forEach(kw => { + keywords.forEach((kw) => { if (lowerSent.includes(kw)) score++; }); // Weighting: First sentence often Objective, last often Conclusion - if (category === 'OBJECTIVE' && sentences.indexOf(sentence) === 0) score += 2; - if (category === 'CONCLUSION' && sentences.indexOf(sentence) === sentences.length - 1) score += 2; + if (category === "OBJECTIVE" && sentences.indexOf(sentence) === 0) score += 2; + if (category === "CONCLUSION" && sentences.indexOf(sentence) === sentences.length - 1) score += 2; if (score > maxScore) { maxScore = score; @@ -59,15 +59,15 @@ function extractOMRC(abstract) { // Join arrays back to strings return { - Objective: omrc.OBJECTIVE.join('. ') + '.', - Method: omrc.METHOD.join('. ') + '.', - Result: omrc.RESULT.join('. ') + '.', - Conclusion: omrc.CONCLUSION.join('. ') + '.' + Objective: omrc.OBJECTIVE.join(". ") + ".", + Method: omrc.METHOD.join(". ") + ".", + Result: omrc.RESULT.join(". ") + ".", + Conclusion: omrc.CONCLUSION.join(". ") + ".", }; } // Export for use in other modules (ESM or CommonJS depending on environment) -if (typeof module !== 'undefined' && module.exports) { +if (typeof module !== "undefined" && module.exports) { module.exports = { extractOMRC }; } else { window.extractOMRC = extractOMRC; diff --git a/paper-recommender-extension/lib/recommender.js b/paper-recommender-extension/lib/recommender.js index 4b0ec3c1f..b5db52658 100644 --- a/paper-recommender-extension/lib/recommender.js +++ b/paper-recommender-extension/lib/recommender.js @@ -3,7 +3,7 @@ * Uses Semantic Scholar API to fetch and rerank papers. */ -const S2_API_BASE = 'https://api.semanticscholar.org/graph/v1'; +const S2_API_BASE = "https://api.semanticscholar.org/graph/v1"; async function fetchRecommendations(title, omrc) { if (!title) return []; @@ -20,13 +20,13 @@ async function fetchRecommendations(title, omrc) { let candidates = data.data || []; // Filter out the exact same paper if found - candidates = candidates.filter(p => p.title.toLowerCase() !== title.toLowerCase()); + candidates = candidates.filter((p) => p.title.toLowerCase() !== title.toLowerCase()); // 2. Rerank based on OMRC similarity // Since we don't have the candidates' full text to extract OMRC reliably without heavy processing, // we will use the candidate's abstract as a proxy and compare it against our extracted OMRC. - const ranked = candidates.map(paper => { + const ranked = candidates.map((paper) => { const score = calculateSimilarity(omrc, paper.abstract || paper.title); return { ...paper, score }; }); @@ -35,7 +35,6 @@ async function fetchRecommendations(title, omrc) { ranked.sort((a, b) => b.score - a.score); return ranked.slice(0, 10); // Return top 10 - } catch (error) { console.error("Recommendation error:", error); return []; @@ -52,11 +51,21 @@ function calculateSimilarity(sourceOMRC, targetText) { // We give higher weight to Method and Result overlap as per the paper's "Discourse-Aware" philosophy const sourceText = `${sourceOMRC.Method} ${sourceOMRC.Result} ${sourceOMRC.Objective} ${sourceOMRC.Conclusion}`; - const sourceTokens = new Set(sourceText.toLowerCase().split(/\W+/).filter(w => w.length > 3)); - const targetTokens = new Set(targetText.toLowerCase().split(/\W+/).filter(w => w.length > 3)); + const sourceTokens = new Set( + sourceText + .toLowerCase() + .split(/\W+/) + .filter((w) => w.length > 3), + ); + const targetTokens = new Set( + targetText + .toLowerCase() + .split(/\W+/) + .filter((w) => w.length > 3), + ); let intersection = 0; - sourceTokens.forEach(token => { + sourceTokens.forEach((token) => { if (targetTokens.has(token)) intersection++; }); @@ -64,7 +73,7 @@ function calculateSimilarity(sourceOMRC, targetText) { return union === 0 ? 0 : intersection / union; } -if (typeof module !== 'undefined' && module.exports) { +if (typeof module !== "undefined" && module.exports) { module.exports = { fetchRecommendations }; } else { window.fetchRecommendations = fetchRecommendations; diff --git a/paper-recommender-extension/popup.js b/paper-recommender-extension/popup.js index e646b4200..10e0ab906 100644 --- a/paper-recommender-extension/popup.js +++ b/paper-recommender-extension/popup.js @@ -1,19 +1,19 @@ -document.addEventListener('DOMContentLoaded', async () => { - const omrcDisplay = document.getElementById('omrc-display'); - const recList = document.getElementById('rec-list'); - const loading = document.getElementById('loading'); - const errorMsg = document.getElementById('error-msg'); +document.addEventListener("DOMContentLoaded", async () => { + const omrcDisplay = document.getElementById("omrc-display"); + const recList = document.getElementById("rec-list"); + const loading = document.getElementById("loading"); + const errorMsg = document.getElementById("error-msg"); function showError(msg) { - loading.style.display = 'none'; + loading.style.display = "none"; errorMsg.textContent = msg; - errorMsg.style.display = 'block'; + errorMsg.style.display = "block"; } function showLoading() { - loading.style.display = 'block'; - recList.innerHTML = ''; - errorMsg.style.display = 'none'; + loading.style.display = "block"; + recList.innerHTML = ""; + errorMsg.style.display = "none"; } // Initialize communication with background script @@ -35,7 +35,7 @@ document.addEventListener('DOMContentLoaded', async () => { } // Display OMRC - let omrcHtml = ''; + let omrcHtml = ""; for (const [key, value] of Object.entries(response.omrc)) { if (value) { omrcHtml += `
${key}: ${value}
`; @@ -44,22 +44,25 @@ document.addEventListener('DOMContentLoaded', async () => { omrcDisplay.innerHTML = omrcHtml || "Could not structure abstract."; // Display Recommendations - loading.style.display = 'none'; + loading.style.display = "none"; if (response.recommendations && response.recommendations.length > 0) { - recList.innerHTML = response.recommendations.map(rec => ` + recList.innerHTML = response.recommendations + .map( + (rec) => `
${rec.title}
- ${rec.authors ? rec.authors.map(a => a.name).join(', ') : 'Unknown Authors'} - (${rec.year || 'n.d.'}) + ${rec.authors ? rec.authors.map((a) => a.name).join(", ") : "Unknown Authors"} + (${rec.year || "n.d."}) Score: ${Math.round(rec.score * 100)}%
- `).join(''); + `, + ) + .join(""); } else { recList.innerHTML = '
No recommendations found.
'; } - } catch (e) { showError("Failed to communicate with Zotero: " + e.message); } diff --git a/paper-recommender-extension/test_extension.js b/paper-recommender-extension/test_extension.js index 1b7e7f05a..5b5844e5c 100644 --- a/paper-recommender-extension/test_extension.js +++ b/paper-recommender-extension/test_extension.js @@ -1,5 +1,5 @@ -const { extractOMRC } = require('./lib/omrc_extractor.js'); -const { fetchRecommendations } = require('./lib/recommender.js'); +const { extractOMRC } = require("./lib/omrc_extractor.js"); +const { fetchRecommendations } = require("./lib/recommender.js"); // Mock fetch for Node.js environment global.fetch = async (url) => { @@ -8,11 +8,23 @@ global.fetch = async (url) => { ok: true, json: async () => ({ data: [ - { title: "Paper A", abstract: "We propose a new method for paper recommendation using deep learning.", authors: [{ name: "Author 1" }], year: 2024, url: "http://example.com/a" }, - { title: "Paper B", abstract: "This study analyzes the impact of OMRC structure on citation counts.", authors: [{ name: "Author 2" }], year: 2023, url: "http://example.com/b" }, - { title: "Paper C", abstract: "Unrelated work on quantum physics.", authors: [{ name: "Author 3" }], year: 2022, url: "http://example.com/c" } - ] - }) + { + title: "Paper A", + abstract: "We propose a new method for paper recommendation using deep learning.", + authors: [{ name: "Author 1" }], + year: 2024, + url: "http://example.com/a", + }, + { + title: "Paper B", + abstract: "This study analyzes the impact of OMRC structure on citation counts.", + authors: [{ name: "Author 2" }], + year: 2023, + url: "http://example.com/b", + }, + { title: "Paper C", abstract: "Unrelated work on quantum physics.", authors: [{ name: "Author 3" }], year: 2022, url: "http://example.com/c" }, + ], + }), }; }; diff --git a/patches/discord-protos.patch b/patches/discord-protos.patch new file mode 100644 index 000000000..ee02038c8 --- /dev/null +++ b/patches/discord-protos.patch @@ -0,0 +1,85 @@ +diff --git a/dist/index.d.ts b/dist/index.d.ts +new file mode 100644 +index 0000000000000000000000000000000000000000..ccc219671ec4c045c9f3f1d450ad48c73e980f58 +--- /dev/null ++++ b/dist/index.d.ts +@@ -0,0 +1,37 @@ ++import type { JsonValue } from "@protobuf-ts/runtime"; ++ ++interface ProtoMessage { ++ [key: string]: unknown; ++} ++ ++interface SettingsVersions { ++ clientVersion: number; ++ serverVersion: number; ++ dataVersion: number; ++} ++ ++interface IProtoMessageType { ++ create(value?: Partial): T; ++ fromJson(json: JsonValue, options?: unknown): T; ++ fromJsonString(json: string, options?: unknown): T; ++ toJson(message: T, options?: unknown): JsonValue; ++ toJsonString(message: T, options?: unknown): string; ++ fromBase64(base64: string): T; ++ toBase64(message: T): string; ++} ++ ++export interface PreloadedUserSettingsData extends ProtoMessage { ++ versions?: SettingsVersions; ++} ++export interface FrecencyUserSettingsData extends ProtoMessage { ++ versions?: SettingsVersions; ++} ++ ++export declare const PreloadedUserSettings: IProtoMessageType; ++export declare const FrecencyUserSettings: IProtoMessageType; ++ ++export type PreloadedUserSettings = PreloadedUserSettingsData; ++export type FrecencyUserSettings = FrecencyUserSettingsData; ++ ++export declare enum User_DisplayNameEffect {} ++export declare enum User_DisplayNameFont {} +diff --git a/dist/index.js b/dist/index.js +new file mode 100644 +index 0000000000000000000000000000000000000000..2e0ffb322f1f463e131daaaa432a3eb78912fc8d +--- /dev/null ++++ b/dist/index.js +@@ -0,0 +1,36 @@ ++"use strict"; ++// Runtime stub for discord-protos npm package ++// The published npm package is missing built output files (dist/index.js). ++// This stub provides the minimal runtime interface needed by the codebase. ++ ++function createStubMessageType(name) { ++ return { ++ create(value) { return value || {}; }, ++ fromJson(json) { return typeof json === 'object' ? json : {}; }, ++ fromJsonString(json) { return JSON.parse(json); }, ++ toJson(message) { return message; }, ++ toJsonString(message) { return JSON.stringify(message); }, ++ fromBase64(base64) { ++ try { ++ const json = Buffer.from(base64, 'base64').toString('utf-8'); ++ return JSON.parse(json); ++ } catch { return {}; } ++ }, ++ toBase64(message) { ++ return Buffer.from(JSON.stringify(message)).toString('base64'); ++ }, ++ }; ++} ++ ++const PreloadedUserSettings = createStubMessageType("PreloadedUserSettings"); ++const FrecencyUserSettings = createStubMessageType("FrecencyUserSettings"); ++ ++const User_DisplayNameEffect = {}; ++const User_DisplayNameFont = {}; ++ ++module.exports = { ++ PreloadedUserSettings, ++ FrecencyUserSettings, ++ User_DisplayNameEffect, ++ User_DisplayNameFont, ++}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5caabdac..59c94091c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,11 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +patchedDependencies: + discord-protos: + hash: euc7fgm2mau5n53lcvhiiwuwye + path: patches/discord-protos.patch + importers: .: @@ -11,18 +16,21 @@ importers: '@protobuf-ts/runtime': specifier: ^2.11.1 version: 2.11.1 - '@sentry/node': - specifier: ^10.38.0 - version: 10.38.0 + '@spacebarchat/pion-webrtc': + specifier: ^0.0.1 + version: 0.0.1(@spacebarchat/spacebar-webrtc-types@1.0.1) '@toondepauw/node-zstd': specifier: ^1.2.0 version: 1.2.0 + '@types/ms': + specifier: ^2.1.0 + version: 2.1.0 ajv: - specifier: ^8.17.1 - version: 8.17.1 + specifier: ^8.18.0 + version: 8.18.0 ajv-formats: specifier: ^3.0.1 - version: 3.0.1(ajv@8.17.1) + version: 3.0.1(ajv@8.18.0) amqplib: specifier: ^0.10.9 version: 0.10.9 @@ -45,14 +53,14 @@ importers: specifier: ^1.4.7 version: 1.4.7 discord-protos: - specifier: ^1.2.102 - version: 1.2.102 + specifier: 1.2.121 + version: 1.2.121(patch_hash=euc7fgm2mau5n53lcvhiiwuwye) dotenv: - specifier: ^17.2.4 - version: 17.2.4 + specifier: ^17.3.1 + version: 17.3.1 email-providers: - specifier: ^2.21.0 - version: 2.21.0 + specifier: ^2.22.0 + version: 2.22.0 exif-be-gone: specifier: ^1.5.1 version: 1.5.1 @@ -63,17 +71,17 @@ importers: specifier: ^2.0.1 version: 2.0.1 fido2-lib: - specifier: ^3.5.6 - version: 3.5.6 + specifier: ^3.5.9 + version: 3.5.9 file-type: - specifier: ^21.3.0 - version: 21.3.0 + specifier: ^21.3.2 + version: 21.3.2 form-data: specifier: ^4.0.5 version: 4.0.5 i18next: - specifier: ^25.8.6 - version: 25.8.7(typescript@5.9.3) + specifier: ^25.8.18 + version: 25.8.18(typescript@5.9.3) i18next-fs-backend: specifier: ^2.6.1 version: 2.6.1 @@ -99,8 +107,8 @@ importers: specifier: ^2.1.3 version: 2.1.3 multer: - specifier: ^2.0.2 - version: 2.0.2 + specifier: ^2.1.1 + version: 2.1.1 murmurhash-js: specifier: ^1.0.0 version: 1.0.0 @@ -108,11 +116,11 @@ importers: specifier: ^2.0.3 version: 2.0.3 pg: - specifier: ^8.18.0 - version: 8.18.0 + specifier: ^8.20.0 + version: 8.20.0 pg-query-stream: - specifier: ^4.12.0 - version: 4.12.0(pg@8.18.0) + specifier: ^4.14.0 + version: 4.14.0(pg@8.20.0) picocolors: specifier: ^1.1.1 version: 1.1.1 @@ -127,7 +135,7 @@ importers: version: 2.8.1 typeorm: specifier: ^0.3.28 - version: 0.3.28(pg-query-stream@4.12.0(pg@8.18.0))(pg@8.18.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.9.3)) + version: 0.3.28(pg-query-stream@4.14.0(pg@8.20.0))(pg@8.20.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@24.12.0)(typescript@5.9.3)) wretch: specifier: ^2.11.1 version: 2.11.1 @@ -145,8 +153,8 @@ importers: specifier: ^1.6.0 version: 1.6.0 mailgun.js: - specifier: ^12.7.0 - version: 12.7.0 + specifier: ^12.7.1 + version: 12.7.1 node-mailjet: specifier: ^6.0.11 version: 6.0.11 @@ -155,8 +163,8 @@ importers: version: 7.0.13 devDependencies: '@eslint/eslintrc': - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^3.3.5 + version: 3.3.5 '@eslint/js': specifier: ^9.14.0 version: 9.34.0 @@ -196,21 +204,18 @@ importers: '@types/morgan': specifier: ^1.9.10 version: 1.9.10 - '@types/ms': + '@types/multer': specifier: ^2.1.0 version: 2.1.0 - '@types/multer': - specifier: ^2.0.0 - version: 2.0.0 '@types/murmurhash-js': specifier: ^1.0.6 version: 1.0.6 '@types/node': - specifier: ^24.10.13 - version: 24.10.13 + specifier: ^24.12.0 + version: 24.12.0 '@types/nodemailer': - specifier: ^7.0.9 - version: 7.0.9 + specifier: ^7.0.11 + version: 7.0.11 '@types/probe-image-size': specifier: ^7.2.5 version: 7.2.5 @@ -221,17 +226,17 @@ importers: specifier: ^8.18.1 version: 8.18.1 '@typescript-eslint/eslint-plugin': - specifier: ^8.55.0 - version: 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + specifier: ^8.57.0 + version: 8.57.0(@typescript-eslint/parser@8.57.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) '@typescript-eslint/parser': - specifier: ^8.55.0 - version: 8.55.0(eslint@9.39.2)(typescript@5.9.3) + specifier: ^8.57.0 + version: 8.57.0(eslint@9.39.4)(typescript@5.9.3) '@typescript/native-preview': specifier: ^7.0.0-dev.20260119.1 - version: 7.0.0-dev.20260210.1 + version: 7.0.0-dev.20260315.1 eslint: - specifier: ^9.39.2 - version: 9.39.2 + specifier: ^9.39.4 + version: 9.39.4 globals: specifier: ^16.5.0 version: 16.5.0 @@ -249,7 +254,7 @@ importers: version: 4.2.2(prettier@3.8.1) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@24.10.13)(typescript@5.9.3) + version: 10.9.2(@types/node@24.12.0)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -259,46 +264,40 @@ importers: packages: - '@apm-js-collab/code-transformer@0.8.2': - resolution: {integrity: sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==} - - '@apm-js-collab/tracing-hooks@0.3.1': - resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==} - '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@borewit/text-codec@0.2.1': - resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} - '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': - resolution: {integrity: sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==} + '@cbor-extract/cbor-extract-darwin-arm64@2.2.2': + resolution: {integrity: sha512-ZKZ/F8US7JR92J4DMct6cLW/Y66o2K576+zjlEN/MevH70bFIsB10wkZEQPLzl2oNh2SMGy55xpJ9JoBRl5DOA==} cpu: [arm64] os: [darwin] - '@cbor-extract/cbor-extract-darwin-x64@2.2.0': - resolution: {integrity: sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==} + '@cbor-extract/cbor-extract-darwin-x64@2.2.2': + resolution: {integrity: sha512-32b1mgc+P61Js+KW9VZv/c+xRw5EfmOcPx990JbCBSkYJFY0l25VinvyyWfl+3KjibQmAcYwmyzKF9J4DyKP/Q==} cpu: [x64] os: [darwin] - '@cbor-extract/cbor-extract-linux-arm64@2.2.0': - resolution: {integrity: sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==} + '@cbor-extract/cbor-extract-linux-arm64@2.2.2': + resolution: {integrity: sha512-wfqgzqCAy/Vn8i6WVIh7qZd0DdBFaWBjPdB6ma+Wihcjv0gHqD/mw3ouVv7kbbUNrab6dKEx/w3xQZEdeXIlzg==} cpu: [arm64] os: [linux] - '@cbor-extract/cbor-extract-linux-arm@2.2.0': - resolution: {integrity: sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==} + '@cbor-extract/cbor-extract-linux-arm@2.2.2': + resolution: {integrity: sha512-tNg0za41TpQfkhWjptD+0gSD2fggMiDCSacuIeELyb2xZhr7PrhPe5h66Jc67B/5dmpIhI2QOUtv4SBsricyYQ==} cpu: [arm] os: [linux] - '@cbor-extract/cbor-extract-linux-x64@2.2.0': - resolution: {integrity: sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==} + '@cbor-extract/cbor-extract-linux-x64@2.2.2': + resolution: {integrity: sha512-rpiLnVEsqtPJ+mXTdx1rfz4RtUGYIUg2rUAZgd1KjiC1SehYUSkJN7Yh+aVfSjvCGtVP0/bfkQkXpPXKbmSUaA==} cpu: [x64] os: [linux] - '@cbor-extract/cbor-extract-win32-x64@2.2.0': - resolution: {integrity: sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==} + '@cbor-extract/cbor-extract-win32-x64@2.2.2': + resolution: {integrity: sha512-dI+9P7cfWxkTQ+oE+7Aa6onEn92PHgfWXZivjNheCRmTBDBf2fx6RyTi0cmgpYLnD1KLZK9ZYrMxaPZ4oiXhGA==} cpu: [x64] os: [win32] @@ -320,8 +319,8 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': @@ -332,16 +331,16 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.3': - resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.34.0': resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.2': - resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -520,200 +519,6 @@ packages: engines: {node: '>=10'} deprecated: This functionality has been moved to @npmcli/fs - '@opentelemetry/api-logs@0.207.0': - resolution: {integrity: sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/api-logs@0.211.0': - resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/context-async-hooks@2.5.0': - resolution: {integrity: sha512-uOXpVX0ZjO7heSVjhheW2XEPrhQAWr2BScDPoZ9UDycl5iuHG+Usyc3AIfG6kZeC1GyLpMInpQ6X5+9n69yOFw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/core@2.5.0': - resolution: {integrity: sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/instrumentation-amqplib@0.58.0': - resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-connect@0.54.0': - resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-dataloader@0.28.0': - resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-express@0.59.0': - resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-fs@0.30.0': - resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-generic-pool@0.54.0': - resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-graphql@0.58.0': - resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-hapi@0.57.0': - resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-http@0.211.0': - resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-ioredis@0.59.0': - resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-kafkajs@0.20.0': - resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-knex@0.55.0': - resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-koa@0.59.0': - resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - - '@opentelemetry/instrumentation-lru-memoizer@0.55.0': - resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongodb@0.64.0': - resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongoose@0.57.0': - resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql2@0.57.0': - resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql@0.57.0': - resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-pg@0.63.0': - resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-redis@0.59.0': - resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-tedious@0.30.0': - resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-undici@0.21.0': - resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.7.0 - - '@opentelemetry/instrumentation@0.207.0': - resolution: {integrity: sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.211.0': - resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/redis-common@0.38.2': - resolution: {integrity: sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==} - engines: {node: ^18.19.0 || >=20.6.0} - - '@opentelemetry/resources@2.5.0': - resolution: {integrity: sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - - '@opentelemetry/sdk-trace-base@2.5.0': - resolution: {integrity: sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - - '@opentelemetry/semantic-conventions@1.39.0': - resolution: {integrity: sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==} - engines: {node: '>=14'} - - '@opentelemetry/sql-common@0.41.2': - resolution: {integrity: sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@peculiar/asn1-schema@2.4.0': resolution: {integrity: sha512-umbembjIWOrPSOzEGG5vxFLkeM8kzIhLkgigtsOrfLKnuzxWxejAcUX+q/SoZCdemlODOcr5WiYa7+dIEzBXZQ==} @@ -733,11 +538,6 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@prisma/instrumentation@7.2.0': - resolution: {integrity: sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==} - peerDependencies: - '@opentelemetry/api': ^1.8 - '@protobuf-ts/runtime@2.11.1': resolution: {integrity: sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ==} @@ -753,35 +553,10 @@ packages: resolution: {integrity: sha512-/ZqxUvKeEztU9drOoPC/8opEPOk+jLlB2q4+xpx6HVLq6aFu3pMpalkTpAQz8XfRfpLp8O25bh6pGPcHDCYpqg==} engines: {node: '>=12.*'} - '@sentry/core@10.38.0': - resolution: {integrity: sha512-1pubWDZE5y5HZEPMAZERP4fVl2NH3Ihp1A+vMoVkb3Qc66Diqj1WierAnStlZP7tCx0TBa0dK85GTW/ZFYyB9g==} - engines: {node: '>=18'} - - '@sentry/node-core@10.38.0': - resolution: {integrity: sha512-ErXtpedrY1HghgwM6AliilZPcUCoNNP1NThdO4YpeMq04wMX9/GMmFCu46TnCcg6b7IFIOSr2S4yD086PxLlHQ==} - engines: {node: '>=18'} + '@spacebarchat/pion-webrtc@0.0.1': + resolution: {integrity: sha512-xhDa21hxe8OnCavTxnEDSnBJwFQsesx6NekiQeco4Rl3f2tG3IPx0DhmgXPnj/T1N70OEygSn96XpBaMg7lFgg==} peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 - '@opentelemetry/core': ^1.30.1 || ^2.1.0 - '@opentelemetry/instrumentation': '>=0.57.1 <1' - '@opentelemetry/resources': ^1.30.1 || ^2.1.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@opentelemetry/semantic-conventions': ^1.39.0 - - '@sentry/node@10.38.0': - resolution: {integrity: sha512-wriyDtWDAoatn8EhOj0U4PJR1WufiijTsCGALqakOHbFiadtBJANLe6aSkXoXT4tegw59cz1wY4NlzHjYksaPw==} - engines: {node: '>=18'} - - '@sentry/opentelemetry@10.38.0': - resolution: {integrity: sha512-YPVhWfYmC7nD3EJqEHGtjp4fp5LwtAbE5rt9egQ4hqJlYFvr8YEz9sdoqSZxO0cZzgs2v97HFl/nmWAXe52G2Q==} - engines: {node: '>=18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 - '@opentelemetry/core': ^1.30.1 || ^2.1.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@opentelemetry/semantic-conventions': ^1.39.0 + '@spacebarchat/spacebar-webrtc-types': ^1.0.1 '@spacebarchat/spacebar-webrtc-types@1.0.1': resolution: {integrity: sha512-WfBRUN2520w7o5vU9HNDug9alNvydQP7H/jwAy8LeHTHwlMMUw/60A54FQDIAtsahw787fR3QZ83UGjhKDzDTg==} @@ -917,15 +692,12 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/multer@2.0.0': - resolution: {integrity: sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==} + '@types/multer@2.1.0': + resolution: {integrity: sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==} '@types/murmurhash-js@1.0.6': resolution: {integrity: sha512-P2RRwRpevEKO0FrK5xNjxBywg0Br24tEv8K2+iWg56PtcCUNGAkaaOWKBQiUpofA8H/gmgdHXrcvSgp2uXCVAQ==} - '@types/mysql@2.15.27': - resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} - '@types/needle@3.3.0': resolution: {integrity: sha512-UFIuc1gdyzAqeVUYpSL+cliw2MmU/ZUhVZKE7Zo4wPbgc8hbljeKSnn6ls6iG8r5jpegPXLUIhJ+Wb2kLVs8cg==} @@ -935,21 +707,15 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@24.10.13': - resolution: {integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==} + '@types/node@24.12.0': + resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} - '@types/nodemailer@7.0.9': - resolution: {integrity: sha512-vI8oF1M+8JvQhsId0Pc38BdUP2evenIIys7c7p+9OZXSPOH5c1dyINP1jT8xQ2xPuBUXmIC87s+91IZMDjH8Ow==} + '@types/nodemailer@7.0.11': + resolution: {integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==} '@types/notp@2.0.5': resolution: {integrity: sha512-ZsZS0PYUa6ZE4K3yOGerBvaxCp4ePf6ZmkFbPeilcqz2Ui/lmXox7KlRt7XZkXzqUgXhFLkc09ixyVmFLCU3gQ==} - '@types/pg-pool@2.0.7': - resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} - - '@types/pg@8.15.6': - resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} - '@types/probe-image-size@7.2.5': resolution: {integrity: sha512-9Bg6d/GNnjmhMMxadDstwrSlquuuLf0jQuPszbU6n3QUfybif3V/ryD3J2i9iaiC5JB/FU/8E41n88SM/UB+Tg==} @@ -968,108 +734,105 @@ packages: '@types/sharp@0.31.1': resolution: {integrity: sha512-5nWwamN9ZFHXaYEincMSuza8nNfOof8nmO+mcI+Agx1uMUk4/pQnNIcix+9rLPXzKrm1pS34+6WRDbDV0Jn7ag==} - '@types/tedious@4.0.14': - resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.55.0': - resolution: {integrity: sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==} + '@typescript-eslint/eslint-plugin@8.57.0': + resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.55.0 - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/parser': ^8.57.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.55.0': - resolution: {integrity: sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==} + '@typescript-eslint/parser@8.57.0': + resolution: {integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.55.0': - resolution: {integrity: sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==} + '@typescript-eslint/project-service@8.57.0': + resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.55.0': - resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==} + '@typescript-eslint/scope-manager@8.57.0': + resolution: {integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.55.0': - resolution: {integrity: sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==} + '@typescript-eslint/tsconfig-utils@8.57.0': + resolution: {integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.55.0': - resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==} + '@typescript-eslint/type-utils@8.57.0': + resolution: {integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.55.0': - resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==} + '@typescript-eslint/types@8.57.0': + resolution: {integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.55.0': - resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==} + '@typescript-eslint/typescript-estree@8.57.0': + resolution: {integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.55.0': - resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==} + '@typescript-eslint/utils@8.57.0': + resolution: {integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.55.0': - resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==} + '@typescript-eslint/visitor-keys@8.57.0': + resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-taEYpsrCbdcyHkqNMBiVcqKR7ZHMC1jwTBM9kn3eUgOjXn68ASRrmyzYBdrujluBJMO7rl+Gm5QRT68onYt53A==} + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-UhRPJWUZMHO1Xuurjr28gR98+nwD+QBJiUWzTLLrvhkGEOA8IG9Q8hqzJ07AQb/V251F/MsEjOSEnHmgGNT6Dg==} cpu: [arm64] os: [darwin] - '@typescript/native-preview-darwin-x64@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-TSgIk2osa3UpivKybsyglBx7KBL+vTNayagmpzYvxBXbPvBnbgGOgzE/5iHkzFJYVUFxqmuj1gopmDT9X/obaQ==} + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-7a2P4KyJQF83Zj+3Vi/VCV9I/0lC+2QRgD4JEIh1H0FliRDZILUIbiAqWaksHHl4pBMtlRZr+1hjad5vPUsQmA==} cpu: [x64] os: [darwin] - '@typescript/native-preview-linux-arm64@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-aSdY/1Uh+4hOpQT1jHvM16cNqXv6lihe3oZmGTV6DmgkeH9soGXRumbu+oA73E3w0Hm6PjD/aIzbvK53yjvN1Q==} + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-DwKY8zVqsQx0+golSKopbzZVIVfnsUVVt7s6Wg5kiAYrFy32TNKqANfbiWLBe5PGSpYu9Mx1XWLpsvi6/58BWw==} cpu: [arm64] os: [linux] - '@typescript/native-preview-linux-arm@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-2matUA2ZU/1Zdv/pWLsdNwdzkOxBPeLa1581wgnaANrzZD3IJm4eCMfidRFTh9fVPN/eMsthYOeSnuVJa/mPmg==} + '@typescript/native-preview-linux-arm@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-n2MhBeDAmdbp6DOtz+I2JeGpNzepzvXxPEDpRE+syAT5mTXstwTk+9w4rF2SLt1YgLuPmon7iZhkUyPTd0dD7A==} cpu: [arm] os: [linux] - '@typescript/native-preview-linux-x64@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-7C5mhiOFzWB+hdoCuog9roQuNFFHALw1jz0zrA9ikH18DOgnnGJpGLuekQJdXG1yQSdrALZROXLidTmVxFYSgg==} + '@typescript/native-preview-linux-x64@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-5kRxtdfqF9X1q/vIg7myq7D0MmF3GywZXS3mmZq6TQEFiu5IClHPaQCuCQqYdtHmHtZllHZz0VaEtnvV9Vamrw==} cpu: [x64] os: [linux] - '@typescript/native-preview-win32-arm64@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-n8/tI1rOrqy+kFqrNc4xBYaVc1eGn5SYS9HHDZOPZ8E2b3Oq7RAPSZdNi+YYwMcOx3MFon0Iu6mZ1N6lqer9Dw==} + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-u0ixXTG/97k2eVRQfwafGckHuK60st5ADYn23KQvZJxeUZ47XWIjzCL1JLcoiRexEAwsEerireyy32l4LxA9BQ==} cpu: [arm64] os: [win32] - '@typescript/native-preview-win32-x64@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-wC/Aoxf/5/m/7alzb7RxLivGuYwZw3/Iq7RO73egG70LL2RLUuP306MDg1sj2TyeAe+S3zZX3rU1L6qMOW439A==} + '@typescript/native-preview-win32-x64@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-iSnVmAZgogCup/5SOF2h+Hwdywa4cGNIw9z8jpTVJof56w5GR9Hx3vN9UszqszjGxPNobYERcOk8QhGZO8uf5g==} cpu: [x64] os: [win32] - '@typescript/native-preview@7.0.0-dev.20260210.1': - resolution: {integrity: sha512-vy52DLNMYVTizp02/Uu8TrHQrt3BU0b7foE7qqxPAZF63zXpwvGg1g4EAgFtu7ZDJlYrAlUqSdZg6INb/3iY6w==} + '@typescript/native-preview@7.0.0-dev.20260315.1': + resolution: {integrity: sha512-t+st0mCz4HpvODTTlj2XxIQtiNT7L7lxP91790MOfA0xTRgwu7ERYV7WB1SbXRyrFDIwuO1bZqT0E0P4qcL4RQ==} hasBin: true '@yarnpkg/lockfile@1.1.0': @@ -1089,11 +852,6 @@ packages: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1128,11 +886,11 @@ packages: ajv: optional: true - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} amqplib@0.10.9: resolution: {integrity: sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==} @@ -1190,8 +948,8 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - asn1js@3.0.6: - resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + asn1js@3.0.7: + resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} engines: {node: '>=12.0.0'} asynckit@0.4.0: @@ -1205,8 +963,8 @@ packages: resolution: {integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==} engines: {node: '>=6.0.0'} - axios@1.13.5: - resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} badge-maker@5.0.2: resolution: {integrity: sha512-Xd3YUmKPEShQcn6PFB03Wxq0RNJRFwVVroyRz0qIjSXwniYUGsGWNHqtNsQYi/Sbs8Ni7qAEf7LKgDOtcAoCDg==} @@ -1216,6 +974,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} @@ -1258,6 +1020,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -1309,12 +1075,12 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - cbor-extract@2.2.0: - resolution: {integrity: sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==} + cbor-extract@2.2.2: + resolution: {integrity: sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng==} hasBin: true - cbor-x@1.6.0: - resolution: {integrity: sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==} + cbor-x@1.6.4: + resolution: {integrity: sha512-UGKHjp6RHC6QuZ2yy5LCKm7MojM4716DwoSaqwQpaH4DvZvbBTGcoDNTiG9Y2lByXZYFEs9WRkS5tLl96IrF1Q==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1345,9 +1111,6 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - cjs-module-lexer@2.2.0: - resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} - clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -1427,8 +1190,8 @@ packages: resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} @@ -1468,8 +1231,8 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.7.1: - resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -1510,8 +1273,8 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} - discord-protos@1.2.102: - resolution: {integrity: sha512-L5KRaTVdixpWXhpABY4McOgBidSID3tPwtVvi5/pQYhBUpasSYEJsuKjwRz3PoqWxJR4PGq8NxIoEUQ7qjbTRQ==} + discord-protos@1.2.121: + resolution: {integrity: sha512-SDQhV3wU/sH//5B1e1s039q6VexAOzN3DBhMkXmDYzTBlmKT8d2N6YCzfZRIkbUd3mLzyu18M+toqaFGtjKqTw==} dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -1530,8 +1293,8 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} - dotenv@17.2.4: - resolution: {integrity: sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==} + dotenv@17.3.1: + resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -1547,8 +1310,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - email-providers@2.21.0: - resolution: {integrity: sha512-c31t0e+U4D15pBwEapk+Ft04O9Zk/T/KrYBglRMen2GQd9W41n7Ev1ShxhLyUWQwtFRnRDcjg8o3JLFAhR+qlg==} + email-providers@2.22.0: + resolution: {integrity: sha512-ucFcMgALX9EJhffm8aOFoogQInbG3A8eK3XCJyx3jwGQtiK0ZkLw2CdXaJyf4xTJfCS3JdgJSJjwUlSyuqf0ZA==} engines: {node: '>=16'} emoji-regex@8.0.0: @@ -1586,6 +1349,9 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + equals-ignore-case@1.0.1: + resolution: {integrity: sha512-krgK/Px09jhcc7wK5/lxApRv7XmIT/fSgrMwdaW/V1FmPJEIJMNGEMhe0U9tJ/97rPe75MHKPRqi7/8Tqz6NMA==} + err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} @@ -1628,8 +1394,12 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.2: - resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1709,8 +1479,8 @@ packages: picomatch: optional: true - fido2-lib@3.5.6: - resolution: {integrity: sha512-uYJjMxO/odAelhdm2SMtiGJWZMRmbccev10c+Jw9FM2yYMS+3IOZfnGItrQiBiMoCt2t3lYqSF8DA85lBzVylg==} + fido2-lib@3.5.9: + resolution: {integrity: sha512-OfiX3VetTaoiuA9Bk8QnIdDVEOyZyR8m6JE++9zLQ3sbpsQN8thaJoLHcDi770cOKGtzvSH49mp2njtXUm6S7w==} engines: {node: '>=10'} file-entry-cache@8.0.0: @@ -1721,8 +1491,8 @@ packages: resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} engines: {node: '>=10'} - file-type@21.3.0: - resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} + file-type@21.3.2: + resolution: {integrity: sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==} engines: {node: '>=20'} file-uri-to-path@1.0.0: @@ -1771,9 +1541,6 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - forwarded-parse@2.1.2: - resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -1913,8 +1680,8 @@ packages: i18next-http-middleware@3.9.2: resolution: {integrity: sha512-vzd0Z89xR1yF2V2USVS2G6oOzNilTXsBkxQ16GFPGJO4whQI/LIreh4p4Qy7HXQlGrWVp83YaekuG36+t9anJA==} - i18next@25.8.7: - resolution: {integrity: sha512-ttxxc5+67S/0hhoeVdEgc1lRklZhdfcUSEPp1//uUG2NB88X3667gRsDar+ZWQFdysnOsnb32bcoMsa4mtzhkQ==} + i18next@25.8.18: + resolution: {integrity: sha512-lzY5X83BiL5AP77+9DydbrqkQHFN9hUzWGjqjLpPcp5ZOzuu1aSoKaU3xbBLSjWx9dAzW431y+d+aogxOZaKRA==} peerDependencies: typescript: ^5 peerDependenciesMeta: @@ -1956,9 +1723,6 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-in-the-middle@2.0.6: - resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==} - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -2040,8 +1804,8 @@ packages: resolution: {integrity: sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==} engines: {node: '>=18'} - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jose@6.2.1: + resolution: {integrity: sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==} jpeg-js@0.4.4: resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} @@ -2130,8 +1894,8 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - mailgun.js@12.7.0: - resolution: {integrity: sha512-TKuGxSGMdGKQDR+Ciocs0zzKNsy+ip+BwLEVatVLlJls30OigFcK61LiBBWoPrVg5D5nmKN/nDR9yKLJCERCsA==} + mailgun.js@12.7.1: + resolution: {integrity: sha512-VG2zRx4hKVoLGdMDpF5Bt+lkhS6g+eWb547FR4/iozoGEszcT+uf8/0EsPBwnpfI2gYlui3aaPnQCzcFDYvGXQ==} engines: {node: '>=18.0.0'} make-dir@3.1.0: @@ -2190,9 +1954,16 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -2239,10 +2010,6 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -2251,9 +2018,6 @@ packages: module-alias@2.3.4: resolution: {integrity: sha512-bOclZt8hkpuGgSSoG07PKmvzTizROilUTvLNyrMqvlC9snhs7y7GzjNWAVbISIOlhCP1T14rH1PDAV9iNyBq/w==} - module-details-from-path@1.0.4: - resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} - morgan@1.10.1: resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} engines: {node: '>= 0.8.0'} @@ -2268,8 +2032,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - multer@2.0.2: - resolution: {integrity: sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==} + multer@2.1.1: + resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==} engines: {node: '>= 10.16.0'} murmurhash-js@1.0.0: @@ -2307,8 +2071,8 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-addon-api@8.5.0: - resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} + node-addon-api@8.6.0: + resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} engines: {node: ^18 || ^20 || >= 21} node-fetch@2.7.0: @@ -2478,11 +2242,11 @@ packages: pg-cloudflare@1.3.0: resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} - pg-connection-string@2.11.0: - resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==} + pg-connection-string@2.12.0: + resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} - pg-cursor@2.17.0: - resolution: {integrity: sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==} + pg-cursor@2.19.0: + resolution: {integrity: sha512-J5cF1MUz7LRJ9emOqF/06QjabMHMZy587rSPF0UuA8rCwKeeYl2co8Pp+6k5UU9YrAYHMzWkLxilfZB0hqsWWw==} peerDependencies: pg: ^8 @@ -2490,19 +2254,16 @@ packages: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} - pg-pool@3.11.0: - resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==} + pg-pool@3.13.0: + resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==} peerDependencies: pg: '>=8.0' - pg-protocol@1.10.3: - resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + pg-protocol@1.13.0: + resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==} - pg-protocol@1.11.0: - resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} - - pg-query-stream@4.12.0: - resolution: {integrity: sha512-H97oiVPQ0+eRqIFOeYMUnjDcv9od7vHHMjiVDAhg2SEzAUr3M/dT83UEV1B+fm+tcVnymI8j2LSp57/+yjF6Fg==} + pg-query-stream@4.14.0: + resolution: {integrity: sha512-B1LLxgqngAATPciOPYYKyaQfsw5wyP6BZq6nHqQOC5QaaEBsfW/0OBwWUga+knCAqENMeoow9I8Zgi2m3P9rWw==} peerDependencies: pg: ^8 @@ -2510,8 +2271,8 @@ packages: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg@8.18.0: - resolution: {integrity: sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==} + pg@8.20.0: + resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==} engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -2572,6 +2333,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prelude-ls@1.2.1: @@ -2638,13 +2400,16 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - qs@6.14.1: - resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -2680,10 +2445,6 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - require-in-the-middle@8.0.1: - resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} - engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} - requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -2720,6 +2481,13 @@ packages: sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + sdp-transform@2.15.0: + resolution: {integrity: sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==} + hasBin: true + + semantic-sdp@3.31.1: + resolution: {integrity: sha512-esBGP11uWeHyvGwqDT1sf1p45H+kb7m5D49TIBHwYXXepzDFD24ar91eIwBQ8kwWWO8MNZfizsZ7CvBgYV9GuA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -2908,11 +2676,11 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tldts-core@7.0.23: - resolution: {integrity: sha512-0g9vrtDQLrNIiCj22HSe9d4mLVG3g5ph5DZ8zCKBr4OtrspmNB6ss7hVyzArAeE88ceZocIEGkyW1Ime7fxPtQ==} + tldts-core@7.0.25: + resolution: {integrity: sha512-ZjCZK0rppSBu7rjHYDYsEaMOIbbT+nWF57hKkv4IUmZWBNrBWBOjIElc0mKRgLM8bm7x/BBlof6t2gi/Oq/Asw==} - tldts@7.0.23: - resolution: {integrity: sha512-ASdhgQIBSay0R/eXggAkQ53G4nTJqTXqC2kbaBbdDwM7SkjyZyO0OaaN1/FH7U/yCeqOHDwFO5j8+Os/IS1dXw==} + tldts@7.0.25: + resolution: {integrity: sha512-keinCnPbwXEUG3ilrWQZU+CqcTTzHq9m2HhoUP2l7Xmi8l1LuijAXLpAJ5zRW+ifKTNscs4NdCkfkDCBYm352w==} hasBin: true tmp@0.2.5: @@ -3066,8 +2834,8 @@ packages: undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - undici@7.21.0: - resolution: {integrity: sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==} + undici@7.24.3: + resolution: {integrity: sha512-eJdUmK/Wrx2d+mnWWmwwLRyA7OQCkLap60sk3dOK4ViZR7DKwwptwuIvFBg2HaiP9ESaEdhtpSymQPvytpmkCA==} engines: {node: '>=20.18.1'} unique-filename@1.1.1: @@ -3219,56 +2987,46 @@ packages: snapshots: - '@apm-js-collab/code-transformer@0.8.2': {} - - '@apm-js-collab/tracing-hooks@0.3.1': - dependencies: - '@apm-js-collab/code-transformer': 0.8.2 - debug: 4.4.3 - module-details-from-path: 1.0.4 - transitivePeerDependencies: - - supports-color - '@babel/runtime@7.28.6': {} - '@borewit/text-codec@0.2.1': {} + '@borewit/text-codec@0.2.2': {} - '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': + '@cbor-extract/cbor-extract-darwin-arm64@2.2.2': optional: true - '@cbor-extract/cbor-extract-darwin-x64@2.2.0': + '@cbor-extract/cbor-extract-darwin-x64@2.2.2': optional: true - '@cbor-extract/cbor-extract-linux-arm64@2.2.0': + '@cbor-extract/cbor-extract-linux-arm64@2.2.2': optional: true - '@cbor-extract/cbor-extract-linux-arm@2.2.0': + '@cbor-extract/cbor-extract-linux-arm@2.2.2': optional: true - '@cbor-extract/cbor-extract-linux-x64@2.2.0': + '@cbor-extract/cbor-extract-linux-x64@2.2.2': optional: true - '@cbor-extract/cbor-extract-win32-x64@2.2.0': + '@cbor-extract/cbor-extract-win32-x64@2.2.2': optional: true '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': dependencies: - eslint: 9.39.2 + eslint: 9.39.4 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.1': + '@eslint/config-array@0.21.2': dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.1 - minimatch: 3.1.2 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -3280,23 +3038,23 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.3': + '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.12.6 + ajv: 6.14.0 debug: 4.4.1 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 3.1.2 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color '@eslint/js@9.34.0': {} - '@eslint/js@9.39.2': {} + '@eslint/js@9.39.4': {} '@eslint/object-schema@2.1.7': {} @@ -3588,259 +3346,9 @@ snapshots: rimraf: 3.0.2 optional: true - '@opentelemetry/api-logs@0.207.0': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/api-logs@0.211.0': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/api@1.9.0': {} - - '@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@types/connect': 3.4.38 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - forwarded-parse: 2.1.2 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@types/mysql': 2.15.27 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) - '@types/pg': 8.15.6 - '@types/pg-pool': 2.0.7 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@types/tedious': 4.0.14 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation@0.207.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.207.0 - import-in-the-middle: 2.0.6 - require-in-the-middle: 8.0.1 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.211.0 - import-in-the-middle: 2.0.6 - require-in-the-middle: 8.0.1 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/redis-common@0.38.2': {} - - '@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - - '@opentelemetry/semantic-conventions@1.39.0': {} - - '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@peculiar/asn1-schema@2.4.0': dependencies: - asn1js: 3.0.6 + asn1js: 3.0.7 pvtsutils: 1.3.6 tslib: 2.8.1 @@ -3861,19 +3369,12 @@ snapshots: '@pkgr/core@0.2.9': {} - '@prisma/instrumentation@7.2.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.207.0(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - '@protobuf-ts/runtime@2.11.1': {} '@sendgrid/client@8.1.6': dependencies: '@sendgrid/helpers': 8.0.0 - axios: 1.13.5 + axios: 1.13.6 transitivePeerDependencies: - debug optional: true @@ -3891,72 +3392,10 @@ snapshots: - debug optional: true - '@sentry/core@10.38.0': {} - - '@sentry/node-core@10.38.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)': - dependencies: - '@apm-js-collab/tracing-hooks': 0.3.1 - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@sentry/core': 10.38.0 - '@sentry/opentelemetry': 10.38.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0) - import-in-the-middle: 2.0.6 - transitivePeerDependencies: - - supports-color - - '@sentry/node@10.38.0': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.0) - '@sentry/core': 10.38.0 - '@sentry/node-core': 10.38.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0) - '@sentry/opentelemetry': 10.38.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0) - import-in-the-middle: 2.0.6 - minimatch: 9.0.5 - transitivePeerDependencies: - - supports-color - - '@sentry/opentelemetry@10.38.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.39.0)': + '@spacebarchat/pion-webrtc@0.0.1(@spacebarchat/spacebar-webrtc-types@1.0.1)': dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.5.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.39.0 - '@sentry/core': 10.38.0 + '@spacebarchat/spacebar-webrtc-types': 1.0.1 + semantic-sdp: 3.31.1 '@spacebarchat/spacebar-webrtc-types@1.0.1': {} @@ -4019,20 +3458,20 @@ snapshots: '@types/amqplib@0.10.8': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/bcrypt@6.0.0': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/connect@3.4.38': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/cookie-parser@1.4.10(@types/express@5.0.6)': dependencies: @@ -4042,7 +3481,7 @@ snapshots: '@types/express-serve-static-core@5.1.1': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -4057,7 +3496,7 @@ snapshots: '@types/i18next-node-fs-backend@2.1.5(typescript@5.9.3)': dependencies: - i18next: 25.8.7(typescript@5.9.3) + i18next: 25.8.18(typescript@5.9.3) transitivePeerDependencies: - typescript @@ -4068,7 +3507,7 @@ snapshots: '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/mime@1.3.5': {} @@ -4076,23 +3515,19 @@ snapshots: '@types/morgan@1.9.10': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/ms@2.1.0': {} - '@types/multer@2.0.0': + '@types/multer@2.1.0': dependencies: '@types/express': 5.0.6 '@types/murmurhash-js@1.0.6': {} - '@types/mysql@2.15.27': - dependencies: - '@types/node': 24.10.13 - '@types/needle@3.3.0': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/node@16.9.1': optional: true @@ -4101,32 +3536,22 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@24.10.13': + '@types/node@24.12.0': dependencies: undici-types: 7.16.0 - '@types/nodemailer@7.0.9': + '@types/nodemailer@7.0.11': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/notp@2.0.5': dependencies: - '@types/node': 24.10.13 - - '@types/pg-pool@2.0.7': - dependencies: - '@types/pg': 8.15.6 - - '@types/pg@8.15.6': - dependencies: - '@types/node': 24.10.13 - pg-protocol: 1.10.3 - pg-types: 2.2.0 + '@types/node': 24.12.0 '@types/probe-image-size@7.2.5': dependencies: '@types/needle': 3.3.0 - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/qs@6.14.0': {} @@ -4135,34 +3560,30 @@ snapshots: '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/sharp@0.31.1': dependencies: - '@types/node': 24.10.13 - - '@types/tedious@4.0.14': - dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 '@types/ws@8.18.1': dependencies: - '@types/node': 24.10.13 + '@types/node': 24.12.0 - '@typescript-eslint/eslint-plugin@8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.55.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.55.0 - '@typescript-eslint/type-utils': 8.55.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.55.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.55.0 - eslint: 9.39.2 + '@typescript-eslint/parser': 8.57.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/type-utils': 8.57.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.0 + eslint: 9.39.4 ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -4170,58 +3591,58 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.55.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.57.0(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.55.0 - '@typescript-eslint/types': 8.55.0 - '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.55.0 + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.0 debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.55.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.57.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) - '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) + '@typescript-eslint/types': 8.57.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.55.0': + '@typescript-eslint/scope-manager@8.57.0': dependencies: - '@typescript-eslint/types': 8.55.0 - '@typescript-eslint/visitor-keys': 8.55.0 + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/visitor-keys': 8.57.0 - '@typescript-eslint/tsconfig-utils@8.55.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.55.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.57.0(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.55.0 - '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.55.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@9.39.4)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.2 + eslint: 9.39.4 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.55.0': {} + '@typescript-eslint/types@8.57.0': {} - '@typescript-eslint/typescript-estree@8.55.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.55.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) - '@typescript-eslint/types': 8.55.0 - '@typescript-eslint/visitor-keys': 8.55.0 + '@typescript-eslint/project-service': 8.57.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/visitor-keys': 8.57.0 debug: 4.4.3 - minimatch: 9.0.5 + minimatch: 10.2.4 semver: 7.7.4 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -4229,52 +3650,52 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.55.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.57.0(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) - '@typescript-eslint/scope-manager': 8.55.0 - '@typescript-eslint/types': 8.55.0 - '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) - eslint: 9.39.2 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.55.0': + '@typescript-eslint/visitor-keys@8.57.0': dependencies: - '@typescript-eslint/types': 8.55.0 - eslint-visitor-keys: 4.2.1 + '@typescript-eslint/types': 8.57.0 + eslint-visitor-keys: 5.0.1 - '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260210.1': + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview-darwin-x64@7.0.0-dev.20260210.1': + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview-linux-arm64@7.0.0-dev.20260210.1': + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview-linux-arm@7.0.0-dev.20260210.1': + '@typescript/native-preview-linux-arm@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview-linux-x64@7.0.0-dev.20260210.1': + '@typescript/native-preview-linux-x64@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview-win32-arm64@7.0.0-dev.20260210.1': + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview-win32-x64@7.0.0-dev.20260210.1': + '@typescript/native-preview-win32-x64@7.0.0-dev.20260315.1': optional: true - '@typescript/native-preview@7.0.0-dev.20260210.1': + '@typescript/native-preview@7.0.0-dev.20260315.1': optionalDependencies: - '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260210.1 - '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260210.1 - '@typescript/native-preview-linux-arm': 7.0.0-dev.20260210.1 - '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260210.1 - '@typescript/native-preview-linux-x64': 7.0.0-dev.20260210.1 - '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260210.1 - '@typescript/native-preview-win32-x64': 7.0.0-dev.20260210.1 + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260315.1 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260315.1 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20260315.1 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260315.1 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20260315.1 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260315.1 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20260315.1 '@yarnpkg/lockfile@1.1.0': {} @@ -4300,10 +3721,6 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-import-attributes@1.9.5(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -4332,18 +3749,18 @@ snapshots: indent-string: 4.0.0 optional: true - ajv-formats@3.0.1(ajv@8.17.1): + ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv@6.12.6: + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.1.0 @@ -4397,7 +3814,7 @@ snapshots: argparse@2.0.1: {} - asn1js@3.0.6: + asn1js@3.0.7: dependencies: pvtsutils: 1.3.6 pvutils: 1.1.3 @@ -4412,7 +3829,7 @@ snapshots: await-to-js@3.0.0: optional: true - axios@1.13.5: + axios@1.13.6: dependencies: follow-redirects: 1.15.11 form-data: 4.0.5 @@ -4428,6 +3845,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base-64@1.0.0: optional: true @@ -4439,7 +3858,7 @@ snapshots: bcrypt@6.0.0: dependencies: - node-addon-api: 8.5.0 + node-addon-api: 8.6.0 node-gyp-build: 4.8.4 bignumber.js@9.3.1: {} @@ -4469,7 +3888,7 @@ snapshots: http-errors: 2.0.0 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.1 + qs: 6.15.0 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -4486,6 +3905,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -4558,21 +3981,21 @@ snapshots: callsites@3.1.0: {} - cbor-extract@2.2.0: + cbor-extract@2.2.2: dependencies: node-gyp-build-optional-packages: 5.1.1 optionalDependencies: - '@cbor-extract/cbor-extract-darwin-arm64': 2.2.0 - '@cbor-extract/cbor-extract-darwin-x64': 2.2.0 - '@cbor-extract/cbor-extract-linux-arm': 2.2.0 - '@cbor-extract/cbor-extract-linux-arm64': 2.2.0 - '@cbor-extract/cbor-extract-linux-x64': 2.2.0 - '@cbor-extract/cbor-extract-win32-x64': 2.2.0 + '@cbor-extract/cbor-extract-darwin-arm64': 2.2.2 + '@cbor-extract/cbor-extract-darwin-x64': 2.2.2 + '@cbor-extract/cbor-extract-linux-arm': 2.2.2 + '@cbor-extract/cbor-extract-linux-arm64': 2.2.2 + '@cbor-extract/cbor-extract-linux-x64': 2.2.2 + '@cbor-extract/cbor-extract-win32-x64': 2.2.2 optional: true - cbor-x@1.6.0: + cbor-x@1.6.4: optionalDependencies: - cbor-extract: 2.2.0 + cbor-extract: 2.2.2 chalk@4.1.2: dependencies: @@ -4605,7 +4028,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.21.0 + undici: 7.24.3 whatwg-mimetype: 4.0.0 chownr@1.1.4: @@ -4616,8 +4039,6 @@ snapshots: ci-info@3.9.0: {} - cjs-module-lexer@2.2.0: {} - clean-stack@2.2.0: optional: true @@ -4695,7 +4116,7 @@ snapshots: css-what@6.2.2: {} - dayjs@1.11.19: {} + dayjs@1.11.20: {} debug@2.6.9: dependencies: @@ -4718,7 +4139,7 @@ snapshots: mimic-response: 3.1.0 optional: true - dedent@1.7.1: {} + dedent@1.7.2: {} deep-extend@0.6.0: optional: true @@ -4746,7 +4167,7 @@ snapshots: diff@4.0.2: {} - discord-protos@1.2.102: + discord-protos@1.2.121(patch_hash=euc7fgm2mau5n53lcvhiiwuwye): dependencies: '@protobuf-ts/runtime': 2.11.1 @@ -4770,7 +4191,7 @@ snapshots: dotenv@16.6.1: {} - dotenv@17.2.4: {} + dotenv@17.3.1: {} dunder-proto@1.0.1: dependencies: @@ -4786,7 +4207,7 @@ snapshots: ee-first@1.1.1: {} - email-providers@2.21.0: {} + email-providers@2.22.0: {} emoji-regex@8.0.0: {} @@ -4818,6 +4239,8 @@ snapshots: env-paths@2.2.1: optional: true + equals-ignore-case@1.0.1: {} + err-code@2.0.3: optional: true @@ -4851,21 +4274,23 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.2: + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.1 + '@eslint/config-array': 0.21.2 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.2 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.12.6 + ajv: 6.14.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.1 @@ -4884,7 +4309,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 transitivePeerDependencies: @@ -4971,16 +4396,16 @@ snapshots: optionalDependencies: picomatch: 4.0.3 - fido2-lib@3.5.6: + fido2-lib@3.5.9: dependencies: '@hexagon/base64': 2.0.4 '@peculiar/webcrypto': 1.5.0 - asn1js: 3.0.6 - cbor-x: 1.6.0 - jose: 6.1.3 + asn1js: 3.0.7 + cbor-x: 1.6.4 + jose: 6.2.1 pkijs: 3.3.3 punycode.js: 2.3.1 - tldts: 7.0.23 + tldts: 7.0.25 file-entry-cache@8.0.0: dependencies: @@ -4993,7 +4418,7 @@ snapshots: token-types: 4.2.1 optional: true - file-type@21.3.0: + file-type@21.3.2: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.4 @@ -5056,8 +4481,6 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - forwarded-parse@2.1.2: {} - forwarded@0.2.0: {} fresh@2.0.0: {} @@ -5237,7 +4660,7 @@ snapshots: i18next-http-middleware@3.9.2: {} - i18next@25.8.7(typescript@5.9.3): + i18next@25.8.18(typescript@5.9.3): dependencies: '@babel/runtime': 7.28.6 optionalDependencies: @@ -5273,13 +4696,6 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-in-the-middle@2.0.6: - dependencies: - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) - cjs-module-lexer: 2.2.0 - module-details-from-path: 1.0.4 - imurmurhash@0.1.4: {} indent-string@4.0.0: @@ -5371,7 +4787,7 @@ snapshots: '@jimp/utils': 1.6.0 optional: true - jose@6.1.3: {} + jose@6.2.1: {} jpeg-js@0.4.4: optional: true @@ -5472,9 +4888,9 @@ snapshots: yallist: 4.0.0 optional: true - mailgun.js@12.7.0: + mailgun.js@12.7.1: dependencies: - axios: 1.13.5 + axios: 1.13.6 base-64: 1.0.0 url-join: 4.0.1 transitivePeerDependencies: @@ -5542,10 +4958,18 @@ snapshots: mimic-response@3.1.0: optional: true + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.12 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 @@ -5600,17 +5024,11 @@ snapshots: mkdirp-classic@0.5.3: optional: true - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mkdirp@1.0.4: optional: true module-alias@2.3.4: {} - module-details-from-path@1.0.4: {} - morgan@1.10.1: dependencies: basic-auth: 2.0.1 @@ -5627,15 +5045,12 @@ snapshots: ms@2.1.3: {} - multer@2.0.2: + multer@2.1.1: dependencies: append-field: 1.0.0 busboy: 1.6.0 concat-stream: 2.0.0 - mkdirp: 0.5.6 - object-assign: 4.1.1 type-is: 1.6.18 - xtend: 4.0.2 murmurhash-js@1.0.0: {} @@ -5675,7 +5090,7 @@ snapshots: node-addon-api@7.1.1: optional: true - node-addon-api@8.5.0: {} + node-addon-api@8.6.0: {} node-fetch@2.7.0(encoding@0.1.13): dependencies: @@ -5710,7 +5125,7 @@ snapshots: node-mailjet@6.0.11: dependencies: - axios: 1.13.5 + axios: 1.13.6 json-bigint: 1.0.0 url-join: 4.0.1 transitivePeerDependencies: @@ -5747,7 +5162,8 @@ snapshots: dependencies: boolbase: 1.0.0 - object-assign@4.1.1: {} + object-assign@4.1.1: + optional: true object-inspect@1.13.4: {} @@ -5871,26 +5287,24 @@ snapshots: pg-cloudflare@1.3.0: optional: true - pg-connection-string@2.11.0: {} + pg-connection-string@2.12.0: {} - pg-cursor@2.17.0(pg@8.18.0): + pg-cursor@2.19.0(pg@8.20.0): dependencies: - pg: 8.18.0 + pg: 8.20.0 pg-int8@1.0.1: {} - pg-pool@3.11.0(pg@8.18.0): + pg-pool@3.13.0(pg@8.20.0): dependencies: - pg: 8.18.0 - - pg-protocol@1.10.3: {} + pg: 8.20.0 - pg-protocol@1.11.0: {} + pg-protocol@1.13.0: {} - pg-query-stream@4.12.0(pg@8.18.0): + pg-query-stream@4.14.0(pg@8.20.0): dependencies: - pg: 8.18.0 - pg-cursor: 2.17.0(pg@8.18.0) + pg: 8.20.0 + pg-cursor: 2.19.0(pg@8.20.0) pg-types@2.2.0: dependencies: @@ -5900,11 +5314,11 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg@8.18.0: + pg@8.20.0: dependencies: - pg-connection-string: 2.11.0 - pg-pool: 3.11.0(pg@8.18.0) - pg-protocol: 1.11.0 + pg-connection-string: 2.12.0 + pg-pool: 3.13.0(pg@8.20.0) + pg-protocol: 1.13.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: @@ -5928,7 +5342,7 @@ snapshots: pkijs@3.3.3: dependencies: '@noble/hashes': 1.4.0 - asn1js: 3.0.6 + asn1js: 3.0.7 bytestreamjs: 2.0.1 pvtsutils: 1.3.6 pvutils: 1.1.3 @@ -6031,12 +5445,16 @@ snapshots: dependencies: side-channel: 1.1.0 - qs@6.14.1: + qs@6.15.0: dependencies: side-channel: 1.1.0 querystringify@2.2.0: {} + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + range-parser@1.2.1: {} raw-body@3.0.2: @@ -6080,13 +5498,6 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@8.0.1: - dependencies: - debug: 4.4.3 - module-details-from-path: 1.0.4 - transitivePeerDependencies: - - supports-color - requires-port@1.0.0: {} resolve-from@4.0.0: {} @@ -6119,6 +5530,14 @@ snapshots: sax@1.4.1: {} + sdp-transform@2.15.0: {} + + semantic-sdp@3.31.1: + dependencies: + equals-ignore-case: 1.0.1 + randombytes: 2.1.0 + sdp-transform: 2.15.0 + semver@6.3.1: optional: true @@ -6359,11 +5778,11 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tldts-core@7.0.23: {} + tldts-core@7.0.25: {} - tldts@7.0.23: + tldts@7.0.25: dependencies: - tldts-core: 7.0.23 + tldts-core: 7.0.25 tmp@0.2.5: {} @@ -6387,7 +5806,7 @@ snapshots: token-types@6.1.2: dependencies: - '@borewit/text-codec': 0.2.1 + '@borewit/text-codec': 0.2.2 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -6416,14 +5835,14 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@24.10.13)(typescript@5.9.3): + ts-node@10.9.2(@types/node@24.12.0)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.10.13 + '@types/node': 24.12.0 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -6464,15 +5883,15 @@ snapshots: typedarray@0.0.6: {} - typeorm@0.3.28(pg-query-stream@4.12.0(pg@8.18.0))(pg@8.18.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@24.10.13)(typescript@5.9.3)): + typeorm@0.3.28(pg-query-stream@4.14.0(pg@8.20.0))(pg@8.20.0)(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@24.12.0)(typescript@5.9.3)): dependencies: '@sqltools/formatter': 1.2.5 ansis: 4.2.0 app-root-path: 3.1.0 buffer: 6.0.3 - dayjs: 1.11.19 + dayjs: 1.11.20 debug: 4.4.3 - dedent: 1.7.1 + dedent: 1.7.2 dotenv: 16.6.1 glob: 10.5.0 reflect-metadata: 0.2.2 @@ -6482,10 +5901,10 @@ snapshots: uuid: 11.1.0 yargs: 17.7.2 optionalDependencies: - pg: 8.18.0 - pg-query-stream: 4.12.0(pg@8.18.0) + pg: 8.20.0 + pg-query-stream: 4.14.0(pg@8.20.0) sqlite3: 5.1.7 - ts-node: 10.9.2(@types/node@24.10.13)(typescript@5.9.3) + ts-node: 10.9.2(@types/node@24.12.0)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -6514,7 +5933,7 @@ snapshots: undici-types@7.16.0: {} - undici@7.21.0: {} + undici@7.24.3: {} unique-filename@1.1.1: dependencies: @@ -6559,7 +5978,7 @@ snapshots: dependencies: '@peculiar/asn1-schema': 2.4.0 '@peculiar/json-schema': 1.1.12 - asn1js: 3.0.6 + asn1js: 3.0.7 pvtsutils: 1.3.6 tslib: 2.8.1 diff --git a/scripts/migrations/AddConnectionPrivacyFields.ts b/scripts/migrations/AddConnectionPrivacyFields.ts new file mode 100644 index 000000000..9153e00a2 --- /dev/null +++ b/scripts/migrations/AddConnectionPrivacyFields.ts @@ -0,0 +1,77 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddConnectionPrivacyFields1755804210000 + implements MigrationInterface +{ + name = "AddConnectionPrivacyFields1755804210000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "ALTER TABLE connected_accounts ADD COLUMN consent_given_at timestamp DEFAULT NULL;", + ); + await queryRunner.query( + "ALTER TABLE connected_accounts ADD COLUMN data_sharing_level integer DEFAULT 1;", + ); + await queryRunner.query( + "ALTER TABLE connected_accounts ADD COLUMN last_activity_sync timestamp DEFAULT NULL;", + ); + await queryRunner.query( + "ALTER TABLE connected_accounts ADD COLUMN privacy_override boolean DEFAULT false;", + ); + + await queryRunner.query( + "ALTER TABLE user_settings ADD COLUMN connections_default_visibility integer DEFAULT 1;", + ); + await queryRunner.query( + "ALTER TABLE user_settings ADD COLUMN connections_activity_sharing boolean DEFAULT true;", + ); + await queryRunner.query( + "ALTER TABLE user_settings ADD COLUMN connections_metadata_sharing boolean DEFAULT true;", + ); + await queryRunner.query( + "ALTER TABLE user_settings ADD COLUMN connections_require_approval boolean DEFAULT false;", + ); + + await queryRunner.query( + "CREATE INDEX idx_connected_accounts_visibility ON connected_accounts(visibility);", + ); + await queryRunner.query( + "CREATE INDEX idx_connected_accounts_data_sharing ON connected_accounts(data_sharing_level);", + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + "DROP INDEX idx_connected_accounts_visibility;", + ); + await queryRunner.query( + "DROP INDEX idx_connected_accounts_data_sharing;", + ); + + await queryRunner.query( + "ALTER TABLE connected_accounts DROP COLUMN consent_given_at;", + ); + await queryRunner.query( + "ALTER TABLE connected_accounts DROP COLUMN data_sharing_level;", + ); + await queryRunner.query( + "ALTER TABLE connected_accounts DROP COLUMN last_activity_sync;", + ); + await queryRunner.query( + "ALTER TABLE connected_accounts DROP COLUMN privacy_override;", + ); + + await queryRunner.query( + "ALTER TABLE user_settings DROP COLUMN connections_default_visibility;", + ); + await queryRunner.query( + "ALTER TABLE user_settings DROP COLUMN connections_activity_sharing;", + ); + await queryRunner.query( + "ALTER TABLE user_settings DROP COLUMN connections_metadata_sharing;", + ); + await queryRunner.query( + "ALTER TABLE user_settings DROP COLUMN connections_require_approval;", + ); + } +} diff --git a/scripts/migrations/DropGuildRegion.ts b/scripts/migrations/DropGuildRegion.ts new file mode 100644 index 000000000..9e830adbc --- /dev/null +++ b/scripts/migrations/DropGuildRegion.ts @@ -0,0 +1,37 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DropGuildRegion1699999999999 implements MigrationInterface { + name = "DropGuildRegion1699999999999"; + + public async up(queryRunner: QueryRunner): Promise { + const driver = queryRunner.connection.options.type; + if (driver === "postgres") { + await queryRunner.query( + "DO $$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'guilds' AND column_name = 'region') THEN ALTER TABLE \"guilds\" DROP COLUMN \"region\"; END IF; END $$;", + ); + } else { + await queryRunner + .query("ALTER TABLE guilds DROP COLUMN IF EXISTS region") + .catch(async () => { + try { + await queryRunner.query( + "ALTER TABLE guilds DROP COLUMN region", + ); + } catch {} + }); + } + } + + public async down(queryRunner: QueryRunner): Promise { + const driver = queryRunner.connection.options.type; + if (driver === "postgres") { + await queryRunner.query( + 'ALTER TABLE "guilds" ADD COLUMN "region" character varying', + ); + } else { + await queryRunner.query( + "ALTER TABLE guilds ADD COLUMN region varchar(255) NULL", + ); + } + } +} diff --git a/scripts/openapi.js b/scripts/openapi.js index 86d502e24..564450eea 100644 --- a/scripts/openapi.js +++ b/scripts/openapi.js @@ -256,4 +256,4 @@ async function main() { ); } -main(); +main().then(() => {}); diff --git a/scripts/schema.js b/scripts/schema.js index 67c5563c3..171abb5a8 100644 --- a/scripts/schema.js +++ b/scripts/schema.js @@ -210,11 +210,11 @@ async function main() { for (const defKey in schema.definitions) { if (definitions[defKey] && deepEqual(definitions[defKey], schema.definitions[defKey])) { // console.log("Definition", defKey, "from schema", schemaName, "is identical to existing definition, skipping."); - schema.definitions = Object.fromEntries(Object.entries(schema.definitions).filter(([k, v]) => k !== defKey)); + schema.definitions = Object.fromEntries(Object.entries(schema.definitions).filter(([k, _]) => k !== defKey)); process.stdout.write(greenBright("T")); } else if (!nestedDefinitions[defKey]) { nestedDefinitions[defKey] = schema.definitions[defKey]; - schema.definitions = Object.fromEntries(Object.entries(schema.definitions).filter(([k, v]) => k !== defKey)); + schema.definitions = Object.fromEntries(Object.entries(schema.definitions).filter(([k, _]) => k !== defKey)); // console.log("Tracking sub-definition", defKey, "from schema", schemaName); process.stdout.write(green("N")); } else if (!deepEqual(nestedDefinitions[defKey], schema.definitions[defKey])) { @@ -222,7 +222,7 @@ async function main() { console.log(columnizedObjectDiff(nestedDefinitions[defKey], schema.definitions[defKey], true)); } else { // console.log("Definition", defKey, "from schema", schemaName, "is identical to existing definition, skipping."); - schema.definitions = Object.fromEntries(Object.entries(schema.definitions).filter(([k, v]) => k !== defKey)); + schema.definitions = Object.fromEntries(Object.entries(schema.definitions).filter(([k, _]) => k !== defKey)); process.stdout.write(greenBright("M")); } } @@ -380,4 +380,4 @@ function columnizedObjectDiff(a, b, trackEqual = false) { return diffs; } -main(); +main().then(() => {}); diff --git a/scripts/stress/login.js b/scripts/stress/login.js index 830d6a48c..9009e56db 100644 --- a/scripts/stress/login.js +++ b/scripts/stress/login.js @@ -13,4 +13,4 @@ async function main() { console.log((await ret.json()).token); } -main(); +main().then(() => {}); diff --git a/scripts/stress/users.js b/scripts/stress/users.js index 2fc41eaef..8135368d4 100644 --- a/scripts/stress/users.js +++ b/scripts/stress/users.js @@ -34,9 +34,9 @@ async function main() { captcha_key: null, }), headers: { "content-type": "application/json" }, - }); + }).then(() => {}); console.log(i); } } -main(); +main().then(() => {}); diff --git a/scripts/syncronise.js b/scripts/syncronise.js index 58c18029f..d0fc38f03 100644 --- a/scripts/syncronise.js +++ b/scripts/syncronise.js @@ -34,5 +34,5 @@ const { initDatabase } = require(".."); console.log("synchronising"); await db.synchronize(); console.log("done"); - db.destroy(); + await db.destroy(); })(); diff --git a/scripts/util/getRouteDescriptions.js b/scripts/util/getRouteDescriptions.js index 5896f049f..69be334aa 100644 --- a/scripts/util/getRouteDescriptions.js +++ b/scripts/util/getRouteDescriptions.js @@ -2,7 +2,7 @@ const express = require("express"); const path = require("path"); const { traverseDirectory } = require("lambert-server"); const RouteUtility = require("../../dist/api/util/handlers/route.js"); -const { bgRedBright, greenBright, yellowBright, blueBright, redBright, underline, bold } = require("picocolors"); +const { greenBright, yellowBright, blueBright, redBright, underline, bgYellow, black } = require("picocolors"); const methods = ["get", "post", "put", "delete", "patch"]; const routes = new Map(); @@ -46,7 +46,7 @@ function proxy(file, apiMethod, apiPathPrefix, apiPath, ...args) { const opts = args.find((x) => x?.prototype?.OPTS_MARKER == true); if (!opts) return console.error( - ` \x1b[5m${bgRedBright("ERROR")}\x1b[25m ${file.replace(path.resolve(__dirname, "..", "..", "dist"), "/src/")} has route without route() description middleware: ${colorizeMethod(apiMethod)} ${formatPath(apiPath)}`, + ` \x1b[5m${bgYellow(black("WARN"))}\x1b[25m ${file.replace(path.resolve(__dirname, "..", "..", "dist"), "/src")} has route without route() description middleware: ${colorizeMethod(apiMethod)} ${formatPath(apiPath)}`, ); console.log(`${colorizeMethod(apiMethod).padStart("DELETE".length + 10)} ${formatPath(apiPathPrefix + apiPath)}`); diff --git a/src/api/Server.ts b/src/api/Server.ts index a6bb5178d..f6c383986 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { Config, ConnectionConfig, ConnectionLoader, Email, JSONReplacer, WebAuthn, initDatabase, initEvent, registerRoutes, getDatabase } from "@spacebar/util"; +import { Config, ConnectionConfig, ConnectionLoader, Email, JSONReplacer, WebAuthn, initDatabase, initEvent, registerRoutes, getDatabase, getRevInfoOrFail } from "@spacebar/util"; import { Authentication, CORS, ImageProxy, BodyParser, ErrorHandler, initRateLimits, initTranslation } from "./middlewares"; import { Request, Response, Router } from "express"; import { Server, ServerOptions } from "lambert-server"; @@ -141,6 +141,13 @@ export class SpacebarServer extends Server { res.sendFile(path.join(ASSETS_FOLDER, "openapi.json")); }); + app.get("/_spacebar/api/version", (req, res) => { + res.json({ + implementation: "spacebar-server-ts", + version: getRevInfoOrFail(), + }); + }); + // current well-known location app.get("/.well-known/spacebar", (req, res) => { res.json({ @@ -193,7 +200,7 @@ export class SpacebarServer extends Server { this.app.use(ErrorHandler); - ConnectionLoader.loadConnections(); + await ConnectionLoader.loadConnections(); if (logRequests) console.log(red(`Warning: Request logging is enabled! This will spam your console!\nTo disable this, unset the 'LOG_REQUESTS' environment variable!`)); diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index ad949b25a..a35c5fbca 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -120,7 +120,7 @@ export async function Authentication(req: Request, res: Response, next: NextFunc if (!req.headers.authorization) return next(new HTTPError("Missing Authorization Header", 401)); try { - const { decoded, user, session, tokenVersion } = (req.tokenData = await checkToken(req.headers.authorization, { + const { decoded, user, session } = (req.tokenData = await checkToken(req.headers.authorization, { ipAddress: req.ip, fingerprint: req.fingerprint, })); diff --git a/src/api/middlewares/ErrorHandler.ts b/src/api/middlewares/ErrorHandler.ts index 87e44c630..02d38def1 100644 --- a/src/api/middlewares/ErrorHandler.ts +++ b/src/api/middlewares/ErrorHandler.ts @@ -50,7 +50,7 @@ export function ErrorHandler(error: Error & { type?: string }, req: Request, res code = 50109; message = "The request body contains invalid JSON."; } else { - console.error(`[Error] ${code} ${req.url}\n`, errors || error, "\nbody:", req.body); + console.error(`[Error] ${code} ${req.url}\n`, errors, "\nbody:", req.body); if (req.server?.options?.production) { // don't expose internal errors to the user, instead human errors should be thrown as HTTPError diff --git a/src/api/middlewares/Translation.ts b/src/api/middlewares/Translation.ts index c8ba16a93..974e12116 100644 --- a/src/api/middlewares/Translation.ts +++ b/src/api/middlewares/Translation.ts @@ -42,7 +42,8 @@ export async function initTranslation(router: Router) { loadPath: path.join(ASSET_FOLDER_PATH, "locales") + "/{{lng}}/{{ns}}.json", }, load: "all", + showSupportNotice: false, }); - router.use(i18nextMiddleware.handle(i18next, {}) as unknown as RequestHandler); + router.use(i18nextMiddleware.handle(i18next, {})); } diff --git a/src/api/routes/-/version.ts b/src/api/routes/-/version.ts new file mode 100644 index 000000000..b70e750d2 --- /dev/null +++ b/src/api/routes/-/version.ts @@ -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 . +*/ + +import { route } from "@spacebar/api"; +import { Config } from "@spacebar/util"; +import { Request, Response, Router } from "express"; + +const router: Router = Router(); + +router.get( + "/", + route({ + responses: { + 200: {}, + }, + }), + async (_req: Request, res: Response) => { + const cfg = Config.get ? Config.get() : ({} as Record); + const version = + (typeof (cfg as Record).version === "string" + ? ((cfg as Record).version as string) + : typeof (cfg as Record).appVersion === + "string" + ? ((cfg as Record).appVersion as string) + : process.env.npm_package_version) || "unknown"; + const api_compatibility = + (typeof (cfg as Record).apiCompatibility === + "string" + ? ((cfg as Record).apiCompatibility as string) + : null) ?? null; + const extensions = ( + Array.isArray( + (cfg as Record).extensions as unknown[], + ) + ? ((cfg as { extensions: unknown[] }).extensions as unknown[]) + : [] + ) as unknown[]; + + res.json({ + version, + api_compatibility, + extensions, + }); + }, +); + +export default router; diff --git a/src/api/routes/applications/#application_id/bot/index.ts b/src/api/routes/applications/#application_id/bot/index.ts index ce864e11d..a5eb31271 100644 --- a/src/api/routes/applications/#application_id/bot/index.ts +++ b/src/api/routes/applications/#application_id/bot/index.ts @@ -1,19 +1,19 @@ /* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors + 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 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. + 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 . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ import { route } from "@spacebar/api"; @@ -28,7 +28,6 @@ const router: Router = Router({ mergeParams: true }); router.post( "/", route({ - right: "MANAGE_APPLICATIONS", responses: { 204: { body: "TokenOnlyResponse", @@ -57,7 +56,6 @@ router.post( router.post( "/reset", route({ - right: "MANAGE_APPLICATIONS", responses: { 200: { body: "TokenResponse", @@ -88,7 +86,6 @@ router.post( router.patch( "/", route({ - right: "MANAGE_APPLICATIONS", requestBody: "BotModifySchema", responses: { 200: { @@ -125,7 +122,7 @@ router.patch( app.bot.assign(body); - app.bot.save(); + await app.bot.save(); await app.save(); res.json(app).status(200); diff --git a/src/api/routes/applications/#application_id/commands/#command_id/index.ts b/src/api/routes/applications/#application_id/commands/#command_id/index.ts index 550110423..e0618613a 100644 --- a/src/api/routes/applications/#application_id/commands/#command_id/index.ts +++ b/src/api/routes/applications/#application_id/commands/#command_id/index.ts @@ -100,7 +100,7 @@ router.patch( }, ); -router.delete("/", async (req: Request, res: Response) => { +router.delete("/", route({}), async (req: Request, res: Response) => { const applicationExists = await Application.exists({ where: { id: req.params.application_id as string } }); if (!applicationExists) { diff --git a/src/api/routes/applications/#application_id/guilds/#guild_id/commands/#command_id/index.ts b/src/api/routes/applications/#application_id/guilds/#guild_id/commands/#command_id/index.ts index e4f847c38..16d7786b3 100644 --- a/src/api/routes/applications/#application_id/guilds/#guild_id/commands/#command_id/index.ts +++ b/src/api/routes/applications/#application_id/guilds/#guild_id/commands/#command_id/index.ts @@ -131,7 +131,7 @@ router.patch( }, ); -router.delete("/", async (req: Request, res: Response) => { +router.delete("/", route({}), async (req: Request, res: Response) => { const applicationExists = await Application.exists({ where: { id: req.params.application_id as string } }); if (!applicationExists) { diff --git a/src/api/routes/applications/#application_id/index.ts b/src/api/routes/applications/#application_id/index.ts index 6af8c29c6..64fac23c0 100644 --- a/src/api/routes/applications/#application_id/index.ts +++ b/src/api/routes/applications/#application_id/index.ts @@ -1,19 +1,19 @@ /* - Spacebar: A FOSS re-implementation and extension of the Discord.com backend. - Copyright (C) 2023 Spacebar and Spacebar Contributors + 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 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. + 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 . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ import { route } from "@spacebar/api"; @@ -51,7 +51,6 @@ router.get( router.patch( "/", route({ - right: "MANAGE_APPLICATIONS", requestBody: "ApplicationModifySchema", responses: { 200: { @@ -114,7 +113,6 @@ router.patch( router.post( "/delete", route({ - right: "MANAGE_APPLICATIONS", responses: { 200: {}, 400: { diff --git a/src/api/routes/applications/@me.ts b/src/api/routes/applications/@me.ts index 7e315a56d..d3d8c70f2 100644 --- a/src/api/routes/applications/@me.ts +++ b/src/api/routes/applications/@me.ts @@ -17,10 +17,9 @@ */ import { route } from "@spacebar/api"; -import { Application, DiscordApiErrors, Guild, handleFile } from "@spacebar/util"; +import { Application, Guild, handleFile } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { verifyToken } from "node-2fa"; import { ApplicationModifySchema } from "@spacebar/schemas"; const router: Router = Router({ mergeParams: true }); diff --git a/src/api/routes/applications/index.ts b/src/api/routes/applications/index.ts index 848242a02..40e7a1aad 100644 --- a/src/api/routes/applications/index.ts +++ b/src/api/routes/applications/index.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Application, Config, User, createAppBotUser, trimSpecial } from "@spacebar/util"; +import { Application, Config, createAppBotUser, trimSpecial } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { ApplicationCreateSchema } from "@spacebar/schemas"; @@ -44,7 +44,6 @@ router.get( router.post( "/", route({ - right: "CREATE_APPLICATIONS", requestBody: "ApplicationCreateSchema", responses: { 200: { @@ -54,7 +53,6 @@ router.post( }), async (req: Request, res: Response) => { const body = req.body as ApplicationCreateSchema; - const user = await User.findOneOrFail({ where: { id: req.user_id } }); const app = Application.create({ name: trimSpecial(body.name), diff --git a/src/api/routes/auth/logout.ts b/src/api/routes/auth/logout.ts index b4df13a0b..331fa2861 100644 --- a/src/api/routes/auth/logout.ts +++ b/src/api/routes/auth/logout.ts @@ -18,7 +18,7 @@ import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; -import { Session } from "@spacebar/util"; +import { emitEvent, Session } from "@spacebar/util"; const router: Router = Router({ mergeParams: true }); export default router; @@ -42,5 +42,12 @@ router.post( if (req.session) await Session.remove(req.session); res.status(204).send(); + + if (req.session) + await emitEvent({ + session_id: req.session.session_id, + event: "SB_SESSION_REMOVE", + origin: "Self logout", + }); }, ); diff --git a/src/api/routes/auth/register.ts b/src/api/routes/auth/register.ts index 7da796eb9..5c40ce1d8 100644 --- a/src/api/routes/auth/register.ts +++ b/src/api/routes/auth/register.ts @@ -124,7 +124,7 @@ router.post( } } - if (!regTokenUsed && register.checkIp) { + if (!regTokenUsed && register.enableAbuseIpDb) { const blacklist = await AbuseIpDbClient.getBlacklist(); if (blacklist) { const entry = blacklist.data.find((e) => e.ipAddress === ip); @@ -140,7 +140,9 @@ router.post( console.log(`[Register] ${ip} blocked from registration: AbuseIPDB score ${checkIp.data.abuseConfidenceScore} >= ${register.blockAbuseIpDbAboveScore} (CHECK)`); throw new HTTPError("Your IP is blocked from registration"); } + } + if (!regTokenUsed && register.enableIpData) { const ipData = await IpDataClient.getIpInfo(ip); if (ipData) { if (!ipData.threat) { diff --git a/src/api/routes/auth/sessions.ts b/src/api/routes/auth/sessions.ts index 3a9095f14..d1e1c6493 100644 --- a/src/api/routes/auth/sessions.ts +++ b/src/api/routes/auth/sessions.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; import { createHash } from "node:crypto"; -import { Session, Snowflake } from "@spacebar/util"; +import { emitEvent, Session } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { SessionsLogoutSchema } from "../../../schemas/api/users/SessionsSchemas"; import { In } from "typeorm"; @@ -69,6 +69,11 @@ router.post( for (const session of sessions) { await session.remove(); + await emitEvent({ + session_id: session.session_id, + event: "SB_SESSION_REMOVE", + origin: "Sessions logout", + }); } res.status(204).send(); }, diff --git a/src/api/routes/auth/whoami.ts b/src/api/routes/auth/whoami.ts index 513663761..0aaad2bbb 100644 --- a/src/api/routes/auth/whoami.ts +++ b/src/api/routes/auth/whoami.ts @@ -16,11 +16,8 @@ along with this program. If not, see . */ import { route } from "@spacebar/api"; -import { createHash } from "node:crypto"; -import { Session, Snowflake } from "@spacebar/util"; import { Request, Response, Router } from "express"; -import { SessionsLogoutSchema } from "../../../schemas/api/users/SessionsSchemas"; -import { In } from "typeorm"; + const router = Router({ mergeParams: true }); router.get( "/", diff --git a/src/api/routes/categories.ts b/src/api/routes/categories.ts new file mode 100644 index 000000000..9a141956f --- /dev/null +++ b/src/api/routes/categories.ts @@ -0,0 +1,48 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors +*/ +import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; +const router: Router = Router(); + +router.get( + "/", + route({ + query: { + locale: { + type: "string", + required: false, + description: "Locale code", + }, + primary_only: { + type: "string", + required: false, + description: "If 'true', only return category IDs", + }, + }, + responses: { + 200: { body: "unknown" }, + }, + }), + async (req: Request, res: Response) => { + const { primary_only } = req.query as { + locale?: string; + primary_only?: string; + }; + const categories = [ + { id: 1, name: "Gaming" }, + { id: 2, name: "Music" }, + { id: 3, name: "Education" }, + { id: 4, name: "Science & Tech" }, + { id: 5, name: "Entertainment" }, + { id: 6, name: "Creative Arts" }, + ]; + if (primary_only === "true") { + return res.json(categories.map((c) => c.id)); + } + return res.json(categories); + }, +); + +export default router; diff --git a/src/api/routes/channels/#channel_id/attachments.ts b/src/api/routes/channels/#channel_id/attachments.ts index bf3c70fbc..0e595ccc5 100644 --- a/src/api/routes/channels/#channel_id/attachments.ts +++ b/src/api/routes/channels/#channel_id/attachments.ts @@ -17,7 +17,7 @@ */ import { randomString, route } from "@spacebar/api"; -import { Channel, Config, Permissions, User } from "@spacebar/util"; +import { Channel, Config, Permissions } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { CloudAttachment } from "@spacebar/util"; import { UploadAttachmentRequestSchema, UploadAttachmentResponseSchema } from "@spacebar/schemas"; @@ -99,7 +99,7 @@ router.post( }, ); -router.delete("/:cloud_attachment_url", async (req: Request, res: Response) => { +router.delete("/:cloud_attachment_url", route({}), async (req: Request, res: Response) => { const { channel_id, cloud_attachment_url } = req.params as { [key: string]: string }; const user = req.user; diff --git a/src/api/routes/channels/#channel_id/greet.ts b/src/api/routes/channels/#channel_id/greet.ts index cb247d2c3..b556547d3 100644 --- a/src/api/routes/channels/#channel_id/greet.ts +++ b/src/api/routes/channels/#channel_id/greet.ts @@ -17,10 +17,10 @@ */ import { route } from "@spacebar/api"; -import { Channel, emitEvent, Message, MessageCreateEvent, MessageType, Permissions, Sticker } from "@spacebar/util"; +import { Channel, emitEvent, Message, MessageCreateEvent, Permissions, Sticker } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { In } from "typeorm"; -import { GreetRequestSchema } from "@spacebar/schemas"; +import { GreetRequestSchema, MessageType } from "@spacebar/schemas"; const router: Router = Router({ mergeParams: true }); @@ -79,14 +79,14 @@ router.post( const randomSticker = stickers[Math.floor(Math.random() * stickers.length)]; - const message: Message = Message.create({ + const message = Message.create({ channel_id: channel_id, author_id: req.user_id, type: MessageType.REPLY, message_reference: { ...payload.message_reference, type: 0 }, referenced_message: targetMessage, sticker_items: randomSticker ? [{ id: randomSticker.id, name: randomSticker.name, format_type: randomSticker.format_type }] : [], - }) as Message; + }); channel.last_message_id = message.id; diff --git a/src/api/routes/channels/#channel_id/index.ts b/src/api/routes/channels/#channel_id/index.ts index 81b942001..ff88703e6 100644 --- a/src/api/routes/channels/#channel_id/index.ts +++ b/src/api/routes/channels/#channel_id/index.ts @@ -17,23 +17,9 @@ */ import { route } from "@spacebar/api"; -import { - Channel, - ChannelDeleteEvent, - ChannelUpdateEvent, - Recipient, - emitEvent, - handleFile, - Config, - FieldError, - ErrorList, - ObjectErrorContent, - makeObjectErrorContent, -} from "@spacebar/util"; +import { Channel, ChannelDeleteEvent, ChannelUpdateEvent, Recipient, emitEvent, handleFile, Config, FieldError, ErrorList, makeObjectErrorContent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { ChannelModifySchema, ChannelType } from "@spacebar/schemas"; -import tickets from "./tickets"; -import ticket from "./ticket"; const router: Router = Router({ mergeParams: true }); // TODO: delete channel @@ -257,6 +243,4 @@ router.patch( }, ); -router.use(tickets); -router.use(ticket); export default router; diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts index 6be927d98..973e20e2e 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/index.ts @@ -34,7 +34,7 @@ import { import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import multer from "multer"; -import { handleMessage, postHandleMessage, route } from "../../../../../util"; +import { handleMessage, postHandleMessage, route } from "@spacebar/api"; import { MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, MessageEditSchema, ChannelType } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); @@ -98,7 +98,7 @@ router.patch( channel_id, id: message_id, edited_timestamp: new Date(), - } as Parameters[0]); + }); await Promise.all([ new_message.save(), @@ -113,7 +113,7 @@ router.patch( } as MessageUpdateEvent), ]); - postHandleMessage(new_message); + postHandleMessage(new_message).catch((e) => console.error("[Message] post-message handler failed", e)); // TODO: a DTO? return res.json({ @@ -265,7 +265,10 @@ router.get( const message = await Message.findOneOrFail({ where: { id: message_id, channel_id }, - relations: { attachments: true }, + relations: { + attachments: true, + author: true, + }, }); const permissions = await getPermission(req.user_id, undefined, channel_id); @@ -295,7 +298,7 @@ router.delete( }); if (channel.type === ChannelType.GUILD_PUBLIC_THREAD) { if (channel.message_count !== undefined) channel.message_count--; - channel.save(); //Save async, it's fine + await channel.save(); } const message = await Message.findOneOrFail({ where: { id: message_id }, diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts index 75fd5d274..e75e4db3d 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/reactions.ts @@ -30,6 +30,7 @@ import { MessageReactionRemoveEvent, User, arrayRemove, + ReactionType, } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -224,14 +225,22 @@ router.put( await message.save(); - const member = - channel.guild_id && - ( - await Member.findOneOrFail({ - where: { id: req.user_id }, - select: PublicMemberProjection, - }) - ).toPublicMember(); + const member = channel.guild_id + ? ( + await Member.findOneOrFail({ + where: { id: req.user_id, guild_id: channel.guild_id }, + relations: { roles: true, user: true }, + select: { + index: true, + ...Object.fromEntries(PublicMemberProjection.map((x) => [x, true])), + user: Object.fromEntries(PublicUserProjection.map((x) => [x, true])), + roles: { + id: true, + }, + }, + }) + ).toPublicMember() + : undefined; await emitEvent({ event: "MESSAGE_REACTION_ADD", @@ -243,8 +252,9 @@ router.put( guild_id: channel.guild_id, emoji, member, + type: ReactionType.normal, }, - } as MessageReactionAddEvent); + } satisfies MessageReactionAddEvent); res.sendStatus(204); }, @@ -300,8 +310,9 @@ router.delete( message_id, guild_id: channel.guild_id, emoji, + type: ReactionType.normal, }, - } as MessageReactionRemoveEvent); + } satisfies MessageReactionRemoveEvent); res.sendStatus(204); }, @@ -357,8 +368,9 @@ router.delete( message_id, guild_id: channel.guild_id, emoji, + type: ReactionType.normal, }, - } as MessageReactionRemoveEvent); + } satisfies MessageReactionRemoveEvent); res.sendStatus(204); }, diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/replies.ts b/src/api/routes/channels/#channel_id/messages/#message_id/replies.ts new file mode 100644 index 000000000..4ace23a14 --- /dev/null +++ b/src/api/routes/channels/#channel_id/messages/#message_id/replies.ts @@ -0,0 +1,170 @@ +/* + 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 . +*/ + +import { Message, Channel, getPermission, getRights, emitEvent, MessageDeleteBulkEvent } from "@spacebar/util"; +import { Request, Response, Router } from "express"; +import { In } from "typeorm"; +import { route } from "../../../../../util"; + +const router = Router(); + +router.get( + "/", + route({ + permission: "VIEW_CHANNEL", + responses: { + 200: { body: "APIMessageArray" }, + 403: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const message_id = req.params.message_id as string; + const channel_id = req.params.channel_id as string; + + const permissions = await getPermission(req.user_id, undefined, channel_id); + permissions.hasThrow("READ_MESSAGE_HISTORY"); + + const parentMessage = await Message.findOneOrFail({ + where: { id: message_id, channel_id }, + }); + + if (!parentMessage.reply_ids) { + const replies = await Message.find({ + where: { + channel_id: channel_id, + message_reference: { + message_id: message_id, + }, + }, + select: ["id"], + }); + + if (replies.length > 0) { + parentMessage.reply_ids = replies.map((r) => r.id); + await Message.update({ id: message_id }, { reply_ids: parentMessage.reply_ids }); + } else { + parentMessage.reply_ids = []; + await Message.update({ id: message_id }, { reply_ids: [] }); + } + } + + const replyMessages = await Message.find({ + where: { + id: In(parentMessage.reply_ids || []), + channel_id: channel_id, + }, + relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"], + order: { timestamp: "ASC" }, + }); + + return res.json( + replyMessages.map((m) => { + const json = m.toJSON(); + json.reply_ids = m.reply_ids ?? undefined; + return json; + }), + ); + }, +); + +router.delete( + "/", + route({ + responses: { + 204: {}, + 403: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const message_id = req.params.message_id as string; + const channel_id = req.params.channel_id as string; + + const channel = await Channel.findOneOrFail({ + where: { id: channel_id }, + }); + + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id); + + const parentMessage = await Message.findOneOrFail({ + where: { id: message_id, channel_id }, + }); + + if (!parentMessage.reply_ids) { + const replies = await Message.find({ + where: { + channel_id: channel_id, + message_reference: { + message_id: message_id, + }, + }, + select: ["id"], + }); + + if (replies.length > 0) { + parentMessage.reply_ids = replies.map((r) => r.id); + await Message.update({ id: message_id }, { reply_ids: parentMessage.reply_ids }); + } else { + parentMessage.reply_ids = []; + await Message.update({ id: message_id }, { reply_ids: [] }); + } + } + + if (parentMessage.reply_ids?.length) { + const replyMessages = await Message.find({ + where: { id: In(parentMessage.reply_ids) }, + select: ["id", "author_id"], + }); + + const userOwnedReplies = replyMessages.filter((msg) => msg.author_id === req.user_id); + const otherOwnedReplies = replyMessages.filter((msg) => msg.author_id !== req.user_id); + + const rights = await getRights(req.user_id); + + if (userOwnedReplies.length > 0) { + rights.hasThrow("SELF_DELETE_MESSAGES"); + } + + if (otherOwnedReplies.length > 0) { + if (!rights.has("MANAGE_MESSAGES")) { + permissions.hasThrow("MANAGE_MESSAGES"); + } + } + + await Message.delete({ id: In(parentMessage.reply_ids) }); + + await emitEvent({ + event: "MESSAGE_DELETE_BULK", + channel_id, + data: { + ids: parentMessage.reply_ids, + channel_id, + guild_id: channel.guild_id, + }, + } as MessageDeleteBulkEvent); + + parentMessage.reply_ids = []; + await Message.update({ id: message_id }, { reply_ids: [] }); + } + + res.sendStatus(204); + }, +); + +export default router; diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/threads.ts b/src/api/routes/channels/#channel_id/messages/#message_id/threads.ts index 785a9da5f..525337b4f 100644 --- a/src/api/routes/channels/#channel_id/messages/#message_id/threads.ts +++ b/src/api/routes/channels/#channel_id/messages/#message_id/threads.ts @@ -17,8 +17,8 @@ */ import { route, sendMessage } from "@spacebar/api"; -import { Message, Channel, emitEvent, User, MessageUpdateEvent, MessageType, Recipient } from "@spacebar/util"; -import { MessageThreadCreationSchema, ChannelType } from "@spacebar/schemas"; +import { Message, Channel, emitEvent, User, MessageUpdateEvent } from "@spacebar/util"; +import { MessageThreadCreationSchema, ChannelType, MessageType } from "@spacebar/schemas"; import { Request, Response, Router } from "express"; @@ -89,7 +89,7 @@ router.post( }, author_id: user.id, }); - sendMessage({ + await sendMessage({ channel_id: channel.id, type: MessageType.THREAD_CREATED, content: thread.name, diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts index 885551695..ff531213a 100644 --- a/src/api/routes/channels/#channel_id/messages/index.ts +++ b/src/api/routes/channels/#channel_id/messages/index.ts @@ -33,7 +33,7 @@ import { NewUrlSignatureData, NewUrlUserSignatureData, ReadState, - Recipient, + Relationship, Rights, Snowflake, uploadFile, @@ -56,10 +56,10 @@ import { MessageCreateSchema, Reaction, ReadStateType, - ChannelType, + RelationshipType, } from "@spacebar/schemas"; -const router: Router = Router(); +const router: Router = Router({ mergeParams: true }); async function populateForwardLinks(messages: Message[]): Promise { for (const message of messages) { @@ -175,7 +175,7 @@ router.get( }), ]); left.push(...right); - messages = left.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime()); + messages = left.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } else { query.take = 1; const message = await Message.findOne({ @@ -202,8 +202,6 @@ router.get( await Message.fillReplies(messages); const endpoint = Config.get().cdn.endpointPublic; - await populateForwardLinks(messages); - const ret = messages.map((x: Message) => { x = x.toJSON(); @@ -267,6 +265,15 @@ router.get( return x; }); + //console.log(ret); + + await Promise.all( + ret + .filter((x: MessageCreateSchema) => x.interaction_metadata && !x.interaction_metadata.user) + .map(async (x: MessageCreateSchema) => { + x.interaction_metadata!.user = x.interaction!.user = await User.findOneOrFail({ where: { id: (x as Message).interaction_metadata!.user_id } }); + }), + ); return res.json(ret); }, @@ -322,7 +329,7 @@ router.post( const channel = await Channel.findOneOrFail({ where: { id: channel_id }, - relations: ["recipients", "recipients.user"], + relations: { recipients: { user: true } }, }); if (channel.thread_metadata?.locked) throw DiscordApiErrors.THREAD_IS_LOCKED; if (channel.isThread()) { @@ -371,6 +378,23 @@ router.post( throw new HTTPError(`Cannot send messages to channel of type ${channel.type}`, 400); } + // handle blocked users in dms + if (channel.recipients?.length == 2) { + const otherUser = channel.recipients.find((r) => r.user_id != req.user_id)?.user; + if (otherUser) { + const relationship = await Relationship.findOne({ + where: [ + { from_id: req.user_id, to_id: otherUser.id }, + { from_id: otherUser.id, to_id: req.user_id }, + ], + }); + + if (relationship?.type === RelationshipType.blocked) { + throw DiscordApiErrors.CANNOT_MESSAGE_USER; + } + } + } + if (body.nonce) { const existing = await Message.findOne({ where: { @@ -386,7 +410,7 @@ router.post( if (!req.rights.has(Rights.FLAGS.BYPASS_RATE_LIMITS)) { const limits = Config.get().limits; - if (limits.absoluteRate.register.enabled) { + if (limits.absoluteRate.sendMessage.enabled) { const count = await Message.count({ where: { channel_id, @@ -402,26 +426,6 @@ router.post( }, }); } - - if (channel.rate_limit_per_user && channel.rate_limit_per_user > 0) { - const lastMessage = await Message.findOne({ - where: { - channel_id, - author_id: req.user_id, - }, - order: { timestamp: "DESC" }, - select: ["timestamp"], - }); - - if (lastMessage) { - const timeSinceLastMessage = Date.now() - lastMessage.timestamp.getTime(); - const slowmodeMs = channel.rate_limit_per_user * 1000; - - if (timeSinceLastMessage < slowmodeMs) { - throw DiscordApiErrors.SLOWMODE_RATE_LIMIT; - } - } - } } const files = (req.files as Express.Multer.File[]) ?? []; @@ -491,12 +495,13 @@ router.post( if (!message.member) { message.member = await Member.findOneOrFail({ where: { id: req.user_id, guild_id: message.guild_id }, - relations: ["roles"], + relations: { roles: true }, }); + message.member.clean_data(); } // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore + // @ts-ignore message.member.roles = message.member.roles.filter((x) => x.id != x.guild_id).map((x) => x.id); } @@ -505,6 +510,8 @@ router.post( }); if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id }); read_state.last_message_id = message.id; + //It's a little more complicated than this but this'll do + read_state.mention_count = 0; await Promise.all([ read_state.save(), diff --git a/src/api/routes/channels/#channel_id/messages/pins/index.ts b/src/api/routes/channels/#channel_id/messages/pins/index.ts index a947d875f..cab2c9a84 100644 --- a/src/api/routes/channels/#channel_id/messages/pins/index.ts +++ b/src/api/routes/channels/#channel_id/messages/pins/index.ts @@ -58,7 +58,7 @@ router.put( const author = await User.getPublicUser(req.user_id); - const systemPinMessage: Message = Message.create({ + const systemPinMessage = Message.create({ timestamp: new Date(), type: 6, guild_id: message.guild_id, @@ -78,7 +78,7 @@ router.put( mention_channels: [], mention_roles: [], mention_everyone: false, - }) as Message; + }); await Promise.all([ message.save(), diff --git a/src/api/routes/channels/#channel_id/messages/search.ts b/src/api/routes/channels/#channel_id/messages/search.ts index 77ec46c75..8363edc67 100644 --- a/src/api/routes/channels/#channel_id/messages/search.ts +++ b/src/api/routes/channels/#channel_id/messages/search.ts @@ -22,7 +22,7 @@ import { route } from "@spacebar/api"; import { Channel, FieldErrors, Message, getPermission } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { FindManyOptions, In, Like } from "typeorm"; +import { FindManyOptions, Like } from "typeorm"; const router: Router = Router({ mergeParams: true }); diff --git a/src/api/routes/channels/#channel_id/pins.ts b/src/api/routes/channels/#channel_id/pins.ts index 8c96fa7a6..9adf6e5ae 100644 --- a/src/api/routes/channels/#channel_id/pins.ts +++ b/src/api/routes/channels/#channel_id/pins.ts @@ -59,7 +59,7 @@ router.put( const author = await User.getPublicUser(req.user_id); - const systemPinMessage: Message = Message.create({ + const systemPinMessage = Message.create({ timestamp: new Date(), type: 6, guild_id: message.guild_id, @@ -79,7 +79,7 @@ router.put( mention_channels: [], mention_roles: [], mention_everyone: false, - }) as Message; + }); await Promise.all([ message.save(), diff --git a/src/api/routes/channels/#channel_id/post-data.ts b/src/api/routes/channels/#channel_id/post-data.ts index f1727109f..d37344a97 100644 --- a/src/api/routes/channels/#channel_id/post-data.ts +++ b/src/api/routes/channels/#channel_id/post-data.ts @@ -16,14 +16,13 @@ along with this program. If not, see . */ -import { handleMessage, postHandleMessage, route, sendMessage } from "@spacebar/api"; -import { Channel, emitEvent, User, uploadFile, Attachment, Member, ReadState, MessageCreateEvent, FieldErrors, getPermission, ThreadMember, Message } from "@spacebar/util"; -import { ChannelType, MessageType, ThreadCreationSchema, MessageCreateAttachment, MessageCreateCloudAttachment, PostDataSchema } from "@spacebar/schemas"; +import { route } from "@spacebar/api"; +import { Channel, Member, Message } from "@spacebar/util"; +import { PostDataSchema } from "@spacebar/schemas"; import { Request, Response, Router } from "express"; import { messageUpload } from "./messages"; -import { HTTPError } from "#util/util/lambert-server"; -import { FindManyOptions, FindOptionsOrder, In, Like } from "typeorm"; +import { In } from "typeorm"; const router = Router({ mergeParams: true }); @@ -61,7 +60,21 @@ router.post( where: { id: In(threads.map(({ id }) => id)), }, - relations: ["author"], + relations: { + author: true, + webhook: true, + application: true, + mentions: true, + mention_roles: true, + mention_channels: true, + sticker_items: true, + attachments: true, + thread: { + recipients: { + user: true, + }, + }, + }, }), Member.find({ where: { @@ -69,6 +82,7 @@ router.post( }, }), ]); + await Message.fillReplies(messages); const objRet: { threads: Record } = { threads: {} }; for (const thread of threads) { const owner = members.find(({ id }) => id === thread.owner_id)?.toJSON() || null; diff --git a/src/api/routes/channels/#channel_id/promote.ts b/src/api/routes/channels/#channel_id/promote.ts new file mode 100644 index 000000000..dfb3c14e4 --- /dev/null +++ b/src/api/routes/channels/#channel_id/promote.ts @@ -0,0 +1,136 @@ +/* + 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 . +*/ + +import { route } from "@spacebar/api"; +import { Channel, Guild, Permissions, emitEvent } from "@spacebar/util"; +import { ChannelType, ChannelPromoteSchema } from "@spacebar/schemas"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; + +const router: Router = Router(); + +router.post( + "/", + route({ + requestBody: "ChannelPromoteSchema", + permission: "MANAGE_CHANNELS", + responses: { + 200: { body: "Channel" }, + 400: { body: "APIErrorResponse" }, + 403: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const channel_id = req.params.channel_id as string; + const body = (req.body || {}) as ChannelPromoteSchema; + + const channel = await Channel.findOneOrFail({ + where: { id: channel_id }, + }); + + if (!channel.guild_id) { + throw new HTTPError("Only guild threads can be promoted", 400); + } + if (channel.parent_id == null) { + throw new HTTPError("Channel is not a thread", 400); + } + + let newType: ChannelType | null = null; + switch (channel.type) { + case ChannelType.GUILD_PUBLIC_THREAD: + newType = ChannelType.GUILD_TEXT; + break; + case ChannelType.GUILD_NEWS_THREAD: + newType = ChannelType.GUILD_NEWS; + break; + case ChannelType.GUILD_PRIVATE_THREAD: + throw new HTTPError("Private threads cannot be promoted", 400); + default: + throw new HTTPError("Unsupported channel type for promotion", 400); + } + + const oldParentId = channel.parent_id; + const guildId = channel.guild_id; + + const guild = await Guild.findOneOrFail({ + where: { id: guildId }, + relations: ["roles"], + }); + + const threadOverwrites = channel.permission_overwrites ?? []; + const computedOverwrites = [] as NonNullable; + + for (const role of guild.roles) { + const base = BigInt(role.permissions); + const desired = Permissions.channelPermission( + threadOverwrites.filter((ow) => ow.type === 0 && ow.id === role.id), + base, + ); + + const allow = desired & ~base; + const deny = base & ~desired; + + if (allow !== BigInt(0) || deny !== BigInt(0)) { + computedOverwrites.push({ + id: role.id, + type: 0, + allow: String(allow), + deny: String(deny), + }); + } + } + + for (const ow of threadOverwrites.filter((o) => o.type === 1)) { + const allow = BigInt(ow.allow || "0"); + const deny = BigInt(ow.deny || "0"); + if (allow === BigInt(0) && deny === BigInt(0)) continue; + computedOverwrites.push({ + id: ow.id, + type: 1, + allow: String(allow), + deny: String(deny), + }); + } + + channel.type = newType; + channel.parent_id = null; + channel.permission_overwrites = computedOverwrites; + + await channel.save(); + + if (typeof body.position === "number") { + await Guild.insertChannelInOrder(guildId, channel.id, body.position); + } else { + await Guild.insertChannelInOrder(guildId, channel.id, oldParentId as string); + } + + channel.position = await Channel.calculatePosition(channel.id, guildId, channel.guild); + + await emitEvent({ + event: "CHANNEL_UPDATE", + data: channel, + channel_id: channel.id, + guild_id: guildId, + }); + + return res.json(channel); + }, +); + +export default router; diff --git a/src/api/routes/channels/#channel_id/tags.ts b/src/api/routes/channels/#channel_id/tags.ts index 15b01d989..a84e4b429 100644 --- a/src/api/routes/channels/#channel_id/tags.ts +++ b/src/api/routes/channels/#channel_id/tags.ts @@ -60,7 +60,7 @@ router.post( tag.save(), emitEvent({ event: "CHANNEL_UPDATE", - data: channel, + data: channel.toJSON(), channel_id, } as ChannelUpdateEvent), ]); @@ -101,7 +101,7 @@ router.put( tag.save(), emitEvent({ event: "CHANNEL_UPDATE", - data: channel, + data: channel.toJSON(), channel_id, } as ChannelUpdateEvent), ]); @@ -140,7 +140,7 @@ router.delete( tag.remove(), emitEvent({ event: "CHANNEL_UPDATE", - data: channel, + data: channel.toJSON(), channel_id, } as ChannelUpdateEvent), ]); diff --git a/src/api/routes/channels/#channel_id/thread-members.ts b/src/api/routes/channels/#channel_id/thread-members.ts index 281ed2f90..83b1b2930 100644 --- a/src/api/routes/channels/#channel_id/thread-members.ts +++ b/src/api/routes/channels/#channel_id/thread-members.ts @@ -50,12 +50,14 @@ router.get( limit = undefined; } - return await ThreadMember.find({ - where: { channel: { id: channel_id }, ...(after ? { user_id: MoreThan(after) } : {}) }, - take: limit ? parseInt(limit) : 50, - order: { member_idx: "ASC" }, - relations: { ...(with_member ? { member: true } : {}) }, - }); + return res.send( + await ThreadMember.find({ + where: { channel: { id: channel_id }, ...(after ? { user_id: MoreThan(after) } : {}) }, + take: limit ? parseInt(limit) : 50, + order: { member_idx: "ASC" }, + relations: { ...(with_member ? { member: true } : {}) }, + }), + ); }, ); router.post( diff --git a/src/api/routes/channels/#channel_id/threads.ts b/src/api/routes/channels/#channel_id/threads.ts index 279dc6c0f..8b8d9bc73 100644 --- a/src/api/routes/channels/#channel_id/threads.ts +++ b/src/api/routes/channels/#channel_id/threads.ts @@ -31,10 +31,8 @@ import { ThreadMember, Message, ChannelFlags, - DiscordApiErrors, - MessageType, } from "@spacebar/util"; -import { ChannelType, ThreadCreationSchema, MessageCreateAttachment, MessageCreateCloudAttachment } from "@spacebar/schemas"; +import { ChannelType, MessageType, ThreadCreationSchema, MessageCreateAttachment, MessageCreateCloudAttachment } from "@spacebar/schemas"; import { Request, Response, Router } from "express"; import { messageUpload } from "./messages"; @@ -127,7 +125,7 @@ router.post( }), ]); if (body.type !== ChannelType.GUILD_PRIVATE_THREAD && !channel.isForum()) - sendMessage({ + await sendMessage({ channel_id: channel.id, type: MessageType.THREAD_CREATED, content: thread.name, @@ -218,6 +216,7 @@ router.get( }, }), async (req: Request, res: Response) => { + // noinspection JSUnusedLocalSymbols - ??? const { name, slop, tag, tag_setting, archived, sort_by, sort_order, limit, offset, max_id, min_id } = req.query as Record; const tags = tag ? tag.split(",") : []; const { channel_id } = req.params as Record; @@ -252,7 +251,7 @@ router.get( }, }); - const permissions = await getPermission(req.user_id, channel.guild_id, channel.id); + const permissions = await getPermission(req.user_id, channel.guild_id, channel); permissions.hasThrow("VIEW_CHANNEL"); if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ threads: [], total_results: 0, members: [], has_more: false, first_messages: [] }); const member = await Member.findOneOrFail({ where: { guild_id: channel.guild_id, id: req.user_id } }); diff --git a/src/api/routes/channels/#channel_id/ticket.ts b/src/api/routes/channels/#channel_id/ticket.ts index 273216bd5..342a19505 100644 --- a/src/api/routes/channels/#channel_id/ticket.ts +++ b/src/api/routes/channels/#channel_id/ticket.ts @@ -6,7 +6,7 @@ import { Request, Response, Router } from "express"; const router: Router = Router(); router.patch( - "/ticket", + "/", route({ requestBody: "TicketPatchSchema", responses: { 200: { body: "Channel" }, 400: {}, 403: {}, 404: {} }, diff --git a/src/api/routes/channels/#channel_id/tickets.ts b/src/api/routes/channels/#channel_id/tickets.ts index 08b4fd47d..4c28a9397 100644 --- a/src/api/routes/channels/#channel_id/tickets.ts +++ b/src/api/routes/channels/#channel_id/tickets.ts @@ -10,7 +10,7 @@ import { Request, Response, Router } from "express"; const router: Router = Router(); router.post( - "/tickets", + "/", route({ requestBody: "TicketCreateSchema", responses: { 200: { body: "Channel" }, 400: {}, 403: {}, 404: {} }, diff --git a/src/api/routes/connections/#connection_name/authorize.ts b/src/api/routes/connections/#connection_name/authorize.ts index ca56bbe39..7d3d03eb4 100644 --- a/src/api/routes/connections/#connection_name/authorize.ts +++ b/src/api/routes/connections/#connection_name/authorize.ts @@ -18,7 +18,7 @@ import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; -import { ConnectionStore, FieldErrors } from "../../../../util"; +import { ConnectionStore, FieldErrors } from "@spacebar/util"; const router = Router({ mergeParams: true }); @@ -43,7 +43,7 @@ router.get("/", route({}), async (req: Request, res: Response) => { }); res.json({ - url: await connection.getAuthorizationUrl(req.user_id), + url: connection.getAuthorizationUrl(req.user_id), }); }); diff --git a/src/api/routes/connections/#connection_name/callback.ts b/src/api/routes/connections/#connection_name/callback.ts index 2f6a25a00..7fc725309 100644 --- a/src/api/routes/connections/#connection_name/callback.ts +++ b/src/api/routes/connections/#connection_name/callback.ts @@ -49,7 +49,7 @@ router.post("/", route({ requestBody: "ConnectionCallbackSchema" }), async (req: // whether we should emit a connections update event, only used when a connection doesnt already exist if (connectedAccnt) - emitEvent({ + await emitEvent({ event: "USER_CONNECTIONS_UPDATE", data: { ...connectedAccnt, token_data: undefined }, user_id: userId, diff --git a/src/api/routes/discoverable-guilds.ts b/src/api/routes/discoverable-guilds.ts index 87de37a3b..2df44c16b 100644 --- a/src/api/routes/discoverable-guilds.ts +++ b/src/api/routes/discoverable-guilds.ts @@ -21,6 +21,7 @@ import { Config, Guild, Member } from "@spacebar/util"; import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; import { In, Like, Not } from "typeorm"; +import { DiscoverableGuildsResponse } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); @@ -64,7 +65,11 @@ router.get( res.send({ total: total, - guilds: guilds, + guilds: guilds.map((g) => ({ + ...g, + discovery_weight: undefined, + discovery_splash: undefined, + })), offset: Number(offset || Config.get().guild.discovery.offset), limit: Number(limit || configLimit), }); diff --git a/src/api/routes/gifs/search.ts b/src/api/routes/gifs/search.ts index ca07956ef..cd20b8d44 100644 --- a/src/api/routes/gifs/search.ts +++ b/src/api/routes/gifs/search.ts @@ -19,7 +19,6 @@ import { route } from "@spacebar/api"; import { getGifApiKey, parseGifResult } from "@spacebar/util"; import { Request, Response, Router } from "express"; -import http from "http"; import { TenorGif, TenorMediaTypes } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); diff --git a/src/api/routes/guilds/#guild_id/auto-moderation/rules.ts b/src/api/routes/guilds/#guild_id/auto-moderation/rules.ts index 9dc308dfa..8b56ded9e 100644 --- a/src/api/routes/guilds/#guild_id/auto-moderation/rules.ts +++ b/src/api/routes/guilds/#guild_id/auto-moderation/rules.ts @@ -17,12 +17,12 @@ */ import { route } from "@spacebar/api"; -import { User, AutomodRule, AutomodEvaluator } from "@spacebar/util"; -import { AutomodRuleSchema } from "@spacebar/schemas"; +import { User, AutomodRule } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; +import { AutomodRuleSchema } from "@spacebar/schemas"; -const router: Router = Router(); +const router: Router = Router({ mergeParams: true }); router.get( "/", @@ -81,8 +81,6 @@ router.post( }); const savedRule = await AutomodRule.save(created); - AutomodEvaluator.clearCache(guild_id); - return res.json(savedRule); }, ); @@ -105,7 +103,7 @@ router.patch( }, }), async (req: Request, res: Response) => { - const { rule_id, guild_id } = req.params as { [key: string]: string }; + const { rule_id } = req.params as { [key: string]: string }; const rule = await AutomodRule.findOneOrFail({ where: { id: rule_id }, }); @@ -114,8 +112,6 @@ router.patch( AutomodRule.merge(rule, data); const savedRule = await AutomodRule.save(rule); - AutomodEvaluator.clearCache(guild_id); - return res.json(savedRule); }, ); @@ -135,10 +131,8 @@ router.delete( }, }), async (req: Request, res: Response) => { - const { rule_id, guild_id } = req.params as { [key: string]: string }; + const { rule_id } = req.params as { [key: string]: string }; await AutomodRule.delete({ id: rule_id }); - AutomodEvaluator.clearCache(guild_id); - return res.status(204).send(); }, ); diff --git a/src/api/routes/guilds/#guild_id/bulk-ban.ts b/src/api/routes/guilds/#guild_id/bulk-ban.ts index 67b23ba5b..6c01cf68d 100644 --- a/src/api/routes/guilds/#guild_id/bulk-ban.ts +++ b/src/api/routes/guilds/#guild_id/bulk-ban.ts @@ -102,7 +102,6 @@ router.post( banned_users.push(banned_user_id); } catch { failed_users.push(banned_user_id); - continue; } } diff --git a/src/api/routes/guilds/#guild_id/channels.ts b/src/api/routes/guilds/#guild_id/channels.ts index 555f5c74e..37c0215f2 100644 --- a/src/api/routes/guilds/#guild_id/channels.ts +++ b/src/api/routes/guilds/#guild_id/channels.ts @@ -19,8 +19,7 @@ import { route } from "@spacebar/api"; import { Channel, ChannelUpdateEvent, Guild, emitEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; -import { ChannelModifySchema, ChannelReorderSchema } from "@spacebar/schemas"; -import { ChannelCreateSchema } from "../../../../schemas/uncategorised/ChannelCreateSchema"; +import { ChannelCreateSchema, ChannelReorderSchema } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); router.get( @@ -48,7 +47,6 @@ router.get( router.post( "/", route({ - right: "CREATE_CHANNELS", requestBody: "ChannelCreateSchema", permission: "MANAGE_CHANNELS", responses: { @@ -56,7 +54,7 @@ router.post( body: "Channel", }, 400: { - body: "APIError Response", + body: "APIErrorResponse", }, 403: { body: "APIErrorResponse", diff --git a/src/api/routes/guilds/#guild_id/emojis.ts b/src/api/routes/guilds/#guild_id/emojis.ts index 1044b8a4c..321331f4d 100644 --- a/src/api/routes/guilds/#guild_id/emojis.ts +++ b/src/api/routes/guilds/#guild_id/emojis.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Config, DiscordApiErrors, Emoji, GuildEmojisUpdateEvent, Member, Snowflake, User, emitEvent, handleFile } from "@spacebar/util"; +import { Config, DiscordApiErrors, Emoji, GuildEmojisUpdateEvent, Member, Snowflake, emitEvent, handleFile } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { EmojiCreateSchema, EmojiModifySchema } from "@spacebar/schemas"; diff --git a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts index 2ccece6bc..3db964d2a 100644 --- a/src/api/routes/guilds/#guild_id/members/#member_id/index.ts +++ b/src/api/routes/guilds/#guild_id/members/#member_id/index.ts @@ -96,7 +96,11 @@ router.patch( const permission = await getPermission(req.user_id, guild_id); if ("nick" in body) { - permission.hasThrow("MANAGE_NICKNAMES"); + if (member_id != req.user_id) { + permission.hasThrow("MANAGE_NICKNAMES"); + } else { + permission.hasThrow("CHANGE_NICKNAME"); + } if (!body.nick) { delete body.nick; @@ -106,7 +110,7 @@ router.patch( } } - if (("bio" in body || "avatar" in body) && req.params.member_id != "@me") { + if (("bio" in body || "avatar" in body) && member_id != req.user_id) { const rights = await getRights(req.user_id); rights.hasThrow("MANAGE_USERS"); } diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts index a762e5297..adc76249b 100644 --- a/src/api/routes/guilds/#guild_id/messages/search.ts +++ b/src/api/routes/guilds/#guild_id/messages/search.ts @@ -16,13 +16,11 @@ along with this program. If not, see . */ -/* eslint-disable @typescript-eslint/ban-ts-comment */ - import { route } from "@spacebar/api"; -import { Channel, FieldErrors, Message, getPermission } from "@spacebar/util"; +import { Channel, FieldErrors, Member, Message, Snowflake, getPermission } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { FindManyOptions, In, Like } from "typeorm"; +import { Between, FindManyOptions, FindOptionsWhere, In, LessThan, Like, MoreThan } from "typeorm"; const router: Router = Router({ mergeParams: true }); @@ -43,21 +41,22 @@ router.get( }), async (req: Request, res: Response) => { const { - channel_id, content, // include_nsfw, // TODO offset, sort_order, // sort_by, // TODO: Handle 'relevance' limit, - author_id, + max_id, + min_id, } = req.query as Record; - + const { author_id } = req.query as Record; + let { channel_id, mentions } = req.query as Record; const parsedLimit = Number(limit) || 50; if (parsedLimit < 1 || parsedLimit > 100) throw new HTTPError("limit must be between 1 and 100", 422); if (sort_order) { - if (typeof sort_order != "string" || ["desc", "asc"].indexOf(sort_order) == -1) + if (["desc", "asc"].indexOf(sort_order) == -1) throw FieldErrors({ sort_order: { message: "Value must be one of ('desc', 'asc').", @@ -65,43 +64,98 @@ router.get( }, }); // todo this is wrong } - - const permissions = await getPermission(req.user_id, req.params.guild_id as string, channel_id as string | undefined); - permissions.hasThrow("VIEW_CHANNEL"); - if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ messages: [], total_results: 0 }); + if (channel_id) { + const ids = new Set(channel_id instanceof Array ? channel_id : [channel_id]); + await Promise.all( + [...ids].map(async (id) => { + const permissions = await getPermission(req.user_id, req.params.guild_id as string, id); + permissions.hasThrow("VIEW_CHANNEL"); + if (!permissions.has("READ_MESSAGE_HISTORY")) ids.delete(id); + }), + ); + if (ids.size === 0) { + res.json({ messages: [], total_results: 0 }); + } else if (ids.size === 1) { + channel_id = [...ids][0]; + } else { + channel_id = [...ids]; + } + } else { + const permissions = await getPermission(req.user_id, req.params.guild_id as string); + permissions.hasThrow("VIEW_CHANNEL"); + if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json({ messages: [], total_results: 0 }); + } + const minStamp = min_id ? Snowflake.deconstruct(min_id).timestamp : 0; + const maxStamp = max_id ? Snowflake.deconstruct(max_id).timestamp : 0; + const where: FindOptionsWhere = { + guild: { + id: req.params.guild_id as string, + }, + ...(content ? { content: Like(`%${content}%`) } : {}), + ...(author_id ? (author_id instanceof Array ? { author_id: In(author_id) } : { author_id }) : {}), + ...(channel_id + ? channel_id instanceof Array + ? { channel_id: In(channel_id) } + : { channel_id } + : { + channel_id: In( + ( + await Promise.all( + ( + await Channel.find({ + where: { guild_id: req.params.guild_id as string }, + select: { id: true }, + }) + ).map(async (channel) => { + const perm = await getPermission(req.user_id, req.params.guild_id as string, channel.id); + return perm.has("VIEW_CHANNEL") && perm.has("READ_MESSAGE_HISTORY") ? channel : undefined; + }), + ) + ) + .filter((_) => _ !== undefined) + .map(({ id }) => id), + ), + }), + ...(minStamp + ? maxStamp + ? { timestamp: Between(new Date(minStamp), new Date(maxStamp)) } + : { timestamp: MoreThan(new Date(minStamp)) } + : maxStamp + ? { timestamp: LessThan(new Date(maxStamp)) } + : {}), + }; + mentions = mentions instanceof Array ? mentions : mentions ? [mentions] : []; + let roleids = [] as string[]; + if (mentions) { + const ms = await Member.find({ where: { id: In(mentions), guild_id: req.params.guild_id as string }, relations: ["roles"] }); + const rSet = new Set(); + ms.forEach((memb) => { + memb.roles.forEach(({ id }) => rSet.add(id)); + }); + roleids = [...rSet]; + } const query: FindManyOptions = { order: { timestamp: sort_order ? (sort_order.toUpperCase() as "ASC" | "DESC") : "DESC", }, - where: { - guild: { - id: req.params.guild_id as string, - }, - ...(content ? { content: Like(`%${content}%`) } : {}), - ...(author_id ? { author_id } : {}), - ...(channel_id - ? { channel_id } - : { - channel_id: In( - ( - await Promise.all( - ( - await Channel.find({ - where: { guild_id: req.params.guild_id as string }, - select: { id: true }, - }) - ).map(async (channel) => { - const perm = await getPermission(req.user_id, req.params.guild_id as string, channel.id); - return perm.has("VIEW_CHANNEL") && perm.has("READ_MESSAGE_HISTORY") ? channel : undefined; - }), - ) - ) - .filter((_) => _ !== undefined) - .map(({ id }) => id), - ), - }), - }, + where: mentions.length + ? [ + { ...where, mention_everyone: true }, + { + ...where, + mentions: { + id: In(mentions), + }, + }, + { + ...where, + mention_roles: { + id: In(roleids), + }, + }, + ] + : where, relations: { author: true, webhook: true, application: true, mentions: true, mention_roles: true, mention_channels: true, sticker_items: true, attachments: true }, }; diff --git a/src/api/routes/guilds/#guild_id/profile/index.ts b/src/api/routes/guilds/#guild_id/profile/index.ts index 0e35aac1e..74f408745 100644 --- a/src/api/routes/guilds/#guild_id/profile/index.ts +++ b/src/api/routes/guilds/#guild_id/profile/index.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { emitEvent, GuildMemberUpdateEvent, handleFile, Member, OrmUtils } from "@spacebar/util"; +import { emitEvent, getPermission, getRights, GuildMemberUpdateEvent, handleFile, Member, OrmUtils, Permissions } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { MemberChangeProfileSchema } from "@spacebar/schemas"; @@ -41,16 +41,31 @@ router.patch( }), async (req: Request, res: Response) => { const { guild_id } = req.params as { [key: string]: string }; - // const member_id = - // req.params.member_id === "@me" ? req.user_id : req.params.member_id; + let { member_id } = req.params as { [key: string]: string }; const body = req.body as MemberChangeProfileSchema; + if (member_id === "@me") member_id = req.user_id; + + const permission = await getPermission(req.user_id, guild_id); + + if (req.user_id === member_id) { + if (body.nick) { + permission.hasThrow(Permissions.FLAGS.CHANGE_NICKNAME); + } + } else { + if (Object.keys(body).length !== 1 || !body.nick) { + const rights = await getRights(req.user_id); + rights.hasThrow("MANAGE_USERS"); + } else { + permission.hasThrow(Permissions.FLAGS.MANAGE_NICKNAMES); + } + } let member = await Member.findOneOrFail({ - where: { id: req.user_id, guild_id }, + where: { id: member_id, guild_id }, relations: { roles: true, user: true }, }); - if (body.banner) body.banner = await handleFile(`/guilds/${guild_id}/users/${req.user_id}/avatars`, body.banner as string); + if (body.banner) body.banner = await handleFile(`/guilds/${guild_id}/users/${member_id}/avatars`, body.banner as string); member = await OrmUtils.mergeDeep(member, body); diff --git a/src/api/routes/guilds/#guild_id/roles/index.ts b/src/api/routes/guilds/#guild_id/roles/index.ts index b8e2b1a3e..b7b58e43d 100644 --- a/src/api/routes/guilds/#guild_id/roles/index.ts +++ b/src/api/routes/guilds/#guild_id/roles/index.ts @@ -60,6 +60,8 @@ router.post( if (role_count > maxRoles) throw DiscordApiErrors.MAXIMUM_ROLES.withParams(maxRoles); + const everyoneRole = await Role.findOne({ where: { id: guild_id } }); + const role = Role.create({ // values before ...body are default and can be overridden position: 1, @@ -69,7 +71,7 @@ router.post( ...body, guild_id: guild_id, managed: false, - permissions: String((req.permission?.bitfield || 0n) & BigInt(body.permissions || "0")), + permissions: String((req.permission?.bitfield || 0n) & BigInt(body.permissions || everyoneRole?.permissions || 0)), tags: undefined, icon: undefined, unicode_emoji: undefined, diff --git a/src/api/routes/guilds/#guild_id/widget.png.ts b/src/api/routes/guilds/#guild_id/widget.png.ts index c1382c906..ee623214c 100644 --- a/src/api/routes/guilds/#guild_id/widget.png.ts +++ b/src/api/routes/guilds/#guild_id/widget.png.ts @@ -24,7 +24,7 @@ import { Request, Response, Router } from "express"; import fs from "fs"; import { HTTPError } from "lambert-server"; import path from "path"; -import { storage } from "../../../../cdn/util/Storage"; +import { storage } from "@spacebar/cdn"; const router: Router = Router({ mergeParams: true }); @@ -83,27 +83,27 @@ router.get( switch (style) { case "shield": ctx.textAlign = "center"; - await drawText(ctx, 73, 13, "#FFFFFF", "thin 10px Verdana", presence); + drawText(ctx, 73, 13, "#FFFFFF", "thin 10px Verdana", presence); break; case "banner1": if (icon) await drawIcon(ctx, 20, 27, 50, icon); - await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22); - await drawText(ctx, 83, 66, "#C9D2F0FF", "thin 11px Verdana", presence); + drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22); + drawText(ctx, 83, 66, "#C9D2F0FF", "thin 11px Verdana", presence); break; case "banner2": if (icon) await drawIcon(ctx, 13, 19, 36, icon); - await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15); - await drawText(ctx, 62, 49, "#C9D2F0FF", "thin 11px Verdana", presence); + drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15); + drawText(ctx, 62, 49, "#C9D2F0FF", "thin 11px Verdana", presence); break; case "banner3": if (icon) await drawIcon(ctx, 20, 20, 50, icon); - await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27); - await drawText(ctx, 83, 58, "#C9D2F0FF", "thin 11px Verdana", presence); + drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27); + drawText(ctx, 83, 58, "#C9D2F0FF", "thin 11px Verdana", presence); break; case "banner4": if (icon) await drawIcon(ctx, 21, 136, 50, icon); - await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27); - await drawText(ctx, 84, 171, "#C9D2F0FF", "thin 12px Verdana", presence); + drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27); + drawText(ctx, 84, 171, "#C9D2F0FF", "thin 12px Verdana", presence); break; default: throw new HTTPError("Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').", 400); diff --git a/src/api/routes/guilds/automations/email-domain-lookup.ts b/src/api/routes/guilds/automations/email-domain-lookup.ts index 943097f8f..63ec39d43 100644 --- a/src/api/routes/guilds/automations/email-domain-lookup.ts +++ b/src/api/routes/guilds/automations/email-domain-lookup.ts @@ -59,6 +59,7 @@ router.post( }, ); +// noinspection JSUnusedLocalSymbols - TODO: implement router.post( "/verify-code", route({ diff --git a/src/api/routes/guilds/index.ts b/src/api/routes/guilds/index.ts index 78ba93a2c..663677dcb 100644 --- a/src/api/routes/guilds/index.ts +++ b/src/api/routes/guilds/index.ts @@ -55,7 +55,7 @@ router.post( const guild = await Guild.createGuild({ ...body, owner_id: req.user_id, - template_guild_id: null, + source_guild_id: null, }); const { autoJoin } = Config.get().guild; diff --git a/src/api/routes/guilds/templates/index.ts b/src/api/routes/guilds/templates/index.ts index 391f9c8ae..6eb6ffbee 100644 --- a/src/api/routes/guilds/templates/index.ts +++ b/src/api/routes/guilds/templates/index.ts @@ -17,10 +17,10 @@ */ import { route } from "@spacebar/api"; -import { Config, DiscordApiErrors, Guild, Member, Template } from "@spacebar/util"; +import { Config, DiscordApiErrors, Guild, Member, Tag, Template } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; -import { GuildTemplateCreateSchema } from "@spacebar/schemas"; +import { ChannelType, GuildTemplateCreateSchema } from "@spacebar/schemas"; const router: Router = Router({ mergeParams: true }); @@ -64,7 +64,7 @@ router.post("/:template_code", route({ requestBody: "GuildTemplateCreateSchema" // body comes after the template ...body, owner_id: req.user_id, - template_guild_id: template.source_guild_id, + source_guild_id: template.source_guild_id, }); await Member.addToGuild(req.user_id, guild.id); @@ -87,7 +87,28 @@ async function getTemplate(code: string) { headers: { "Content-Type": "application/json" }, }); - return (await discordTemplateData.json()) as Template; + const templateData = (await discordTemplateData.json()) as Template; + + // Role ID is position in new Discord template schema. Do a little converting. + templateData.serialized_source_guild.roles.forEach((role) => { + role.position = role.id as unknown as number; + }); + + templateData.serialized_source_guild.channels.forEach((channel) => { + if (channel.type === ChannelType.GUILD_FORUM) { + channel.available_tags = + channel.available_tags?.map((tag) => + Tag.create({ + name: tag.name, + emoji_id: tag.emoji_id, + emoji_name: tag.emoji_name, + moderated: tag.moderated, + }), + ) ?? []; + } + }); + + return templateData; } if (code.startsWith("external:")) { diff --git a/src/api/routes/index.ts b/src/api/routes/index.ts new file mode 100644 index 000000000..41250a38d --- /dev/null +++ b/src/api/routes/index.ts @@ -0,0 +1,5 @@ +import { Router } from "express"; + +const router = Router(); + +export default router; diff --git a/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts b/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts index 071fd76af..3083b300d 100644 --- a/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts +++ b/src/api/routes/interactions/#interaction_id/#interaction_token/callback.ts @@ -16,12 +16,10 @@ along with this program. If not, see . */ -import { ButtonStyle, Embed, InteractionCallbackSchema, InteractionCallbackType, MessageComponentType } from "@spacebar/schemas"; -import { route } from "@spacebar/api"; -import { MessageCreateAttachment, MessageCreateCloudAttachment } from "@spacebar/schemas"; +import { ButtonStyle, InteractionCallbackSchema, InteractionCallbackType, MessageComponentType, MessageType } from "@spacebar/schemas"; +import { route, sendMessage } from "@spacebar/api"; import { Request, Response, Router } from "express"; -import { emitEvent, FieldErrors, InteractionSuccessEvent, uploadFile, Attachment, pendingInteractions, User, MessageType } from "@spacebar/util"; -import { sendMessage } from "../../../../util/handlers/Message"; +import { emitEvent, FieldErrors, InteractionSuccessEvent, pendingInteractions, User } from "@spacebar/util"; const router = Router({ mergeParams: true }); @@ -77,7 +75,7 @@ router.post("/", route({}), async (req: Request, res: Response) => { clearTimeout(interaction.timeout); - emitEvent({ + await emitEvent({ event: "INTERACTION_SUCCESS", user_id: interaction?.userId, data: { @@ -121,7 +119,7 @@ router.post("/", route({}), async (req: Request, res: Response) => { content: body.data.content, components: body.data.components || [], tts: body.data.tts, - embeds: (body.data.embeds || []).map((e) => ({ ...e })) as Embed[], + embeds: body.data.embeds || [], attachments: body.data.attachments, poll: body.data.poll, flags: body.data.flags, diff --git a/src/api/routes/interactions/index.ts b/src/api/routes/interactions/index.ts index 5e7721754..5542b9c19 100644 --- a/src/api/routes/interactions/index.ts +++ b/src/api/routes/interactions/index.ts @@ -1,26 +1,26 @@ /* 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 . */ import { randomBytes } from "crypto"; -import { InteractionSchema } from "@spacebar/schemas"; +import { InteractionFailureReason, InteractionSchema } from "@spacebar/schemas"; import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; -import { Config, emitEvent, getPermission, Guild, InteractionCreateEvent, InteractionFailureEvent, InteractionType, Member, Message, Snowflake, User } from "@spacebar/util"; +import { Config, emitEvent, getPermission, Guild, InteractionCreateEvent, InteractionFailureEvent, InteractionType, Member, Message, Snowflake } from "@spacebar/util"; import { pendingInteractions } from "@spacebar/util/imports/Interactions"; import { InteractionCreateSchema } from "@spacebar/schemas/api/bots/InteractionCreateSchema"; @@ -32,7 +32,7 @@ router.post("/", route({}), async (req: Request, res: Response) => { const interactionId = Snowflake.generate(); const interactionToken = randomBytes(24).toString("base64url"); - emitEvent({ + await emitEvent({ event: "INTERACTION_CREATE", user_id: req.user_id, data: { @@ -94,7 +94,7 @@ router.post("/", route({}), async (req: Request, res: Response) => { interactionData.message = await Message.findOneOrFail({ where: { id: body.message_id, flags: undefined }, relations: { author: true } }); } - emitEvent({ + await emitEvent({ event: "INTERACTION_CREATE", user_id: body.application_id, data: interactionData, @@ -107,7 +107,7 @@ router.post("/", route({}), async (req: Request, res: Response) => { data: { id: interactionId, nonce: body.nonce, - reason_code: 2, // when types are done: InteractionFailureReason.TIMEOUT, + reason_code: InteractionFailureReason.TIMEOUT, }, } as InteractionFailureEvent); }, 3000); diff --git a/src/api/routes/invites/index.ts b/src/api/routes/invites/index.ts index 316e76867..15da05dc2 100644 --- a/src/api/routes/invites/index.ts +++ b/src/api/routes/invites/index.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Ban, Config, DiscordApiErrors, emitEvent, getPermission, Guild, Invite, InviteDeleteEvent, PublicInviteRelation, User } from "@spacebar/util"; +import { Ban, Config, DiscordApiErrors, emitEvent, getPermission, Guild, Invite, InviteDeleteEvent, PublicInviteRelation } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { UserFlags } from "@spacebar/schemas"; diff --git a/src/api/routes/lobbies/#lobby_id/index.ts b/src/api/routes/lobbies/#lobby_id/index.ts new file mode 100644 index 000000000..4d5068dde --- /dev/null +++ b/src/api/routes/lobbies/#lobby_id/index.ts @@ -0,0 +1,183 @@ +/* + 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 . +*/ + +import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; +import { LobbyStore, DiscordApiErrors, Channel, Member, Lobby, LobbyMemberDTO, LobbyDTO } from "@spacebar/util"; + +const router = Router(); + +function validateMetadata(metadata?: Record | null): void { + if (!metadata) return; + + const totalLength = Object.entries(metadata).reduce((sum, [key, value]) => { + return sum + key.length + value.length; + }, 0); + + if (totalLength > 1000) { + throw new Error("Metadata total length cannot exceed 1000 characters"); + } +} + +function validateIdleTimeout(seconds?: number): void { + if (seconds !== undefined && (seconds < 5 || seconds > 604800)) { + throw new Error("Idle timeout must be between 5 and 604800 seconds"); + } +} + +router.get( + "/", + route({ + responses: { + 200: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + + const lobby = LobbyStore.getLobby(lobby_id); + if (!lobby) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + LobbyStore.updateLobbyActivity(lobby_id); + const lobbyResponse: LobbyDTO = LobbyStore.toLobbyResponse(lobby); + return res.json(lobbyResponse); + }, +); + +router.patch( + "/", + route({ + requestBody: "LobbyUpdateSchema", + responses: { + 200: {}, + 400: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + const body = req.body as { + metadata?: Record; + members?: LobbyMemberDTO[]; + idle_timeout_seconds?: number; + }; + + const lobby = LobbyStore.getLobby(lobby_id); + if (!lobby) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + validateMetadata(body.metadata); + validateIdleTimeout(body.idle_timeout_seconds); + + if (body.members && body.members.length > 25) { + throw new Error("Cannot update lobby with more than 25 members"); + } + + const updates: Partial> = {}; + if (body.metadata !== undefined) updates.metadata = body.metadata; + if (body.members !== undefined) updates.members = body.members; + if (body.idle_timeout_seconds !== undefined) updates.idle_timeout_seconds = body.idle_timeout_seconds; + + const updatedLobby = LobbyStore.updateLobby(lobby_id, updates); + const lobbyResponse: LobbyDTO = LobbyStore.toLobbyResponse(updatedLobby!); + return res.json(lobbyResponse); + }, +); + +router.delete( + "/", + route({ + responses: { + 204: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + + const deleted = LobbyStore.deleteLobby(lobby_id); + if (!deleted) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + return res.status(204).send(); + }, +); + +router.patch( + "/channel-linking", + route({ + requestBody: "LobbyChannelLinkSchema", + responses: { + 200: {}, + 400: {}, + 403: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + const body = req.body as { channel_id?: string }; + + const lobby = LobbyStore.getLobby(lobby_id); + if (!lobby) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + const member = lobby.members.find((m) => m.id === req.user_id); + if (!member) { + throw new Error("Unknown member"); + } + + if (!(member.flags && member.flags & 1)) { + throw new Error("Missing permissions"); + } + + if (body.channel_id) { + try { + const channel = await Channel.findOneOrFail({ + where: { id: body.channel_id }, + }); + + if (channel.guild_id) { + const memberInGuild = await Member.findOne({ + where: { id: req.user_id, guild_id: channel.guild_id }, + }); + if (!memberInGuild) { + throw new Error("Missing permissions"); + } + } + } catch (error) { + throw new Error("Unknown channel"); + } + } + + const updatedLobby = LobbyStore.updateLobby(lobby_id, { + linked_channel: body.channel_id || undefined, + }); + + const lobbyResponse: LobbyDTO = LobbyStore.toLobbyResponse(updatedLobby!); + return res.json(lobbyResponse); + }, +); + +export default router; diff --git a/src/api/routes/lobbies/#lobby_id/members/#user_id.ts b/src/api/routes/lobbies/#lobby_id/members/#user_id.ts new file mode 100644 index 000000000..6a80b8375 --- /dev/null +++ b/src/api/routes/lobbies/#lobby_id/members/#user_id.ts @@ -0,0 +1,89 @@ +/* + 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 . +*/ + +import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; +import { LobbyStore, DiscordApiErrors, LobbyMemberDTO } from "@spacebar/util"; + +const router = Router(); + +router.put( + "/", + route({ + requestBody: "LobbyMemberUpdateSchema", + responses: { + 200: {}, + 400: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + const user_id = req.params.user_id as string; + const body = req.body as { + metadata?: Record; + flags?: number; + }; + + const lobby = LobbyStore.getLobby(lobby_id); + if (!lobby) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + const member: LobbyMemberDTO = { + id: user_id, + metadata: body.metadata, + flags: body.flags, + }; + + const success = LobbyStore.addMember(lobby_id, member); + if (!success) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + return res.json(member); + }, +); + +router.delete( + "/", + route({ + responses: { + 204: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + const user_id = req.params.user_id as string; + + const lobby = LobbyStore.getLobby(lobby_id); + if (!lobby) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + const success = LobbyStore.removeMember(lobby_id, user_id); + if (!success) { + throw new Error("Unknown member"); + } + + return res.status(204).send(); + }, +); + +export default router; diff --git a/src/api/routes/lobbies/#lobby_id/members/index.ts b/src/api/routes/lobbies/#lobby_id/members/index.ts new file mode 100644 index 000000000..f4b140a1e --- /dev/null +++ b/src/api/routes/lobbies/#lobby_id/members/index.ts @@ -0,0 +1,50 @@ +/* + 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 . +*/ + +import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; +import { LobbyStore, DiscordApiErrors } from "@spacebar/util"; + +const router = Router(); + +router.delete( + "/@me", + route({ + responses: { + 204: {}, + 404: {}, + }, + }), + async (req: Request, res: Response) => { + const lobby_id = req.params.lobby_id as string; + + const lobby = LobbyStore.getLobby(lobby_id); + if (!lobby) { + throw DiscordApiErrors.UNKNOWN_LOBBY; + } + + const success = LobbyStore.removeMember(lobby_id, req.user_id); + if (!success) { + throw new Error("Unknown member"); + } + + return res.status(204).send(); + }, +); + +export default router; diff --git a/src/api/routes/lobbies/index.ts b/src/api/routes/lobbies/index.ts new file mode 100644 index 000000000..635b1e85a --- /dev/null +++ b/src/api/routes/lobbies/index.ts @@ -0,0 +1,87 @@ +/* + 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 . +*/ + +import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; +import { + LobbyStore, + DiscordApiErrors, + Snowflake, + LobbyMemberDTO, + LobbyDTO, +} from "@spacebar/util"; + +const router = Router(); + +function validateMetadata(metadata?: Record | null): void { + if (!metadata) return; + + const totalLength = Object.entries(metadata).reduce((sum, [key, value]) => { + return sum + key.length + value.length; + }, 0); + + if (totalLength > 1000) { + throw new Error("Metadata total length cannot exceed 1000 characters"); + } +} + +function validateIdleTimeout(seconds?: number): void { + if (seconds !== undefined && (seconds < 5 || seconds > 604800)) { + throw new Error("Idle timeout must be between 5 and 604800 seconds"); + } +} + +router.post( + "/", + route({ + requestBody: "LobbyCreateSchema", + responses: { + 200: {}, + 400: {}, + 401: {}, + }, + }), + async (req: Request, res: Response) => { + const body = req.body as { + metadata?: Record; + members?: LobbyMemberDTO[]; + idle_timeout_seconds?: number; + }; + + validateMetadata(body.metadata); + validateIdleTimeout(body.idle_timeout_seconds); + + if (body.members && body.members.length > 25) { + throw new Error("Cannot create lobby with more than 25 members"); + } + + const lobbyId = Snowflake.generate(); + const lobby = LobbyStore.createLobby({ + id: lobbyId, + application_id: req.user_id, + metadata: body.metadata, + members: body.members || [], + idle_timeout_seconds: body.idle_timeout_seconds || 300, + }); + + const lobbyResponse: LobbyDTO = LobbyStore.toLobbyResponse(lobby); + return res.json(lobbyResponse); + }, +); + +export default router; diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts index b989899ae..47a21eb49 100644 --- a/src/api/routes/oauth2/authorize.ts +++ b/src/api/routes/oauth2/authorize.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { ApiError, Application, DiscordApiErrors, FieldErrors, Member, Permissions, User, getPermission } from "@spacebar/util"; +import { ApiError, Application, DiscordApiErrors, FieldErrors, Member, Permissions, User, getPermission, Role } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { ApplicationAuthorizeSchema } from "@spacebar/schemas"; const router = Router({ mergeParams: true }); @@ -213,6 +213,16 @@ router.post( if (!app.bot) throw new ApiError("OAuth2 application does not have a bot", 50010, 400); await Member.addToGuild(app.id, body.guild_id); + if (body.permissions) { + const role = Role.create({ + managed: true, + name: app.name, + permissions: body.permissions, + guild_id: body.guild_id, + }); + await role.save(); + await Member.addRole(body.guild_id, req.user_id, role.id); + } return res.json({ location: "/oauth2/authorized", // redirect URL diff --git a/src/api/routes/policies/instance/connections.ts b/src/api/routes/policies/instance/connections.ts new file mode 100644 index 000000000..e6eb3f787 --- /dev/null +++ b/src/api/routes/policies/instance/connections.ts @@ -0,0 +1,28 @@ +/* + 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 . +*/ + +import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; + +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + res.json({}); +}); + +export default router; diff --git a/src/api/routes/reporting/index.ts b/src/api/routes/reporting/index.ts index c4ae61169..7f8e539e9 100644 --- a/src/api/routes/reporting/index.ts +++ b/src/api/routes/reporting/index.ts @@ -65,7 +65,8 @@ for (const type of Object.values(ReportMenuTypeNames)) { res.sendFile(path.join(__dirname, "..", "..", "..", "..", "assets", "temp_report_menu_responses", `${type}.json`)); }, ); - console.log(`[Server] Route /reporting/menu/${type} registered (reports).`); + if (process.env.LOG_ROUTES !== "false") console.log(`[Server] Route /reporting/menu/${type} registered (reports).`); + // noinspection JSUnusedLocalSymbols - TODO: implement router.post( `/${type}`, route({ @@ -201,6 +202,6 @@ for (const type of Object.values(ReportMenuTypeNames)) { throw new HTTPError("Validation success - implementation TODO", 418); }, ); - console.log(`[Server] Route /reporting/${type} registered (reports).`); + if (process.env.LOG_ROUTES !== "false") console.log(`[Server] Route /reporting/${type} registered (reports).`); } export default router; diff --git a/src/api/routes/stickers/#sticker_id/index.ts b/src/api/routes/stickers/#sticker_id/index.ts index ddfbbe4cf..7188d93fb 100644 --- a/src/api/routes/stickers/#sticker_id/index.ts +++ b/src/api/routes/stickers/#sticker_id/index.ts @@ -33,7 +33,22 @@ router.get( async (req: Request, res: Response) => { const { sticker_id } = req.params as { [key: string]: string }; - res.json(await Sticker.find({ where: { id: sticker_id } })); + res.json(await Sticker.findOne({ where: { id: sticker_id } })); + }, +); +router.get( + "/guild", + route({ + responses: { + 200: { + body: "Sticker", + }, + }, + }), + async (req: Request, res: Response) => { + const { sticker_id } = req.params as { [key: string]: string }; + const sticker = await Sticker.findOne({ where: { id: sticker_id }, relations: { guild: true } }); + res.json(await sticker?.guild?.ToGuildSource()); }, ); diff --git a/src/api/routes/users/#user_id/delete.ts b/src/api/routes/users/#user_id/delete.ts index f1eea7fe2..a0730cb79 100644 --- a/src/api/routes/users/#user_id/delete.ts +++ b/src/api/routes/users/#user_id/delete.ts @@ -71,7 +71,7 @@ router.post( console.log(`[Instance ban] Deleting DM channel ${channel.id} for user ${user.id}`); await emitEvent({ event: "CHANNEL_DELETE", - data: channel, + data: channel.toJSON(), channel_id: channel.id, } as ChannelDeleteEvent); await Recipient.delete({ channel_id: channel.id }); @@ -113,7 +113,7 @@ router.post( if (remainingRecipients.length === 0) { await emitEvent({ event: "CHANNEL_DELETE", - data: channel, + data: channel.toJSON(), channel_id: channel.id, } as ChannelDeleteEvent); await Channel.deleteChannel(channel); diff --git a/src/api/routes/users/@me/billing/country-code.ts b/src/api/routes/users/@me/billing/country-code.ts index dd2be6d43..b65517ebb 100644 --- a/src/api/routes/users/@me/billing/country-code.ts +++ b/src/api/routes/users/@me/billing/country-code.ts @@ -21,8 +21,8 @@ import { route } from "@spacebar/api"; const router: Router = Router({ mergeParams: true }); -router.get("/", route({}), (req: Request, res: Response) => { - //TODO +router.get("/", route({}), async (req: Request, res: Response) => { + // Stub: returns empty country_code since IpDataClient is not available res.json({ country_code: "US" }).status(200); }); diff --git a/src/api/routes/users/@me/billing/location-info.ts b/src/api/routes/users/@me/billing/location-info.ts index 5a57c0ea6..eb654f50f 100644 --- a/src/api/routes/users/@me/billing/location-info.ts +++ b/src/api/routes/users/@me/billing/location-info.ts @@ -16,14 +16,13 @@ along with this program. If not, see . */ -import { Request, Response, Router } from "express"; import { route } from "@spacebar/api"; +import { Request, Response, Router } from "express"; const router: Router = Router({ mergeParams: true }); -router.get("/", route({}), (req: Request, res: Response) => { - //TODO - // TODO: subdivision_code (optional) +router.get("/", route({}), async (req: Request, res: Response) => { + // Stub: returns empty country_code since IpDataClient is not available res.json({ country_code: "US" }).status(200); }); diff --git a/src/api/routes/users/@me/channels.ts b/src/api/routes/users/@me/channels.ts index 8810f9c39..dc44935e3 100644 --- a/src/api/routes/users/@me/channels.ts +++ b/src/api/routes/users/@me/channels.ts @@ -24,44 +24,37 @@ import { DmChannelCreateSchema } from "@spacebar/schemas"; const router: Router = Router({ mergeParams: true }); router.get( - "/", - route({ - responses: { - 200: { - body: "APIDMChannelArray", - }, - }, - }), - async (req: Request, res: Response) => { - const recipients = await Recipient.find({ - where: { user_id: req.user_id, closed: false }, - relations: { channel: { recipients: true } }, - }); - res.json(await Promise.all(recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id])))); - }, + "/", + route({ + responses: { + 200: { + body: "APIDMChannelArray", + }, + }, + }), + async (req: Request, res: Response) => { + const recipients = await Recipient.find({ + where: { user_id: req.user_id, closed: false }, + relations: { channel: { recipients: true } }, + }); + res.json(await Promise.all(recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id])))); + }, ); router.post( - "/", - route({ - right: "CREATE_DMS", - requestBody: "DmChannelCreateSchema", - responses: { - 200: { - body: "DmChannelDTO", - }, - }, - }), - async (req: Request, res: Response) => { - const body = req.body as DmChannelCreateSchema; - res.json( - await Channel.createDMChannel( - body.recipients || (body.recipient_id ? [body.recipient_id] : []), - req.user_id, - body.name, - ), - ); - }, + "/", + route({ + requestBody: "DmChannelCreateSchema", + responses: { + 200: { + body: "DmChannelDTO", + }, + }, + }), + async (req: Request, res: Response) => { + const body = req.body as DmChannelCreateSchema; + res.json(await Channel.createDMChannel(body.recipients || (body.recipient_id ? [body.recipient_id] : []), req.user_id, body.name)); + }, ); export default router; diff --git a/src/api/routes/users/@me/consent-grants/index.ts b/src/api/routes/users/@me/consent-grants/index.ts index bd224ae0a..2dfb64582 100644 --- a/src/api/routes/users/@me/consent-grants/index.ts +++ b/src/api/routes/users/@me/consent-grants/index.ts @@ -19,6 +19,7 @@ import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; import { ConsentGrant, ConsentGrantStatus, ConsentType, UserConsent, ConsentStatus, DiscordApiErrors, User } from "@spacebar/util"; +import { Snowflake } from "@spacebar/util"; import { FindOptionsWhere } from "typeorm"; const router: Router = Router(); @@ -186,16 +187,6 @@ router.post( const consent = UserConsent.create({ user_id, service_id: grant.service_id || "gnap_grant", - consent_type: grant.consent_type, - item_id: grant.item_id, - target_user_id: grant.requester_id, - status: provisional ? ConsentStatus.PROVISIONAL : ConsentStatus.GRANTED, - granted_at: new Date(), - expires_at: expires_at ? new Date(expires_at) : grant.expires_at || undefined, - extra_data: { - grant_id: grant.id, - granted_access: grant.granted_access, - }, }); await consent.save(); @@ -309,15 +300,11 @@ router.delete( const consent = await UserConsent.findOne({ where: { user_id, - target_user_id: grant.requester_id, - consent_type: grant.consent_type, - item_id: grant.item_id, + service_id: grant.service_id || "gnap_grant", }, }); if (consent) { - consent.status = ConsentStatus.RETRACTED; - consent.retracted_at = new Date(); - await consent.save(); + await consent.remove(); } } grant.status = ConsentGrantStatus.DENIED; diff --git a/src/api/routes/users/@me/consents/#service_id/index.ts b/src/api/routes/users/@me/consents/#service_id/index.ts index 9eeae25f6..e4fecaced 100644 --- a/src/api/routes/users/@me/consents/#service_id/index.ts +++ b/src/api/routes/users/@me/consents/#service_id/index.ts @@ -1,189 +1,44 @@ -/* - 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 . -*/ - import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; -import { UserConsent, ConsentType, ConsentStatus, DiscordApiErrors } from "@spacebar/util"; +import { UserConsent } from "@spacebar/util"; const router: Router = Router(); -router.get( - "/", - route({ - summary: "Get consent details for a specific service", - description: - "Returns the consent record for a specific service for the authenticated user. For HB 805 pairwise consents, use item_id and target_user_id to identify specific consent records.", - query: { - consent_type: { - type: "string", - required: false, - description: "Filter by consent type", - values: Object.values(ConsentType), - }, - item_id: { - type: "string", - required: false, - description: "Filter by item ID (for per-item consents)", - }, - target_user_id: { - type: "string", - required: false, - description: "Filter by target user ID (for HB 805 pairwise consents)", - }, - }, - responses: { - 200: { body: "UserConsentResponse" }, - 404: { body: "APIErrorResponse" }, - }, - }), - async (req: Request, res: Response) => { - const user_id = req.user_id!; - const service_id = req.params.service_id as string; - const consent_type = (req.query.consent_type as ConsentType) || ConsentType.CUSTOM; - const item_id = req.query.item_id as string | undefined; - const target_user_id = req.query.target_user_id as string | undefined; - - const consent = await UserConsent.findOne({ - where: { - user_id, - service_id, - consent_type, - ...(item_id && { item_id }), - ...(target_user_id && { target_user_id }), - }, - }); - - if (!consent) { - throw DiscordApiErrors.UNKNOWN_CONSENT; - } - - res.json(consent.toJSON()); - }, -); - router.put( "/", route({ - summary: "Grant consent for a service", - description: - "Grants consent for a service. Supports GDPR-compliant consent with basis documents for off-platform consents. For HB 805 pairwise consents (O(n²) where n is number of persons), use target_user_id to specify who receives consent to view/share. Based on GNAP (RFC 9635) grant negotiation principles.", - requestBody: "UserConsentGrantSchema", - responses: { - 200: { body: "UserConsentResponse" }, - 204: { body: "null" }, - }, + summary: "Grant consent for a service for the current user", + responses: { 204: { body: "null" } }, }), async (req: Request, res: Response) => { const user_id = req.user_id!; const service_id = req.params.service_id as string; - const { consent_type = ConsentType.CUSTOM, item_id, target_user_id, basis_document_url, basis_document_hash, expires_at, provisional, extra_data } = req.body; - const existing = await UserConsent.findOne({ - where: { - user_id, - service_id, - consent_type, - ...(item_id && { item_id }), - ...(target_user_id && { target_user_id }), - }, + where: { user_id, service_id }, }); - - if (existing) { - existing.status = provisional ? ConsentStatus.PROVISIONAL : ConsentStatus.GRANTED; - existing.granted_at = new Date(); - existing.retracted_at = undefined; - existing.basis_document_url = basis_document_url; - existing.basis_document_hash = basis_document_hash; - existing.expires_at = expires_at ? new Date(expires_at) : undefined; - existing.item_id = item_id; - existing.target_user_id = target_user_id; - existing.extra_data = extra_data; - await existing.save(); - return res.json(existing.toJSON()); + if (!existing) { + const uc = UserConsent.create({ user_id, service_id }); + await uc.save(); } - - const consent = UserConsent.create({ - user_id, - service_id, - consent_type, - item_id, - target_user_id, - status: provisional ? ConsentStatus.PROVISIONAL : ConsentStatus.GRANTED, - basis_document_url, - basis_document_hash, - granted_at: new Date(), - expires_at: expires_at ? new Date(expires_at) : undefined, - extra_data, - }) as UserConsent; - await consent.save(); - return res.json(consent.toJSON()); + return res.status(204).send(); }, ); router.delete( "/", route({ - summary: "Revoke consent for a service (GDPR-compliant)", - description: - "Revokes consent for a service. Per GDPR Article 7(3), withdrawal of consent is as easy as giving consent. For HB 805 pairwise consents, specify item_id and target_user_id. The consent record is retained with RETRACTED status for audit purposes.", - query: { - consent_type: { - type: "string", - required: false, - description: "Consent type to revoke", - values: Object.values(ConsentType), - }, - item_id: { - type: "string", - required: false, - description: "Item ID for per-item consent revocation", - }, - target_user_id: { - type: "string", - required: false, - description: "Target user ID for HB 805 pairwise consent revocation", - }, - }, + summary: "Revoke consent for a service for the current user", responses: { 204: { body: "null" } }, }), async (req: Request, res: Response) => { const user_id = req.user_id!; const service_id = req.params.service_id as string; - const consent_type = (req.query.consent_type as ConsentType) || ConsentType.CUSTOM; - const item_id = req.query.item_id as string | undefined; - const target_user_id = req.query.target_user_id as string | undefined; - const existing = await UserConsent.findOne({ - where: { - user_id, - service_id, - consent_type, - ...(item_id && { item_id }), - ...(target_user_id && { target_user_id }), - }, + where: { user_id, service_id }, }); - if (existing) { - existing.status = ConsentStatus.RETRACTED; - existing.retracted_at = new Date(); - await existing.save(); + await existing.remove(); } - return res.status(204).send(); }, ); diff --git a/src/api/routes/users/@me/consents/index.ts b/src/api/routes/users/@me/consents/index.ts index e6c303296..76a5617d3 100644 --- a/src/api/routes/users/@me/consents/index.ts +++ b/src/api/routes/users/@me/consents/index.ts @@ -1,95 +1,27 @@ -/* - 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 . -*/ - import { route } from "@spacebar/api"; import { Request, Response, Router } from "express"; -import { UserConsent, ConsentType, ConsentStatus } from "@spacebar/util"; -import { FindOptionsWhere } from "typeorm"; +import { UserConsent } from "@spacebar/util"; const router: Router = Router(); router.get( - "/", - route({ - summary: "List consents for the current user", - description: - "Returns all consents for the authenticated user. Supports filtering by consent_type, status, service_id, item_id, and target_user_id. For HB 805 pairwise consents (O(n²) per item), use item_id and target_user_id filters.", - query: { - consent_type: { - type: "string", - required: false, - description: "Filter by consent type", - values: Object.values(ConsentType), - }, - status: { - type: "string", - required: false, - description: "Filter by consent status", - values: Object.values(ConsentStatus), - }, - service_id: { - type: "string", - required: false, - description: "Filter by service ID", - }, - item_id: { - type: "string", - required: false, - description: "Filter by item ID (for per-item consents)", - }, - target_user_id: { - type: "string", - required: false, - description: "Filter by target user ID (for HB 805 pairwise consents)", - }, - }, - responses: { - 200: { body: "UserConsentListResponse" }, - }, - }), - async (req: Request, res: Response) => { - const user_id = req.user_id!; - const { consent_type, status, service_id, item_id, target_user_id } = req.query; - - const where: FindOptionsWhere = { user_id }; - - if (consent_type) { - where.consent_type = consent_type as ConsentType; - } - if (status) { - where.status = status as ConsentStatus; - } - if (service_id) { - where.service_id = service_id as string; - } - if (item_id) { - where.item_id = item_id as string; - } - if (target_user_id) { - where.target_user_id = target_user_id as string; - } - - const consents = await UserConsent.find({ where }); - - res.json({ - consents: consents.map((c) => c.toJSON()), - }); - }, + "/", + route({ + summary: "List consents for the current user", + responses: { + 200: { body: "any" }, + }, + }), + async (req: Request, res: Response) => { + const user_id = req.user_id!; + const consents = await UserConsent.find({ where: { user_id } }); + res.json( + consents.map((c) => ({ + service_id: c.service_id, + consented_at: c.created_at, + })), + ); + }, ); export default router; diff --git a/src/api/routes/users/@me/delete.ts b/src/api/routes/users/@me/delete.ts index 749baf70f..3d5fa6426 100644 --- a/src/api/routes/users/@me/delete.ts +++ b/src/api/routes/users/@me/delete.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Member, User } from "@spacebar/util"; +import { Guild, Member, User, UserSettingsProtos } from "@spacebar/util"; import bcrypt from "bcrypt"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; @@ -55,7 +55,14 @@ router.post( // TODO: decrement guild member count if (correctpass) { + // Check if the user owns any guilds. + const ownedGuilds = await Guild.findOne({ where: { owner_id: req.user_id } }); + if (ownedGuilds) { + throw new HTTPError("User owns guilds and cannot be deleted", 403); + } + const members = await Member.find({ where: { id: req.user_id } }); + await UserSettingsProtos.delete({ user_id: req.user_id }); await Promise.all([User.delete({ id: req.user_id }), ...members.map((member) => Member.removeFromGuild(member.id, member.guild_id))]); res.sendStatus(204); diff --git a/src/api/routes/users/@me/guilds.ts b/src/api/routes/users/@me/guilds.ts index d27406784..8751c3c87 100644 --- a/src/api/routes/users/@me/guilds.ts +++ b/src/api/routes/users/@me/guilds.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Config, Guild, GuildDeleteEvent, GuildMemberRemoveEvent, Member, User, emitEvent } from "@spacebar/util"; +import { Config, Guild, Member } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; diff --git a/src/api/routes/users/@me/guilds/#guild_id/settings.ts b/src/api/routes/users/@me/guilds/#guild_id/settings.ts index 1501ac4b5..72ba07733 100644 --- a/src/api/routes/users/@me/guilds/#guild_id/settings.ts +++ b/src/api/routes/users/@me/guilds/#guild_id/settings.ts @@ -59,8 +59,9 @@ router.patch( const body = req.body as UserGuildSettingsSchema; if (body.channel_overrides) { + // TODO: rewrite to a single query? for (const channel in body.channel_overrides) { - Channel.findOneOrFail({ where: { id: channel } }); + await Channel.findOneOrFail({ where: { id: channel } }); } } @@ -69,7 +70,7 @@ router.patch( select: { settings: true }, }); OrmUtils.mergeDeep(user.settings || {}, body); - Member.update({ id: req.user_id, guild_id: req.params.guild_id as string }, user); + await user.save(); res.json(user.settings); }, diff --git a/src/api/routes/users/@me/mentions.ts b/src/api/routes/users/@me/mentions.ts index 9d4f7060e..d3c05b442 100644 --- a/src/api/routes/users/@me/mentions.ts +++ b/src/api/routes/users/@me/mentions.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { Snowflake, User, Message, Member, Channel, Permissions, timePromise, NewUrlUserSignatureData, Stopwatch, Attachment } from "@spacebar/util"; +import { Snowflake, Message, Member, Channel, Permissions, NewUrlUserSignatureData, Stopwatch, Attachment } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { In, LessThan, FindOptionsWhere } from "typeorm"; diff --git a/src/api/routes/users/@me/notes.ts b/src/api/routes/users/@me/notes.ts index 0b174791a..3fe1682e8 100644 --- a/src/api/routes/users/@me/notes.ts +++ b/src/api/routes/users/@me/notes.ts @@ -79,9 +79,9 @@ router.put( }, }) ) { - Note.update({ owner: { id: owner.id }, target: { id: target.id } }, { owner, target, content: note }); + await Note.update({ owner: { id: owner.id }, target: { id: target.id } }, { owner, target, content: note }); } else { - Note.insert({ + await Note.insert({ id: Snowflake.generate(), owner, target, diff --git a/src/api/routes/users/@me/settings.ts b/src/api/routes/users/@me/settings.ts index 47c206c92..2013611b7 100644 --- a/src/api/routes/users/@me/settings.ts +++ b/src/api/routes/users/@me/settings.ts @@ -17,7 +17,7 @@ */ import { route } from "@spacebar/api"; -import { User, UserSettings, emitEvent, Session, PrivateSessionProjection, PresenceUpdateEvent } from "@spacebar/util"; +import { User, UserSettings, emitEvent, Session, PresenceUpdateEvent } from "@spacebar/util"; import { Request, Response, Router } from "express"; import { UserSettingsUpdateSchema } from "@spacebar/schemas"; diff --git a/src/api/routes/webhooks/#webhook_id/#token/index.ts b/src/api/routes/webhooks/#webhook_id/#token/index.ts index 556ee489f..1dd2dac43 100644 --- a/src/api/routes/webhooks/#webhook_id/#token/index.ts +++ b/src/api/routes/webhooks/#webhook_id/#token/index.ts @@ -149,6 +149,7 @@ router.patch( }, }), async (req: Request, res: Response) => { + // noinspection JSUnusedLocalSymbols - TODO: shouldnt token be checked? const { webhook_id, token } = req.params as { [key: string]: string }; const body = req.body as WebhookUpdateSchema; diff --git a/src/api/util/handlers/Instance.ts b/src/api/util/handlers/Instance.ts index e71c3eda8..6b8d1ec6a 100644 --- a/src/api/util/handlers/Instance.ts +++ b/src/api/util/handlers/Instance.ts @@ -17,7 +17,6 @@ */ import { Session, TimeSpan } from "@spacebar/util"; -import { Like } from "typeorm"; import { setInterval } from "timers"; export async function initInstance() { diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 256ff8b77..88845f853 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -16,9 +16,6 @@ along with this program. If not, see . */ -import * as Sentry from "@sentry/node"; -import { AutomodEvaluator, AutomodActionExecutor } from "@spacebar/util"; - import { EmbedHandlers } from "@spacebar/api"; import { Application, @@ -34,7 +31,6 @@ import { HERE_MENTION, Message, MessageCreateEvent, - MessageType, MessageUpdateEvent, Role, ROLE_MENTION, @@ -55,12 +51,12 @@ import { } from "@spacebar/util"; import { HTTPError } from "lambert-server"; import { In, Or, Equal, IsNull } from "typeorm"; -import { ChannelType, Embed, EmbedType, MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, Reaction } from "@spacebar/schemas"; +import { ChannelType, Embed, EmbedType, MessageCreateAttachment, MessageCreateCloudAttachment, MessageCreateSchema, MessageType, Reaction, ReadStateType } from "@spacebar/schemas"; const allow_empty = false; // TODO: check webhook, application, system author, stickers // TODO: embed gifs/videos/images -const LINK_REGEX = /?/g; +const LINK_REGEX = /?/g; export async function handleMessage(opts: MessageOptions): Promise { const channel = await Channel.findOneOrFail({ @@ -69,14 +65,14 @@ export async function handleMessage(opts: MessageOptions): Promise { }); if (!channel || !opts.channel_id) throw new HTTPError("Channel not found", 404); - let permission: undefined | Permissions; + let permission: null | Permissions = null; const limit = channel.rate_limit_per_user; if (limit) { const lastMsgTime = (await Message.findOne({ where: { channel_id: channel.id, author_id: opts.author_id }, select: { timestamp: true }, order: { timestamp: "DESC" } })) ?.timestamp; if (lastMsgTime && Date.now() - limit * 1000 < +lastMsgTime) { - permission ||= await getPermission(opts.author_id, channel.guild_id, channel.id); + permission = await getPermission(opts.author_id, channel.guild_id, channel); //FIXME MANAGE_MESSAGES and MANAGE_CHANNELS will need to be removed once they're gone as checks if (!permission.has("MANAGE_MESSAGES") && !permission.has("MANAGE_CHANNELS") && !permission.has("BYPASS_SLOWMODE")) { throw DiscordApiErrors.SLOWMODE_RATE_LIMIT; @@ -96,9 +92,8 @@ export async function handleMessage(opts: MessageOptions): Promise { [] as { attachment: MessageCreateCloudAttachment; index: number }[], ); - const { interaction_metadata: _im, ...restOpts } = opts; - const message: Message = Message.create({ - ...restOpts, + const message = Message.create({ + ...opts, poll: opts.poll, sticker_items: stickers, guild_id: channel.guild_id, @@ -109,18 +104,8 @@ export async function handleMessage(opts: MessageOptions): Promise { type: opts.type ?? 0, mentions: [], components: opts.components ?? undefined, // Fix Discord-Go? - interaction_metadata: opts.interaction_metadata - ? { - id: String(opts.interaction_metadata.id), - type: opts.interaction_metadata.type, - user_id: String(opts.interaction_metadata.user_id), - authorizing_integration_owners: opts.interaction_metadata.authorizing_integration_owners, - original_response_message_id: opts.interaction_metadata.original_response_message_id ? String(opts.interaction_metadata.original_response_message_id) : undefined, - interacted_message_id: opts.interaction_metadata.interacted_message_id ? String(opts.interaction_metadata.interacted_message_id) : undefined, - name: opts.interaction_metadata.name, - } - : undefined, - }) as Message; + message_reference: opts.message_reference ?? undefined, + }); const ephermal = (message.flags & (1 << 6)) !== 0; if (!ephermal && channel.type === ChannelType.GUILD_PUBLIC_THREAD) { const rep = Channel.getRepository(); @@ -130,7 +115,7 @@ export async function handleMessage(opts: MessageOptions): Promise { } if (!ephermal) { channel.last_message_id = message.id; - channel.save(); + await channel.save(); } if (cloudAttachments && cloudAttachments.length > 0) { @@ -187,6 +172,7 @@ export async function handleMessage(opts: MessageOptions): Promise { message.author = await User.findOneOrFail({ where: { id: opts.author_id }, }); + message.author.clean_data(); const rights = await getRights(opts.author_id); rights.hasThrow("SEND_MESSAGES"); } @@ -241,7 +227,8 @@ export async function handleMessage(opts: MessageOptions): Promise { message.author.avatar = message.avatar; } } else { - permission ||= await getPermission(opts.author_id, channel.guild_id, channel.id); + permission ||= await getPermission(opts.author_id, channel.guild_id, channel); + if (permission === null) throw new HTTPError("permission was null after getPermission", 500); permission.hasThrow("SEND_MESSAGES"); if (permission.cache.member) { message.member = permission.cache.member; @@ -252,7 +239,7 @@ export async function handleMessage(opts: MessageOptions): Promise { permission.hasThrow("READ_MESSAGE_HISTORY"); // code below has to be redone when we add custom message routing if (message.guild_id !== null) { - const guild = await Guild.findOneOrFail({ + await Guild.findOneOrFail({ where: { id: channel.guild_id }, }); if (!opts.message_reference.guild_id) opts.message_reference.guild_id = channel.guild_id; @@ -297,7 +284,7 @@ export async function handleMessage(opts: MessageOptions): Promise { } } /** Q: should be checked if the referenced message exists? ANSWER: NO - otherwise backfilling won't work **/ + otherwise backfilling won't work **/ if (MessageType.THREAD_STARTER_MESSAGE !== message.type && MessageType.THREAD_CREATED !== message.type) message.type = MessageType.REPLY; } } @@ -332,9 +319,9 @@ export async function handleMessage(opts: MessageOptions): Promise { content = content.replace(/ *`[^)]*` */g, ""); // remove codeblocks // root@Rory - 20/02/2023 - This breaks channel mentions in test client. We're not sure this was used in older clients. /*for (const [, mention] of content.matchAll(CHANNEL_MENTION)) { - if (!mention_channel_ids.includes(mention)) - mention_channel_ids.push(mention); - }*/ + if (!mention_channel_ids.includes(mention)) + mention_channel_ids.push(mention); + }*/ for (const [, mention] of content.matchAll(USER_MENTION)) { if (!mention_user_ids.includes(mention)) mention_user_ids.push(mention); @@ -364,8 +351,10 @@ export async function handleMessage(opts: MessageOptions): Promise { }, }); if (referencedMessage && referencedMessage.author_id !== message.author_id) { - const referencedAuthor = await User.findOne({ where: { id: referencedMessage.author_id } }); - if (referencedAuthor) message.mentions.push(referencedAuthor); + message.mentions.push( + // @ts-expect-error it does not like the .toPublicUser() lol + (await User.findOne({ where: { id: referencedMessage.author_id } }))!.toPublicUser(), + ); } // FORWARD @@ -400,8 +389,8 @@ export async function handleMessage(opts: MessageOptions): Promise { // root@Rory - 20/02/2023 - This breaks channel mentions in test client. We're not sure this was used in older clients. /*message.mention_channels = mention_channel_ids.map((x) => - Channel.create({ id: x }), - );*/ + Channel.create({ id: x }), + );*/ message.mention_roles = ( await Promise.all( mention_role_ids.map((x) => { @@ -442,8 +431,8 @@ export async function handleMessage(opts: MessageOptions): Promise { const id = message.interaction_metadata?.user_id; if (id) { let pinged = mention_everyone || channel.type === ChannelType.DM || channel.type === ChannelType.GROUP_DM; - if (!pinged) pinged = !!message.mentions.find((user: User) => user.id === id); - if (!pinged) pinged = !!(await Member.find({ where: { id, roles: Or(...message.mention_roles.map(({ id }: { id: string }) => Equal(id))) } })); + if (!pinged) pinged = !!message.mentions.find((user) => user.id === id); + if (!pinged) pinged = !!(await Member.find({ where: { id, roles: Or(...message.mention_roles.map(({ id }) => Equal(id))) } })); if (pinged) { //stuff } @@ -458,7 +447,7 @@ export async function handleMessage(opts: MessageOptions): Promise { await fillInMissingIDs((await Member.find({ where: { guild_id: channel.guild_id } })).map(({ id }) => id)); } const repository = ReadState.getRepository(); - const condition = { channel_id: channel.id }; + const condition = { channel_id: channel.id, read_state_type: ReadStateType.CHANNEL }; await repository.update({ ...condition, mention_count: IsNull() }, { mention_count: 0 }); await repository.increment(condition, "mention_count", 1); } else { @@ -466,14 +455,14 @@ export async function handleMessage(opts: MessageOptions): Promise { ...(message.mention_roles.length ? await Member.find({ where: [ - ...message.mention_roles.map((role: Role) => { + ...message.mention_roles.map((role) => { return { roles: { id: role.id } }; }), ], }) : [] ).map((member) => member.id), - ...message.mentions.map((user: User) => user.id), + ...message.mentions.map((user) => user.id), ]); if (!!message.content?.match(HERE_MENTION) && permission?.has("MENTION_EVERYONE")) { const ids = (await Member.find({ where: { guild_id: channel.guild_id } })).map(({ id }) => id); @@ -481,36 +470,59 @@ export async function handleMessage(opts: MessageOptions): Promise { } if (users.size) { const repository = ReadState.getRepository(); - const condition = { user_id: Or(...[...users].map((id) => Equal(id))), channel_id: channel.id }; + const condition = { user_id: Or(...[...users].map((id) => Equal(id))), channel_id: channel.id, read_state_type: ReadStateType.CHANNEL }; await fillInMissingIDs([...users]); - - await repository.update({ ...condition, mention_count: IsNull() }, { mention_count: 0 }); await repository.increment(condition, "mention_count", 1); } } - // Automod enforcement - evaluate message against guild automod rules - if (message.guild_id && message.content && message.author) { - const automodResult = await AutomodEvaluator.evaluateMessage({ - content: message.content, - channel, - author: message.author, - guild_id: message.guild_id, - member_roles: permission?.cache.member?.roles?.map((r) => r.id), - }); + const attachmentIndices = new Map( + message.attachments?.map((attachment, index) => { + return [`attachment://${attachment.filename}`, index]; + }), + ); + const attachmentsToRemove = new Set(); + function fetchAttachment(url: string | undefined): Attachment | undefined { + if (url == undefined) { + return undefined; + } + const index = attachmentIndices.get(url); + if (index === undefined) { + return undefined; + } + const attachment = message.attachments?.[index]; + if (attachment === undefined) { + return undefined; + } + attachmentsToRemove.add(index); + return attachment; + } + for (const embed of message.embeds) { + const footer = embed.footer; + const footerAttachment = fetchAttachment(footer?.icon_url); + if (footerAttachment !== undefined) { + footer!.icon_url = footerAttachment.url; + footer!.proxy_icon_url = footerAttachment.proxy_url; + } - if (automodResult.triggered && automodResult.rule) { - await AutomodActionExecutor.executeActions(automodResult.actions, { - message, - channel, - member: permission?.cache.member, - rule_name: automodResult.rule.name, - matched_content: automodResult.matched_content, - keyword: automodResult.keyword, - }); + const image = embed.image; + const imageAttachment = fetchAttachment(image?.url); + if (imageAttachment !== undefined) { + image!.url = imageAttachment.url; + image!.proxy_url = imageAttachment.proxy_url; + } + + const author = embed.author; + const authorAttachment = fetchAttachment(author?.icon_url); + if (authorAttachment !== undefined) { + author!.icon_url = authorAttachment.url; + author!.proxy_icon_url = authorAttachment.proxy_url; } } + message.attachments = message.attachments?.filter((_, index) => { + return !attachmentsToRemove.has(index); + }); // TODO: check and put it all in the body @@ -522,7 +534,7 @@ export async function postHandleMessage(message: Message) { const content = message.content?.replace(/ *`[^)]*` */g, ""); // remove markdown const linkMatches = content?.match(LINK_REGEX) || []; - + message.clean_data(); const data = { ...message }; const currentNormalizedUrls = new Set(); @@ -535,17 +547,20 @@ export async function postHandleMessage(message: Message) { const normalized = normalizeUrl(link); currentNormalizedUrls.add(normalized); } catch (e) { - continue; + /* empty */ } } - - data.embeds.forEach((embed) => { - if (!embed.type) { - embed.type = EmbedType.rich; - } - }); + if (data.embeds != undefined) { + data.embeds?.forEach((embed) => { + if (!embed.type) { + embed.type = EmbedType.rich; + } + }); + } // Filter out embeds that could be links, start from scratch - data.embeds = data.embeds.filter((embed) => embed.type === "rich"); + if (data.embeds != undefined) { + data.embeds = data.embeds?.filter((embed) => embed.type === "rich"); + } const seenNormalizedUrls = new Set(); const uniqueLinks: string[] = []; @@ -564,13 +579,14 @@ export async function postHandleMessage(message: Message) { } } catch (e) { // Invalid URL, skip - continue; } } if (uniqueLinks.length === 0) { // No valid unique links found, update message to remove old embeds - data.embeds = data.embeds.filter((embed) => embed.type === "rich"); + if (data.embeds != undefined) { + data.embeds = data.embeds?.filter((embed) => embed.type === "rich"); + } const author = data.author?.toPublicUser(); const event = { event: "MESSAGE_UPDATE", @@ -580,7 +596,8 @@ export async function postHandleMessage(message: Message) { author, }, } as MessageUpdateEvent; - await Promise.all([emitEvent(event), Message.update({ id: message.id, channel_id: message.channel_id }, { embeds: data.embeds })]); + const embeds = data.embeds == undefined ? [] : data.embeds; + await Promise.all([emitEvent(event), Message.update({ id: message.id, channel_id: message.channel_id }, { embeds: embeds })]); return; } @@ -603,7 +620,10 @@ export async function postHandleMessage(message: Message) { }); if (cached) { - data.embeds.push(cached.embed); + if (data.embeds == undefined) { + data.embeds = []; + } + data.embeds?.push(cached.embed); continue; } @@ -624,20 +644,23 @@ export async function postHandleMessage(message: Message) { embed: embed, }); cachePromises.push(cache.save()); - data.embeds.push(embed); + if (data.embeds == undefined) { + data.embeds = []; + } + data.embeds?.push(embed); } } catch (e) { console.error(`[Embeds] Error while generating embed for ${link}`, e); } } - + const embeds = data.embeds == undefined ? [] : data.embeds; await Promise.all([ emitEvent({ event: "MESSAGE_UPDATE", channel_id: message.channel_id, data, } as MessageUpdateEvent), - Message.update({ id: message.id, channel_id: message.channel_id }, { embeds: data.embeds }), + Message.update({ id: message.id, channel_id: message.channel_id }, { embeds: embeds }), ...cachePromises, ]); } @@ -669,7 +692,7 @@ interface MessageOptions extends MessageCreateSchema { author_id?: string; webhook_id?: string; application_id?: string; - embeds?: Embed[]; + embeds?: Embed[] | null; reactions?: Reaction[]; channel_id?: string; attachments?: (MessageCreateAttachment | MessageCreateCloudAttachment | Attachment)[]; // why are we masking this? diff --git a/src/api/util/handlers/Webhook.ts b/src/api/util/handlers/Webhook.ts index fe1441716..439edbb23 100644 --- a/src/api/util/handlers/Webhook.ts +++ b/src/api/util/handlers/Webhook.ts @@ -1,5 +1,5 @@ import { handleMessage, postHandleMessage } from "@spacebar/api"; -import { Attachment, Config, DiscordApiErrors, emitEvent, FieldErrors, Message, MessageCreateEvent, uploadFile, ValidateName, Webhook } from "@spacebar/util"; +import { Attachment, Channel, Config, DiscordApiErrors, emitEvent, FieldErrors, Message, MessageCreateEvent, uploadFile, ValidateName, Webhook } from "@spacebar/util"; import { Request, Response } from "express"; import { HTTPError } from "lambert-server"; import { MoreThan } from "typeorm"; @@ -35,6 +35,7 @@ export const executeWebhook = async (req: Request, res: Response) => { } const wait = req.query.wait === "true"; + const thread_id = typeof req.query.thread_id === "string" ? req.query.thread_id : undefined; if (!wait) { res.status(204).send(); @@ -52,7 +53,7 @@ export const executeWebhook = async (req: Request, res: Response) => { // TODO: creating messages by users checks if the user can bypass rate limits, we cant do that on webhooks, but maybe we could check the application if there is one? const limits = Config.get().limits; - if (limits.absoluteRate.register.enabled) { + if (limits.absoluteRate.sendMessage.enabled) { const count = await Message.count({ where: { channel_id: webhook.channel_id, @@ -73,10 +74,20 @@ export const executeWebhook = async (req: Request, res: Response) => { } } + let sendChannel = webhook.channel; + if (thread_id) { + sendChannel = await Channel.findOneOrFail({ + where: { + id: thread_id, + parent_id: webhook.channel.id, + }, + }); + } + const files = (req.files as Express.Multer.File[]) ?? []; for (const currFile of files) { try { - const file = await uploadFile(`/attachments/${webhook.channel.id}`, currFile); + const file = await uploadFile(`/attachments/${sendChannel.id}`, currFile); attachments.push(Attachment.create({ ...file, proxy_url: file.url })); } catch (error) { if (wait) res.status(400).json({ message: error?.toString() }); @@ -95,7 +106,7 @@ export const executeWebhook = async (req: Request, res: Response) => { application_id: webhook.application?.id, embeds, // TODO: Support thread_id/thread_name once threads are implemented - channel_id: webhook.channel_id, + channel_id: sendChannel.id, attachments, timestamp: new Date(), }); @@ -104,14 +115,14 @@ export const executeWebhook = async (req: Request, res: Response) => { //@ts-ignore dont care2 message.edited_timestamp = null; - webhook.channel.last_message_id = message.id; + sendChannel.last_message_id = message.id; await Promise.all([ message.save(), - webhook.channel.save(), + sendChannel.save(), emitEvent({ event: "MESSAGE_CREATE", - channel_id: webhook.channel_id, + channel_id: sendChannel.id, data: message, } as MessageCreateEvent), ]); diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts index 2e50c536e..77d0c8f0c 100644 --- a/src/api/util/utility/EmbedHandlers.ts +++ b/src/api/util/utility/EmbedHandlers.ts @@ -27,6 +27,7 @@ export const DEFAULT_FETCH_OPTIONS: RequestInit = { redirect: "follow", headers: { "user-agent": "Mozilla/5.0 (compatible; Spacebar/1.0; +https://github.com/spacebarchat/server)", + "accept-language": "en-US,en;q=0.9", }, // size: 1024 * 1024 * 5, // grabbed from config later method: "GET", @@ -105,10 +106,11 @@ export const getMetaDescriptions = (text: string) => { }; }; -const doFetch = async (url: URL) => { +const doFetch = async (url: URL, opts?: RequestInit) => { try { const res = await fetch(url, { ...DEFAULT_FETCH_OPTIONS, + ...opts, }); if (res.headers.get("content-length")) { const contentLength = parseInt(res.headers.get("content-length")!); @@ -449,7 +451,7 @@ export const EmbedHandlers: { "youtu.be": (url) => EmbedHandlers["www.youtube.com"](url), "youtube.com": (url) => EmbedHandlers["www.youtube.com"](url), "www.youtube.com": async (url: URL): Promise => { - const response = await doFetch(url); + const response = await doFetch(url, { headers: { cookie: "CONSENT=PENDING+999; hl=en" } }); if (!response) return null; const metas = getMetaDescriptions(await response.text()); diff --git a/src/apply-migrations.ts b/src/apply-migrations.ts index d43f67bf4..7d3ac90af 100644 --- a/src/apply-migrations.ts +++ b/src/apply-migrations.ts @@ -28,4 +28,4 @@ async function main() { } } -main().then((r) => console.log("meow")); +main().then(() => console.log("meow")); diff --git a/src/bundle/start.ts b/src/bundle/start.ts index af288f8ee..254241f67 100644 --- a/src/bundle/start.ts +++ b/src/bundle/start.ts @@ -16,7 +16,6 @@ along with this program. If not, see . */ -// process.env.MONGOMS_DEBUG = "true"; import moduleAlias from "module-alias"; moduleAlias(__dirname + "../../../package.json"); @@ -28,38 +27,10 @@ import { initStats } from "./stats"; import { config } from "dotenv"; config({ quiet: true }); -import { execSync } from "child_process"; -import { centerString, Logo } from "@spacebar/util"; -import fs from "fs"; -import path from "path"; +import { centerString, getRevInfoOrFail, Logo } from "@spacebar/util"; const cores = process.env.THREADS ? parseInt(process.env.THREADS) : 1; -function getRevInfoOrFail(): { rev: string | null; lastModified: number } { - const rootDir = path.join(__dirname, "../../"); - // sanity check - if (!fs.existsSync(path.join(rootDir, "package.json"))) { - console.log(red("Error: Cannot find package.json in root directory. Are you running from the correct location?")); - } - - // use .rev file if it exists - if (fs.existsSync(path.join(__dirname, "../../.rev"))) { - return JSON.parse(fs.readFileSync(path.join(rootDir, ".rev"), "utf-8")); - } - - // fall back to invoking git - try { - const rev = execSync(`git -C "${rootDir}" rev-parse HEAD`).toString().trim(); - const lastModified = Number(execSync(`git -C "${rootDir}" log -1 --format=%cd --date=unix`).toString().trim()); - return { - rev, - lastModified, - }; - } catch (e) { - return { rev: null, lastModified: 0 }; - } -} - if (cluster.isPrimary) { const revInfo = getRevInfoOrFail(); Logo.printLogo().then(() => { diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts index c76e93a81..dff76494e 100644 --- a/src/cdn/Server.ts +++ b/src/cdn/Server.ts @@ -20,9 +20,7 @@ import { Server, ServerOptions } from "lambert-server"; import { Attachment, Config, initDatabase, registerRoutes } from "@spacebar/util"; import { CORS, BodyParser } from "@spacebar/api"; import path from "path"; -import avatarsRoute from "./routes/avatars"; import guildProfilesRoute from "./routes/guild-profiles"; -import iconsRoute from "./routes/role-icons"; import morgan from "morgan"; import { Like } from "typeorm"; @@ -64,10 +62,10 @@ export class CDNServer extends Server { await registerRoutes(this, path.join(__dirname, "routes/")); this.app.use("/guilds/:guild_id/users/:user_id/avatars", guildProfilesRoute); - console.log("[Server] Route /guilds/:guild_id/users/:user_id/avatars registered"); + if (process.env.LOG_ROUTES !== "false") console.log("[Server] Route /guilds/:guild_id/users/:user_id/avatars registered"); this.app.use("/guilds/:guild_id/users/:user_id/banners", guildProfilesRoute); - console.log("[Server] Route /guilds/:guild_id/users/:user_id/banners registered"); + if (process.env.LOG_ROUTES !== "false") console.log("[Server] Route /guilds/:guild_id/users/:user_id/banners registered"); return super.start(); } diff --git a/src/cdn/routes/app-assets.ts b/src/cdn/routes/app-assets.ts index 4ac514258..149d8b351 100644 --- a/src/cdn/routes/app-assets.ts +++ b/src/cdn/routes/app-assets.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/app-icons.ts b/src/cdn/routes/app-icons.ts index c7b1e8678..b67bc1fb0 100644 --- a/src/cdn/routes/app-icons.ts +++ b/src/cdn/routes/app-icons.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/attachments.ts b/src/cdn/routes/attachments.ts index 86a7f0c89..87d96a4c6 100644 --- a/src/cdn/routes/attachments.ts +++ b/src/cdn/routes/attachments.ts @@ -21,8 +21,8 @@ import { Request, Response, Router } from "express"; import imageSize from "image-size"; import { HTTPError } from "lambert-server"; import { multer } from "../util/multer"; -import { storage } from "../util/Storage"; -import { CloudAttachment } from "../../util/entities/CloudAttachment"; +import { storage } from "@spacebar/cdn"; +import { CloudAttachment } from "@spacebar/util"; import { fileTypeFromBuffer } from "file-type"; import { cache } from "../util/cache"; diff --git a/src/cdn/routes/avatar-decoration-presets.ts b/src/cdn/routes/avatar-decoration-presets.ts index 736a281d1..591e0ab17 100644 --- a/src/cdn/routes/avatar-decoration-presets.ts +++ b/src/cdn/routes/avatar-decoration-presets.ts @@ -17,7 +17,7 @@ */ import { Router, Response, Request } from "express"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { HTTPError } from "lambert-server"; import { fileTypeFromBuffer } from "file-type"; import { cache } from "../util/cache"; diff --git a/src/cdn/routes/avatars.ts b/src/cdn/routes/avatars.ts index 793bc45bb..f2d539bdc 100644 --- a/src/cdn/routes/avatars.ts +++ b/src/cdn/routes/avatars.ts @@ -18,7 +18,7 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; diff --git a/src/cdn/routes/badge-icons.ts b/src/cdn/routes/badge-icons.ts index 829baeb18..33134e1a4 100644 --- a/src/cdn/routes/badge-icons.ts +++ b/src/cdn/routes/badge-icons.ts @@ -17,7 +17,7 @@ */ import { Router, Response, Request } from "express"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { HTTPError } from "lambert-server"; import { fileTypeFromBuffer } from "file-type"; import { cache } from "../util/cache"; diff --git a/src/cdn/routes/banners.ts b/src/cdn/routes/banners.ts index ebbbcc4eb..979bdad02 100644 --- a/src/cdn/routes/banners.ts +++ b/src/cdn/routes/banners.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/channel-icons.ts b/src/cdn/routes/channel-icons.ts index 5885b64fe..3b85d4e46 100644 --- a/src/cdn/routes/channel-icons.ts +++ b/src/cdn/routes/channel-icons.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/discover-splashes.ts b/src/cdn/routes/discover-splashes.ts index ea4fbd885..e9c07cb19 100644 --- a/src/cdn/routes/discover-splashes.ts +++ b/src/cdn/routes/discover-splashes.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/discovery-splashes.ts b/src/cdn/routes/discovery-splashes.ts index f87158941..9d6d81c70 100644 --- a/src/cdn/routes/discovery-splashes.ts +++ b/src/cdn/routes/discovery-splashes.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/emojis.ts b/src/cdn/routes/emojis.ts index 56eae53e3..f11af3bad 100644 --- a/src/cdn/routes/emojis.ts +++ b/src/cdn/routes/emojis.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -37,11 +37,11 @@ const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; const router = Router({ mergeParams: true }); const pathPrefix = "emojis"; -router.post("/:guild_id", multer.single("file"), async (req: Request, res: Response) => { +router.post("/:emoji_id", multer.single("file"), async (req: Request, res: Response) => { if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); if (!req.file) throw new HTTPError("Missing file"); const { buffer, size } = req.file; - const { guild_id } = req.params as { [key: string]: string }; + const { emoji_id } = req.params as { [key: string]: string }; let hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); @@ -49,7 +49,7 @@ router.post("/:guild_id", multer.single("file"), async (req: Request, res: Respo if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash - const path = `${pathPrefix}/${guild_id}/${hash}`; + const path = `${pathPrefix}/${emoji_id}`; const endpoint = Config.get().cdn.endpointPublic; await storage.set(path, buffer); @@ -58,16 +58,17 @@ router.post("/:guild_id", multer.single("file"), async (req: Request, res: Respo id: hash, content_type: type.mime, size, - url: `${endpoint}${req.baseUrl}/${guild_id}/${hash}`, + url: `${endpoint}${req.baseUrl}/${emoji_id}`, }); }); -router.get("/:guild_id", cache, async (req: Request, res: Response) => { - let { guild_id } = req.params as { [key: string]: string }; - guild_id = guild_id.split(".")[0]; // remove .file extension - const path = `${pathPrefix}/${guild_id}`; +router.get("/:emoji_id", cache, async (req: Request, res: Response) => { + let { emoji_id } = req.params as { [key: string]: string }; + emoji_id = emoji_id.split(".")[0]; // remove .file extension + const path = `${pathPrefix}/${emoji_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -75,43 +76,14 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { return res.send(file); }); -export const getAvatar = async (req: Request, res: Response) => { - const { guild_id } = req.params as { [key: string]: string }; - let { hash } = req.params as { [key: string]: string }; - hash = hash.split(".")[0]; // remove .file extension - const path = `${pathPrefix}/${guild_id}/${hash}`; - - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); - const type = await fileTypeFromBuffer(file); - - res.set("Content-Type", type?.mime); - - return res.send(file); -}; - -router.get("/:guild_id/:hash", cache, getAvatar); - -router.delete("/:guild_id/:id", async (req: Request, res: Response) => { +router.delete("/:emoji_id", async (req: Request, res: Response) => { if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); - const { guild_id, id } = req.params as { [key: string]: string }; - const path = `${pathPrefix}/${guild_id}/${id}`; + const { emoji_id } = req.params as { [key: string]: string }; + const path = `${pathPrefix}/${emoji_id}`; await storage.delete(path); return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/guild-profiles.ts b/src/cdn/routes/guild-profiles.ts index 279b43611..9d27debf3 100644 --- a/src/cdn/routes/guild-profiles.ts +++ b/src/cdn/routes/guild-profiles.ts @@ -21,7 +21,7 @@ import crypto from "crypto"; import { Request, Response, Router } from "express"; import { HTTPError } from "lambert-server"; import { multer } from "../util/multer"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { cache } from "../util/cache"; diff --git a/src/cdn/routes/icons.ts b/src/cdn/routes/icons.ts index 0106c05c5..6ddd49e9d 100644 --- a/src/cdn/routes/icons.ts +++ b/src/cdn/routes/icons.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/role-icons.ts b/src/cdn/routes/role-icons.ts index 2aaa6197d..f169309c8 100644 --- a/src/cdn/routes/role-icons.ts +++ b/src/cdn/routes/role-icons.ts @@ -18,7 +18,7 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; diff --git a/src/cdn/routes/splashes.ts b/src/cdn/routes/splashes.ts index 07a98cec8..a10709d71 100644 --- a/src/cdn/routes/splashes.ts +++ b/src/cdn/routes/splashes.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/stickers.ts b/src/cdn/routes/stickers.ts index bb7ab2add..a92069493 100644 --- a/src/cdn/routes/stickers.ts +++ b/src/cdn/routes/stickers.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -37,11 +37,11 @@ const ALLOWED_MIME_TYPES = [...ANIMATED_MIME_TYPES, ...STATIC_MIME_TYPES]; const router = Router({ mergeParams: true }); const pathPrefix = "stickers"; -router.post("/:guild_id", multer.single("file"), async (req: Request, res: Response) => { +router.post("/:sticker_id", multer.single("file"), async (req: Request, res: Response) => { if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); if (!req.file) throw new HTTPError("Missing file"); const { buffer, size } = req.file; - const { guild_id } = req.params as { [key: string]: string }; + const { sticker_id } = req.params as { [key: string]: string }; let hash = crypto.createHash("md5").update(Snowflake.generate()).digest("hex"); @@ -49,7 +49,7 @@ router.post("/:guild_id", multer.single("file"), async (req: Request, res: Respo if (!type || !ALLOWED_MIME_TYPES.includes(type.mime)) throw new HTTPError("Invalid file type"); if (ANIMATED_MIME_TYPES.includes(type.mime)) hash = `a_${hash}`; // animated icons have a_ infront of the hash - const path = `${pathPrefix}/${guild_id}/${hash}`; + const path = `${pathPrefix}/${sticker_id}`; const endpoint = Config.get().cdn.endpointPublic; await storage.set(path, buffer); @@ -58,16 +58,17 @@ router.post("/:guild_id", multer.single("file"), async (req: Request, res: Respo id: hash, content_type: type.mime, size, - url: `${endpoint}${req.baseUrl}/${guild_id}/${hash}`, + url: `${endpoint}${req.baseUrl}/${sticker_id}`, }); }); -router.get("/:guild_id", cache, async (req: Request, res: Response) => { - let { guild_id } = req.params as { [key: string]: string }; - guild_id = guild_id.split(".")[0]; // remove .file extension - const path = `${pathPrefix}/${guild_id}`; +router.get("/:sticker_id", cache, async (req: Request, res: Response) => { + let { sticker_id } = req.params as { [key: string]: string }; + sticker_id = sticker_id.split(".")[0]; // remove .file extension + const path = `${pathPrefix}/${sticker_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -75,43 +76,14 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { return res.send(file); }); -export const getAvatar = async (req: Request, res: Response) => { - const { guild_id } = req.params as { [key: string]: string }; - let { hash } = req.params as { [key: string]: string }; - hash = hash.split(".")[0]; // remove .file extension - const path = `${pathPrefix}/${guild_id}/${hash}`; - - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); - const type = await fileTypeFromBuffer(file); - - res.set("Content-Type", type?.mime); - - return res.send(file); -}; - -router.get("/:guild_id/:hash", cache, getAvatar); - -router.delete("/:guild_id/:id", async (req: Request, res: Response) => { +router.delete("/:sticker_id/", async (req: Request, res: Response) => { if (req.headers.signature !== Config.get().security.requestSignature) throw new HTTPError("Invalid request signature"); - const { guild_id, id } = req.params as { [key: string]: string }; - const path = `${pathPrefix}/${guild_id}/${id}`; + const { sticker_id } = req.params as { [key: string]: string }; + const path = `${pathPrefix}/${sticker_id}`; await storage.delete(path); return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/routes/team-icons.ts b/src/cdn/routes/team-icons.ts index 3ddbd1ed0..c0e7e7854 100644 --- a/src/cdn/routes/team-icons.ts +++ b/src/cdn/routes/team-icons.ts @@ -18,12 +18,12 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; import { multer } from "../util/multer"; -import { cache } from "../util/cache"; +import { cache, cacheNotFound } from "../util/cache"; // TODO: check premium and animated pfp are allowed in the config // TODO: generate different sizes of icon @@ -67,7 +67,8 @@ router.get("/:guild_id", cache, async (req: Request, res: Response) => { guild_id = guild_id.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -81,7 +82,8 @@ export const getAvatar = async (req: Request, res: Response) => { hash = hash.split(".")[0]; // remove .file extension const path = `${pathPrefix}/${guild_id}/${hash}`; - const file = await getOrMoveFile(path, `avatars/${guild_id}/${hash}`); + const file = await storage.get(path); + if (!file) return cacheNotFound(req, res); const type = await fileTypeFromBuffer(file); res.set("Content-Type", type?.mime); @@ -101,17 +103,4 @@ router.delete("/:guild_id/:id", async (req: Request, res: Response) => { return res.send({ success: true }); }); -async function getOrMoveFile(newPath: string, oldPath: string) { - let file = await storage.get(newPath); - if (!file) { - if (await storage.exists(oldPath)) { - console.log(`[${pathPrefix}] found file at old path ${oldPath}, moving to new path ${newPath}`); - await storage.move(oldPath, newPath); - file = await storage.get(newPath); - } - } - if (!file) throw new HTTPError("not found", 404); - return file; -} - export default router; diff --git a/src/cdn/util/FileStorage.ts b/src/cdn/util/FileStorage.ts index ff7a56ee8..f54ff2cc7 100644 --- a/src/cdn/util/FileStorage.ts +++ b/src/cdn/util/FileStorage.ts @@ -24,23 +24,26 @@ import { Readable } from "stream"; import ExifTransformer from "exif-be-gone"; // TODO: split stored files into separate folders named after cloned route +export class FileStorage implements Storage { + getFsPath(path: string): string { + // STORAGE_LOCATION has a default value in start.ts + const root = process.env.STORAGE_LOCATION || "../"; + const filename = join(root, path); -function getPath(path: string) { - // STORAGE_LOCATION has a default value in start.ts - const root = process.env.STORAGE_LOCATION || "../"; - const filename = join(root, path); - - if (path.indexOf("\0") !== -1 || !filename.startsWith(root)) throw new Error("invalid path"); - return filename; -} + if (path.indexOf("\0") !== -1 || !filename.startsWith(root)) throw new Error("invalid path"); + return filename; + } + isFile(path: string): Promise { + return Promise.resolve(fs.statSync(this.getFsPath(path)).isFile()); + } -export class FileStorage implements Storage { async get(path: string): Promise { - path = getPath(path); + path = this.getFsPath(path); try { return await fsp.readFile(path); } catch (error) { try { + console.warn("[CDN] Warning: falling back to first file in dir for path", path); const files = fs.readdirSync(path); if (!files.length) return null; return await fsp.readFile(join(path, files[0])); @@ -51,8 +54,8 @@ export class FileStorage implements Storage { } async clone(path: string, newPath: string) { - path = getPath(path); - newPath = getPath(newPath); + path = this.getFsPath(path); + newPath = this.getFsPath(newPath); if (!fs.existsSync(dirname(newPath))) fs.mkdirSync(dirname(newPath), { recursive: true }); @@ -61,7 +64,7 @@ export class FileStorage implements Storage { } async set(path: string, value: Buffer) { - path = getPath(path); + path = this.getFsPath(path); if (!fs.existsSync(dirname(path))) fs.mkdirSync(dirname(path), { recursive: true }); const ret = Readable.from(value); @@ -72,16 +75,16 @@ export class FileStorage implements Storage { async delete(path: string) { //TODO we should delete the parent directory if empty - fs.unlinkSync(getPath(path)); + fs.unlinkSync(this.getFsPath(path)); } async exists(path: string) { - return fs.existsSync(getPath(path)); + return fs.existsSync(this.getFsPath(path)); } async move(path: string, newPath: string) { - path = getPath(path); - newPath = getPath(newPath); + path = this.getFsPath(path); + newPath = this.getFsPath(newPath); if (!fs.existsSync(dirname(newPath))) fs.mkdirSync(dirname(newPath), { recursive: true }); diff --git a/src/cdn/util/S3Storage.ts b/src/cdn/util/S3Storage.ts index de575e5d2..82d9954ff 100644 --- a/src/cdn/util/S3Storage.ts +++ b/src/cdn/util/S3Storage.ts @@ -38,6 +38,9 @@ export class S3Storage implements Storage { const { S3 } = require("@aws-sdk/client-s3"); this.client = new S3({ region: region, endpoint: endpoint }); } + isFile(path: string): Promise { + return this.exists(path); + } /** * Always return a string, to ensure consistency. diff --git a/src/cdn/util/Storage.ts b/src/cdn/util/Storage.ts index 199c53ff7..03d71d1d9 100644 --- a/src/cdn/util/Storage.ts +++ b/src/cdn/util/Storage.ts @@ -28,6 +28,7 @@ export interface Storage { get(path: string): Promise; delete(path: string): Promise; exists(path: string): Promise; + isFile(path: string): Promise; move(path: string, newPath: string): Promise; } diff --git a/src/cdn/util/basicCrdFileRouter.ts b/src/cdn/util/basicCrdFileRouter.ts index d489f373e..7490d5465 100644 --- a/src/cdn/util/basicCrdFileRouter.ts +++ b/src/cdn/util/basicCrdFileRouter.ts @@ -18,7 +18,7 @@ import { Router, Response, Request } from "express"; import { Config, Snowflake } from "@spacebar/util"; -import { storage } from "../util/Storage"; +import { storage } from "@spacebar/cdn"; import { fileTypeFromBuffer } from "file-type"; import { HTTPError } from "lambert-server"; import crypto from "crypto"; diff --git a/src/cdn/util/cache.ts b/src/cdn/util/cache.ts index a36f2c4c0..3f011ad4b 100644 --- a/src/cdn/util/cache.ts +++ b/src/cdn/util/cache.ts @@ -23,3 +23,9 @@ export function cache(req: Request, res: Response, next: NextFunction) { res.setHeader("Cache-Control", `public, max-age=${cacheDuration}, s-maxage=${cacheDuration}, immutable`); next(); } + +export function cacheNotFound(req: Request, res: Response) { + const cacheDuration = 60; // 1 minute + res.setHeader("Cache-Control", `public, max-age=${cacheDuration}, s-maxage=${cacheDuration}, immutable`); + res.status(404).send(req.path + " not found"); +} diff --git a/src/connections/BattleNet/index.ts b/src/connections/BattleNet/index.ts index 843ab79b3..f7d08cbe8 100644 --- a/src/connections/BattleNet/index.ts +++ b/src/connections/BattleNet/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { BattleNetSettings } from "./BattleNetSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface BattleNetConnectionUser { sub: string; diff --git a/src/connections/Discord/index.ts b/src/connections/Discord/index.ts index ab44f4b31..f209a81bd 100644 --- a/src/connections/Discord/index.ts +++ b/src/connections/Discord/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { DiscordSettings } from "./DiscordSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface UserResponse { id: string; diff --git a/src/connections/EpicGames/index.ts b/src/connections/EpicGames/index.ts index 69c44cb04..959192e64 100644 --- a/src/connections/EpicGames/index.ts +++ b/src/connections/EpicGames/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { EpicGamesSettings } from "./EpicGamesSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; export interface UserResponse { accountId: string; diff --git a/src/connections/Facebook/index.ts b/src/connections/Facebook/index.ts index b661cbe87..29317c2e6 100644 --- a/src/connections/Facebook/index.ts +++ b/src/connections/Facebook/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { FacebookSettings } from "./FacebookSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; export interface FacebookErrorResponse { error: { diff --git a/src/connections/GitHub/index.ts b/src/connections/GitHub/index.ts index 42dc0724f..df64912ae 100644 --- a/src/connections/GitHub/index.ts +++ b/src/connections/GitHub/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { GitHubSettings } from "./GitHubSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface UserResponse { login: string; diff --git a/src/connections/Reddit/index.ts b/src/connections/Reddit/index.ts index 14faf4a6e..781e097a1 100644 --- a/src/connections/Reddit/index.ts +++ b/src/connections/Reddit/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { RedditSettings } from "./RedditSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; export interface UserResponse { verified: boolean; diff --git a/src/connections/Spotify/index.ts b/src/connections/Spotify/index.ts index e1381cf2f..2186396bb 100644 --- a/src/connections/Spotify/index.ts +++ b/src/connections/Spotify/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, ConnectionLoader, DiscordApiErrors, RefreshableConnection } from "@spacebar/util"; +import { ConnectedAccount, ConnectionLoader, DiscordApiErrors, RefreshableConnection } from "@spacebar/util"; import wretch from "wretch"; import { SpotifySettings } from "./SpotifySettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; export interface UserResponse { display_name: string; diff --git a/src/connections/Twitch/index.ts b/src/connections/Twitch/index.ts index 84e8b3c04..5952caf1a 100644 --- a/src/connections/Twitch/index.ts +++ b/src/connections/Twitch/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, ConnectionLoader, DiscordApiErrors, RefreshableConnection } from "@spacebar/util"; +import { ConnectedAccount, ConnectionLoader, DiscordApiErrors, RefreshableConnection } from "@spacebar/util"; import wretch from "wretch"; import { TwitchSettings } from "./TwitchSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface TwitchConnectionUserResponse { data: { diff --git a/src/connections/Twitter/index.ts b/src/connections/Twitter/index.ts index 46b9289a0..3c79515be 100644 --- a/src/connections/Twitter/index.ts +++ b/src/connections/Twitter/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, ConnectionLoader, DiscordApiErrors, RefreshableConnection } from "@spacebar/util"; +import { ConnectedAccount, ConnectionLoader, DiscordApiErrors, RefreshableConnection } from "@spacebar/util"; import wretch from "wretch"; import { TwitterSettings } from "./TwitterSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface TwitterUserResponse { data: { diff --git a/src/connections/Xbox/index.ts b/src/connections/Xbox/index.ts index 184fe7637..59d4a0054 100644 --- a/src/connections/Xbox/index.ts +++ b/src/connections/Xbox/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { XboxSettings } from "./XboxSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface XboxUserResponse { IssueInstant: string; diff --git a/src/connections/Youtube/index.ts b/src/connections/Youtube/index.ts index de452dffa..2db9289f3 100644 --- a/src/connections/Youtube/index.ts +++ b/src/connections/Youtube/index.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { ConnectedAccount, ConnectedAccountCommonOAuthTokenResponse, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; +import { ConnectedAccount, Connection, ConnectionLoader, DiscordApiErrors } from "@spacebar/util"; import wretch from "wretch"; import { YoutubeSettings } from "./YoutubeSettings"; -import { ConnectionCallbackSchema } from "@spacebar/schemas"; +import { ConnectedAccountCommonOAuthTokenResponse, ConnectionCallbackSchema } from "@spacebar/schemas"; interface YouTubeConnectionChannelListResult { items: { diff --git a/src/gateway/Server.ts b/src/gateway/Server.ts index 7fea1a279..72faa9836 100644 --- a/src/gateway/Server.ts +++ b/src/gateway/Server.ts @@ -22,7 +22,7 @@ import { checkToken, closeDatabase, Config, initDatabase, initEvent, Rights } fr import ws from "ws"; import { Connection, openConnections } from "./events/Connection"; import http from "http"; -import { cleanupOnStartup } from "./util/Utils"; +import { cleanupOnStartup } from "./util"; import { randomString } from "@spacebar/api"; import { setInterval } from "timers"; @@ -38,9 +38,9 @@ export class Server { if (server) this.server = server; else { - const elu = [1, 5, 15].map((x) => performance.eventLoopUtilization()); - const eluP = [1, 5, 15].map((x) => performance.eventLoopUtilization()); - const cpu = [1, 5, 15].map((x) => process.cpuUsage()); + const elu = [1, 5, 15].map(() => performance.eventLoopUtilization()); + const eluP = [1, 5, 15].map(() => performance.eventLoopUtilization()); + const cpu = [1, 5, 15].map(() => process.cpuUsage()); let sec = 0; setInterval(() => { sec += 1; diff --git a/src/gateway/events/Close.ts b/src/gateway/events/Close.ts index 988f17fa4..2f30564c3 100644 --- a/src/gateway/events/Close.ts +++ b/src/gateway/events/Close.ts @@ -17,7 +17,7 @@ */ import { WebSocket } from "@spacebar/gateway"; -import { emitEvent, PresenceUpdateEvent, PrivateSessionProjection, Session, SessionsReplace, User, VoiceState, VoiceStateUpdateEvent } from "@spacebar/util"; +import { emitEvent, PresenceUpdateEvent, Session, SessionsReplace, User, VoiceState, VoiceStateUpdateEvent } from "@spacebar/util"; export async function Close(this: WebSocket, code: number, reason: Buffer) { console.log("[WebSocket] closed", code, reason.toString()); @@ -28,7 +28,7 @@ export async function Close(this: WebSocket, code: number, reason: Buffer) { this.removeAllListeners(); if (this.session_id) { - await Session.delete({ session_id: this.session_id }); + // await Session.delete({ session_id: this.session_id }); const voiceState = await VoiceState.findOne({ where: { user_id: this.user_id }, diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts index 07a5706cd..06ab9e5a1 100644 --- a/src/gateway/events/Connection.ts +++ b/src/gateway/events/Connection.ts @@ -28,7 +28,6 @@ import { Message } from "./Message"; import { Deflate, Inflate } from "fast-zlib"; import { URL } from "url"; import { Config, ErlpackType } from "@spacebar/util"; -import zlib from "node:zlib"; import { Decoder, Encoder } from "@toondepauw/node-zstd"; let erlpack: ErlpackType | null = null; @@ -75,8 +74,7 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In } //Create session ID when the connection is opened. This allows gateway dump to group the initial websocket messages with the rest of the conversation. - const session_id = "TEMP_" + genSessionId(); - socket.session_id = session_id; //Set the session of the WebSocket object + socket.session_id = "TEMP_" + genSessionId(); //Set the session of the WebSocket object try { // @ts-ignore @@ -84,7 +82,7 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In // @ts-ignore socket.on("message", Message); - socket.on("error", (err) => console.error("[Gateway]", err)); + socket.on("error", (err) => console.error(`[Gateway/${socket.user_id ?? socket.ipAddress}]`, err)); console.log(`[Gateway] New connection from ${ipAddress}, total ${this.clients.size}`); @@ -106,7 +104,7 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In // @ts-ignore socket.encoding = searchParams.get("encoding") || "json"; if (!["json", "etf"].includes(socket.encoding)) { - console.error(`[Gateway] Unknown encoding: ${socket.encoding}`); + console.error(`[Gateway/${socket.ipAddress}] Unknown encoding: ${socket.encoding}`); return socket.close(CLOSECODES.Decode_error); } @@ -114,7 +112,7 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In socket.version = Number(searchParams.get("version")) || 8; if (socket.version != 8) { - console.error(`[Gateway] Invalid API version: ${socket.version}`); + console.error(`[Gateway/${socket.ipAddress}] Invalid API version: ${socket.version}`); return socket.close(CLOSECODES.Invalid_API_version); } @@ -128,7 +126,7 @@ export async function Connection(this: WS.Server, socket: WebSocket, request: In socket.zstdEncoder = new Encoder(6); socket.zstdDecoder = new Decoder(); } else { - console.error(`[Gateway] Unknown compression: ${socket.compress}`); + console.error(`[Gateway/${socket.user_id}] Unknown compression: ${socket.compress}`); return socket.close(CLOSECODES.Decode_error); } } diff --git a/src/gateway/events/Message.ts b/src/gateway/events/Message.ts index 532d765f1..b4263b403 100644 --- a/src/gateway/events/Message.ts +++ b/src/gateway/events/Message.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { CLOSECODES, OPCODES, Payload, WebSocket } from "@spacebar/gateway"; +import { CLOSECODES, Payload, WebSocket } from "@spacebar/gateway"; import { ErlpackType } from "@spacebar/util"; import fs from "fs/promises"; import BigIntJson from "json-bigint"; @@ -40,11 +40,11 @@ export async function Message(this: WebSocket, buffer: WS.Data) { let data: Payload; if ( - (buffer instanceof Buffer && buffer[0] === 123) || // ASCII 123 = `{`. Bad check for JSON + (Buffer.isBuffer(buffer) && buffer[0] === 123) || // ASCII 123 = `{`. Bad check for JSON typeof buffer === "string" ) { data = bigIntJson.parse(buffer.toString()); - } else if (this.encoding === "json" && buffer instanceof Buffer) { + } else if (this.encoding === "json" && Buffer.isBuffer(buffer)) { if (this.compress === "zlib-stream") { try { buffer = this.inflate!.process(buffer); @@ -59,15 +59,15 @@ export async function Message(this: WebSocket, buffer: WS.Data) { } } data = bigIntJson.parse(buffer as string); - } else if (this.encoding === "etf" && buffer instanceof Buffer && erlpack) { + } else if (this.encoding === "etf" && Buffer.isBuffer(buffer) && erlpack) { try { data = erlpack.unpack(buffer); } catch { - console.error("[Gateway] Failed to decode ETF payload"); + console.error(`[Gateway/${this.user_id ?? this.ipAddress}] Failed to decode ETF payload`); return this.close(CLOSECODES.Decode_error); } } else { - console.error("[Gateway] Unknown payload format"); + console.error(`[Gateway/${this.user_id ?? this.ipAddress}] Unknown payload format`); return this.close(CLOSECODES.Decode_error); } @@ -79,14 +79,14 @@ export async function Message(this: WebSocket, buffer: WS.Data) { await fs.mkdir(path.join("dump", id), { recursive: true }); await fs.writeFile(path.join("dump", id, `${Date.now()}.in.json`), JSON.stringify(data, null, 2)); - if (!this.session_id) console.log("[Gateway] Unknown session id, dumping to unknown folder"); + if (!this.session_id) console.log(`[Gateway/${this.user_id ?? this.ipAddress}] Unknown session id, dumping to unknown folder`); } check.call(this, PayloadSchema, data); const OPCodeHandler = OPCodeHandlers[data.op]; if (!OPCodeHandler) { - console.error("[Gateway] Unknown opcode " + data.op); + console.error(`[Gateway/${this.user_id ?? this.ipAddress}] Unknown opcode`, data.op); // TODO: if all opcodes are implemented comment this out: // this.close(CLOSECODES.Unknown_opcode); return; @@ -95,7 +95,7 @@ export async function Message(this: WebSocket, buffer: WS.Data) { try { return await OPCodeHandler.call(this, data); } catch (error) { - console.error(`Error: Op ${data.op}`, error); + console.error(`[Gateway/${this.user_id ?? this.ipAddress}] Error: Op ${data.op}`, error); // if (!this.CLOSED && this.CLOSING) return this.close(CLOSECODES.Unknown_error); } diff --git a/src/gateway/listener/listener.ts b/src/gateway/listener/listener.ts index b49514927..112f55bbf 100644 --- a/src/gateway/listener/listener.ts +++ b/src/gateway/listener/listener.ts @@ -17,26 +17,23 @@ */ import { + Ban, + EVENTEnum, + EventOpts, getPermission, - Permissions, - RabbitMQ, listenEvent, - EventOpts, ListenEventOpts, Member, - EVENTEnum, - Relationship, Message, NewUrlUserSignatureData, - GuildMemberAddEvent, - Ban, + Permissions, + RabbitMQ, + Recipient, + Relationship, } from "@spacebar/util"; -import { OPCODES } from "../util/Constants"; -import { Send } from "../util/Send"; +import { CLOSECODES, OPCODES, Send } from "../util"; import { WebSocket } from "@spacebar/gateway"; import { Channel as AMQChannel } from "amqplib"; -import { Recipient } from "@spacebar/util"; -import * as console from "node:console"; import { PublicMember, RelationshipType } from "@spacebar/schemas"; import { bgRedBright } from "picocolors"; @@ -106,6 +103,7 @@ export async function setupListener(this: WebSocket) { } this.events[this.user_id] = await listenEvent(this.user_id, consumer, opts); + this.events[this.session_id] = await listenEvent(this.session_id, consumer, opts); await Promise.all( relationships.map(async (relationship) => { @@ -207,6 +205,27 @@ async function consume(this: WebSocket, opts: EventOpts) { opts.acknowledge?.(); // console.log("event", event); + // special codes + switch (event) { + case "SB_SESSION_CLOSE": + // TODO: what do we even send here? + await Send(this, { + op: OPCODES.Reconnect, + s: this.sequence++, + d: opts.reconnect_delay ?? opts.data ?? 1000, + }); + this.close(1000); // not a discord close code, standard WS "Normal Closure" + return; + case "SB_SESSION_REMOVE": + // TODO: what do we even send here? + await Send(this, { + op: OPCODES.Invalid_Session, + s: this.sequence++, + }); + this.close(CLOSECODES.Invalid_session); // TODO: this is deprecated? + return; + } + // subscription managment switch (event) { case "GUILD_MEMBER_REMOVE": @@ -219,7 +238,7 @@ async function consume(this: WebSocket, opts: EventOpts) { break; case "GUILD_MEMBER_UPDATE": if (!this.member_events[data.user.id]) break; - this.member_events[data.user.id](); + await this.member_events[data.user.id](); break; case "RELATIONSHIP_REMOVE": case "CHANNEL_DELETE": @@ -245,7 +264,7 @@ async function consume(this: WebSocket, opts: EventOpts) { this.events[data.user.id] = await listenEvent(data.user.id, handlePresenceUpdate.bind(this), this.listen_options); break; case "GUILD_CREATE": - Promise.all([ + await Promise.all([ ...data.channels.map(async ({ id }: { id: string }) => { this.events[id] = await listenEvent(id, consumer, listenOpts); }), @@ -340,7 +359,12 @@ async function consume(this: WebSocket, opts: EventOpts) { if (event === "GUILD_MEMBER_ADD") { if ((data as PublicMember).roles === undefined || (data as PublicMember).roles === null) { - console.log(bgRedBright("[Gateway]"), "[GUILD_MEMBER_ADD] roles is undefined, setting to empty array!", opts.origin ?? "(Event origin not defined)", data); + console.log( + bgRedBright(`[Gateway/${this.user_id}]`), + "[GUILD_MEMBER_ADD] roles is undefined, setting to empty array!", + opts.origin ?? "(Event origin not defined)", + data, + ); (data as PublicMember).roles = []; } } diff --git a/src/gateway/opcodes/GuildSubscriptionsBulk.ts b/src/gateway/opcodes/GuildSubscriptionsBulk.ts index be6935740..ec232829b 100644 --- a/src/gateway/opcodes/GuildSubscriptionsBulk.ts +++ b/src/gateway/opcodes/GuildSubscriptionsBulk.ts @@ -19,5 +19,7 @@ export async function onGuildSubscriptionsBulk(this: WebSocket, payload: Payload }, }); } - console.log(`[Gateway] GuildSubscriptionsBulk processed ${Object.keys(body.subscriptions).length} subscriptions for user ${this.user_id} in ${Date.now() - startTime}ms`); + console.log( + `[Gateway/${this.user_id}] GuildSubscriptionsBulk processed ${Object.keys(body.subscriptions).length} subscriptions for user ${this.user_id} in ${Date.now() - startTime}ms`, + ); } diff --git a/src/gateway/opcodes/GuildSync.ts b/src/gateway/opcodes/GuildSync.ts index ab2796394..9a6260e78 100644 --- a/src/gateway/opcodes/GuildSync.ts +++ b/src/gateway/opcodes/GuildSync.ts @@ -16,51 +16,23 @@ along with this program. If not, see . */ -import { - getDatabase, - getPermission, - listenEvent, - Member, - Role, - Session, - User, - Presence, - Channel, - Permissions, - arrayPartition, - timePromise, - Stopwatch, - Guild, -} from "@spacebar/util"; -import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send } from "@spacebar/gateway"; -import murmur from "murmurhash-js/murmurhash3_gc"; -import { check } from "./instanceOf"; -import { LazyRequestSchema, PublicMember } from "@spacebar/schemas"; +import { Member, Session, Presence, timePromise, Stopwatch, Config } from "@spacebar/util"; +import { WebSocket, Payload, OPCODES, Send, getMostRelevantSession, handleOffloadedGatewayRequest } from "@spacebar/gateway"; +import { PublicMember } from "@spacebar/schemas"; import { In } from "typeorm"; // TODO: only show roles/members that have access to this channel // TODO: config: to list all members (even those who are offline) sorted by role, or just those who are online // TODO: rewrite typeorm -const getMostRelevantSession = (sessions: Session[]) => { - const statusMap = { - online: 0, - idle: 1, - dnd: 2, - invisible: 3, - offline: 4, - }; - // sort sessions by relevance - sessions = sessions.sort((a, b) => { - return statusMap[a.status] - statusMap[b.status] + ((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2; - }); - - return sessions[0]; -}; - export async function onGuildSync(this: WebSocket, { d }: Payload) { const sw = Stopwatch.startNew(); if (!Array.isArray(d)) throw new Error("Invalid payload for GUILD_SYNC"); + + if (Config.get().offload.gateway.guildSyncUrl !== null) { + return await handleOffloadedGatewayRequest(this, Config.get().offload.gateway.guildSyncUrl!, d); + } + const guild_ids = d as string[]; const joinedGuildIds = await Member.find({ where: { id: this.user_id, guild_id: In(guild_ids) }, select: { guild_id: true } }).then((members) => @@ -71,14 +43,14 @@ export async function onGuildSync(this: WebSocket, { d }: Payload) { // not awaiting lol Promise.all(tasks) .then((res) => { - console.log(`[Gateway] GUILD_SYNC processed ${guild_ids.length} guilds in ${sw.elapsed().totalMilliseconds}ms:`, { + console.log(`[Gateway/${this.user_id}] GUILD_SYNC processed ${guild_ids.length} guilds in ${sw.elapsed().totalMilliseconds}ms:`, { ...Object.fromEntries( res.map((r) => [r.result.id, `${r.result.id}: ${r.result.members.length}U/${r.result.presences.length}P in ${r.elapsed.totalMilliseconds}ms`]), ), }); }) .catch((err) => { - console.error("[Gateway] Error processing GUILD_SYNC:", err); + console.error(`[Gateway/${this.user_id}] Error processing GUILD_SYNC:`, err); }); } diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index a46b05130..ce0bd8ef0 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -64,11 +64,6 @@ import { ChannelType, DefaultUserGuildSettings, DMChannel, IdentifySchema, Priva // TODO: user sharding // TODO: check privileged intents, if defined in the config -function logAuth(message: string) { - if (process.env.LOG_AUTH != "true") return; - console.log(`[Gateway/Auth] ${message}`); -} - export async function onIdentify(this: WebSocket, data: Payload) { const totalSw = Stopwatch.startNew(); const taskSw = Stopwatch.startNew(); @@ -103,7 +98,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { const user = tokenData.user; if (!user) { - console.log("[Gateway] Failed to identify user"); + console.log(`[Gateway/${this.ipAddress}] Failed to identify user`); return this.close(CLOSECODES.Authentication_failed); } @@ -124,7 +119,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { if (this.shard_count == null || this.shard_id == null || this.shard_id > this.shard_count || this.shard_id < 0 || this.shard_count <= 0) { // TODO: why do we even care about this right now? - console.log(`[Gateway] Invalid sharding from ${user.id}: ${identify.shard}`); + console.log(`[Gateway/${this.user_id}] Invalid sharding from ${user.id}: ${identify.shard}`); return this.close(CLOSECODES.Invalid_shard); } } @@ -141,6 +136,30 @@ export async function onIdentify(this: WebSocket, data: Payload) { isNewSession: true, }; + if (isNewSession) + console.warn( + "[Identify/WARN] Created new session", + session.session_id, + "for user", + tokenData.user.id, + `(${tokenData.user.tag})! - Access token version`, + tokenData.tokenVersion, + "- Access token session ID:", + tokenData.decoded.did ?? "(undefined)", + ); + + if (tokenData.tokenVersion < CurrentTokenFormatVersion) + console.warn( + "[Identify/WARN] Access token version", + tokenData.tokenVersion, + "used by user", + tokenData.user.id, + `(${tokenData.user.tag})! - Client`, + this.capabilities.has(Capabilities.FLAGS.AUTH_TOKEN_REFRESH) ? "did" : "did not", + "opt for token refresh.", + ); + + this.session_id = session.session_id; this.session = session; this.session.status = identify.presence?.status || "online"; this.session.last_seen = new Date(); @@ -352,29 +371,29 @@ export async function onIdentify(this: WebSocket, data: Payload) { //channels g.channels = memberGuildChannels.filter((c) => c.guild_id === m.guild_id); - trace.calls.push("filterChannels", { micros: sw.getElapsedAndReset().totalMicroseconds }); + trace.calls.push(`filterChannels(${g.channels.length}/${memberGuildChannels.length})`, { micros: sw.getElapsedAndReset().totalMicroseconds }); //emojis g.emojis = memberGuildEmojis.filter((e) => e.guild_id === m.guild_id); - trace.calls.push("filterEmojis", { micros: sw.getElapsedAndReset().totalMicroseconds }); + trace.calls.push(`filterEmojis(${g.emojis.length}/${memberGuildEmojis.length})`, { micros: sw.getElapsedAndReset().totalMicroseconds }); //roles g.roles = memberGuildRoles.filter((r) => r.guild_id === m.guild_id); - trace.calls.push("filterRoles", { micros: sw.getElapsedAndReset().totalMicroseconds }); + trace.calls.push(`filterRoles(${g.roles.length}/${memberGuildRoles.length})`, { micros: sw.getElapsedAndReset().totalMicroseconds }); //stickers g.stickers = memberGuildStickers.filter((s) => s.guild_id === m.guild_id); - trace.calls.push("filterStickers", { micros: sw.getElapsedAndReset().totalMicroseconds }); + trace.calls.push(`filterStickers(${g.stickers.length}/${memberGuildStickers.length})`, { micros: sw.getElapsedAndReset().totalMicroseconds }); //voice states g.voice_states = memberGuildVoiceStates.filter((v) => v.guild_id === m.guild_id); - trace.calls.push("filterVoiceStates", { micros: sw.getElapsedAndReset().totalMicroseconds }); + trace.calls.push(`filterVoiceStates(${g.voice_states.length}/${memberGuildVoiceStates.length})`, { micros: sw.getElapsedAndReset().totalMicroseconds }); //total trace.micros = totalSw.elapsed().totalMicroseconds; mergeMemberGuildsTrace.calls!.push(`guild_${m.guild_id}`, trace); } else { - console.error(`[Gateway] Member ${m.id} has invalid guild_id ${m.guild_id}`); + console.error(`[Gateway/${this.user_id}] Member ${m.id} has invalid guild_id ${m.guild_id}`); mergeMemberGuildsTrace.calls!.push(`guild_~~${m.guild_id}~~`, trace); } }); @@ -632,7 +651,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { private_channels: channels, presences: [], // TODO: Send actual data session_id: this.session_id, - country_code: user.settings!.locale, // TODO: do ip analysis instead + country_code: this.session?.last_seen_location_info?.country_code ?? user.settings!.locale, users: Array.from(users), merged_members: merged_members, sessions: allSessions, @@ -781,13 +800,13 @@ export async function onIdentify(this: WebSocket, data: Payload) { ] : [], }, - })?.catch((e) => console.error(`[Gateway] error when sending bot guilds`, e)); + })?.catch((e) => console.error(`[Gateway/${this.user_id}] error when sending bot guilds`, e)); }), ); const readySupplementalGuilds = (guilds.filter((guild) => !guild.unavailable) as Guild[]).map((guild) => { return { - voice_states: guild.voice_states.map((state) => state.toPublicVoiceState()), + voice_states: guild.voice_states.map((state) => VoiceState.prototype.toPublicVoiceState.apply(state)), id: guild.id, embedded_activities: [], }; @@ -815,5 +834,8 @@ export async function onIdentify(this: WebSocket, data: Payload) { //TODO send GUILD_MEMBER_LIST_UPDATE //TODO send VOICE_STATE_UPDATE to let the client know if another device is already connected to a voice channel await setupListener.call(this); - console.log(`[Gateway] IDENTIFY ${this.user_id} in ${totalSw.elapsed().totalMilliseconds}ms`, process.env.LOG_GATEWAY_TRACES ? JSON.stringify(d._trace, null, 2) : ""); + console.log( + `[Gateway/${this.user_id}] IDENTIFY ${this.user_id} in ${totalSw.elapsed().totalMilliseconds}ms`, + process.env.LOG_GATEWAY_TRACES ? JSON.stringify(d._trace, null, 2) : "", + ); } diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts index 3a56a0c82..cb465cf07 100644 --- a/src/gateway/opcodes/LazyRequest.ts +++ b/src/gateway/opcodes/LazyRequest.ts @@ -17,7 +17,7 @@ */ import { getDatabase, getPermission, listenEvent, Member, Role, Session, User, Presence, Channel, Permissions, arrayPartition } from "@spacebar/util"; -import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send } from "@spacebar/gateway"; +import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send, getMostRelevantSession } from "@spacebar/gateway"; import murmur from "murmurhash-js/murmurhash3_gc"; import { check } from "./instanceOf"; import { LazyRequestSchema } from "@spacebar/schemas"; @@ -26,22 +26,6 @@ import { LazyRequestSchema } from "@spacebar/schemas"; // TODO: config: to list all members (even those who are offline) sorted by role, or just those who are online // TODO: rewrite typeorm -function getMostRelevantSession(sessions: Session[]) { - const statusMap = { - online: 0, - idle: 1, - dnd: 2, - invisible: 3, - offline: 4, - }; - // sort sessions by relevance - sessions = sessions.sort((a, b) => { - return statusMap[a.status] - statusMap[b.status] + ((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2; - }); - - return sessions[0]; -} - async function getMembers(guild_id: string, range: [number, number]) { if (!Array.isArray(range) || range.length !== 2) { throw new Error("range is not a valid array"); @@ -174,6 +158,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { const startTime = Date.now(); // TODO: check data check.call(this, LazyRequestSchema, d); + // noinspection JSUnusedLocalSymbols - TODO: implement typing/activities subscriptions const { guild_id, typing, channels, activities, members } = d as LazyRequestSchema; if (members) { @@ -212,7 +197,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { if (!channels) return; } - if (!channels) throw new Error("Must provide channel ranges"); + if (!channels) return; const channel_id = Object.keys(channels || {})[0]; if (!channel_id) return; @@ -275,5 +260,5 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { }, }); - console.log(`[Gateway] LAZY_REQUEST ${guild_id} ${channel_id} took ${Date.now() - startTime}ms`); + console.log(`[Gateway/${this.user_id}] LAZY_REQUEST ${guild_id} ${channel_id} took ${Date.now() - startTime}ms`); } diff --git a/src/gateway/opcodes/RequestChannelInfo.ts b/src/gateway/opcodes/RequestChannelInfo.ts new file mode 100644 index 000000000..efa3e728c --- /dev/null +++ b/src/gateway/opcodes/RequestChannelInfo.ts @@ -0,0 +1,53 @@ +/* + 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 . +*/ + +import { WebSocket, Payload, OPCODES, Send, handleOffloadedGatewayRequest } from "@spacebar/gateway"; +import { ChannelType } from "@spacebar/schemas"; +import { Channel, Config } from "@spacebar/util"; + +export async function onRequestChannelInfo(this: WebSocket, { d }: Payload) { + // Schema validation can only accept either string or array, so transforming it here to support both + if (!d.guild_id) throw new Error('"guild_id" is required'); + if (!d.fields) throw new Error('"fields" is required'); + + if (Config.get().offload.gateway.channelInfoUrl !== null) { + return await handleOffloadedGatewayRequest(this, Config.get().offload.gateway.channelInfoUrl!, d); + } + + const channels = ( + await Channel.find({ + where: { guild_id: d.guild_id, type: ChannelType.GUILD_VOICE }, + relations: { + voice_states: true, + }, + }) + ).filter((c) => c.voice_states && c.voice_states.length > 0); + + await Send(this, { + op: OPCODES.Dispatch, + t: "CHANNEL_INFO", // This is an educated guess... + d: { + guild_id: d.guild_id, + channels: channels.map((c) => ({ + id: c.id, + status: d.fields.includes("status") ? null : undefined, // TODO: we dont track this + voice_start_time: d.fields.includes("voice_start_time") ? new Date().toISOString() : undefined, // TODO: we dont track this + })), + }, + }); +} diff --git a/src/gateway/opcodes/RequestChannelStatuses.ts b/src/gateway/opcodes/RequestChannelStatuses.ts index 507cfd57c..1747faae3 100644 --- a/src/gateway/opcodes/RequestChannelStatuses.ts +++ b/src/gateway/opcodes/RequestChannelStatuses.ts @@ -16,13 +16,17 @@ along with this program. If not, see . */ -import { WebSocket, Payload, OPCODES, Send } from "@spacebar/gateway"; +import { WebSocket, Payload, OPCODES, Send, handleOffloadedGatewayRequest } from "@spacebar/gateway"; +import { Config } from "@spacebar/util"; export async function onRequestChannelStatuses(this: WebSocket, { d }: Payload) { - const startTime = Date.now(); // Schema validation can only accept either string or array, so transforming it here to support both if (!d.guild_id) throw new Error('"guild_id" is required'); + if (Config.get().offload.gateway.channelStatusesUrl !== null) { + return await handleOffloadedGatewayRequest(this, Config.get().offload.gateway.channelStatusesUrl!, d); + } + // TODO: implement await Send(this, { op: OPCODES.Dispatch, diff --git a/src/gateway/opcodes/RequestGuildMembers.ts b/src/gateway/opcodes/RequestGuildMembers.ts index 8fa901790..cba2c89c4 100644 --- a/src/gateway/opcodes/RequestGuildMembers.ts +++ b/src/gateway/opcodes/RequestGuildMembers.ts @@ -16,10 +16,10 @@ along with this program. If not, see . */ -import { getDatabase, getPermission, GuildMembersChunkEvent, Member, Presence, Session } from "@spacebar/util"; -import { WebSocket, Payload, OPCODES, Send } from "@spacebar/gateway"; +import { Config, DateBuilder, getDatabase, getPermission, GuildMembersChunkEvent, Member, Presence, Session } from "@spacebar/util"; +import { WebSocket, Payload, OPCODES, Send, handleOffloadedGatewayRequest } from "@spacebar/gateway"; import { check } from "./instanceOf"; -import { FindManyOptions, ILike, In } from "typeorm"; +import { FindManyOptions, ILike, In, MoreThan } from "typeorm"; import { RequestGuildMembersSchema } from "@spacebar/schemas"; export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { @@ -30,6 +30,10 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { if (d.user_ids && !Array.isArray(d.user_ids)) d.user_ids = [d.user_ids]; + if (Config.get().offload.gateway.guildMembersUrl !== null) { + return await handleOffloadedGatewayRequest(this, Config.get().offload.gateway.guildMembersUrl!, d); + } + check.call(this, RequestGuildMembersSchema, d); const { presences, nonce, query: requestQuery } = d as RequestGuildMembersSchema; @@ -127,62 +131,82 @@ export async function onRequestGuildMembers(this: WebSocket, { d }: Payload) { nonce, }; - const chunkCount = Math.ceil(members.length / 1000); + const memberResultCount = members.length; + const chunkSize = 1000; + const chunkCount = Math.ceil(members.length / chunkSize); + let sentChunkCount = 0; let notFound: string[] = []; if (user_ids && user_ids.length > 0) notFound = user_ids.filter((id) => !members.some((member) => member.id == id)); - const chunks: GuildMembersChunkEvent["data"][] = []; + const recentlyActiveSince = new DateBuilder().addMinutes(-15).build(); + while (members.length > 0) { - const chunk: Member[] = members.splice(0, 1000); + const chunk: Member[] = members.splice(0, chunkSize); - const presenceList: Presence[] = []; + let presenceList: Presence[] = []; if (presences) { - for await (const member of chunk) { - const session = await Session.findOne({ - where: { user_id: member.id }, - }); - if (session) - presenceList.push({ - user: member.user.toPublicUser(), - status: session.status, - activities: session.activities, - client_status: session.client_status, - }); - } - } + const sessions = await Session.find({ + where: { user_id: In(chunk.map((m) => m.id)), is_admin_session: false, last_seen: MoreThan(recentlyActiveSince) }, + select: { + user: true, + status: true, + activities: true, + client_status: true, + }, + relations: { user: true }, + }); - chunks.push({ - ...baseData, - members: chunk.map((member) => member.toPublicMember()), - presences: presences ? presenceList : undefined, - chunk_index: chunks.length, - chunk_count: chunkCount, - }); - } + const foundUids = new Set(); + presenceList = sessions + .filter((s) => { + if (foundUids.has(s.user.id)) return false; + foundUids.add(s.user.id); + return true; + }) + .map((session) => ({ + user: session.user.toPublicUser(), + status: session.getPublicStatus(), + activities: session.activities, + client_status: session.client_status, + })); + } - if (chunks.length == 0) { - chunks.push({ - ...baseData, - members: [], - presences: presences ? [] : undefined, - chunk_index: 0, - chunk_count: 1, + await Send(this, { + op: OPCODES.Dispatch, + s: this.sequence++, + t: "GUILD_MEMBERS_CHUNK", + d: { + ...baseData, + members: chunk.map((member) => member.toPublicMember()), + presences: presences ? presenceList : undefined, + chunk_index: sentChunkCount, + chunk_count: chunkCount, + + ...(sentChunkCount == 0 ? { not_found: notFound } : {}), + } satisfies GuildMembersChunkEvent["data"], }); - } + sentChunkCount++; - if (notFound.length > 0) { - chunks[0].not_found = notFound; + console.log( + `[Gateway/${this.user_id}] REQUEST_GUILD_MEMBERS @ ${Date.now() - startTime}ms for guild ${guild_id}: pushed ${sentChunkCount}/${chunkCount} chunks (${memberResultCount} total members considered)`, + ); } - chunks.forEach((chunk) => { - Send(this, { + if (sentChunkCount == 0) + await Send(this, { op: OPCODES.Dispatch, s: this.sequence++, t: "GUILD_MEMBERS_CHUNK", - d: chunk, + d: { + ...baseData, + members: [], + presences: presences ? [] : undefined, + chunk_index: 0, + chunk_count: 1, + not_found: notFound, + } satisfies GuildMembersChunkEvent["data"], }); - }); - console.log(`[Gateway] REQUEST_GUILD_MEMBERS took ${Date.now() - startTime}ms for guild ${guild_id} with ${members.length} members`); + console.log(`[Gateway/${this.user_id}] REQUEST_GUILD_MEMBERS took ${Date.now() - startTime}ms for guild ${guild_id} with ${memberResultCount} (${memberCount}) members`); } diff --git a/src/gateway/opcodes/StreamCreate.ts b/src/gateway/opcodes/StreamCreate.ts index 0c6c8c2db..cd3c4b77b 100644 --- a/src/gateway/opcodes/StreamCreate.ts +++ b/src/gateway/opcodes/StreamCreate.ts @@ -46,7 +46,11 @@ export async function onStreamCreate(this: WebSocket, data: Payload) { // TODO: actually apply preferred_region from the event payload const regions = Config.get().regions; - const guildRegion = regions.available.filter((r) => r.id === regions.default)[0]; + const guildRegion = regions.available.find((r) => r.id === regions.default); + + if (!guildRegion) { + throw new Error("No default region configured"); + } // first make sure theres no other streams for this user that somehow didnt get cleared await Stream.delete({ @@ -109,7 +113,7 @@ export async function onStreamCreate(this: WebSocket, data: Payload) { channel_id: voiceState.channel_id, } as VoiceStateUpdateEvent); - console.log(`[Gateway] STREAM_CREATE for user ${this.user_id} in channel ${body.channel_id} with stream key ${streamKey} in ${Date.now() - startTime}ms`); + console.log(`[Gateway/${this.user_id}] STREAM_CREATE for user ${this.user_id} in channel ${body.channel_id} with stream key ${streamKey} in ${Date.now() - startTime}ms`); } //stream key: diff --git a/src/gateway/opcodes/StreamDelete.ts b/src/gateway/opcodes/StreamDelete.ts index c684b6c70..6135e1f35 100644 --- a/src/gateway/opcodes/StreamDelete.ts +++ b/src/gateway/opcodes/StreamDelete.ts @@ -21,6 +21,7 @@ export async function onStreamDelete(this: WebSocket, data: Payload) { return this.close(4000, "Invalid stream key"); } + // noinspection JSUnusedLocalSymbols - TODO: what is type here? const { userId, channelId, guildId, type } = parsedKey; // when a user selects to stop watching another user stream, this event gets triggered @@ -69,5 +70,5 @@ export async function onStreamDelete(this: WebSocket, data: Payload) { channel_id: channelId, } as StreamDeleteEvent); - console.log(`[Gateway] STREAM_DELETE for user ${this.user_id} in channel ${channelId} with stream key ${body.stream_key} in ${Date.now() - startTime}ms`); + console.log(`[Gateway/${this.user_id}] STREAM_DELETE for user ${this.user_id} in channel ${channelId} with stream key ${body.stream_key} in ${Date.now() - startTime}ms`); } diff --git a/src/gateway/opcodes/StreamWatch.ts b/src/gateway/opcodes/StreamWatch.ts index 3a3c553e2..92588a9d5 100644 --- a/src/gateway/opcodes/StreamWatch.ts +++ b/src/gateway/opcodes/StreamWatch.ts @@ -82,5 +82,5 @@ export async function onStreamWatch(this: WebSocket, data: Payload) { user_id: this.user_id, } as StreamServerUpdateEvent); - console.log(`[Gateway] STREAM_WATCH for user ${this.user_id} in channel ${channelId} with stream key ${body.stream_key} in ${Date.now() - startTime}ms`); + console.log(`[Gateway/${this.user_id}] STREAM_WATCH for user ${this.user_id} in channel ${channelId} with stream key ${body.stream_key} in ${Date.now() - startTime}ms`); } diff --git a/src/gateway/opcodes/VoiceStateUpdate.ts b/src/gateway/opcodes/VoiceStateUpdate.ts index 5d363825b..678afad8d 100644 --- a/src/gateway/opcodes/VoiceStateUpdate.ts +++ b/src/gateway/opcodes/VoiceStateUpdate.ts @@ -18,7 +18,7 @@ import { Payload, WebSocket } from "@spacebar/gateway"; import { Config, emitEvent, Guild, Member, VoiceServerUpdateEvent, VoiceState, VoiceStateUpdateEvent } from "@spacebar/util"; -import { genVoiceToken } from "../util/SessionUtils"; +import { genVoiceToken } from "@spacebar/gateway"; import { check } from "./instanceOf"; import { Region, VoiceStateUpdateSchema } from "@spacebar/schemas"; // TODO: check if a voice server is setup @@ -61,7 +61,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { //The event send by Discord's client on channel leave has both guild_id and channel_id as null //if (body.guild_id === null) body.guild_id = voiceState.guild_id; prevState = { ...voiceState }; - voiceState.assign(body); + VoiceState.merge(voiceState, body); } catch (error) { voiceState = VoiceState.create({ ...body, @@ -70,6 +70,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { mute: false, suppress: false, }); + isChanged = true; } // if user left voice channel, send an update to previous channel/guild to let other people know that the user left @@ -90,10 +91,14 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { //TODO the member.user should only have these properties: avatar, discriminator, id, username //TODO this may fail if (body.guild_id) { - voiceState.member = await Member.findOneOrFail({ + const member = await Member.findOne({ where: { id: voiceState.user_id, guild_id: voiceState.guild_id }, relations: { user: true, roles: true }, }); + + if (member) { + voiceState.member = member; + } } //If the session changed we generate a new token @@ -122,11 +127,21 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { where: { id: voiceState.guild_id }, }); const regions = Config.get().regions; - let guildRegion: Region; + let guildRegion: Region | undefined; + + const defaultRegion = regions.available.find((r) => r.id === regions.default); + if (guild && guild.region) { - guildRegion = regions.available.filter((r) => r.id === guild.region)[0]; + // in case the configured guild region does not exist (which can + // happen when server regions config is updated after guild creation), + // fallback to default region + guildRegion = regions.available.find((r) => r.id === guild.region) ?? defaultRegion; } else { - guildRegion = regions.available.filter((r) => r.id === regions.default)[0]; + guildRegion = defaultRegion; + } + + if (!guildRegion) { + throw new Error("Unable to find suitable region due to misconfiguration of regions"); } await emitEvent({ @@ -141,5 +156,7 @@ export async function onVoiceStateUpdate(this: WebSocket, data: Payload) { } as VoiceServerUpdateEvent); } - console.log(`[Gateway] VOICE_STATE_UPDATE for user ${this.user_id} in channel ${voiceState.channel_id} in guild ${voiceState.guild_id} in ${Date.now() - startTime}ms`); + console.log( + `[Gateway/${this.user_id}] VOICE_STATE_UPDATE for user ${this.user_id} in channel ${voiceState.channel_id} in guild ${voiceState.guild_id} in ${Date.now() - startTime}ms`, + ); } diff --git a/src/gateway/opcodes/index.ts b/src/gateway/opcodes/index.ts index 358a9420d..fb594237d 100644 --- a/src/gateway/opcodes/index.ts +++ b/src/gateway/opcodes/index.ts @@ -30,6 +30,7 @@ import { onStreamDelete } from "./StreamDelete"; import { onStreamWatch } from "./StreamWatch"; import { onGuildSync } from "./GuildSync"; import { onRequestChannelStatuses } from "./RequestChannelStatuses"; +import { onRequestChannelInfo } from "./RequestChannelInfo"; export type OPCodeHandler = (this: WebSocket, data: Payload) => unknown; @@ -54,4 +55,5 @@ export default { 37: onGuildSubscriptionsBulk, 40: onHeartbeat, // same as 1, except with extra data 41: () => {}, // "Update Time Spent Session ID", just tracking nonsense + 43: onRequestChannelInfo, // extended op36...? } as { [key: number]: OPCodeHandler }; diff --git a/src/gateway/start.ts b/src/gateway/start.ts index 50d69a611..79c4ca193 100644 --- a/src/gateway/start.ts +++ b/src/gateway/start.ts @@ -37,4 +37,4 @@ const server = new Server({ if (fs.existsSync("/proc/self/comm")) fs.writeFileSync("/proc/self/comm", `spacebar-gw-${cluster.worker ? cluster.worker.id : port}`); process.title = `sb-gw-${cluster.worker ? cluster.worker.id : port}`; -server.start(); +server.start().then(() => {}); diff --git a/src/gateway/util/Constants.ts b/src/gateway/util/Constants.ts index af5fa1a58..3e4599c65 100644 --- a/src/gateway/util/Constants.ts +++ b/src/gateway/util/Constants.ts @@ -42,7 +42,7 @@ export enum OPCODES { Stream_Set_Paused = 22, Request_Application_Commands = 24, // We don't know the names for these: - ThirtySix = 36, // this is just a guild id? + Request_Channel_Statuses = 36, // this is just a guild id? Guild_Subscriptions_Bulk = 37, // Already implemented it seems? SetQoS = 40, ClientInitSession = 41, diff --git a/src/gateway/util/SessionUtils.ts b/src/gateway/util/SessionUtils.ts index 843341cc5..9d2f01b33 100644 --- a/src/gateway/util/SessionUtils.ts +++ b/src/gateway/util/SessionUtils.ts @@ -16,6 +16,8 @@ along with this program. If not, see . */ +import { Session } from "@spacebar/util"; + export function genSessionId() { return genRanHex(32); } @@ -27,3 +29,19 @@ export function genVoiceToken() { function genRanHex(size: number) { return [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join(""); } + +export function getMostRelevantSession(sessions: Session[]) { + const statusMap = { + online: 0, + idle: 1, + dnd: 2, + invisible: 3, + offline: 4, + }; + // sort sessions by relevance + sessions = sessions.sort((a, b) => { + return statusMap[a.status] - statusMap[b.status] + ((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2; + }); + + return sessions[0]; +} diff --git a/src/gateway/util/Utils.ts b/src/gateway/util/Utils.ts index 1757b8458..1ca45ee44 100644 --- a/src/gateway/util/Utils.ts +++ b/src/gateway/util/Utils.ts @@ -1,4 +1,7 @@ -import { VoiceState } from "@spacebar/util"; +import { Event, VoiceState } from "@spacebar/util"; +import { WebSocket } from "./WebSocket"; +import { OPCODES } from "./Constants"; +import { Send } from "./Send"; export function parseStreamKey(streamKey: string): { type: "guild" | "call"; @@ -53,9 +56,40 @@ export async function cleanupOnStartup(): Promise { // }, //); - try { - await VoiceState.clear(); - } catch (e) { - console.error("Error clearing voice states on startup:", e); + console.log("[Gateway] Starting async voice state wipe..."); + VoiceState.clear() + .then(() => console.log("[Gateway] Successfully cleaned voice states")) + .catch((e) => console.error("[Gateway] Error cleaning voice states on startup:", e)); +} + +export async function handleOffloadedGatewayRequest(socket: WebSocket, url: string, body: unknown) { + // TODO: async json object streaming + const resp = await fetch(url, { + body: JSON.stringify(body), + method: "POST", + headers: { + Authorization: `Bearer ${socket.accessToken}`, + // because the session may not have an id in the token! + "X-Session-Id": socket.session_id, + }, + }); + + if (!resp.ok) { + const text = await resp.text(); + console.error(`[Gateway] Offloaded request to ${url} failed with status ${resp.status}: ${text}`); + if (resp.status === 415) console.log(typeof body, body); + throw new Error(`Offloaded request failed with status ${resp.status}: ${text}`); + } + + const data = ((await resp.json()) as Event[]).toReversed(); + while (data.length > 0) { + const event = data.pop()!; + if (process.env.WS_VERBOSE) console.log(`[Gateway] Received offloaded event: ${JSON.stringify(event)}`); + await Send(socket, { + op: OPCODES.Dispatch, + s: socket.sequence++, + t: event.event, + d: event.data, + }); } } diff --git a/src/schemas/api/bots/ApplicationCommandCreateSchema.ts b/src/schemas/api/bots/ApplicationCommandCreateSchema.ts index 4408a9e0f..4c6072f2e 100644 --- a/src/schemas/api/bots/ApplicationCommandCreateSchema.ts +++ b/src/schemas/api/bots/ApplicationCommandCreateSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { ApplicationCommandHandlerType, ApplicationCommandOption, diff --git a/src/schemas/api/bots/ApplicationCommandSchema.ts b/src/schemas/api/bots/ApplicationCommandSchema.ts index 7d739e453..4ffc37a25 100644 --- a/src/schemas/api/bots/ApplicationCommandSchema.ts +++ b/src/schemas/api/bots/ApplicationCommandSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { ApplicationCommandOption, Snowflake, StringStringDictionary } from "@spacebar/schemas"; export interface ApplicationCommandSchema { diff --git a/src/schemas/api/bots/InteractionCallbackSchema.ts b/src/schemas/api/bots/InteractionCallbackSchema.ts index 5eb3f2964..66a4e66f8 100644 --- a/src/schemas/api/bots/InteractionCallbackSchema.ts +++ b/src/schemas/api/bots/InteractionCallbackSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { Message } from "@spacebar/util"; import { InteractionCallbackType } from "./InteractionCallbackType"; diff --git a/src/schemas/api/bots/InteractionCallbackType.ts b/src/schemas/api/bots/InteractionCallbackType.ts index fe9251f15..a664b8883 100644 --- a/src/schemas/api/bots/InteractionCallbackType.ts +++ b/src/schemas/api/bots/InteractionCallbackType.ts @@ -1,3 +1,21 @@ +/* + 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 enum InteractionCallbackType { PONG = 1, ACKNOWLEDGE = 2, // Deprecated diff --git a/src/schemas/api/bots/InteractionCreateSchema.ts b/src/schemas/api/bots/InteractionCreateSchema.ts index 2f01c5912..44e7e651d 100644 --- a/src/schemas/api/bots/InteractionCreateSchema.ts +++ b/src/schemas/api/bots/InteractionCreateSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { PublicMember, PublicUser, Snowflake } from "@spacebar/schemas"; import { Channel, InteractionType, Message } from "@spacebar/util"; diff --git a/src/schemas/api/bots/InteractionFailureReason.ts b/src/schemas/api/bots/InteractionFailureReason.ts new file mode 100644 index 000000000..62e7dc7e2 --- /dev/null +++ b/src/schemas/api/bots/InteractionFailureReason.ts @@ -0,0 +1,40 @@ +/* + 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 const enum InteractionFailureReason { + UNKNOWN = 1, + TIMEOUT = 2, + ACTIVITY_LAUNCH_UNKNOWN_APPLICATION = 3, + ACTIVITY_LAUNCH_UNKNOWN_CHANNEL = 4, + ACTIVITY_LAUNCH_UNKNOWN_GUILD = 5, + ACTIVITY_LAUNCH_INVALID_PLATFORM = 6, + ACTIVITY_LAUNCH_NOT_IN_EXPERIMENT = 7, + ACTIVITY_LAUNCH_INVALID_CHANNEL_TYPE = 8, + ACTIVITY_LAUNCH_INVALID_CHANNEL_NO_AFK = 9, + ACTIVITY_LAUNCH_INVALID_DEV_PREVIEW_GUILD_SIZE = 10, + ACTIVITY_LAUNCH_INVALID_USER_AGE_GATE = 11, + ACTIVITY_LAUNCH_INVALID_USER_VERIFICATION_LEVEL = 12, + ACTIVITY_LAUNCH_INVALID_USER_PERMISSIONS = 13, + ACTIVITY_LAUNCH_INVALID_CONFIGURATION_NOT_EMBEDDED = 14, + ACTIVITY_LAUNCH_INVALID_CONFIGURATION_PLATFORM_NOT_SUPPORTED = 15, + ACTIVITY_LAUNCH_INVALID_CONFIGURATION_PLATFORM_NOT_RELEASED = 16, + ACTIVITY_LAUNCH_FAILED_TO_LAUNCH = 17, + ACTIVITY_LAUNCH_INVALID_USER_NO_ACCESS_TO_ACTIVITY = 18, + ACTIVITY_LAUNCH_INVALID_LOCATION_TYPE = 19, + ACTIVITY_LAUNCH_INVALID_USER_REGION_FOR_APPLICATION = 20, +} diff --git a/src/schemas/api/bots/InteractionSchema.ts b/src/schemas/api/bots/InteractionSchema.ts index da3c3c121..9e79df526 100644 --- a/src/schemas/api/bots/InteractionSchema.ts +++ b/src/schemas/api/bots/InteractionSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { ApplicationCommandOption, Snowflake, UploadAttachmentRequestSchema } from "@spacebar/schemas"; import { InteractionType } from "@spacebar/util"; diff --git a/src/schemas/api/bots/SendableApplicationCommandDataSchema.ts b/src/schemas/api/bots/SendableApplicationCommandDataSchema.ts index 3f5496e75..e559abe02 100644 --- a/src/schemas/api/bots/SendableApplicationCommandDataSchema.ts +++ b/src/schemas/api/bots/SendableApplicationCommandDataSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { Snowflake } from "@spacebar/util"; import { ApplicationCommandOption } from "../developers"; import { ApplicationCommandType } from "./ApplicationCommandSchema"; diff --git a/src/schemas/api/bots/SendableMessageComponentDataSchema.ts b/src/schemas/api/bots/SendableMessageComponentDataSchema.ts index eb8d2ee1e..0ad37cc6e 100644 --- a/src/schemas/api/bots/SendableMessageComponentDataSchema.ts +++ b/src/schemas/api/bots/SendableMessageComponentDataSchema.ts @@ -1,3 +1,21 @@ +/* + 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 . +*/ + import { Snowflake } from "@spacebar/util"; import { MessageComponentType } from "../messages"; import { ApplicationCommandType } from "./ApplicationCommandSchema"; diff --git a/src/schemas/api/bots/SendableModalSubmitDataSchema.ts b/src/schemas/api/bots/SendableModalSubmitDataSchema.ts index e2c64cdf4..f898c0cae 100644 --- a/src/schemas/api/bots/SendableModalSubmitDataSchema.ts +++ b/src/schemas/api/bots/SendableModalSubmitDataSchema.ts @@ -1,5 +1,23 @@ +/* + 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 . +*/ + import { UploadAttachmentRequestSchema } from "@spacebar/schemas"; -import { Attachment, Snowflake } from "@spacebar/util"; +import { Snowflake } from "@spacebar/util"; export interface SendableModalSubmitDataSchema { id: Snowflake; diff --git a/src/schemas/api/bots/index.ts b/src/schemas/api/bots/index.ts index a08972f15..fba2677a9 100644 --- a/src/schemas/api/bots/index.ts +++ b/src/schemas/api/bots/index.ts @@ -24,3 +24,4 @@ export * from "./InteractionCreateSchema"; export * from "./SendableApplicationCommandDataSchema"; export * from "./SendableMessageComponentDataSchema"; export * from "./SendableModalSubmitDataSchema"; +export * from "./InteractionFailureReason"; diff --git a/src/schemas/api/channels/Channel.ts b/src/schemas/api/channels/Channel.ts index 8b604fe8e..c017092aa 100644 --- a/src/schemas/api/channels/Channel.ts +++ b/src/schemas/api/channels/Channel.ts @@ -21,13 +21,7 @@ export enum ChannelType { GUILD_MEDIA = 16, // channel for media sharing LOBBY = 17, // a game lobby channel EPHEMERAL_DM = 18, // a private channel created by the social layer sdk - ENCRYPTED = 19, // end-to-end encrypted channel - ENCRYPTED_THREAD = 20, // end-to-end encrypted thread channel - TRANSACTIONAL = 21, // event chain style transactional channel - 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 + TICKET_TRACKER = 19, // a channel that tracks support tickets (threads) UNHANDLED = 255, // unhandled unowned pass-through channel type } @@ -74,8 +68,6 @@ export function isTextChannel(type: ChannelType): boolean { 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); diff --git a/src/schemas/api/messages/Components.ts b/src/schemas/api/messages/Components.ts index 843a15c65..23d08957e 100644 --- a/src/schemas/api/messages/Components.ts +++ b/src/schemas/api/messages/Components.ts @@ -99,7 +99,6 @@ export enum TextInputStyle { } export enum MessageComponentType { - Script = 0, // self command script ActionRow = 1, Button = 2, StringSelect = 3, @@ -108,4 +107,19 @@ export enum MessageComponentType { RoleSelect = 6, MentionableSelect = 7, ChannelSelect = 8, + Section = 9, + TextDisplay = 10, + Thumbnail = 11, + MediaGallery = 12, + File = 13, + Separator = 14, + // 15 is unknown? + ContentInventoryEntry = 16, // activity feed entry + Container = 17, + Label = 18, + FileUpload = 19, + CheckpointCard = 20, // year in review 2026 + RadioGroup = 21, + CheckboxGroup = 22, + Checkbox = 23, } diff --git a/src/schemas/api/messages/Message.ts b/src/schemas/api/messages/Message.ts index e8202ddf4..96c6ab37d 100644 --- a/src/schemas/api/messages/Message.ts +++ b/src/schemas/api/messages/Message.ts @@ -84,12 +84,6 @@ export enum MessageType { REPORT_TO_MOD_BAN_USER = 61, REPORT_TO_MOD_CLOSED_REPORT = 62, EMOJI_ADDED = 63, - CUSTOM_START = 127, // start custom message types from here - ENCRYPTED = 128, - ROUTE_ADDED = 129, // custom message routing: new route affecting that channel - ROUTE_DISABLED = 130, // custom message routing: given route no longer affecting that channel - SELF_COMMAND_SCRIPT = 131, // self command scripts - ENCRYPTION = 132, UNHANDLED = 255, } @@ -129,14 +123,21 @@ export interface Reaction { user_ids: Snowflake[]; } -export interface PartialEmoji { - id?: string; - name: string; - animated?: boolean; -} +// aka { animated } & OneOf<{id},{name}> +export type PartialEmoji = + | { + id?: string; + name: string; + animated?: boolean; + } + | { + id: string; + name?: string; + animated?: boolean; + }; export interface AllowedMentions { - parse?: string[]; + parse?: ("users" | "roles" | "everyone")[]; roles?: Snowflake[]; users?: Snowflake[]; replied_user?: boolean; diff --git a/src/schemas/api/users/InstanceUserDeleteSchema.ts b/src/schemas/api/users/InstanceUserDeleteSchema.ts index 497e33484..aee124fe8 100644 --- a/src/schemas/api/users/InstanceUserDeleteSchema.ts +++ b/src/schemas/api/users/InstanceUserDeleteSchema.ts @@ -1,5 +1,3 @@ -import { ConnectedAccount } from "@spacebar/util"; - export type InstanceUserDeleteSchema = InstanceUserDeleteSchemaContent | undefined; //unsure if this a correct way to make the body optional export interface InstanceUserDeleteSchemaContent { reason?: string; diff --git a/src/schemas/api/users/Member.ts b/src/schemas/api/users/Member.ts index cc2c33421..89e096502 100644 --- a/src/schemas/api/users/Member.ts +++ b/src/schemas/api/users/Member.ts @@ -69,7 +69,8 @@ export type PublicMemberKeys = | "bio" | "theme_colors" | "pronouns" - | "communication_disabled_until"; + | "communication_disabled_until" + | "flags"; export const PublicMemberProjection: PublicMemberKeys[] = [ "id", @@ -87,6 +88,7 @@ export const PublicMemberProjection: PublicMemberKeys[] = [ "theme_colors", "pronouns", "communication_disabled_until", + "flags", ]; export type PublicMember = Omit, "roles"> & { diff --git a/src/schemas/gateway/LazyRequestSchema.ts b/src/schemas/gateway/LazyRequestSchema.ts index fcefe680e..dfeb5e131 100644 --- a/src/schemas/gateway/LazyRequestSchema.ts +++ b/src/schemas/gateway/LazyRequestSchema.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) 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 @@ -15,32 +15,3 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ - -export interface LazyRequestSchema { - guild_id: string; - channels?: { - /** - * @items.type integer - * @minItems 2 - * @maxItems 2 - */ - [key: string]: number[][]; // puyo: changed from [number, number] because it breaks openapi - }; - activities?: boolean; - threads?: boolean; - typing?: true; - members?: string[]; - member_updates?: boolean; - thread_member_lists?: unknown[]; -} - -export const LazyRequestSchema = { - guild_id: String, - $activities: Boolean, - $channels: Object, - $typing: Boolean, - $threads: Boolean, - $members: [] as string[], - $member_updates: Boolean, - $thread_member_lists: [] as unknown[], -}; diff --git a/src/schemas/responses/AccountStandingResponse.ts b/src/schemas/responses/AccountStandingResponse.ts index bbb8cd85d..6f29ebef7 100644 --- a/src/schemas/responses/AccountStandingResponse.ts +++ b/src/schemas/responses/AccountStandingResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { Attachment } from "../../util/entities"; +// TODO: remove dependency on entities... +import { Attachment } from "@spacebar/util"; export enum AccountStandingState { ALL_GOOD = 100, diff --git a/src/schemas/responses/DiscoverableGuildsResponse.ts b/src/schemas/responses/DiscoverableGuildsResponse.ts index 3e22c992a..02c46f1c6 100644 --- a/src/schemas/responses/DiscoverableGuildsResponse.ts +++ b/src/schemas/responses/DiscoverableGuildsResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { Guild } from "../../util/entities"; +// TODO: remove dependency on entities +import { Guild } from "@spacebar/util"; export interface DiscoverableGuildsResponse { total: number; diff --git a/src/schemas/responses/EmailDomainLookupVerifyCodeResponse.ts b/src/schemas/responses/EmailDomainLookupVerifyCodeResponse.ts index 6efeaba63..b2400e95d 100644 --- a/src/schemas/responses/EmailDomainLookupVerifyCodeResponse.ts +++ b/src/schemas/responses/EmailDomainLookupVerifyCodeResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { Guild } from "../../util/entities"; +// TODO: remove dependency on entities +import { Guild } from "@spacebar/util"; export interface EmailDomainLookupVerifyCodeResponse { guild: Guild; diff --git a/src/schemas/responses/GuildMessagesSearchResponse.ts b/src/schemas/responses/GuildMessagesSearchResponse.ts index 6777ad8a9..555092ee2 100644 --- a/src/schemas/responses/GuildMessagesSearchResponse.ts +++ b/src/schemas/responses/GuildMessagesSearchResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { Attachment, Role } from "../../util/entities"; +// TODO: remove dependency on entities +import { Attachment, Role } from "@spacebar/util"; import { ActionRowComponent, Embed, MessageType, Poll, PublicUser } from "@spacebar/schemas"; export interface GuildMessagesSearchMessage { diff --git a/src/schemas/responses/GuildRecommendationsResponse.ts b/src/schemas/responses/GuildRecommendationsResponse.ts index 04f9d1efc..c8fed0721 100644 --- a/src/schemas/responses/GuildRecommendationsResponse.ts +++ b/src/schemas/responses/GuildRecommendationsResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { Guild } from "../../util/entities"; +// TODO: remove dependency on entities +import { Guild } from "@spacebar/util"; export interface GuildRecommendationsResponse { recommended_guilds: Guild[]; diff --git a/src/schemas/responses/HubDirectoryEntriesResponse.ts b/src/schemas/responses/HubDirectoryEntriesResponse.ts index 677199d37..c19b6a141 100644 --- a/src/schemas/responses/HubDirectoryEntriesResponse.ts +++ b/src/schemas/responses/HubDirectoryEntriesResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { Guild } from "../../util/entities"; +// TODO: remove dependency on entities +import { Guild } from "@spacebar/util"; export interface HubDirectoryEntry { author_id: string; diff --git a/src/schemas/responses/MemberJoinGuildResponse.ts b/src/schemas/responses/MemberJoinGuildResponse.ts index 1f9bc8c9a..75081656b 100644 --- a/src/schemas/responses/MemberJoinGuildResponse.ts +++ b/src/schemas/responses/MemberJoinGuildResponse.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { Emoji, Role, Sticker } from "../../util/entities"; +import { Emoji, Role, Sticker } from "@spacebar/util"; import { GuildCreateResponse } from "@spacebar/schemas"; export interface MemberJoinGuildResponse { diff --git a/src/schemas/responses/TokenResponse.ts b/src/schemas/responses/TokenResponse.ts index 0af06fd11..4a0c0834e 100644 --- a/src/schemas/responses/TokenResponse.ts +++ b/src/schemas/responses/TokenResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { BackupCode, UserSettings } from "../../util/entities"; +// TODO: remove dependency on entities +import { BackupCode, UserSettings } from "@spacebar/util"; export interface TokenResponse { token: string; diff --git a/src/schemas/responses/TypedResponses.ts b/src/schemas/responses/TypedResponses.ts index 13956ec86..13143a9f9 100644 --- a/src/schemas/responses/TypedResponses.ts +++ b/src/schemas/responses/TypedResponses.ts @@ -18,10 +18,11 @@ import { GeneralConfiguration, LimitsConfiguration } from "../../util/config/types"; import { DmChannelDTO } from "../../util/dtos"; -import { Application, BackupCode, Categories, Channel, Emoji, Guild, Invite, Member, Message, Role, Sticker, StickerPack, Template, Webhook } from "../../util/entities"; +import { Application, BackupCode, Categories, Channel, Emoji, Guild, Invite, Member, Message, Role, Sticker, StickerPack, Template, Webhook } from "@spacebar/util"; import { GuildVoiceRegion } from "./GuildVoiceRegionsResponse"; import { GuildBansResponse, GuildCreateResponse, PrivateUser, PublicMember, PublicUser } from "@spacebar/schemas"; +// TODO: remove this entire file! // removes internal properties from the guild class export type APIGuild = Omit; diff --git a/src/schemas/responses/WebhookCreateResponse.ts b/src/schemas/responses/WebhookCreateResponse.ts index 1246c7422..08d997fb9 100644 --- a/src/schemas/responses/WebhookCreateResponse.ts +++ b/src/schemas/responses/WebhookCreateResponse.ts @@ -16,7 +16,8 @@ along with this program. If not, see . */ -import { User, Webhook } from "../../util/entities"; +// TODO: remove dependency on entities +import { User, Webhook } from "@spacebar/util"; export interface WebhookCreateResponse { user: User; diff --git a/src/util/config/types/subconfigurations/client/index.ts b/src/schemas/uncategorised/ChannelPromoteSchema.ts similarity index 92% rename from src/util/config/types/subconfigurations/client/index.ts rename to src/schemas/uncategorised/ChannelPromoteSchema.ts index 37231b431..b6a3e3bb8 100644 --- a/src/util/config/types/subconfigurations/client/index.ts +++ b/src/schemas/uncategorised/ChannelPromoteSchema.ts @@ -1,19 +1,21 @@ /* 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 . */ -export * from "./ClientReleaseConfiguration"; +export interface ChannelPromoteSchema { + position?: number; +} diff --git a/src/schemas/uncategorised/ConnectedAccountSchema.ts b/src/schemas/uncategorised/ConnectedAccountSchema.ts index c1d24d70a..a754f9f43 100644 --- a/src/schemas/uncategorised/ConnectedAccountSchema.ts +++ b/src/schemas/uncategorised/ConnectedAccountSchema.ts @@ -16,8 +16,6 @@ along with this program. If not, see . */ -import { ConnectedAccountTokenData } from "../../util/interfaces"; - export interface ConnectedAccountSchema { external_id: string; user_id: string; @@ -34,3 +32,21 @@ export interface ConnectedAccountSchema { metadata_visibility?: number; two_way_link?: boolean; } + +export interface ConnectedAccountCommonOAuthTokenResponse { + access_token: string; + token_type: string; + scope: string; + refresh_token?: string; + expires_in?: number; +} + +export interface ConnectedAccountTokenData { + access_token: string; + token_type?: string; + scope?: string; + refresh_token?: string; + expires_in?: number; + expires_at?: number; + fetched_at: number; +} diff --git a/src/schemas/uncategorised/GuildSubscriptionsBulkSchema.ts b/src/schemas/uncategorised/GuildSubscriptionsBulkSchema.ts index 32be9dd0a..272110c5f 100644 --- a/src/schemas/uncategorised/GuildSubscriptionsBulkSchema.ts +++ b/src/schemas/uncategorised/GuildSubscriptionsBulkSchema.ts @@ -1,5 +1,3 @@ -import { LazyRequestSchema } from "../gateway/LazyRequestSchema"; - export interface GuildSubscriptionsBulkSchema { subscriptions: { [key: string]: GuildSubscriptionSchema }; } @@ -9,3 +7,32 @@ export type GuildSubscriptionSchema = Omit; export const GuildSubscriptionsBulkSchema = { $subscriptions: Object, }; + +export interface LazyRequestSchema { + guild_id: string; + channels?: { + /** + * @items.type integer + * @minItems 2 + * @maxItems 2 + */ + [key: string]: number[][]; // puyo: changed from [number, number] because it breaks openapi + }; + activities?: boolean; + threads?: boolean; + typing?: true; + members?: string[]; + member_updates?: boolean; + thread_member_lists?: unknown[]; +} + +export const LazyRequestSchema = { + guild_id: String, + $activities: Boolean, + $channels: Object, + $typing: Boolean, + $threads: Boolean, + $members: [] as string[], + $member_updates: Boolean, + $thread_member_lists: [] as unknown[], +}; diff --git a/src/schemas/uncategorised/GuildTemplateCreateSchema.ts b/src/schemas/uncategorised/GuildTemplateCreateSchema.ts index 3ce9862f0..31035b784 100644 --- a/src/schemas/uncategorised/GuildTemplateCreateSchema.ts +++ b/src/schemas/uncategorised/GuildTemplateCreateSchema.ts @@ -18,5 +18,5 @@ export interface GuildTemplateCreateSchema { name: string; - avatar?: string | null; + icon?: string | null; } diff --git a/src/schemas/uncategorised/LobbyCreateSchema.ts b/src/schemas/uncategorised/LobbyCreateSchema.ts new file mode 100644 index 000000000..1ca313c25 --- /dev/null +++ b/src/schemas/uncategorised/LobbyCreateSchema.ts @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +export interface LobbyMemberSchema { + id: string; + metadata?: Record | null; + flags?: number; +} + +export interface LobbyCreateSchema { + metadata?: Record | null; + /** + * @maxItems 25 + */ + members?: LobbyMemberSchema[]; + /** + * @minimum 5 + * @maximum 604800 + */ + idle_timeout_seconds: number; +} + +export interface LobbyUpdateSchema { + metadata?: Record | null; + /** + * @maxItems 25 + */ + members?: LobbyMemberSchema[]; + /** + * @minimum 5 + * @maximum 604800 + */ + idle_timeout_seconds?: number; +} + +export interface LobbyMemberUpdateSchema { + metadata?: Record | null; + flags?: number; +} + +export interface LobbyChannelLinkSchema { + channel_id?: string; +} diff --git a/src/schemas/uncategorised/MessageCreateSchema.ts b/src/schemas/uncategorised/MessageCreateSchema.ts index 7c0d0728b..b9c8119e0 100644 --- a/src/schemas/uncategorised/MessageCreateSchema.ts +++ b/src/schemas/uncategorised/MessageCreateSchema.ts @@ -39,22 +39,22 @@ export interface MessageCreateSchema { channel_id?: string; tts?: boolean; flags?: number; - embeds?: Embed[]; - embed?: Embed; + embeds?: Embed[] | null; + embed?: Embed | null; // TODO: ^ embed is deprecated in favor of embeds (https://discord.com/developers/docs/resources/channel#message-object) allowed_mentions?: { - parse?: string[]; - roles?: string[]; - users?: string[]; + parse?: string[] | null; + roles?: string[] | null; + users?: string[] | null; replied_user?: boolean; - }; + } | null; message_reference?: { message_id?: string; channel_id?: string; guild_id?: string; fail_if_not_exists?: boolean; type?: number; - }; + } | null; payload_json?: string; file?: { filename: string }; /** diff --git a/src/schemas/uncategorised/RelationshipPutSchema.ts b/src/schemas/uncategorised/RelationshipPutSchema.ts index e29170eb2..5a5748020 100644 --- a/src/schemas/uncategorised/RelationshipPutSchema.ts +++ b/src/schemas/uncategorised/RelationshipPutSchema.ts @@ -18,6 +18,7 @@ export interface RelationshipPutSchema { type?: RelationshipType; + confirm_stranger_request?: boolean; } export enum RelationshipType { diff --git a/src/schemas/uncategorised/TicketSchemas.ts b/src/schemas/uncategorised/TicketSchemas.ts index 433d9bdc5..a924b9637 100644 --- a/src/schemas/uncategorised/TicketSchemas.ts +++ b/src/schemas/uncategorised/TicketSchemas.ts @@ -1,10 +1,10 @@ export interface TicketCreateSchema { - name?: string; + name?: string; } export interface TicketPatchSchema { - owner_id?: string | null; - resolved?: boolean | null; - public?: boolean | null; - closed?: boolean | null; + owner_id?: string | null; + resolved?: boolean | null; + public?: boolean | null; + closed?: boolean | null; } diff --git a/src/schemas/uncategorised/index.ts b/src/schemas/uncategorised/index.ts index 2b28768de..23b2acf88 100644 --- a/src/schemas/uncategorised/index.ts +++ b/src/schemas/uncategorised/index.ts @@ -65,6 +65,7 @@ export * from "./RefreshUrlsRequestSchema"; export * from "./RegisterSchema"; export * from "./RelationshipPatchSchema"; export * from "./RelationshipPostSchema"; +export * from "./RelationshipPatchSchema"; export * from "./RelationshipPutSchema"; export * from "./RequestGuildMembersSchema"; export * from "./RoleModifySchema"; @@ -91,10 +92,11 @@ export * from "./WebhookCreateSchema"; export * from "./WebhookExecuteSchema"; export * from "./WebhookUpdateSchema"; export * from "./WidgetModifySchema"; -export * from "./TicketSchemas"; export * from "./MessageThreadCreationSchema"; export * from "./ThreadCreationSchema"; export * from "./MessageActivity"; export * from "./PostDataSchema"; export * from "./TagCreateSchema"; export * from "./ChannelCreateSchema"; +export * from "./ChannelPromoteSchema"; +export * from "./TicketSchemas"; diff --git a/src/util/Signing.ts b/src/util/Signing.ts index 26daf808a..24f3c2061 100644 --- a/src/util/Signing.ts +++ b/src/util/Signing.ts @@ -114,7 +114,7 @@ export class UrlSignResult { } export const getUrlSignature = (data: NewUrlSignatureData): UrlSignResult => { - const { cdnSignatureKey, cdnSignatureDuration } = Config.get().security; + const { cdnSignatureDuration } = Config.get().security; // calculate the expiration time const now = Date.now(); diff --git a/src/util/config/types/ConnectionsConfiguration.ts b/src/util/config/types/ConnectionsConfiguration.ts new file mode 100644 index 000000000..7adf9f2c9 --- /dev/null +++ b/src/util/config/types/ConnectionsConfiguration.ts @@ -0,0 +1,26 @@ +/* + 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 . +*/ + +export class ConnectionsConfiguration { + enabled: boolean = true; + defaultVisibility: number = 1; + allowUserOverride: boolean = true; + providers: string[] = []; + requireExplicitConsent: boolean = false; + dataRetentionDays: number = 365; +} diff --git a/src/util/config/types/OffloadConfiguration.ts b/src/util/config/types/OffloadConfiguration.ts index b52919fb7..fd15df6d5 100644 --- a/src/util/config/types/OffloadConfiguration.ts +++ b/src/util/config/types/OffloadConfiguration.ts @@ -21,6 +21,9 @@ export class OffloadConfiguration { } export class GatewayOffloadConfiguration { - op12BaseUrl: string | null = null; - op14BaseUrl: string | null = null; + guildMembersUrl: string | null = null; // op8 + guildSyncUrl: string | null = null; // op12 + lazyRequestUrl: string | null = null; // op14 + channelStatusesUrl: string | null = null; //op36 + channelInfoUrl: string | null = null; //op43 } diff --git a/src/util/config/types/RegisterConfiguration.ts b/src/util/config/types/RegisterConfiguration.ts index e71f16dd0..484ba66b1 100644 --- a/src/util/config/types/RegisterConfiguration.ts +++ b/src/util/config/types/RegisterConfiguration.ts @@ -34,5 +34,6 @@ export class RegisterConfiguration { blockAbuseIpDbAboveScore: number = 75; // 0 to disable incrementingDiscriminators: boolean = false; // random otherwise defaultRights: string = "875069521787904"; // See `npm run generate:rights` - checkIp: boolean = true; + enableAbuseIpDb: boolean = false; + enableIpData: boolean = false; } diff --git a/src/util/config/types/SecurityConfiguration.ts b/src/util/config/types/SecurityConfiguration.ts index 7a15f8986..10900c58d 100644 --- a/src/util/config/types/SecurityConfiguration.ts +++ b/src/util/config/types/SecurityConfiguration.ts @@ -22,7 +22,6 @@ import { CaptchaConfiguration, TwoFactorConfiguration } from "."; export class SecurityConfiguration { captcha: CaptchaConfiguration = new CaptchaConfiguration(); twoFactor: TwoFactorConfiguration = new TwoFactorConfiguration(); - autoUpdate: boolean | number = true; requestSignature: string = crypto.randomBytes(32).toString("base64"); jwtSecret: string | null = null; // header to get the real user ip address diff --git a/src/util/config/types/index.ts b/src/util/config/types/index.ts index 3b18cf483..efbb25fe9 100644 --- a/src/util/config/types/index.ts +++ b/src/util/config/types/index.ts @@ -18,6 +18,7 @@ export * from "./ApiConfiguration"; export * from "./CdnConfiguration"; +export * from "./ConnectionsConfiguration"; export * from "./DefaultsConfiguration"; export * from "./EmailConfiguration"; export * from "./EndpointConfiguration"; diff --git a/src/util/config/types/subconfigurations/email/SMTP.ts b/src/util/config/types/subconfigurations/email/SMTP.ts index 85c286a37..cf81aec92 100644 --- a/src/util/config/types/subconfigurations/email/SMTP.ts +++ b/src/util/config/types/subconfigurations/email/SMTP.ts @@ -20,6 +20,8 @@ export class SMTPConfiguration { host: string | null = null; port: number | null = null; secure: boolean | null = null; + starttls: boolean = false; + allowInsecure: boolean = false; username: string | null = null; password: string | null = null; } diff --git a/src/util/config/types/subconfigurations/index.ts b/src/util/config/types/subconfigurations/index.ts index 2f4eb1ce9..82385df20 100644 --- a/src/util/config/types/subconfigurations/index.ts +++ b/src/util/config/types/subconfigurations/index.ts @@ -16,7 +16,6 @@ along with this program. If not, see . */ -export * from "./client"; export * from "./defaults"; export * from "./guild"; export * from "./kafka"; diff --git a/src/util/config/types/subconfigurations/limits/ApplicationLimits.ts b/src/util/config/types/subconfigurations/limits/ApplicationLimits.ts new file mode 100644 index 000000000..c522aec43 --- /dev/null +++ b/src/util/config/types/subconfigurations/limits/ApplicationLimits.ts @@ -0,0 +1,32 @@ +/* + 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 . +*/ + +export class ApplicationLimits { + maxEmojiNameLength: number = 32; + maxStickerNameLength: number = 30; + maxApplicationNameLength: number = 32; + maxWebhookNameLength: number = 80; + maxInviteCodeLength: number = 32; + maxVanityUrlLength: number = 16; + maxTemplateNameLength: number = 100; + maxTemplateDescriptionLength: number = 120; + maxScheduledEventNameLength: number = 100; + maxScheduledEventDescriptionLength: number = 1000; + maxForumTagNameLength: number = 20; + maxDiscriminatorLength: number = 4; +} diff --git a/src/api/global.d.ts b/src/util/config/types/subconfigurations/limits/AttachmentLimits.ts similarity index 86% rename from src/api/global.d.ts rename to src/util/config/types/subconfigurations/limits/AttachmentLimits.ts index 9423b781a..335e71e94 100644 --- a/src/api/global.d.ts +++ b/src/util/config/types/subconfigurations/limits/AttachmentLimits.ts @@ -16,11 +16,7 @@ along with this program. If not, see . */ -// declare global { -// namespace Express { -// interface Request { -// user_id: any; -// token: any; -// } -// } -// } +export class AttachmentLimits { + maxFilenameLength: number = 100; + maxDescriptionLength: number = 1024; +} diff --git a/src/util/config/types/subconfigurations/client/ClientReleaseConfiguration.ts b/src/util/config/types/subconfigurations/limits/AutomodLimits.ts similarity index 78% rename from src/util/config/types/subconfigurations/client/ClientReleaseConfiguration.ts rename to src/util/config/types/subconfigurations/limits/AutomodLimits.ts index 6f277e31a..8471c1e7a 100644 --- a/src/util/config/types/subconfigurations/client/ClientReleaseConfiguration.ts +++ b/src/util/config/types/subconfigurations/limits/AutomodLimits.ts @@ -16,7 +16,10 @@ along with this program. If not, see . */ -export class ClientReleaseConfiguration { - useLocalRelease: boolean = true; //TODO - upstreamVersion: string = "0.0.264"; +export class AutomodLimits { + maxRuleNameLength: number = 100; + maxKeywordFilterLength: number = 60; + maxRegexPatternLength: number = 260; + maxAllowListLength: number = 1000; + maxMentionTotalLimit: number = 50; } diff --git a/src/util/config/types/subconfigurations/limits/ComponentLimits.ts b/src/util/config/types/subconfigurations/limits/ComponentLimits.ts new file mode 100644 index 000000000..c5494543a --- /dev/null +++ b/src/util/config/types/subconfigurations/limits/ComponentLimits.ts @@ -0,0 +1,31 @@ +/* + 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 . +*/ + +export class ComponentLimits { + maxLabelLength: number = 80; + maxPlaceholderLength: number = 100; + maxSelectOptionLabelLength: number = 100; + maxSelectOptionDescriptionLength: number = 100; + maxSelectOptionValueLength: number = 100; + maxModalTitleLength: number = 45; + maxTextInputLabelLength: number = 45; + maxTextInputPlaceholderLength: number = 100; + maxTextInputValueLength: number = 4000; + maxTextInputMinLength: number = 0; + maxTextInputMaxLength: number = 4000; +} diff --git a/src/util/config/types/subconfigurations/limits/EmbedLimits.ts b/src/util/config/types/subconfigurations/limits/EmbedLimits.ts new file mode 100644 index 000000000..1f254b171 --- /dev/null +++ b/src/util/config/types/subconfigurations/limits/EmbedLimits.ts @@ -0,0 +1,26 @@ +/* + 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 . +*/ + +export class EmbedLimits { + maxTitleLength: number = 256; + maxDescriptionLength: number = 4096; + maxFieldNameLength: number = 256; + maxFieldValueLength: number = 1024; + maxFooterTextLength: number = 2048; + maxAuthorNameLength: number = 256; +} diff --git a/src/util/config/types/subconfigurations/limits/index.ts b/src/util/config/types/subconfigurations/limits/index.ts index 89435e13b..7d7cc82ff 100644 --- a/src/util/config/types/subconfigurations/limits/index.ts +++ b/src/util/config/types/subconfigurations/limits/index.ts @@ -22,4 +22,9 @@ export * from "./GuildLimits"; export * from "./MessageLimits"; export * from "./RateLimits"; export * from "./UserLimits"; +export * from "./EmbedLimits"; +export * from "./ComponentLimits"; +export * from "./AttachmentLimits"; +export * from "./AutomodLimits"; +export * from "./ApplicationLimits"; export * from "./ratelimits/index"; diff --git a/src/util/connections/Connection.ts b/src/util/connections/Connection.ts index afb33acc5..e48b13a11 100644 --- a/src/util/connections/Connection.ts +++ b/src/util/connections/Connection.ts @@ -33,7 +33,8 @@ export abstract class Connection { /** * Generates an authorization url for the connection. - * @param args + * @param userId the user id to generate the url for + * @returns the authorization url */ abstract getAuthorizationUrl(userId: string): string; @@ -47,7 +48,7 @@ export abstract class Connection { /** * Processes the callback - * @param args Callback arguments + * @param params Callback arguments */ abstract handleCallback(params: ConnectionCallbackSchema): Promise; @@ -63,7 +64,7 @@ export abstract class Connection { /** * Generates a state - * @param user_id The user id to generate a state for. + * @param userId The user id to generate a state for. * @returns a new state */ createState(userId: string): string { diff --git a/src/util/connections/ConnectionConfig.ts b/src/util/connections/ConnectionConfig.ts index a5e0095ad..c5da3878a 100644 --- a/src/util/connections/ConnectionConfig.ts +++ b/src/util/connections/ConnectionConfig.ts @@ -17,7 +17,7 @@ */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ConnectionConfigEntity } from "../entities/ConnectionConfigEntity"; +import { ConnectionConfigEntity } from "../entities"; import { OrmUtils } from "../imports"; let config: any; diff --git a/src/util/connections/ConnectionLoader.ts b/src/util/connections/ConnectionLoader.ts index 609c53b00..f75b430f5 100644 --- a/src/util/connections/ConnectionLoader.ts +++ b/src/util/connections/ConnectionLoader.ts @@ -28,7 +28,7 @@ const connectionsLoaded = false; export class ConnectionLoader { public static async loadConnections() { if (connectionsLoaded) return; - ConnectionConfig.init(); + await ConnectionConfig.init(); const dirs = fs.readdirSync(root).filter((x) => { try { fs.readdirSync(path.join(root, x)); @@ -38,7 +38,7 @@ export class ConnectionLoader { } }); - dirs.forEach(async (x) => { + dirs.forEach((x) => { const modPath = path.resolve(path.join(root, x)); const mod = new (require(modPath).default)() as Connection; ConnectionStore.connections.set(mod.id, mod); @@ -54,7 +54,7 @@ export class ConnectionLoader { if (cfg) cfg = Object.assign({}, defaults, cfg); else { cfg = defaults; - this.setConnectionConfig(id, cfg); + this.setConnectionConfig(id, cfg).catch((e) => console.error(`[Connections/ERROR] Failed to set default config for '${id}'!`, e)); } } diff --git a/src/util/connections/RefreshableConnection.ts b/src/util/connections/RefreshableConnection.ts index 32cf36acd..3d7761f71 100644 --- a/src/util/connections/RefreshableConnection.ts +++ b/src/util/connections/RefreshableConnection.ts @@ -17,8 +17,8 @@ */ import { ConnectedAccount } from "../entities"; -import { ConnectedAccountCommonOAuthTokenResponse } from "../interfaces"; import { Connection } from "./Connection"; +import { ConnectedAccountCommonOAuthTokenResponse } from "@spacebar/schemas"; /** * A connection that can refresh its token. diff --git a/src/util/dtos/LobbyDTO.ts b/src/util/dtos/LobbyDTO.ts new file mode 100644 index 000000000..81e192dc5 --- /dev/null +++ b/src/util/dtos/LobbyDTO.ts @@ -0,0 +1,31 @@ +/* + 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 . +*/ + +export interface LobbyMemberDTO { + id: string; + metadata?: Record | null; + flags?: number; +} + +export interface LobbyDTO { + id: string; + application_id: string; + metadata?: Record | null; + members: LobbyMemberDTO[]; + linked_channel?: { id: string }; +} diff --git a/src/util/dtos/index.ts b/src/util/dtos/index.ts index b70942277..5cfdf685d 100644 --- a/src/util/dtos/index.ts +++ b/src/util/dtos/index.ts @@ -18,5 +18,6 @@ export * from "./ConnectedAccountDTO"; export * from "./DmChannelDTO"; +export * from "./LobbyDTO"; export * from "./ReadyGuildDTO"; export * from "./UserDTO"; diff --git a/src/util/entities/Attachment.ts b/src/util/entities/Attachment.ts index c6858eb8e..9dc9c3ac0 100644 --- a/src/util/entities/Attachment.ts +++ b/src/util/entities/Attachment.ts @@ -18,7 +18,7 @@ import { BeforeRemove, Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { URL } from "url"; -import { deleteFile } from "../util/cdn"; +import { deleteFile } from "../util"; import { BaseClass } from "./BaseClass"; import { getUrlSignature, NewUrlUserSignatureData, NewUrlSignatureData } from "../Signing"; diff --git a/src/util/entities/AutomodRule.ts b/src/util/entities/AutomodRule.ts index ac7f330e2..4a1976ac2 100644 --- a/src/util/entities/AutomodRule.ts +++ b/src/util/entities/AutomodRule.ts @@ -19,7 +19,7 @@ import { BaseClass } from "./BaseClass"; import { Entity, JoinColumn, ManyToOne, Column } from "typeorm"; import { User } from "./User"; -import { AutomodAction, AutomodRuleActionType, AutomodRuleEventType, AutomodRuleTriggerMetadata, AutomodRuleTriggerType } from "@spacebar/schemas"; +import { AutomodAction, AutomodRuleEventType, AutomodRuleTriggerMetadata, AutomodRuleTriggerType } from "@spacebar/schemas"; @Entity({ name: "automod_rules", diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts index 1aaf1479d..24a3aee18 100644 --- a/src/util/entities/BaseClass.ts +++ b/src/util/entities/BaseClass.ts @@ -16,16 +16,56 @@ along with this program. If not, see . */ -import { BaseEntity, BeforeInsert, BeforeUpdate, Column, ColumnOptions, FindOptionsWhere, InsertResult, ObjectIdColumn, ObjectLiteral, PrimaryColumn } from "typeorm"; -import { Snowflake } from "../util/Snowflake"; -import { getDatabase } from "../util/Database"; -import { OrmUtils } from "../imports/OrmUtils"; +import { BaseEntity, BeforeInsert, BeforeUpdate, Column, ColumnOptions, FindOptionsWhere, ObjectIdColumn, PrimaryColumn } from "typeorm"; +import { Snowflake, getDatabase } from "../util"; +import { OrmUtils } from "../imports"; +import { annotationsKey } from "../util/Decorators"; export class BaseClassWithoutId extends BaseEntity { private get construct() { return this.constructor; } + // stores custom annotations we may stick on the properties + [annotationsKey]: { [p: string]: string[] }; + + // retrieves the custom annotations as its not super straight forward + get_annotations() { + return Object.getPrototypeOf(this)[annotationsKey]; + } + + // Loops through all the keys and compares it to annotations. If the RemoveEmpty is there it sets the value to undefined if null + clean_data() { + const annotations = this.get_annotations(); + if (annotations == undefined || annotations.length > 0) + //prevent errors if there are no annotations on an object + return; + for (const key in this) { + if ( + key in this && // This object has this property, should never fail but better to be safe + key in annotations && // If this property has an annotation + annotations[key].indexOf("JsonRemoveEmpty") > -1 && // if one of the annotations is JsonRemoveEmpty + (this[key] == null || // If this property is null + (typeof this[key] == "object" && Object.keys(this[key]).length == 0)) + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this[key] = undefined; // set to undefined to remove + } + if ( + key in this && // This object has this property, should never fail but better to be safe + key in annotations && // If this property has an annotation + annotations[key].indexOf("JsonNumber") > -1 && // if one of the annotations is JsonRemoveEmpty + typeof this[key] == "string" // and its a String + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this[key] = Number(this[key]); // convert string back to number + } + } + return this; + } + private get metadata() { return getDatabase()?.getMetadata(this.construct); } @@ -38,6 +78,7 @@ export class BaseClassWithoutId extends BaseEntity { // TODO: fix eslint // eslint-disable-next-line @typescript-eslint/no-explicit-any toJSON(): any { + this.clean_data(); return Object.fromEntries( // eslint-disable-next-line @typescript-eslint/ban-ts-comment this.metadata!.columns // @ts-ignore diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index ba87d15fc..9c7ddb571 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -20,7 +20,7 @@ import { HTTPError } from "lambert-server"; import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm"; import { DmChannelDTO } from "../dtos"; import { ChannelCreateEvent, ChannelRecipientRemoveEvent, ThreadCreateEvent, ThreadMembersUpdateEvent } from "../interfaces"; -import { InvisibleCharacters, Snowflake, emitEvent, getPermission, trimSpecial, Permissions, BitField, Config, DiscordApiErrors } from "../util"; +import { InvisibleCharacters, Snowflake, emitEvent, getPermission, trimSpecial, Permissions, Config, DiscordApiErrors } from "../util"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Invite } from "./Invite"; @@ -32,7 +32,7 @@ import { User } from "./User"; import { VoiceState } from "./VoiceState"; import { Webhook } from "./Webhook"; import { Member } from "./Member"; -import { ChannelPermissionOverwrite, ChannelPermissionOverwriteType, ChannelType, PublicUserProjection, ThreadMetadata } from "@spacebar/schemas"; +import { ChannelPermissionOverwrite, ChannelType, PublicUserProjection, ThreadMetadata } from "@spacebar/schemas"; import { OrmUtils } from "../imports"; import { ThreadMember } from "./ThreadMember"; @@ -95,8 +95,8 @@ export class Channel extends BaseClass { @ManyToOne(() => User) owner: User; - @Column({ nullable: true }) - last_pin_timestamp?: number; + @Column({ nullable: true, type: "timestamp with time zone" }) + last_pin_timestamp?: Date | null; // ISO8601 @Column({ nullable: true }) default_auto_archive_duration?: number; @@ -197,7 +197,7 @@ export class Channel extends BaseClass { skipEventEmit?: boolean; skipNameChecks?: boolean; }, - ) { + ): Promise { if (!opts?.skipPermissionCheck) { // Always check if user has permission first const permissions = await getPermission(user_id, channel.guild_id); @@ -218,7 +218,10 @@ export class Channel extends BaseClass { for (const character of InvisibleCharacters) if (channel.name.includes(character)) throw new HTTPError("Channel name cannot include invalid characters", 403); // Categories skip these checks on discord.com - if (channel.type !== ChannelType.GUILD_CATEGORY || guild.features.includes("IRC_LIKE_CATEGORY_NAMES")) { + if ( + (channel.type !== ChannelType.GUILD_CATEGORY && channel.type !== ChannelType.GUILD_STAGE_VOICE && channel.type !== ChannelType.GUILD_VOICE) || + guild.features.includes("IRC_LIKE_CATEGORY_NAMES") + ) { if (channel.name.includes(" ")) throw new HTTPError("Channel name cannot include invalid characters", 403); if (channel.name.match(/--+/g)) throw new HTTPError("Channel name cannot include multiple adjacent dashes.", 403); @@ -233,8 +236,8 @@ export class Channel extends BaseClass { } } - // TODO: should threads even be routed through this function instead of createThreadChannel? switch (channel.type) { + // TODO: should threads even be routed through this function instead of createThreadChannel? case ChannelType.GUILD_PUBLIC_THREAD: case ChannelType.GUILD_PRIVATE_THREAD: case ChannelType.GUILD_NEWS_THREAD: @@ -248,13 +251,7 @@ export class Channel extends BaseClass { where: { id: channel.parent_id }, }); if (!exists) throw new HTTPError("Parent id channel doesn't exist", 400); - if (exists.guild_id !== channel.guild_id) throw new HTTPError("The thread parent needs to be in the guild"); - const allowedParents = [ChannelType.GUILD_TEXT, ChannelType.GUILD_NEWS, ChannelType.TICKET_TRACKER]; - if ( - (channel.type === ChannelType.GUILD_PUBLIC_THREAD || channel.type === ChannelType.GUILD_PRIVATE_THREAD || channel.type === ChannelType.GUILD_NEWS_THREAD) && - !allowedParents.includes(exists.type) - ) - throw new HTTPError("Invalid thread parent channel type", 400); + if (exists.guild_id !== channel.guild_id) throw new HTTPError("The category channel needs to be in the guild"); } break; case ChannelType.GUILD_CATEGORY: @@ -284,7 +281,8 @@ export class Channel extends BaseClass { // total_message_sent: 0, }; - const ret = Channel.create(channel); + // TODO: figure out why the generic is required here + const ret = Channel.create(channel); await Promise.all([ ret.save(), @@ -300,7 +298,6 @@ export class Channel extends BaseClass { return ret; } - threadOnly() { return this.type === ChannelType.GUILD_FORUM || this.type === ChannelType.GUILD_MEDIA; } @@ -428,7 +425,7 @@ export class Channel extends BaseClass { } static async createDMChannel(recipients: string[], creator_user_id: string, name?: string) { - recipients = recipients.distinct().filter((x) => x !== creator_user_id); + recipients = [...new Set(recipients)].filter((x) => x !== creator_user_id); // TODO: check config for max number of recipients /** if you want to disallow note to self channels, uncomment the conditional below @@ -447,7 +444,7 @@ export class Channel extends BaseClass { const userRecipients = await Recipient.find({ where: { user_id: creator_user_id }, - relations: ["channel", "channel.recipients"], + relations: { channel: { recipients: true } }, }); for (const ur of userRecipients) { @@ -470,7 +467,7 @@ export class Channel extends BaseClass { channel = await Channel.create({ name, type, - owner_id: undefined, + owner_id: type === ChannelType.GROUP_DM ? creator_user_id : undefined, created_at: new Date(), last_message_id: undefined, recipients: channelRecipients.map((x) => @@ -556,13 +553,15 @@ export class Channel extends BaseClass { // TODO Delete attachments from the CDN for messages in the channel await Channel.delete({ id: channel.id }); - const guild = await Guild.findOneOrFail({ - where: { id: channel.guild_id }, - select: { channel_ordering: true }, - }); + if (channel.guild_id) { + const guild = await Guild.findOneOrFail({ + where: { id: channel.guild_id }, + select: { channel_ordering: true }, + }); - const updatedOrdering = guild.channel_ordering.filter((id) => id != channel.id); - await Guild.update({ id: channel.guild_id }, { channel_ordering: updatedOrdering }); + const updatedOrdering = guild.channel_ordering.filter((id) => id != channel.id); + await Guild.update({ id: channel.guild_id }, { channel_ordering: updatedOrdering }); + } } static async calculatePosition(channel_id: string, guild_id: string, guild?: Guild) { @@ -602,7 +601,6 @@ export class Channel extends BaseClass { isThread() { return this.type === ChannelType.GUILD_NEWS_THREAD || this.type === ChannelType.GUILD_PUBLIC_THREAD || this.type === ChannelType.GUILD_PRIVATE_THREAD; } - isForum() { return this.type === ChannelType.GUILD_FORUM || this.type === ChannelType.GUILD_MEDIA; } @@ -617,11 +615,12 @@ export class Channel extends BaseClass { // Does the channel support sending messages ( eg categories do not ) isWritable() { - const disallowedChannelTypes = [ChannelType.GUILD_CATEGORY, ChannelType.GUILD_STAGE_VOICE, ChannelType.VOICELESS_WHITEBOARD]; + const disallowedChannelTypes = [ChannelType.GUILD_CATEGORY, ChannelType.GUILD_STAGE_VOICE]; return disallowedChannelTypes.indexOf(this.type) == -1; } async getUserPermissions(opts: { user_id?: string; user?: User; member?: Member; guild?: Guild }): Promise { + if (this.isDm()) return this.owner_id == (opts.user_id ?? opts.user?.id) ? Permissions.ALL : Permissions.DEFAULT_DM_PERMISSIONS; let guild = opts.guild; if (!guild) { if (this.guild) guild = this.guild; @@ -641,37 +640,41 @@ export class Channel extends BaseClass { let member = opts.member; if (!member) { - if (opts.user) member = await Member.findOneOrFail({ where: { guild_id: guild.id, id: opts.user.id }, relations: ["roles"] }); - else if (opts.user_id) member = await Member.findOneOrFail({ where: { guild_id: guild.id, id: opts.user_id }, relations: ["roles"] }); + if (opts.user) member = await Member.findOneOrFail({ where: { guild_id: guild.id, id: opts.user.id }, relations: { roles: true } }); + else if (opts.user_id) member = await Member.findOneOrFail({ where: { guild_id: guild.id, id: opts.user_id }, relations: { roles: true } }); else { console.error("Channel.getUserPermissions: called without user or member for non-DM channel."); return Permissions.NONE; } } - const roles = (member.roles || (await Member.findOneOrFail({ where: { guild_id: guild.id, index: member.index }, relations: ["roles"] })).roles).sort( - (a, b) => a.position - b.position, - ); // ascending by position - - // calculate user's channel perms - should in theory match https://docs.discord.food/topics/permissions#permission-overwrites - // start at role permissions - let userPerms = new Permissions(new BitField(0).add(roles.map((r) => r.permissions))); - - // TODO: do we want to have an instance-wide opt out of this behavior? It would just be an extra if statement here - if (userPerms.has(Permissions.FLAGS.ADMINISTRATOR)) return userPerms; - - // apply channel overrides - if (this.permission_overwrites) { - // role overwrites - TODO: this probably violates the geneva conventions - we should probably be ordering roles here - for (const overwrite of this.permission_overwrites.filter((o) => o.type === ChannelPermissionOverwriteType.role && roles.map((r) => r.id).includes(o.id))) - userPerms = new Permissions(userPerms.remove(overwrite.deny).add(overwrite.allow)); - - // member overwrite, throws if somehow we have multiple overwrites for the same member - const memberOverwrite = this.permission_overwrites.single((o) => o.type === ChannelPermissionOverwriteType.member && o.id === member?.id); - if (memberOverwrite) userPerms = new Permissions(userPerms.remove(memberOverwrite.deny).add(memberOverwrite.allow)); - } - - return userPerms; + const roles = ( + member.roles || + ( + await Member.findOneOrFail({ + where: { guild_id: guild.id, index: member.index }, + relations: { roles: true }, + select: { + roles: { + id: true, + permissions: true, + position: true, + }, + }, + loadEagerRelations: false, + }) + ).roles + ).sort((a, b) => a.position - b.position); // ascending by position + + return Permissions.finalPermission({ + user: { + ...member, + roles: roles.map((r) => r.id), + flags: member.user?.flags ?? (await User.findOneOrFail({ where: { id: member.id }, select: { flags: true } })).flags, + }, + guild: { id: guild.id, owner_id: guild.owner_id!, roles }, // We don't care about including *all* guild roles, as not all of them are relevant... + channel: this, + }); } // TODO: should we throw for missing args? @@ -698,27 +701,15 @@ export class Channel extends BaseClass { } toJSON() { - const base = { ...this } as unknown as Record; - - base.bitrate = this.bitrate || undefined; - base.user_limit = this.user_limit || undefined; - base.rate_limit_per_user = this.rate_limit_per_user || undefined; - base.owner_id = this.owner_id || undefined; - if (this.isThread() && this.thread_members) { - base.member_ids_preview = this.thread_members.map((_) => _.member.id); - } - - if ( - this.parent?.type === ChannelType.TICKET_TRACKER || - (this.parent_id && (this.type === ChannelType.GUILD_PRIVATE_THREAD || this.type === ChannelType.GUILD_PUBLIC_THREAD)) - ) { - const prefix = "ticket:initiator:"; - if (this.topic && this.topic.startsWith(prefix)) { - const v = this.topic.substring(prefix.length).trim(); - if (v) base.ticket_initiator_id = v; - } - } - - return base; + return { + ...this, + + // these fields are not returned depending on the type of channel + bitrate: this.bitrate || undefined, + user_limit: this.user_limit || undefined, + rate_limit_per_user: this.rate_limit_per_user || undefined, + owner_id: this.owner_id || undefined, + ...(this.isThread() && this.thread_members ? { member_ids_preview: this.thread_members.map((_) => _.member.id) } : {}), + }; } } diff --git a/src/util/entities/ConnectedAccount.ts b/src/util/entities/ConnectedAccount.ts index 7c5ffb550..6a5fed759 100644 --- a/src/util/entities/ConnectedAccount.ts +++ b/src/util/entities/ConnectedAccount.ts @@ -17,9 +17,9 @@ */ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; -import { ConnectedAccountTokenData } from "../interfaces"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; +import { ConnectedAccountTokenData } from "@spacebar/schemas"; @Entity({ name: "connected_accounts", diff --git a/src/util/entities/Guild.ts b/src/util/entities/Guild.ts index aac3daf0e..29d4de584 100644 --- a/src/util/entities/Guild.ts +++ b/src/util/entities/Guild.ts @@ -304,13 +304,52 @@ export class Guild extends BaseClass { @Column() discovery_excluded: boolean = false; + async ToGuildSource() { + if (!this.features.includes("DISCOVERABLE")) { + return null; + } + return { + id: this.id, + name: this.name, + icon: this.icon, + description: this.description, + banner: this.banner, + splash: this.splash, + discovery_splash: this.discovery_splash, + features: this.features, + vanity_url_code: null, + preferred_locale: this.preferred_locale || "en", + premium_subscription_count: this.premium_subscription_count, + approximate_member_count: await Member.countBy({ + guild_id: this.id, + }), + approximate_presence_count: await Member.countBy({ + guild_id: this.id, + user: { + sessions: { + status: "online", + }, + }, + }), + emojis: this.emojis ?? undefined, + emoji_count: this.emojis ? this.emojis.length : undefined, + stickers: this.stickers ?? undefined, + sticker_count: this.stickers ? this.stickers.length : undefined, + auto_removed: false, + primary_category_id: this.primary_category_id, + keywords: [], + is_published: false, + reasons_to_join: [], + }; + } + static async createGuild(body: { name?: string; icon?: string | null; owner_id?: string; roles?: Partial[]; channels?: Partial[]; - template_guild_id: string | null; + source_guild_id: string | null; }) { const guild_id = Snowflake.generate(); @@ -372,7 +411,7 @@ export class Guild extends BaseClass { guild_id, id: // role.id === body.template_guild_id indicates that this is the @everyone role - role.id === body.template_guild_id ? guild_id : Snowflake.generate(), + role.id === body.source_guild_id || role.id == "0" ? guild_id : Snowflake.generate(), }) .save() .then(resolve); @@ -438,6 +477,9 @@ export class Guild extends BaseClass { ...this, unavailable: this.unavailable == false ? undefined : true, channel_ordering: undefined, + discovery_weight: undefined, + discovery_excluded: undefined, + parent: undefined, }; } } diff --git a/src/util/entities/InstanceBan.ts b/src/util/entities/InstanceBan.ts index 2c9d301b2..a82865a10 100644 --- a/src/util/entities/InstanceBan.ts +++ b/src/util/entities/InstanceBan.ts @@ -16,11 +16,8 @@ along with this program. If not, see . */ -import { Column, CreateDateColumn, Entity, FindOptionsWhere, JoinColumn, ManyToOne, OneToOne, RelationId } from "typeorm"; +import { Column, CreateDateColumn, Entity, FindOptionsWhere, JoinColumn, OneToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; -import { Team } from "./Team"; -import { User } from "./User"; -import { Guild } from "./Guild"; @Entity({ name: "instance_bans", diff --git a/src/util/entities/Member.ts b/src/util/entities/Member.ts index b15a9276e..6d93cf5db 100644 --- a/src/util/entities/Member.ts +++ b/src/util/entities/Member.ts @@ -21,14 +21,13 @@ import { BeforeInsert, BeforeUpdate, Column, Entity, Index, JoinColumn, JoinTabl import { Ban, Channel, PublicGuildRelations } from "."; import { ReadyGuildDTO } from "../dtos"; import { GuildCreateEvent, GuildDeleteEvent, GuildMemberAddEvent, GuildMemberRemoveEvent, GuildMemberUpdateEvent, MessageCreateEvent } from "../interfaces"; -import { Config, emitEvent } from "../util"; -import { DiscordApiErrors } from "../util/Constants"; +import { Config, emitEvent, DiscordApiErrors } from "../util"; import { BaseClassWithoutId } from "./BaseClass"; import { Guild } from "./Guild"; import { Message } from "./Message"; import { Role } from "./Role"; import { User } from "./User"; -import { AvatarDecorationData, Collectibles, DisplayNameStyle, PrimaryGuild, PublicMember, PublicMemberProjection, UserGuildSettings } from "@spacebar/schemas"; +import { AvatarDecorationData, Collectibles, DisplayNameStyle, PublicMember, PublicMemberProjection, UserGuildSettings } from "@spacebar/schemas"; export const MemberPrivateProjection: (keyof Member)[] = [ "id", @@ -50,6 +49,7 @@ export const MemberPrivateProjection: (keyof Member)[] = [ "theme_colors", "pronouns", "communication_disabled_until", + "flags", ]; @Entity({ @@ -155,6 +155,9 @@ export class Member extends BaseClassWithoutId { @Column({ type: "simple-json", nullable: true }) collectibles?: Collectibles; + @Column({ type: "int", default: 0 }) + flags: number = 0; + @BeforeUpdate() @BeforeInsert() validate() { diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts index ba28e26f2..506205d21 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts @@ -20,52 +20,18 @@ import { User } from "./User"; import { Member } from "./Member"; import { Role } from "./Role"; import { Channel } from "./Channel"; -import { InteractionType } from "../interfaces/Interaction"; +import { InteractionType } from "../interfaces"; import { Application } from "./Application"; -import { Column, CreateDateColumn, Entity, FindOneOptions, In, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, Not, OneToMany, Raw, RelationId } from "typeorm"; +import { Column, CreateDateColumn, Entity, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId, FindOneOptions, Raw, Not, BaseEntity, In } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Webhook } from "./Webhook"; import { Sticker } from "./Sticker"; import { Attachment } from "./Attachment"; import { NewUrlUserSignatureData } from "../Signing"; -import { MessageFlags } from "../util/MessageFlags"; -import { PartialMessage } from "@spacebar/schemas"; - -export enum MessageType { - DEFAULT = 0, - RECIPIENT_ADD = 1, - RECIPIENT_REMOVE = 2, - CALL = 3, - CHANNEL_NAME_CHANGE = 4, - CHANNEL_ICON_CHANGE = 5, - CHANNEL_PINNED_MESSAGE = 6, - GUILD_MEMBER_JOIN = 7, - USER_PREMIUM_GUILD_SUBSCRIPTION = 8, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, - USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, - CHANNEL_FOLLOW_ADD = 12, - ACTION = 13, // /me messages - GUILD_DISCOVERY_DISQUALIFIED = 14, - GUILD_DISCOVERY_REQUALIFIED = 15, - GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16, - GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17, - THREAD_CREATED = 18, - REPLY = 19, - APPLICATION_COMMAND = 20, // application command or self command invocation - THREAD_STARTER_MESSAGE = 21, - GUILD_INVITE_REMINDER = 22, - CONTEXT_MENU_COMMAND = 23, - AUTO_MODERATION_ACTION = 24, - CUSTOM_START = 127, // start custom message types from here - ENCRYPTED = 128, - ROUTE_ADDED = 129, // custom message routing: new route affecting that channel - ROUTE_DISABLED = 130, // custom message routing: given route no longer affecting that channel - SELF_COMMAND_SCRIPT = 131, // self command scripts - ENCRYPTION = 132, - UNHANDLED = 255, -} +import { ActionRowComponent, ApplicationCommandType, Embed, MessageSnapshot, MessageType, PartialMessage, Poll, Reaction } from "@spacebar/schemas"; +import { MessageFlags } from "@spacebar/util"; +import { JsonRemoveEmpty } from "../util/Decorators"; @Entity({ name: "messages", @@ -157,14 +123,17 @@ export class Message extends BaseClass { mention_everyone?: boolean; @JoinTable({ name: "message_user_mentions" }) + @JsonRemoveEmpty @ManyToMany(() => User) mentions: User[]; @JoinTable({ name: "message_role_mentions" }) + @JsonRemoveEmpty @ManyToMany(() => Role) mention_roles: Role[]; @JoinTable({ name: "message_channel_mentions" }) + @JsonRemoveEmpty @ManyToMany(() => Channel) mention_channels: Channel[]; @@ -176,23 +145,27 @@ export class Message extends BaseClass { cascade: true, orphanedRowAction: "delete", }) + @JsonRemoveEmpty attachments?: Attachment[]; @Column({ type: "simple-json" }) + @JsonRemoveEmpty embeds: Embed[]; @Column({ type: "simple-json" }) + @JsonRemoveEmpty reactions: Reaction[]; @Column({ type: "text", nullable: true }) nonce?: string; - @Column({ nullable: true }) - pinned?: boolean; - - @Column({ type: "timestamp", nullable: true }) + @Column({ nullable: true, type: Date }) pinned_at?: Date | null; + get pinned(): boolean { + return this.pinned_at != null; + } + @Column({ type: "int" }) type: MessageType; @@ -210,44 +183,37 @@ export class Message extends BaseClass { message_id?: string; channel_id?: string; guild_id?: string; - type?: number; + type?: number; // 0 = DEFAULT, 1 = FORWARD }; @JoinColumn({ name: "message_reference_id" }) - @ManyToOne(() => Message) - referenced_message?: Message; + @ManyToOne(() => Message, { onDelete: "SET NULL" }) + referenced_message?: Message | null; @Column({ type: "simple-json", nullable: true }) interaction?: { id: string; type: InteractionType; name: string; - user_id: string; // the user who invoked the interaction - // user: User; // TODO: autopopulate user }; - @Column({ type: "simple-json", nullable: true }) - components?: ActionRowComponent[]; - - @Column({ type: "simple-json", nullable: true }) - poll?: Poll; - @Column({ type: "simple-json", nullable: true }) interaction_metadata?: { id: string; type: InteractionType; user_id: string; - authorizing_integration_owners?: Record; - original_response_message_id?: string; - interacted_message_id?: string; - name?: string; + authorizing_integration_owners: object; + name: string; + command_type: ApplicationCommandType; }; @Column({ type: "simple-json", nullable: true }) - message_snapshots?: MessageSnapshot[]; + @JsonRemoveEmpty + components?: ActionRowComponent[]; @Column({ type: "simple-json", nullable: true }) - reply_ids?: string[]; + @JsonRemoveEmpty + poll?: Poll; @Column({ nullable: true }) username?: string; @@ -255,6 +221,12 @@ export class Message extends BaseClass { @Column({ nullable: true }) avatar?: string; + @Column({ default: "[]", type: "simple-json" }) + message_snapshots: MessageSnapshot[]; + + @Column({ type: "simple-json", nullable: true }) + reply_ids?: string[]; + static async fillReplies(messages: Message[]) { const ms = messages .filter((msg) => msg.message_reference && !msg.referenced_message?.id && msg.message_reference.message_id) @@ -272,7 +244,7 @@ export class Message extends BaseClass { newMessages.forEach((msg) => curMs.set(msg.id, msg)); } for (const message of ms) { - message.referenced_message = curMs.get(message.message_reference!.message_id as string) || undefined; + message.referenced_message = curMs.get(message.message_reference!.message_id as string) || null; } } @@ -283,7 +255,7 @@ export class Message extends BaseClass { member_id: undefined, webhook_id: this.webhook_id ?? undefined, application_id: undefined, - mentions: this.mentions.map((user) => { + mentions: this.mentions?.map((user) => { if (user && !user.toPublicUser) console.trace("toPublic user missing!!!"); return user?.toPublicUser?.() ?? user ?? undefined; }), @@ -293,6 +265,7 @@ export class Message extends BaseClass { guild: this.guild ?? undefined, webhook: this.webhook ?? undefined, interaction: this.interaction ?? undefined, + interaction_metadata: this.interaction_metadata ?? undefined, reactions: this.reactions ?? undefined, sticker_items: this.sticker_items ?? undefined, message_reference: this.message_reference ?? undefined, @@ -307,20 +280,12 @@ export class Message extends BaseClass { components: this.components ?? undefined, poll: this.poll ?? undefined, content: this.content ?? "", - reply_ids: this.reply_ids ?? undefined, pinned: this.pinned, thread: this.thread ? this.thread.toJSON() : this.thread, referenced_message: this.referenced_message && !shallow ? this.referenced_message.toJSON(true) : undefined, }; } - withSignedAttachments(data: NewUrlUserSignatureData) { - return { - ...this, - attachments: this.attachments?.map((attachment: Attachment) => Attachment.prototype.signUrls.call(attachment, data)), - }; - } - toPartialMessage(): PartialMessage { return { id: this.id, @@ -336,6 +301,13 @@ export class Message extends BaseClass { }; } + withSignedAttachments(data: NewUrlUserSignatureData) { + return { + ...this, + attachments: this.attachments?.map((attachment: Attachment) => Attachment.prototype.signUrls.call(attachment, data)), + }; + } + static async createWithDefaults(opts: Partial): Promise { const message = Message.create(); @@ -389,7 +361,6 @@ export class Message extends BaseClass { }); return message; } - static addDefault(options: FindOneOptions) { if (options.where) { const arr = options.where instanceof Array ? options.where : [options.where]; @@ -402,207 +373,21 @@ export class Message extends BaseClass { } } -export interface MessageComponent { - type: MessageComponentType; -} - -export interface ActionRowComponent extends MessageComponent { - type: MessageComponentType.ActionRow; - components: (ButtonComponent | StringSelectMenuComponent | SelectMenuComponent | TextInputComponent)[]; -} - -export interface ButtonComponent extends MessageComponent { - type: MessageComponentType.Button; - style: ButtonStyle; - label?: string; - emoji?: PartialEmoji; - custom_id?: string; - sku_id?: string; - url?: string; - disabled?: boolean; -} - -export enum ButtonStyle { - Primary = 1, - Secondary = 2, - Success = 3, - Danger = 4, - Link = 5, - Premium = 6, -} - -export interface SelectMenuComponent extends MessageComponent { - type: - | MessageComponentType.StringSelect - | MessageComponentType.UserSelect - | MessageComponentType.RoleSelect - | MessageComponentType.MentionableSelect - | MessageComponentType.ChannelSelect; - custom_id: string; - channel_types?: number[]; - placeholder?: string; - default_values?: SelectMenuDefaultOption[]; // only for non-string selects - min_values?: number; - max_values?: number; - disabled?: boolean; -} - -export interface SelectMenuOption { - label: string; - value: string; - description?: string; - emoji?: PartialEmoji; - default?: boolean; -} - -export interface SelectMenuDefaultOption { - id: string; - type: "user" | "role" | "channel"; -} - -export interface StringSelectMenuComponent extends SelectMenuComponent { - type: MessageComponentType.StringSelect; - options: SelectMenuOption[]; -} - -export interface TextInputComponent extends MessageComponent { - type: MessageComponentType.TextInput; - custom_id: string; - style: TextInputStyle; - label: string; - min_length?: number; - max_length?: number; - required?: boolean; - value?: string; - placeholder?: string; -} - -export enum TextInputStyle { - Short = 1, - Paragraph = 2, -} - -export enum MessageComponentType { - Script = 0, // self command script - ActionRow = 1, - Button = 2, - StringSelect = 3, - TextInput = 4, - UserSelect = 5, - RoleSelect = 6, - MentionableSelect = 7, - ChannelSelect = 8, -} - -export interface Embed { - title?: string; //title of embed - type?: EmbedType; // type of embed (always "rich" for webhook embeds) - description?: string; // description of embed - url?: string; // url of embed - timestamp?: Date; // timestamp of embed content - color?: number; // color code of the embed - footer?: { - text: string; - icon_url?: string; - proxy_icon_url?: string; - }; // footer object footer information - image?: EmbedImage; // image object image information - thumbnail?: EmbedImage; // thumbnail object thumbnail information - video?: EmbedImage; // video object video information - provider?: { - name?: string; - url?: string; - }; // provider object provider information - author?: { - name?: string; - url?: string; - icon_url?: string; - proxy_icon_url?: string; - }; // author object author information - fields?: { - name: string; - value: string; - inline?: boolean; - }[]; -} - -export enum EmbedType { - rich = "rich", - image = "image", - video = "video", - gifv = "gifv", - article = "article", - link = "link", - auto_moderation_message = "auto_moderation_message", -} - -export interface EmbedImage { - url?: string; - proxy_url?: string; - height?: number; - width?: number; -} - -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[]; -} - -export interface PartialEmoji { - id?: string; - name: string; - animated?: boolean; -} - -export interface AllowedMentions { - parse?: string[]; - roles?: string[]; - users?: string[]; - replied_user?: boolean; -} - -export interface Poll { - question: PollMedia; - answers: PollAnswer[]; - expiry: Date; - allow_multiselect: boolean; - results?: PollResult; -} - -export interface PollMedia { - text?: string; - emoji?: PartialEmoji; -} - -export interface PollAnswer { - answer_id?: string; - poll_media: PollMedia; -} - -export interface PollResult { - is_finalized: boolean; - answer_counts: PollAnswerCount[]; -} - -export interface PollAnswerCount { - id: string; - count: number; - me_voted: boolean; -} - -export interface MessageSnapshot { - message: { - attachments?: Attachment[]; - components?: ActionRowComponent[]; - content: string; - edited_timestamp?: Date; - embeds: Embed[]; - flags?: number; - mention_roles: string[]; - mentions: string[]; - timestamp?: Date; - type?: MessageType; - }; -} +//@ts-expect-error It works but TS types hate it +Message.findOneOrFail = function (this: Message, options: FindOneOptions): Promise { + Message.addDefault(options as FindOneOptions); + //@ts-expect-error how to use generics on call, who knows! + return BaseEntity.findOneOrFail.call(Message, options); +}; +//@ts-expect-error It works but TS types hate it +Message.findOne = function (this: Message, options: FindOneOptions): Promise { + Message.addDefault(options as FindOneOptions); + //@ts-expect-error how to use generics on call, who knows! + return BaseEntity.findOne.call(Message, options); +}; +//@ts-expect-error It works but TS types hate it +Message.find = function (this: Message, options: FindOneOptions): Promise { + Message.addDefault(options as FindOneOptions); + //@ts-expect-error how to use generics on call, who knows! + return BaseEntity.find.call(Message, options); +}; diff --git a/src/util/entities/ReadState.ts b/src/util/entities/ReadState.ts index e334cd73c..4d15c6cc8 100644 --- a/src/util/entities/ReadState.ts +++ b/src/util/entities/ReadState.ts @@ -20,6 +20,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne, RelationId } from "typeor import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; import { User } from "./User"; +import { ReadStateFlags, ReadStateType } from "@spacebar/schemas"; // for read receipts // notification cursor and public read receipt need to be forwards-only (the former to prevent re-pinging when marked as unread, and the latter to be acceptable as a legal acknowledgement in criminal proceedings), and private read marker needs to be advance-rewind capable @@ -50,25 +51,43 @@ export class ReadState extends BaseClass { }) user: User; - // fully read marker @Column({ nullable: true }) - last_message_id: string; + last_message_id?: string; - // public read receipt @Column({ nullable: true }) - public_ack: string; + last_acked_id?: string; - // notification cursor / private read receipt @Column({ nullable: true }) notifications_cursor: string; + @Column({ default: 0 }) + mention_count: number; + + @Column({ default: 0 }) + badge_count: number; + @Column({ nullable: true }) last_pin_timestamp?: Date; - @Column({ nullable: true }) - mention_count: number; + @Column({ default: ReadStateType.CHANNEL }) + read_state_type: ReadStateType; + + @Column({ default: 0 }) + flags: ReadStateFlags; - // @Column({ nullable: true }) - // TODO: derive this from (last_message_id=notifications_cursor=public_ack)=true - manual: boolean; + // toJSON() { + // const res = { ...this } as Partial; + // if (this.read_state_type === ReadStateType.CHANNEL) { + // delete res.badge_count; + // delete res.last_acked_id; + // } else { + // delete res.mention_count; // mutually exclusive with badge_count + // delete res.last_message_id; // mutually exclusive with last_acked_id + // // these only apply to channels: + // delete res.last_pin_timestamp; + // delete res.flags; + // // delete res.last_viewed; // TODO + // } + // return res; + // } } diff --git a/src/util/entities/ReportMenu.ts b/src/util/entities/ReportMenu.ts index 26d66adc3..a37ae159d 100644 --- a/src/util/entities/ReportMenu.ts +++ b/src/util/entities/ReportMenu.ts @@ -16,10 +16,8 @@ along with this program. If not, see . */ -import { BaseClass, BaseClassWithoutId } from "./BaseClass"; -import { Entity, JoinColumn, ManyToOne, Column } from "typeorm"; -import { User } from "./User"; -import { AutomodAction, AutomodRuleActionType, AutomodRuleEventType, AutomodRuleTriggerMetadata, AutomodRuleTriggerType } from "@spacebar/schemas"; +import { BaseClass } from "./BaseClass"; +import { Entity, Column } from "typeorm"; import { ReportMenuType } from "../../schemas/api/reports/ReportMenu"; @Entity({ diff --git a/src/util/entities/Role.ts b/src/util/entities/Role.ts index 7908f42d5..0647d6da6 100644 --- a/src/util/entities/Role.ts +++ b/src/util/entities/Role.ts @@ -42,7 +42,7 @@ export class Role extends BaseClass { @Column() hoist: boolean; - @Column() + @Column({ default: false }) managed: boolean; @Column() diff --git a/src/util/entities/Session.ts b/src/util/entities/Session.ts index 585b2ec2b..e6ada0d94 100644 --- a/src/util/entities/Session.ts +++ b/src/util/entities/Session.ts @@ -22,7 +22,6 @@ import { BaseClassWithoutId } from "./BaseClass"; import { Column, CreateDateColumn, Entity, Index, JoinColumn, ManyToOne, PrimaryColumn, RelationId } from "typeorm"; import { Activity, ClientStatus, GatewaySession, GatewaySessionClientInfo, Status } from "../interfaces"; import { randomUpperString } from "@spacebar/api"; -import { IpDataIpLookupResponse } from "../util/networking/ipdata/IpDataSampleResponses"; import { DateBuilder, IpDataClient, TimeSpan } from "../util"; //TODO we need to remove all sessions on server start because if the server crashes without closing websockets it won't delete them @@ -60,7 +59,7 @@ export class Session extends BaseClassWithoutId { client_status: ClientStatus; @Column({ nullable: false, type: String }) - status: Status; //TODO enum + status: Status; @Column({ default: false }) is_admin_session: boolean; diff --git a/src/util/entities/Tag.ts b/src/util/entities/Tag.ts index 143fad54b..63ad4b864 100644 --- a/src/util/entities/Tag.ts +++ b/src/util/entities/Tag.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; +import { Column, Entity, JoinColumn, ManyToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; import { Channel } from "./Channel"; diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index d8f52988d..df4664b54 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -17,7 +17,7 @@ */ import { Request } from "express"; -import { Column, Entity, FindOneOptions, JoinColumn, OneToMany, OneToOne } from "typeorm"; +import { Column, Entity, JoinColumn, OneToMany, OneToOne } from "typeorm"; import { Channel, Config, Email, FieldErrors, Snowflake, trimSpecial } from ".."; import { Random } from "../util"; import { BaseClass } from "./BaseClass"; @@ -38,6 +38,7 @@ import { PublicUserProjection, UserPrivate, } from "@spacebar/schemas"; +import { JsonNumber } from "../util/Decorators"; @Entity({ name: "users", @@ -124,18 +125,22 @@ export class User extends BaseClass { email?: string; // email of the user @Column({ type: "bigint" }) + @JsonNumber flags: number = 0; // UserFlags // TODO: generate @Column({ type: "bigint" }) + @JsonNumber public_flags: number = 0; @Column({ type: "bigint" }) + @JsonNumber purchased_flags: number = 0; @Column() premium_usage_flags: number = 0; @Column({ type: "bigint" }) + @JsonNumber rights: string; @OneToMany(() => Session, (session: Session) => session.user) @@ -194,7 +199,7 @@ export class User extends BaseClass { validate() { if (this.discriminator) { const discrim = Number(this.discriminator); - if (isNaN(discrim) || !(typeof discrim == "number") || !Number.isInteger(discrim) || discrim <= 0 || discrim >= 10000) + if (isNaN(discrim) || !Number.isInteger(discrim) || discrim <= 0 || discrim >= 10000) throw FieldErrors({ discriminator: { message: "Discriminator must be a number.", @@ -207,6 +212,7 @@ export class User extends BaseClass { } toPublicUser() { + this.clean_data(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const user: any = {}; PublicUserProjection.forEach((x) => { @@ -216,6 +222,7 @@ export class User extends BaseClass { } toPrivateUser(extraFields: (keyof User)[] = []) { + this.clean_data(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const user: any = {}; [...PrivateUserProjection, ...extraFields].forEach((x) => { diff --git a/src/util/entities/UserConsent.ts b/src/util/entities/UserConsent.ts index 679ee8ed1..844c3f4a0 100644 --- a/src/util/entities/UserConsent.ts +++ b/src/util/entities/UserConsent.ts @@ -1,110 +1,32 @@ -/* - 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 . -*/ - -import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm"; +import { Column, Entity, Index, ManyToOne } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; export enum ConsentType { - MEDIA_ITEM = "media_item", - MESSAGE_ITEM = "message_item", - EXTERNAL_DATA_PROCESSING = "external_data_processing", - BOTS_AI = "bots_ai", + DATA_PROCESSING = "data_processing", + MARKETING = "marketing", + THIRD_PARTY_SHARING = "third_party_sharing", CUSTOM = "custom", } export enum ConsentStatus { - PENDING = "pending", GRANTED = "granted", PROVISIONAL = "provisional", RETRACTED = "retracted", } @Entity({ name: "user_consents" }) -@Index(["user_id", "service_id", "consent_type", "item_id", "target_user_id"], { - unique: true, -}) +@Index(["user_id", "service_id"], { unique: true }) export class UserConsent extends BaseClass { @Column() - @Index() user_id: string; @Column() service_id: string; - @Column({ type: "varchar", default: ConsentType.CUSTOM }) - consent_type: ConsentType; - - @Column({ nullable: true }) - @Index() - item_id?: string; - - @Column({ nullable: true }) - @Index() - target_user_id?: string; - - @Column({ type: "varchar", default: ConsentStatus.GRANTED }) - status: ConsentStatus; - - @Column({ nullable: true }) - basis_document_url?: string; - - @Column({ nullable: true }) - basis_document_hash?: string; - - @Column({ nullable: true }) - granted_at?: Date; - - @Column({ nullable: true }) - retracted_at?: Date; - - @Column({ nullable: true }) - expires_at?: Date; - - @Column({ type: "simple-json", nullable: true }) - extra_data?: Record; - @Column() created_at: Date = new Date(); @ManyToOne(() => User, (user) => user.id, { onDelete: "CASCADE" }) - @JoinColumn({ name: "user_id" }) user?: User; - - @ManyToOne(() => User, { onDelete: "CASCADE" }) - @JoinColumn({ name: "target_user_id" }) - target_user?: User; - - toJSON() { - return { - id: this.id, - service_id: this.service_id, - consent_type: this.consent_type, - item_id: this.item_id, - target_user_id: this.target_user_id, - status: this.status, - basis_document_url: this.basis_document_url, - basis_document_hash: this.basis_document_hash, - granted_at: this.granted_at, - retracted_at: this.retracted_at, - expires_at: this.expires_at, - extra_data: this.extra_data, - created_at: this.created_at, - }; - } } diff --git a/src/util/entities/UserSettingsProtos.ts b/src/util/entities/UserSettingsProtos.ts index c08b98ff6..a3c1e4fbf 100644 --- a/src/util/entities/UserSettingsProtos.ts +++ b/src/util/entities/UserSettingsProtos.ts @@ -99,7 +99,7 @@ export class UserSettingsProtos extends BaseClassWithoutId { } static async getOrDefault(user_id: string, save: boolean = false): Promise { - const user = await User.findOneOrFail({ + await User.findOneOrFail({ where: { id: user_id }, select: { settings: true }, }); diff --git a/src/util/entities/index.ts b/src/util/entities/index.ts index ddcc89677..d7007212d 100644 --- a/src/util/entities/index.ts +++ b/src/util/entities/index.ts @@ -63,6 +63,6 @@ export * from "./UserSettingsProtos"; export * from "./ValidRegistrationTokens"; export * from "./VoiceState"; export * from "./Webhook"; +export * from "./Tag"; export * from "./UserConsent"; export * from "./ConsentGrant"; -export * from "./Tag"; diff --git a/src/util/index.ts b/src/util/index.ts index dd2f7f320..6fb7c21e5 100644 --- a/src/util/index.ts +++ b/src/util/index.ts @@ -28,3 +28,4 @@ export * from "./imports"; export * from "./config"; export * from "./connections"; export * from "./Signing"; +export * from "./stores/LobbyStore"; diff --git a/src/util/interfaces/ConnectedAccount.ts b/src/util/interfaces/ConnectedAccount.ts index 57cda25d2..0433355cc 100644 --- a/src/util/interfaces/ConnectedAccount.ts +++ b/src/util/interfaces/ConnectedAccount.ts @@ -15,21 +15,3 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ - -export interface ConnectedAccountCommonOAuthTokenResponse { - access_token: string; - token_type: string; - scope: string; - refresh_token?: string; - expires_in?: number; -} - -export interface ConnectedAccountTokenData { - access_token: string; - token_type?: string; - scope?: string; - refresh_token?: string; - expires_in?: number; - expires_at?: number; - fetched_at: number; -} diff --git a/src/util/interfaces/Event.ts b/src/util/interfaces/Event.ts index 6ddf6b298..aca555924 100644 --- a/src/util/interfaces/Event.ts +++ b/src/util/interfaces/Event.ts @@ -36,19 +36,31 @@ import { ReadyPrivateChannel, GuildOrUnavailable, Snowflake, + ThreadMember, } from "@spacebar/util"; import { JsonValue } from "@protobuf-ts/runtime"; -import { ApplicationCommand, GuildCreateResponse, PartialEmoji, PublicMember, PublicUser, PublicVoiceState, RelationshipType, UserPrivate } from "@spacebar/schemas"; -import { ThreadMember } from "../entities/ThreadMember"; +import { + ApplicationCommand, + GuildCreateResponse, + InteractionFailureReason, + PartialEmoji, + PublicMember, + PublicUser, + PublicVoiceState, + RelationshipType, + UserPrivate, +} from "@spacebar/schemas"; export interface Event { guild_id?: string; user_id?: string; + session_id?: string; channel_id?: string; created_at?: Date; event: EVENT; // eslint-disable-next-line @typescript-eslint/no-explicit-any data?: any; + reconnect_delay?: number; origin?: string; } @@ -366,7 +378,10 @@ export interface MessageDeleteBulkEvent extends Event { guild_id?: string; }; } - +export const enum ReactionType { + normal = 0, + burst = 1, +} export interface MessageReactionAddEvent extends Event { event: "MESSAGE_REACTION_ADD"; data: { @@ -376,6 +391,7 @@ export interface MessageReactionAddEvent extends Event { guild_id?: string; member?: PublicMember; emoji: PartialEmoji; + type: ReactionType; }; } @@ -387,6 +403,7 @@ export interface MessageReactionRemoveEvent extends Event { message_id: string; guild_id?: string; emoji: PartialEmoji; + type: ReactionType; }; } @@ -536,7 +553,7 @@ export interface InteractionFailureEvent extends Event { data: { id: Snowflake; nonce?: string; - reason_code: number; // TODO: types? + reason_code: InteractionFailureReason; }; } @@ -837,4 +854,13 @@ export type EVENT = | "THREAD_MEMBERS_UPDATE" | CUSTOMEVENTS; -export type CUSTOMEVENTS = "INVALIDATED" | "RATELIMIT"; +export type CUSTOMEVENTS = + | "INVALIDATED" + | "RATELIMIT" + | "SB_SESSION_REMOVE" + | "SB_SESSION_CLOSE" + | "LOBBY_CREATE" + | "LOBBY_UPDATE" + | "LOBBY_DELETE" + | "LOBBY_MEMBER_ADD" + | "LOBBY_MEMBER_REMOVE"; diff --git a/src/util/migration/mariadb/1755848356000-doublyLinkedReplies.ts b/src/util/migration/mariadb/1755848356000-doublyLinkedReplies.ts new file mode 100644 index 000000000..5713f3e0b --- /dev/null +++ b/src/util/migration/mariadb/1755848356000-doublyLinkedReplies.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DoublyLinkedReplies1755848356000 implements MigrationInterface { + name = "DoublyLinkedReplies1755848356000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages ADD reply_ids text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages DROP COLUMN reply_ids"); + } +} diff --git a/src/util/migration/mysql/1755848356000-doublyLinkedReplies.ts b/src/util/migration/mysql/1755848356000-doublyLinkedReplies.ts new file mode 100644 index 000000000..5713f3e0b --- /dev/null +++ b/src/util/migration/mysql/1755848356000-doublyLinkedReplies.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DoublyLinkedReplies1755848356000 implements MigrationInterface { + name = "DoublyLinkedReplies1755848356000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages ADD reply_ids text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages DROP COLUMN reply_ids"); + } +} diff --git a/src/util/migration/postgres-initial.ts b/src/util/migration/postgres-initial.ts index 20a747c2d..b158ba2ad 100644 --- a/src/util/migration/postgres-initial.ts +++ b/src/util/migration/postgres-initial.ts @@ -719,7 +719,7 @@ export class initial0 implements MigrationInterface { await queryRunner.query(`ALTER TABLE ONLY public.team_members ADD CONSTRAINT "FK_fdad7d5768277e60c40e01cdcea" FOREIGN KEY (team_id) REFERENCES public.teams(id) ON DELETE CASCADE;`); } - public async down(queryRunner: QueryRunner): Promise { + public async down(_: QueryRunner): Promise { throw new Error("Can't revert this: just throw away your database lol"); } } diff --git a/src/util/migration/postgres/1755848356000-doublyLinkedReplies.ts b/src/util/migration/postgres/1755848356000-doublyLinkedReplies.ts new file mode 100644 index 000000000..5713f3e0b --- /dev/null +++ b/src/util/migration/postgres/1755848356000-doublyLinkedReplies.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class DoublyLinkedReplies1755848356000 implements MigrationInterface { + name = "DoublyLinkedReplies1755848356000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages ADD reply_ids text NULL"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query("ALTER TABLE messages DROP COLUMN reply_ids"); + } +} diff --git a/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts b/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts index cf770afca..d8e13fdc6 100644 --- a/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts +++ b/src/util/migration/postgres/1761113394664-delete-bot-users-without-an-application.ts @@ -6,5 +6,5 @@ export class DeleteBotUsersWithoutAnApplication1761113394664 implements Migratio await queryRunner.query(`DELETE FROM users WHERE bot = true AND id NOT IN (SELECT bot_user_id FROM applications);`); } - public async down(queryRunner: QueryRunner): Promise {} + public async down(_: QueryRunner): Promise {} } diff --git a/src/util/migration/postgres/1771271159006-set-role-managed-default.ts b/src/util/migration/postgres/1771271159006-set-role-managed-default.ts new file mode 100644 index 000000000..fad55f237 --- /dev/null +++ b/src/util/migration/postgres/1771271159006-set-role-managed-default.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class SetRoleManagedDefault1771271159006 implements MigrationInterface { + name = "SetRoleManagedDefault1771271159006"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "roles" ALTER COLUMN "managed" SET DEFAULT false`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "roles" ALTER COLUMN "managed" DROP DEFAULT`); + } +} diff --git a/src/util/migration/postgres/1771825341528-MoreReadStateFields.ts b/src/util/migration/postgres/1771825341528-MoreReadStateFields.ts new file mode 100644 index 000000000..13ddd6a40 --- /dev/null +++ b/src/util/migration/postgres/1771825341528-MoreReadStateFields.ts @@ -0,0 +1,26 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MoreReadStateFields1771825341528 implements MigrationInterface { + name = "MoreReadStateFields1771825341528"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "read_states" DROP COLUMN "public_ack"`); + await queryRunner.query(`ALTER TABLE "read_states" ADD "last_acked_id" character varying`); + await queryRunner.query(`ALTER TABLE "read_states" ADD "badge_count" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "read_states" ADD "read_state_type" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "read_states" ADD "flags" integer NOT NULL DEFAULT '0'`); + await queryRunner.query(`ALTER TABLE "read_states" ALTER COLUMN "mention_count" SET DEFAULT '0'`); + await queryRunner.query(`UPDATE "read_states" SET "mention_count" = 0 WHERE "mention_count" IS NULL`); + await queryRunner.query(`ALTER TABLE "read_states" ALTER COLUMN "mention_count" SET NOT NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "read_states" ALTER COLUMN "mention_count" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "read_states" ALTER COLUMN "mention_count" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "read_states" DROP COLUMN "flags"`); + await queryRunner.query(`ALTER TABLE "read_states" DROP COLUMN "read_state_type"`); + await queryRunner.query(`ALTER TABLE "read_states" DROP COLUMN "badge_count"`); + await queryRunner.query(`ALTER TABLE "read_states" DROP COLUMN "last_acked_id"`); + await queryRunner.query(`ALTER TABLE "read_states" ADD "public_ack" character varying`); + } +} diff --git a/src/util/migration/postgres/1771997061671-LastPinTimestampAsDate.ts b/src/util/migration/postgres/1771997061671-LastPinTimestampAsDate.ts new file mode 100644 index 000000000..a5aaf4749 --- /dev/null +++ b/src/util/migration/postgres/1771997061671-LastPinTimestampAsDate.ts @@ -0,0 +1,15 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class LastPinTimestampAsDate1771997061671 implements MigrationInterface { + name = "LastPinTimestampAsDate1771997061671"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "last_pin_timestamp"`); + await queryRunner.query(`ALTER TABLE "channels" ADD "last_pin_timestamp" TIMESTAMP WITH TIME ZONE`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "channels" DROP COLUMN "last_pin_timestamp"`); + await queryRunner.query(`ALTER TABLE "channels" ADD "last_pin_timestamp" integer`); + } +} diff --git a/src/util/migration/postgres/1772404321400-MemberFlags.ts b/src/util/migration/postgres/1772404321400-MemberFlags.ts new file mode 100644 index 000000000..011c3e4e9 --- /dev/null +++ b/src/util/migration/postgres/1772404321400-MemberFlags.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class MemberFlags1772404321400 implements MigrationInterface { + name = "MemberFlags1772404321400"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "members" ADD "flags" integer NOT NULL DEFAULT '0'`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "members" DROP COLUMN "flags"`); + } +} diff --git a/src/util/stores/LobbyStore.ts b/src/util/stores/LobbyStore.ts new file mode 100644 index 000000000..b77e5b9c4 --- /dev/null +++ b/src/util/stores/LobbyStore.ts @@ -0,0 +1,203 @@ +/* + 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 . +*/ + +import { emitEvent } from "../util/Event"; +import { LobbyMemberDTO } from "../dtos"; + +declare global { + function setInterval(callback: () => void, ms: number): NodeJS.Timeout; + function clearInterval(id: NodeJS.Timeout): void; + function clearTimeout(id: NodeJS.Timeout): void; +} + +export interface Lobby { + id: string; + application_id: string; + metadata?: Record | null; + members: LobbyMemberDTO[]; + linked_channel?: string; + idle_timeout_seconds: number; + created_at: Date; + last_activity: Date; + timeout_handle?: NodeJS.Timeout; +} + +export class LobbyStore { + public static lobbies: Map = new Map(); + private static cleanupInterval?: NodeJS.Timeout; + + public static init() { + this.cleanupInterval = setInterval(() => { + const now = new Date(); + this.lobbies.forEach((lobby, lobbyId) => { + const idleTime = now.getTime() - lobby.last_activity.getTime(); + if (idleTime > lobby.idle_timeout_seconds * 1000) { + this.deleteLobby(lobbyId); + } + }); + }, 60000); + } + + public static createLobby( + lobby: Omit, + ): Lobby { + const now = new Date(); + const fullLobby: Lobby = { + ...lobby, + created_at: now, + last_activity: now, + }; + this.lobbies.set(lobby.id, fullLobby); + + emitEvent({ + event: "LOBBY_CREATE", + data: this.toLobbyResponse(fullLobby), + guild_id: lobby.application_id, + }); + + return fullLobby; + } + + public static getLobby(id: string): Lobby | undefined { + return this.lobbies.get(id); + } + + public static updateLobby( + id: string, + updates: Partial< + Pick< + Lobby, + | "metadata" + | "members" + | "idle_timeout_seconds" + | "linked_channel" + > + >, + ): Lobby | undefined { + const lobby = this.lobbies.get(id); + if (!lobby) return undefined; + + Object.assign(lobby, updates); + lobby.last_activity = new Date(); + + emitEvent({ + event: "LOBBY_UPDATE", + data: this.toLobbyResponse(lobby), + guild_id: lobby.application_id, + }); + + return lobby; + } + + public static updateLobbyActivity(id: string) { + const lobby = this.lobbies.get(id); + if (lobby) { + lobby.last_activity = new Date(); + } + } + + public static addMember(lobbyId: string, member: LobbyMemberDTO): boolean { + const lobby = this.lobbies.get(lobbyId); + if (!lobby) return false; + + const existingIndex = lobby.members.findIndex( + (m) => m.id === member.id, + ); + if (existingIndex >= 0) { + lobby.members[existingIndex] = member; + } else { + lobby.members.push(member); + } + + lobby.last_activity = new Date(); + + emitEvent({ + event: "LOBBY_MEMBER_ADD", + data: { + lobby_id: lobbyId, + member: member, + }, + user_id: member.id, + }); + + return true; + } + + public static removeMember(lobbyId: string, userId: string): boolean { + const lobby = this.lobbies.get(lobbyId); + if (!lobby) return false; + + const memberIndex = lobby.members.findIndex((m) => m.id === userId); + if (memberIndex === -1) return false; + + const removedMember = lobby.members[memberIndex]; + lobby.members.splice(memberIndex, 1); + lobby.last_activity = new Date(); + + emitEvent({ + event: "LOBBY_MEMBER_REMOVE", + data: { + lobby_id: lobbyId, + member: removedMember, + }, + user_id: userId, + }); + + if (lobby.members.length === 0) { + this.deleteLobby(lobbyId); + } + + return true; + } + + public static deleteLobby(id: string): boolean { + const lobby = this.lobbies.get(id); + if (!lobby) return false; + + if (lobby.timeout_handle) { + clearTimeout(lobby.timeout_handle); + } + + emitEvent({ + event: "LOBBY_DELETE", + data: { lobby_id: id }, + guild_id: lobby.application_id, + }); + + return this.lobbies.delete(id); + } + + public static toLobbyResponse(lobby: Lobby) { + return { + id: lobby.id, + application_id: lobby.application_id, + metadata: lobby.metadata, + members: lobby.members, + linked_channel: lobby.linked_channel + ? { id: lobby.linked_channel } + : undefined, + }; + } + + public static shutdown() { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval); + } + this.lobbies.clear(); + } +} diff --git a/src/util/util/AutoUpdate.ts b/src/util/util/AutoUpdate.ts deleted file mode 100644 index f33d211e5..000000000 --- a/src/util/util/AutoUpdate.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - 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 . -*/ - -import readline from "readline"; -import fs from "fs/promises"; -import path from "path"; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -export function enableAutoUpdate(opts: { checkInterval: number | boolean; packageJsonLink: string; path: string; downloadUrl: string; downloadType?: "zip" }) { - if (!opts.checkInterval) return; - const interval = 1000 * 60 * 60 * 24; - if (typeof opts.checkInterval === "number") opts.checkInterval = 1000 * interval; - - const i = setInterval(async () => { - const currentVersion = await getCurrentVersion(opts.path); - const latestVersion = await getLatestVersion(opts.packageJsonLink); - if (currentVersion !== latestVersion) { - clearInterval(i); - console.log(`[Auto Update] Current version (${currentVersion}) is out of date, updating ...`); - await download(opts.downloadUrl, opts.path); - } - }, interval); - setImmediate(async () => { - const currentVersion = await getCurrentVersion(opts.path); - const latestVersion = await getLatestVersion(opts.packageJsonLink); - if (currentVersion !== latestVersion) { - rl.question(`[Auto Update] Current version (${currentVersion}) is out of date, would you like to update? (Y/n)`, (answer) => { - if (answer === "" || answer.toLowerCase() === "y") { - console.log(`[Auto update] updating ...`); - download(opts.downloadUrl, opts.path); - } else { - console.log(`[Auto update] aborted`); - } - }); - } - }); -} - -async function download(url: string, dir: string) { - try { - // TODO: use file stream instead of buffer (to prevent crash because of high memory usage for big files) - // TODO check file hash - const response = await fetch(url); - const buffer = Buffer.from(await response.arrayBuffer()); - const tempDir = await fs.mkdtemp("spacebar"); - await fs.writeFile(path.join(tempDir, "Spacebar.zip"), buffer); - } catch (error) { - console.error(`[Auto Update] download failed`, error); - } -} - -async function getCurrentVersion(dir: string) { - try { - const content = await fs.readFile(path.join(dir, "package.json"), { - encoding: "utf8", - }); - return JSON.parse(content).version; - } catch (error) { - throw new Error("[Auto update] couldn't get current version in " + dir); - } -} - -async function getLatestVersion(url: string) { - try { - const response = await fetch(url); - const content = (await response.json()) as { version: string }; - return content.version; - } catch (error) { - throw new Error("[Auto update] check failed for " + url); - } -} diff --git a/src/util/util/AutomodActionExecutor.ts b/src/util/util/AutomodActionExecutor.ts index 22126552a..02274e8ec 100644 --- a/src/util/util/AutomodActionExecutor.ts +++ b/src/util/util/AutomodActionExecutor.ts @@ -1,174 +1,150 @@ import { AutomodActionTypes } from "./Constants"; import { DiscordApiErrors } from "./Constants"; -import { Channel, Message, Member, MessageType, EmbedType } from "../entities"; +import { Channel, Message, Member } from "../entities"; +import { MessageType, EmbedType } from "@spacebar/schemas"; import { emitEvent } from "./Event"; interface AutomodAction { - type: number; - metadata?: Record; + type: number; + metadata?: Record; } interface AutomodActionContext { - message: Message; - channel: Channel; - member?: Member; - rule_name: string; - matched_content?: string; - keyword?: string; + message: Message; + channel: Channel; + member?: Member; + rule_name: string; + matched_content?: string; + keyword?: string; } export class AutomodActionExecutor { - static async executeActions( - actions: AutomodAction[], - context: AutomodActionContext, - ): Promise { - for (const action of actions) { - try { - await this.executeAction(action, context); - } catch (error) { - console.error( - `Failed to execute automod action ${action.type}:`, - error, - ); - } - } - } + static async executeActions(actions: AutomodAction[], context: AutomodActionContext): Promise { + for (const action of actions) { + try { + await this.executeAction(action, context); + } catch (error) { + console.error(`Failed to execute automod action ${action.type}:`, error); + } + } + } - private static async executeAction( - action: AutomodAction, - context: AutomodActionContext, - ): Promise { - switch (action.type) { - case AutomodActionTypes.BLOCK_MESSAGE: - await this.blockMessage(action, context); - break; - case AutomodActionTypes.SEND_ALERT: - await this.sendAlert(action, context); - break; - case AutomodActionTypes.TIMEOUT_MEMBER: - await this.timeoutMember(action, context); - break; - default: - console.warn(`Unknown automod action type: ${action.type}`); - } - } + private static async executeAction(action: AutomodAction, context: AutomodActionContext): Promise { + switch (action.type) { + case AutomodActionTypes.BLOCK_MESSAGE: + await this.blockMessage(action, context); + break; + case AutomodActionTypes.SEND_ALERT: + await this.sendAlert(action, context); + break; + case AutomodActionTypes.TIMEOUT_MEMBER: + await this.timeoutMember(action, context); + break; + default: + console.warn(`Unknown automod action type: ${action.type}`); + } + } - private static async blockMessage( - action: AutomodAction, - context: AutomodActionContext, - ): Promise { - throw DiscordApiErrors.AUTOMODERATOR_BLOCK; - } + private static async blockMessage(action: AutomodAction, context: AutomodActionContext): Promise { + throw DiscordApiErrors.AUTOMODERATOR_BLOCK; + } - private static async sendAlert( - action: AutomodAction, - context: AutomodActionContext, - ): Promise { - const alertChannelId = action.metadata?.channel_id as string; - if (!alertChannelId) return; + private static async sendAlert(action: AutomodAction, context: AutomodActionContext): Promise { + const alertChannelId = action.metadata?.channel_id as string; + if (!alertChannelId) return; - const alertChannel = await Channel.findOne({ - where: { id: alertChannelId }, - }); - if (!alertChannel) return; + const alertChannel = await Channel.findOne({ + where: { id: alertChannelId }, + }); + if (!alertChannel) return; - const decisionId = - Math.random().toString(36).substring(2, 15) + - Math.random().toString(36).substring(2, 15); + const decisionId = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); - const createdMessage = Message.create({ - channel_id: alertChannelId, - guild_id: alertChannel.guild_id, - author_id: context.message.author_id, - content: "", - type: MessageType.AUTO_MODERATION_ACTION, - reactions: [], - embeds: [ - { - type: EmbedType.auto_moderation_message, - description: - context.message.content?.substring(0, 500) || "", - fields: [ - { - name: "rule_name", - value: context.rule_name, - inline: false, - }, - { - name: "decision_id", - value: decisionId, - inline: false, - }, - { - name: "channel_id", - value: context.channel.id, - inline: false, - }, - { - name: "flagged_message_id", - value: context.message.id, - inline: false, - }, - ...(context.keyword - ? [ - { - name: "keyword", - value: context.keyword, - inline: false, - }, - ] - : []), - ...(context.matched_content - ? [ - { - name: "keyword_matched_content", - value: context.matched_content, - inline: false, - }, - ] - : []), - { - name: "decision_outcome", - value: "blocked", - inline: false, - }, - ], - }, - ], - timestamp: new Date(), - }); - await Promise.all([ - Message.insert(createdMessage), - emitEvent({ - event: "MESSAGE_CREATE", - channel_id: alertChannelId, - data: createdMessage, - }), - ]); - } + const createdMessage = Message.create({ + channel_id: alertChannelId, + guild_id: alertChannel.guild_id, + author_id: context.message.author_id, + content: "", + type: MessageType.AUTO_MODERATION_ACTION, + reactions: [], + embeds: [ + { + type: "auto_moderation_message" as EmbedType, + description: context.message.content?.substring(0, 500) || "", + fields: [ + { + name: "rule_name", + value: context.rule_name, + inline: false, + }, + { + name: "decision_id", + value: decisionId, + inline: false, + }, + { + name: "channel_id", + value: context.channel.id, + inline: false, + }, + { + name: "flagged_message_id", + value: context.message.id, + inline: false, + }, + ...(context.keyword + ? [ + { + name: "keyword", + value: context.keyword, + inline: false, + }, + ] + : []), + ...(context.matched_content + ? [ + { + name: "keyword_matched_content", + value: context.matched_content, + inline: false, + }, + ] + : []), + { + name: "decision_outcome", + value: "blocked", + inline: false, + }, + ], + }, + ], + timestamp: new Date(), + }); + await Promise.all([ + Message.insert(createdMessage), + emitEvent({ + event: "MESSAGE_CREATE", + channel_id: alertChannelId, + data: createdMessage, + }), + ]); + } - private static async timeoutMember( - action: AutomodAction, - context: AutomodActionContext, - ): Promise { - if (!context.member || !context.channel.guild_id) return; + private static async timeoutMember(action: AutomodAction, context: AutomodActionContext): Promise { + if (!context.member || !context.channel.guild_id) return; - const durationSeconds = - (action.metadata?.duration_seconds as number) || 60; - const timeoutUntil = new Date(Date.now() + durationSeconds * 1000); + const durationSeconds = (action.metadata?.duration_seconds as number) || 60; + const timeoutUntil = new Date(Date.now() + durationSeconds * 1000); - await Member.update( - { id: context.member.id, guild_id: context.channel.guild_id }, - { communication_disabled_until: timeoutUntil }, - ); + await Member.update({ id: context.member.id, guild_id: context.channel.guild_id }, { communication_disabled_until: timeoutUntil }); - await emitEvent({ - event: "GUILD_MEMBER_UPDATE", - guild_id: context.channel.guild_id, - data: { - ...context.member, - communication_disabled_until: timeoutUntil, - }, - }); - } + await emitEvent({ + event: "GUILD_MEMBER_UPDATE", + guild_id: context.channel.guild_id, + data: { + ...context.member, + communication_disabled_until: timeoutUntil, + }, + }); + } } diff --git a/src/util/util/AutomodEvaluator.ts b/src/util/util/AutomodEvaluator.ts index ad234fed6..d844cb22c 100644 --- a/src/util/util/AutomodEvaluator.ts +++ b/src/util/util/AutomodEvaluator.ts @@ -37,10 +37,6 @@ export class AutomodEvaluator { private static CACHE_TTL = 5 * 60 * 1000; // 5 minutes static async evaluateMessage(context: AutomodEvaluationContext): Promise { - if (context.channel.type === ChannelType.ENCRYPTED || context.channel.type === ChannelType.ENCRYPTED_THREAD) { - return { triggered: false, actions: [] }; - } - if (!context.guild_id) { return { triggered: false, actions: [] }; } diff --git a/src/util/util/BitField.ts b/src/util/util/BitField.ts index 8a7dc94ef..ea79fe1c4 100644 --- a/src/util/util/BitField.ts +++ b/src/util/util/BitField.ts @@ -88,7 +88,6 @@ export class BitField { /** * Gets an object mapping field names to a {@link boolean} indicating whether the * bit is available. - * @param {...*} hasParams Additional parameters for the has method, if any */ serialize() { const serialized: Record = {}; diff --git a/src/util/util/Config.ts b/src/util/util/Config.ts index 1cd24b901..27f0b4502 100644 --- a/src/util/util/Config.ts +++ b/src/util/util/Config.ts @@ -20,7 +20,7 @@ import { existsSync } from "fs"; import fs from "fs/promises"; import { OrmUtils } from ".."; import { ConfigValue } from "../config"; -import { ConfigEntity } from "../entities/Config"; +import { ConfigEntity } from "../entities"; import { JsonValue } from "@protobuf-ts/runtime"; // TODO: yaml instead of json diff --git a/src/util/util/ConnectionPrivacy.ts b/src/util/util/ConnectionPrivacy.ts new file mode 100644 index 000000000..505dc92f7 --- /dev/null +++ b/src/util/util/ConnectionPrivacy.ts @@ -0,0 +1,58 @@ +/* + 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 . +*/ + +import { ConnectedAccount } from "../entities/ConnectedAccount"; + +export enum VisibilityLevel { + PRIVATE = 0, + FRIENDS_ONLY = 1, + MUTUAL_GUILDS = 2, + PUBLIC = 3, +} + +export class ConnectionPrivacy { + static filterConnectedAccounts(accounts: ConnectedAccount[], viewerUserId: string, targetUserId: string): ConnectedAccount[] { + return accounts.filter((account) => this.isConnectionVisible(account, viewerUserId, targetUserId)); + } + + static isConnectionVisible(account: ConnectedAccount, viewerUserId: string, targetUserId: string): boolean { + const effectiveVisibility = this.getEffectiveVisibility(account); + + if (effectiveVisibility === VisibilityLevel.PRIVATE) { + return viewerUserId === targetUserId; + } + + if (effectiveVisibility === VisibilityLevel.PUBLIC) { + return true; + } + + return viewerUserId === targetUserId || effectiveVisibility >= VisibilityLevel.MUTUAL_GUILDS; + } + + static shouldShareActivity(account: ConnectedAccount): boolean { + return account.show_activity !== 0; + } + + static shouldShareMetadata(account: ConnectedAccount): boolean { + return account.metadata_visibility !== 0; + } + + static getEffectiveVisibility(account: ConnectedAccount): number { + return account.visibility ?? 0; + } +} diff --git a/src/util/util/Constants.ts b/src/util/util/Constants.ts index 2b81c87a6..9a193ea8a 100644 --- a/src/util/util/Constants.ts +++ b/src/util/util/Constants.ts @@ -113,11 +113,6 @@ export const Events = { TYPING_START: "typingStart", TYPING_STOP: "typingStop", WEBHOOKS_UPDATE: "webhookUpdate", - LOBBY_CREATE: "lobbyCreate", - LOBBY_UPDATE: "lobbyUpdate", - LOBBY_DELETE: "lobbyDelete", - LOBBY_MEMBER_ADD: "lobbyMemberAdd", - LOBBY_MEMBER_REMOVE: "lobbyMemberRemove", ERROR: "error", WARN: "warn", DEBUG: "debug", @@ -319,15 +314,10 @@ export const AutomodActionTypes = { // Automod trigger types mapping: 1–6 follow discord-api-docs; 30+ are Spacebar extensions. export const AutomodTriggerTypes = { CUSTOM_WORDS: 1, - HARMFUL_LINKS: 2, + UNKNOWN_2: 2, SUSPECTED_SPAM_CONTENT: 3, COMMONLY_FLAGGED_WORDS: 4, MENTION_SPAM: 5, - MEMBER_PROFILE: 6, - PROHIBITED_LANGUAGES: 30, - SIMILARITY: 31, - ARTIFICIAL_CONTENT: 32, - EXECUTABLE_FILES: 33, }; export const Colors = { @@ -569,8 +559,6 @@ export const DiscordApiErrors = { UNKNOWN_GUILD_WELCOME_SCREEN: new ApiError("Unknown Guild Welcome Screen", 10069), UNKNOWN_GUILD_SCHEDULED_EVENT: new ApiError("Unknown Guild Scheduled Event", 10070), UNKNOWN_GUILD_SCHEDULED_EVENT_USER: new ApiError("Unknown Guild Scheduled Event User", 10071), - UNKNOWN_CONSENT: new ApiError("Unknown consent", 10080, 404), - UNKNOWN_CONSENT_GRANT: new ApiError("Unknown consent grant", 10081, 404), BOT_PROHIBITED_ENDPOINT: new ApiError("Bots cannot use this endpoint", 20001), BOT_ONLY_ENDPOINT: new ApiError("Only bots can use this endpoint", 20002), EXPLICIT_CONTENT_CANNOT_BE_SENT_TO_RECIPIENT: new ApiError("Explicit content cannot be sent to the desired recipient(s)", 20009), @@ -675,6 +663,7 @@ export const DiscordApiErrors = { //Other errors UNKNOWN_VOICE_STATE: new ApiError("Unknown Voice State", 10065, 404), + UNKNOWN_CONSENT_GRANT: new ApiError("Unknown Consent Grant", 10070, 404), }; export const TicketFlags = { diff --git a/src/util/util/Database.ts b/src/util/util/Database.ts index 805a9c18c..8f4119a16 100644 --- a/src/util/util/Database.ts +++ b/src/util/util/Database.ts @@ -22,8 +22,6 @@ import { green, red, yellow } from "picocolors"; import { DataSource } from "typeorm"; // noinspection ES6PreferShortImport import { ConfigEntity } from "../entities/Config"; -// noinspection ES6PreferShortImport -import { Migration } from "../entities/Migration"; import fs from "fs"; // UUID extension option is only supported with postgres @@ -59,7 +57,8 @@ if (!isHeadlessProcess) { ); console.log(`[Database] ${red(`If you would like to try *anyways*, see the error below:`)}`); try { - const _ = require("sqlite3"); + // TODO: fully remove sqlite3 + require("sqlite3"); } catch (e) { console.log(`[Database] ${red(`Failed to load sqlite3 package. Please install it with 'npm install --no-save sqlite3', or switch to a real database like Postgres.`)}`); process.exit(1); diff --git a/src/util/util/DateBuilder.ts b/src/util/util/DateBuilder.ts index 981a99fc5..58588c8b7 100644 --- a/src/util/util/DateBuilder.ts +++ b/src/util/util/DateBuilder.ts @@ -19,10 +19,7 @@ export class DateBuilder { private date: Date; // constructors - constructor(date = new Date()) { - if (!(date instanceof Date)) { - throw new Error("Invalid date object."); - } + constructor(date: Date = new Date()) { this.date = new Date(date.getTime()); // Create a copy to avoid mutating the original date } diff --git a/src/util/util/Decorators.ts b/src/util/util/Decorators.ts new file mode 100644 index 000000000..18910b9d1 --- /dev/null +++ b/src/util/util/Decorators.ts @@ -0,0 +1,26 @@ +import { BaseClassWithoutId } from "@spacebar/util"; + +export const annotationsKey = Symbol("Annotations"); + +// Generates an array using the annotationsKey as a property on a class when annotated with the below decorators +export function initAnnotationMetadata(target: BaseClassWithoutId, propertyKey: string) { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + target[annotationsKey] || (target[annotationsKey] = {}); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + target[annotationsKey][propertyKey] || (target[annotationsKey][propertyKey] = []); +} + +// Adds to the generated array on the class with the annotation added. +export function addAnnotationMetadata(target: BaseClassWithoutId, propertyKey: string, annotation: string) { + target[annotationsKey] = { ...target[annotationsKey], [propertyKey]: [...target[annotationsKey][propertyKey], annotation] }; +} + +export function JsonRemoveEmpty(target: BaseClassWithoutId, propertyKey: string) { + initAnnotationMetadata(target, propertyKey); + addAnnotationMetadata(target, propertyKey, "JsonRemoveEmpty"); +} + +export function JsonNumber(target: BaseClassWithoutId, propertyKey: string) { + initAnnotationMetadata(target, propertyKey); + addAnnotationMetadata(target, propertyKey, "JsonNumber"); +} diff --git a/src/util/util/Event.ts b/src/util/util/Event.ts index 27b1986e7..78f487b8c 100644 --- a/src/util/util/Event.ts +++ b/src/util/util/Event.ts @@ -26,12 +26,16 @@ import { Socket } from "node:net"; import { FSWatcher } from "node:fs"; import { Stopwatch } from "./Stopwatch"; import { Config } from "./Config"; +import net from "net"; +import fs from "fs"; +import { red } from "picocolors"; + export const events = new EventEmitter(); let unixSocketListener: UnixSocketListener | null = null; let unixSocketWriter: UnixSocketWriter | null = null; export async function emitEvent(payload: Omit) { - const id = (payload.guild_id || payload.channel_id || payload.user_id) as string; + const id = (payload.guild_id || payload.channel_id || payload.user_id || payload.session_id) as string; if (!id) return console.error("event doesn't contain any id", payload); if (RabbitMQ.connection) { @@ -65,8 +69,8 @@ export async function emitEvent(payload: Omit) { await publishEvent(); } else if (process.env.EVENT_TRANSMISSION === "unix" && process.env.EVENT_SOCKET_PATH) { if (!unixSocketWriter) { - unixSocketWriter = new UnixSocketWriter(process.env.EVENT_SOCKET_PATH); - await unixSocketWriter.init(); + console.error("[Event] Unix socket writer not initialized, cannot emit event!"); + throw new Error("Unix socket writer not initialized"); } await unixSocketWriter.emit(payload); } else if (process.env.EVENT_TRANSMISSION === "process") { @@ -79,6 +83,13 @@ export async function emitEvent(payload: Omit) { export async function initEvent() { await RabbitMQ.init(); // does nothing if rabbitmq is not setup + if (process.env.EVENT_TRANSMISSION === "unix" && process.env.EVENT_SOCKET_PATH) { + if (!unixSocketWriter) { + unixSocketWriter = new UnixSocketWriter(process.env.EVENT_SOCKET_PATH); + await unixSocketWriter.init(); + } + } + // Set up the spacebar event listener (used for config reload, etc.) const setupSpacebarListener = async () => { console.log("[Event] Setting up spacebar event listener"); @@ -227,9 +238,6 @@ class UnixSocketListener { } async init() { - const net = await import("net"); - const fs = await import("fs"); - // remove stale socket file if it exists // can happen if there's a PID conflict (across containers/PID namespaces) try { @@ -312,25 +320,37 @@ class UnixSocketListener { } } +function getPidCmdline(pid: number): string | null { + try { + const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf-8"); + return cmdline.replaceAll("\0", " ").trim(); + } catch (e) { + return null; + } +} + class UnixSocketWriter { socketPath: string; clients: { [key: string]: Socket } = {}; watcher?: FSWatcher; + backlog: Event[] = []; + broadcastLock: Promise = Promise.resolve(); + replayLock: Promise = Promise.resolve(); + isInitializing = true; constructor(socketPath: string) { this.socketPath = socketPath; } async init() { - const net = await import("net"); - const fs = await import("fs"); - if (!fs.opendirSync(this.socketPath)) throw new Error("Unix socket path does not exist or is not a directory: " + this.socketPath); console.log("[Events] Unix socket writer initializing for", this.socketPath); const connect = (file: string) => { const fullPath = path.join(this.socketPath, file); + const pid = Number(path.basename(file, ".sock")); + console.log("[Events] Attempting to connect to unix socket:", fullPath, "| proc:", getPidCmdline(pid) ?? red("No such pid: " + pid)); // avoid duplicate connections if (this.clients[fullPath] && !this.clients[fullPath].destroyed) { @@ -340,6 +360,7 @@ class UnixSocketWriter { // clean up old connection if it exists if (this.clients[fullPath]) { + console.log("[Events] Removing stale unix socket client for", fullPath); try { this.clients[fullPath].destroy(); } catch (e) { @@ -425,6 +446,8 @@ class UnixSocketWriter { } catch (err) { console.error("[Events] Unix socket writer failed to read directory:", err); } + + this.isInitializing = false; } async emit(event: Event) { @@ -433,33 +456,59 @@ class UnixSocketWriter { // check if there are any listeners const clientCount = Object.entries(this.clients).length; if (clientCount === 0) { - console.warn("[Events] Unix socket writer has no connected clients to emit to"); + console.warn("[Events] Unix socket writer has no connected clients to emit to, backlog size:", this.backlog.length + 1); + this.backlog.push(event); + if (!this.isInitializing) { + this.isInitializing = true; + console.log("[Events] Re-initializing unix socket writer due to new event with no listeners"); + await this.close(); + await this.init(); + } return; } - const tsw = Stopwatch.startNew(); - const payloadBuf = Buffer.from(JSON.stringify({ id: (event.guild_id || event.channel_id || event.user_id) as string, event })); - const lenBuf = Buffer.alloc(4); - lenBuf.writeUInt32BE(payloadBuf.length, 0); - const framed = Buffer.concat([lenBuf, payloadBuf]); - - for (const [socketPath, socket] of Object.entries(this.clients)) { - if (socket.destroyed) { - console.log("[Events] Unix socket writer found destroyed socket, removing:", socketPath); - delete this.clients[socketPath]; - continue; + await this.replayLock; + await (this.replayLock = Promise.resolve().then(async () => { + if (this.backlog.length > 0) { + console.log(`[Events] Replaying ${this.backlog.length} backlog events`); + for (const backlogEvent of this.backlog) { + await this.broadcast(backlogEvent); + } + this.backlog = []; } + })); - try { - socket.write(framed); - } catch (e) { - console.error("[Events] Unix socket writer failed to write to socket", socketPath, ":", e); + await this.broadcast(event); + } + + private async broadcast(event: Event) { + await this.broadcastLock; + return await (this.broadcastLock = new Promise((res) => { + const tsw = Stopwatch.startNew(); + const payloadBuf = Buffer.from(JSON.stringify({ id: (event.guild_id || event.channel_id || event.user_id || event.session_id) as string, event })); + const lenBuf = Buffer.alloc(4); + lenBuf.writeUInt32BE(payloadBuf.length, 0); + const framed = Buffer.concat([lenBuf, payloadBuf]); + + for (const [socketPath, socket] of Object.entries(this.clients)) { + if (socket.destroyed) { + console.log("[Events] Unix socket writer found destroyed socket, removing:", socketPath); + delete this.clients[socketPath]; + continue; + } + + try { + socket.write(framed); + } catch (e) { + console.error("[Events] Unix socket writer failed to write to socket", socketPath, ":", e); + } } - } - if (tsw.elapsed().totalMilliseconds > 5) - // else it's too noisy - console.log(`[Events] Unix socket writer emitted to ${Object.entries(this.clients).length} sockets in ${tsw.elapsed().totalMilliseconds}ms`); + if (tsw.elapsed().totalMilliseconds > 5) + // else it's too noisy + console.log(`[Events] Unix socket writer emitted to ${Object.entries(this.clients).length} sockets in ${tsw.elapsed().totalMilliseconds}ms`); + res(); + })); } async close() { diff --git a/src/util/util/JSON.ts b/src/util/util/JSON.ts index 004792289..e716f7d4a 100644 --- a/src/util/util/JSON.ts +++ b/src/util/util/JSON.ts @@ -18,7 +18,6 @@ // Discord.com sends ISO strings with +00:00 extension, not Z // This causes issues with Python bot libs -import Stream from "node:stream"; export function JSONReplacer(this: { [key: string]: unknown }, key: string, value: unknown) { if (this[key] instanceof Date) { diff --git a/src/util/util/MessageFlags.ts b/src/util/util/MessageFlags.ts index 84eca46f0..38b7e2e9e 100644 --- a/src/util/util/MessageFlags.ts +++ b/src/util/util/MessageFlags.ts @@ -6,13 +6,22 @@ import { BitField } from "./BitField"; export class MessageFlags extends BitField { static FLAGS = { - CROSSPOSTED: BigInt(1) << BigInt(0), - IS_CROSSPOST: BigInt(1) << BigInt(1), - SUPPRESS_EMBEDS: BigInt(1) << BigInt(2), - // SOURCE_MESSAGE_DELETED: BigInt(1) << BigInt(3), // spacebar will delete them from destination too, making this redundant - URGENT: BigInt(1) << BigInt(4), - // HAS_THREAD: BigInt(1) << BigInt(5) // does not apply to spacebar due to infrastructural differences - EPHEMERAL: BigInt(1) << BigInt(6), // it that has been routed to only some of the users that can see the channel - INTERACTION_WAIT: BigInt(1) << BigInt(7), // discord.com calls this LOADING + CROSSPOSTED: 1n, + IS_CROSSPOST: 1n << 1n, + SUPPRESS_EMBEDS: 1n << 2n, + SOURCE_MESSAGE_DELETED: 1n << 3n, + URGENT: 1n << 4n, + HAS_THREAD: 1n << 5n, + EPHEMERAL: 1n << 6n, + LOADING: 1n << 17n, + FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: 1n << 8n, + GUILD_FEED_HIDDEN: 1n << 9n, + SHOULD_SHOW_LINK_NOT_DISCORD_WARNING: 1n << 10n, + // 1<<11 not documented + SUPPRESS_NOTIFICATIONS: 1n << 12n, + IS_VOICE_MESSAGE: 1n << 13n, + HAS_SNAPSHOT: 1n << 14n, + IS_COMPONENTS_V2: 1n << 15n, + SENT_BY_SOCIAL_LAYER_INTEGRATION: 1n << 16n, }; } diff --git a/src/util/util/Permissions.ts b/src/util/util/Permissions.ts index 3506f2dca..fd8f63eef 100644 --- a/src/util/util/Permissions.ts +++ b/src/util/util/Permissions.ts @@ -3,11 +3,9 @@ // @fc-license-skip import { Channel, Guild, Member, Role, User } from "../entities"; -import { ChannelPermissionOverwrite } from "@spacebar/schemas"; -import { BitField } from "./BitField"; -import { BitFieldResolvable, BitFlag } from "./BitField"; +import { BitField, BitFieldResolvable, BitFlag } from "./BitField"; import { HTTPError } from "lambert-server"; -import { ChannelType } from "@spacebar/schemas"; +import { ChannelPermissionOverwrite, ChannelPermissionOverwriteType, ChannelType, UserFlags } from "@spacebar/schemas"; import { FindOneOptions } from "typeorm"; export type PermissionResolvable = bigint | number | Permissions | PermissionResolvable[] | PermissionString; @@ -18,18 +16,12 @@ type PermissionString = keyof typeof Permissions.FLAGS; // const CUSTOM_PERMISSION_OFFSET = BigInt(1) << BigInt(64); // 27 permission bits left for discord to add new ones export class Permissions extends BitField { - static get NONE(): Permissions { - return new Permissions(0); - } - static get ALL(): Permissions { - return new Permissions(Object.values(Permissions.FLAGS).reduce((total, val) => total | val, BigInt(0))); - } cache: PermissionCache = {}; constructor(bits: BitFieldResolvable = 0) { super(bits); if (this.bitfield & Permissions.FLAGS.ADMINISTRATOR) { - this.bitfield = ALL_PERMISSIONS; + this.bitfield = Permissions.ALL_PERMISSIONS; } } @@ -87,16 +79,19 @@ export class Permissions extends BitField { USE_EXTERNAL_APPS: BitFlag(50), PIN_MESSAGES: BitFlag(51), BYPASS_SLOWMODE: BitFlag(52), - MANAGE_TICKETS: BitFlag(55), + MANAGE_TICKETS: BitFlag(53), /** * CUSTOM PERMISSIONS ideas: * - allow user to dm members + * - allow user to pin messages (without MANAGE_MESSAGES) * - allow user to publish messages (without MANAGE_MESSAGES) */ // CUSTOM_PERMISSION: BigInt(1) << BigInt(0) + CUSTOM_PERMISSION_OFFSET }; + static ALL_PERMISSIONS = Object.values(Permissions.FLAGS).reduce((total, val) => total | val, BigInt(0)); + any(permission: PermissionResolvable, checkAdmin = true) { return (checkAdmin && super.any(Permissions.FLAGS.ADMINISTRATOR)) || super.any(permission); } @@ -118,10 +113,10 @@ export class Permissions extends BitField { overwriteChannel(overwrites: ChannelPermissionOverwrite[]) { if (!overwrites) return this; - if (!this.cache) throw new Error("permission chache not available"); + if (!this.cache) throw new Error("permission cache not available"); overwrites = overwrites.filter((x) => { - if (x.type === 0 && this.cache.roles?.some((r) => r.id === x.id)) return true; - if (x.type === 1 && x.id == this.cache.user_id) return true; + if (x.type === ChannelPermissionOverwriteType.role && this.cache.roles?.some((r) => r.id === x.id)) return true; + if (x.type === ChannelPermissionOverwriteType.member && x.id == this.cache.user_id) return true; return false; }); return new Permissions(Permissions.channelPermission(overwrites, this.bitfield)); @@ -154,8 +149,8 @@ export class Permissions extends BitField { guild, channel, }: { - user: { id: string; roles: string[]; communication_disabled_until?: Date | null; flags?: number }; - guild: { roles: Role[]; id?: string; owner_id?: string }; + user: { id: string; roles: string[]; communication_disabled_until: Date | null; flags: number }; + guild: { id: string; owner_id: string; roles: Role[] }; channel?: { overwrites?: ChannelPermissionOverwrite[]; recipient_ids?: string[] | null; @@ -163,14 +158,15 @@ export class Permissions extends BitField { }; }) { if (user.id === "0") return new Permissions("ADMINISTRATOR"); // system user id + if (guild?.owner_id === user.id) return new Permissions(Permissions.ALL); const roles = guild.roles.filter((x) => user.roles.includes(x.id)); let permission = Permissions.rolePermission(roles); if (channel?.overwrites) { const overwrites = channel.overwrites.filter((x) => { - if (x.type === 0 && user.roles.includes(x.id)) return true; - if (x.type === 1 && x.id == user.id) return true; + if (x.type === ChannelPermissionOverwriteType.role && user.roles.includes(x.id)) return true; + if (x.type === ChannelPermissionOverwriteType.member && x.id == user.id) return true; return false; }); permission = Permissions.channelPermission(overwrites, permission); @@ -199,11 +195,41 @@ export class Permissions extends BitField { return new Permissions(); } + if (user.communication_disabled_until) { + if (user.communication_disabled_until > new Date()) return new Permissions(permission & Permissions.TIMED_OUT_MASK.bitfield); + else { + user.communication_disabled_until = null; + Member.update({ id: user.id, guild_id: guild.id }, { communication_disabled_until: null }).catch((_) => { + // ignored + }); + } + } + if ((BigInt(user.flags) & UserFlags.FLAGS.QUARANTINED) === UserFlags.FLAGS.QUARANTINED) { + permission = permission & Permissions.QUARANTINED_MASK.bitfield; + } + return new Permissions(permission); } -} -const ALL_PERMISSIONS = Object.values(Permissions.FLAGS).reduce((total, val) => total | val, BigInt(0)); + static NONE: Permissions = new Permissions(0); + static TIMED_OUT_MASK: Permissions = new Permissions(Permissions.FLAGS.VIEW_CHANNEL | Permissions.FLAGS.READ_MESSAGE_HISTORY); + static QUARANTINED_MASK: Permissions = new Permissions(Permissions.FLAGS.VIEW_CHANNEL | Permissions.FLAGS.READ_MESSAGE_HISTORY | Permissions.FLAGS.CHANGE_NICKNAME); + static DEFAULT_DM_PERMISSIONS: Permissions = new Permissions( + Permissions.FLAGS.VIEW_CHANNEL | + Permissions.FLAGS.SEND_MESSAGES | + Permissions.FLAGS.STREAM | + Permissions.FLAGS.ADD_REACTIONS | + Permissions.FLAGS.EMBED_LINKS | + Permissions.FLAGS.ATTACH_FILES | + Permissions.FLAGS.READ_MESSAGE_HISTORY | + Permissions.FLAGS.MENTION_EVERYONE | + Permissions.FLAGS.USE_EXTERNAL_EMOJIS | + Permissions.FLAGS.CONNECT | + Permissions.FLAGS.SPEAK | + Permissions.FLAGS.MANAGE_CHANNELS, + ); + static ALL: Permissions = new Permissions(Object.values(Permissions.FLAGS).reduce((total, val) => total | val, BigInt(0))); +} export type PermissionCache = { channel?: Channel | undefined; @@ -215,7 +241,7 @@ export type PermissionCache = { export async function getPermission( user_id?: string, - guild_id?: string, + guild_id?: string | Guild, channel_id?: string | Channel, opts: { guild_select?: (keyof Guild)[]; @@ -260,19 +286,24 @@ export async function getPermission( } if (guild_id) { - guild = await Guild.findOneOrFail({ - where: { id: guild_id }, - select: ["id", "owner_id", ...(opts.guild_select || [])], - relations: opts.guild_relations, - }); + if (typeof guild_id === "string") { + guild = await Guild.findOneOrFail({ + where: { id: guild_id }, + select: ["id", "owner_id", ...(opts.guild_select || [])], + relations: opts.guild_relations, + }); + } else { + guild = guild_id; + } if (guild.owner_id === user_id) return new Permissions(Permissions.FLAGS.ADMINISTRATOR); member = await Member.findOneOrFail({ - where: { guild_id, id: user_id }, + where: { guild_id: guild.id, id: user_id }, relations: ["roles", ...(opts.member_relations || [])], // select: [ // "id", // TODO: Bug in typeorm? adding these selects breaks the query. // "roles", + // "communication_disabled_until", // ...(opts.member_select || []), // ], }); @@ -286,8 +317,12 @@ export async function getPermission( user: { id: user_id, roles: member?.roles.map((x) => x.id) || [], + communication_disabled_until: member?.communication_disabled_until ?? null, + flags: user.flags, }, guild: { + id: guild?.id || "", + owner_id: guild?.owner_id || "", roles: member?.roles || [], }, channel: { diff --git a/src/util/util/RabbitMQ.ts b/src/util/util/RabbitMQ.ts index 288a7fa9a..7969bb8bf 100644 --- a/src/util/util/RabbitMQ.ts +++ b/src/util/util/RabbitMQ.ts @@ -102,7 +102,7 @@ export class RabbitMQ { this.events.emit("disconnected"); // Schedule reconnection - this.scheduleReconnect(host); + this.scheduleReconnect(host).catch((e) => console.error("[RabbitMQ] Failed to schedule reconnection:", e)); }); } diff --git a/src/util/util/Rights.ts b/src/util/util/Rights.ts index 6b90f7489..809563e49 100644 --- a/src/util/util/Rights.ts +++ b/src/util/util/Rights.ts @@ -107,6 +107,13 @@ export class Rights extends BitField { const ALL_RIGHTS = Object.values(Rights.FLAGS).reduce((total, val) => total | val, BigInt(0)); +export function shouldRoutePresenceFromRights(raw: bigint | number | string): boolean { + const val = typeof raw === "string" ? BigInt(raw) : BigInt(raw || 0); + const isOperator = (val & Rights.FLAGS.OPERATOR) !== BigInt(0); + const hasPresenceBit = (val & Rights.FLAGS.PRESENCE) !== BigInt(0); + return isOperator ? hasPresenceBit : !hasPresenceBit; +} + export async function getRights( user_id: string, /**, opts: { diff --git a/src/util/util/Snowflake.ts b/src/util/util/Snowflake.ts index ee6036bc2..06b8c0d01 100644 --- a/src/util/util/Snowflake.ts +++ b/src/util/util/Snowflake.ts @@ -39,7 +39,7 @@ export class Snowflake { * @returns {string} * @private */ - static idToBinary(num) { + private static idToBinary(num: string): string { let bin = ""; let high = parseInt(num.slice(0, -10)) || 0; let low = parseInt(num.slice(-10)); @@ -54,36 +54,6 @@ export class Snowflake { return bin; } - /** - * Transforms a snowflake from a bit string to a decimal string. - * @param {string} num Bit string to be transformed - * @returns {Snowflake} - * @private - */ - static binaryToID(num) { - let dec = ""; - - while (num.length > 50) { - const high = parseInt(num.slice(0, -32), 2); - const low = parseInt((high % 10).toString(2) + num.slice(-32), 2); - - dec = (low % 10).toString() + dec; - num = - Math.floor(high / 10).toString(2) + - Math.floor(low / 10) - .toString(2) - .padStart(32, "0"); - } - - num = parseInt(num, 2); - while (num > 0) { - dec = (num % 10).toString() + dec; - num = Math.floor(num / 10); - } - - return dec; - } - static generateWorkerProcess() { // worker process - returns a number const time = BigInt(Date.now() - Snowflake.EPOCH) << BigInt(22); @@ -112,7 +82,7 @@ export class Snowflake { * @param {Snowflake} snowflake Snowflake to deconstruct * @returns {DeconstructedSnowflake} Deconstructed snowflake */ - static deconstruct(snowflake) { + static deconstruct(snowflake: string) { const BINARY = Snowflake.idToBinary(snowflake).toString(2).padStart(64, "0"); const res = { timestamp: parseInt(BINARY.substring(0, 42), 2) + Snowflake.EPOCH, diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index ba5d427da..8bd45bcbd 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ -import jwt, { VerifyOptions } from "jsonwebtoken"; +import jwt from "jsonwebtoken"; import { Config } from "./Config"; import { InstanceBan, Session, User } from "../entities"; import crypto from "node:crypto"; @@ -42,8 +42,10 @@ export type UserTokenData = { decoded: { id: string; iat: number; - ver?: number; // token format version - did?: string; // device id + // token format version + ver?: number; + // device id + did?: string; }; }; @@ -79,7 +81,8 @@ export const checkToken = ( return rejectAndLog(reject, 401, "Invalid Token meow " + err); } - const [user, session] = await Promise.all([ + // eslint-disable-next-line prefer-const + let [user, session] = await Promise.all([ User.findOne({ where: { id: decoded.id }, select: [...(opts?.select || []), "id", "bot", "disabled", "deleted", "rights", "data"], @@ -94,6 +97,16 @@ export const checkToken = ( } if (decoded.did && !session) { + // temporary hack: create new session + session = Session.create({ + session_id: decoded.did, + user_id: user.id, + is_admin_session: false, + client_status: {}, + status: "online", + client_info: {}, + }); + await session.save(); logAuth("validateUser rejected: Session not found"); return rejectAndLog(reject, 401, "Invalid Token"); } diff --git a/src/util/util/Version.ts b/src/util/util/Version.ts new file mode 100644 index 000000000..2d7c4b566 --- /dev/null +++ b/src/util/util/Version.ts @@ -0,0 +1,47 @@ +/* + 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 . +*/ + +import path from "path"; +import fs from "fs"; +import { execSync } from "child_process"; +import { red } from "picocolors"; + +export function getRevInfoOrFail(): { rev: string | null; lastModified: number } { + const rootDir = path.join(__dirname, "../../../"); + // sanity check + if (!fs.existsSync(path.join(rootDir, "package.json"))) { + console.log(red("Error: Cannot find package.json in root directory. Are you running from the correct location?")); + } + + // use .rev file if it exists + if (fs.existsSync(path.join(rootDir, ".rev"))) { + return JSON.parse(fs.readFileSync(path.join(rootDir, ".rev"), "utf-8")); + } + + // fall back to invoking git + try { + const rev = execSync(`git -C "${rootDir}" rev-parse HEAD`).toString().trim(); + const lastModified = Number(execSync(`git -C "${rootDir}" log -1 --format=%cd --date=unix`).toString().trim()); + return { + rev, + lastModified, + }; + } catch (e) { + return { rev: null, lastModified: 0 }; + } +} diff --git a/src/util/util/WebAuthn.ts b/src/util/util/WebAuthn.ts index f3750e7ca..f6c5dbc4f 100644 --- a/src/util/util/WebAuthn.ts +++ b/src/util/util/WebAuthn.ts @@ -18,7 +18,6 @@ import { Fido2Lib } from "fido2-lib"; import jwt from "jsonwebtoken"; -import { Config } from "./Config"; import { loadOrGenerateKeypair } from "./Token"; const jwtSignOptions: jwt.SignOptions = { diff --git a/src/util/util/email/clients/IEmailClient.ts b/src/util/util/email/clients/IEmailClient.ts index 9f118a968..722dd6c47 100644 --- a/src/util/util/email/clients/IEmailClient.ts +++ b/src/util/util/email/clients/IEmailClient.ts @@ -33,6 +33,8 @@ export class BaseEmailClient implements IEmailClient { return; } sendMail(email: IEmail): Promise { + // noinspection JSUnusedLocalSymbols - parameter exists for public API reasons + const _ = email; throw new Error("Method not implemented."); } } diff --git a/src/util/util/email/clients/SMTPEmailClient.ts b/src/util/util/email/clients/SMTPEmailClient.ts index f0d3e8fe6..278a41980 100644 --- a/src/util/util/email/clients/SMTPEmailClient.ts +++ b/src/util/util/email/clients/SMTPEmailClient.ts @@ -34,7 +34,7 @@ export class SMTPEmailClient extends BaseEmailClient { return; } // get configuration - const { host, port, secure, username, password } = Config.get().email.smtp; + const { host, port, secure, starttls, allowInsecure, username, password } = Config.get().email.smtp; // ensure all required configuration values are set if (!host || !port || secure === null) return console.error("[Email] SMTP has not been configured correctly."); @@ -45,24 +45,27 @@ export class SMTPEmailClient extends BaseEmailClient { ); /* Allow for SMTP relays with and without username/passwords (IE: Smarthosts/Local Relays, etc) */ - let nodemailer_opts: unknown; - if (!username || !password) { - nodemailer_opts = { - host, - port, - secure, - }; - } else { - nodemailer_opts = { - host, - port, - secure, - auth: { - user: username, - pass: password, - }, - }; - } + const nodemailer_opts = { + host: host, + port: port, + secure: secure, + ...(starttls ? { requireTLS: true } : { ignoreTLS: true }), + ...(allowInsecure + ? { + tls: { + rejectUnauthorized: false, + }, + } + : {}), + ...(username && password + ? { + auth: { + user: username, + pass: password, + }, + } + : {}), + }; // construct the transporter // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/src/util/util/email/index.ts b/src/util/util/email/index.ts index ca87e2799..802ed271c 100644 --- a/src/util/util/email/index.ts +++ b/src/util/util/email/index.ts @@ -124,7 +124,8 @@ export const Email: { }, /** - * + * Generates a password reset link + * @param type the MailType to generate a link for * @param id user id */ generateLink: async function (type, id) { diff --git a/src/util/util/extensions/Array.ts b/src/util/util/extensions/Array.ts index b04770aba..04b381c84 100644 --- a/src/util/util/extensions/Array.ts +++ b/src/util/util/extensions/Array.ts @@ -6,33 +6,16 @@ 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 . */ -declare global { - interface Array { - /** - * @deprecated never use, idk why but I can't get rid of this without errors - */ - remove(h: T): never; - /** - * Returns a new array with duplicate elements removed. - */ - distinct(this: T[]): T[]; - /** - * Returns the only element matching the predicate, or undefined. - * Throws if more than one element matches. - */ - single(this: T[], predicate: (elem: T) => boolean): T | undefined; - } -} /* https://stackoverflow.com/a/50636286 */ export function arrayPartition(array: T[], filter: (elem: T) => boolean): [T[], T[]] { const pass: T[] = [], @@ -47,26 +30,3 @@ export function arrayRemove(array: T[], item: T): void { array.splice(index, 1); } } - -// register extensions -// We intentionally define as non-enumerable to avoid breaking for..in loops -const _arrayProtoObj = Array.prototype as unknown as Record; -if (!("distinct" in _arrayProtoObj)) { - Object.defineProperty(Array.prototype, "distinct", { - value: function (this: T[]): T[] { - return [...new Set(this as unknown as T[])]; - }, - enumerable: false, - }); -} - -if (!("single" in _arrayProtoObj)) { - Object.defineProperty(Array.prototype, "single", { - value: function (this: T[], predicate: (elem: T) => boolean): T | undefined { - const matches = (this as unknown as T[]).filter(predicate); - if (matches.length > 1) throw new Error("Array.single: more than one element matches predicate"); - return matches[0]; - }, - enumerable: false, - }); -} diff --git a/src/util/util/index.ts b/src/util/util/index.ts index 6ef8bd851..8d5013aad 100644 --- a/src/util/util/index.ts +++ b/src/util/util/index.ts @@ -17,13 +17,16 @@ */ export * from "./ApiError"; +export * from "./extensions/Array"; export * from "./BitField"; //export * from "./Categories"; export * from "./cdn"; export * from "./Config"; export * from "./Constants"; export * from "./Database"; +export * from "./DateBuilder"; export * from "./email"; +export * from "./ElapsedTime"; export * from "./Event"; export * from "./FieldError"; export * from "./Intents"; @@ -32,26 +35,25 @@ export * from "./JSON"; export * from "./KittyLogo"; export * from "./Logo"; export * from "./MessageFlags"; +export * from "./networking"; export * from "./Permissions"; export * from "./RabbitMQ"; export * from "./Regex"; export * from "./Rights"; export * from "./Snowflake"; +export * from "./Stopwatch"; export * from "./String"; +export * from "./Timespan"; export * from "./Token"; export * from "./TraverseDirectory"; export * from "./WebAuthn"; -export * from "./Url"; export * from "./ChannelFlags"; export * from "./Gifs"; export * from "./Application"; export * from "./NameValidation"; -export * from "./AutomodEvaluator"; -export * from "./AutomodActionExecutor"; -export * from "./DateBuilder"; -export * from "./ElapsedTime"; -export * from "./Random"; -export * from "./Stopwatch"; -export * from "./Timespan"; +export * from "../../schemas/HelperTypes"; export * from "./extensions"; -export * from "./networking"; +export * from "./Random"; +export * from "./Url"; +export * from "./Version"; +export * from "./ConnectionPrivacy"; diff --git a/src/util/util/json/JsonSerializer.ts b/src/util/util/json/JsonSerializer.ts index c88e4a2f9..a35bb947d 100644 --- a/src/util/util/json/JsonSerializer.ts +++ b/src/util/util/json/JsonSerializer.ts @@ -2,7 +2,6 @@ import { JsonSerializerOptions } from "./JsonSerializerOptions"; import { Worker } from "worker_threads"; import { join } from "path"; import os from "os"; -import Stream from "node:stream"; import { ReadStream, WriteStream } from "node:fs"; // const worker = new Worker(join(process.cwd(), 'dist', 'util', 'util', 'json', 'jsonWorker.js')); @@ -23,6 +22,7 @@ function getNextWorker(): Worker { return worker; } +// noinspection JSUnusedLocalSymbols - TODO: implement options export class JsonSerializer { public static Serialize(value: T, opts?: JsonSerializerOptions): string { return JSON.stringify(value); diff --git a/src/util/util/lambert-server/Server.ts b/src/util/util/lambert-server/Server.ts index 47cc457b7..e9cb675b4 100644 --- a/src/util/util/lambert-server/Server.ts +++ b/src/util/util/lambert-server/Server.ts @@ -1,10 +1,5 @@ -import express, { Application, NextFunction, Request, Response, Router } from "express"; -import { traverseDirectory } from "./Utils"; +import express, { Application, Router } from "express"; import { Server as HTTPServer } from "http"; -import { HTTPError } from "./HTTPError"; -// import "express-async-errors"; -import bodyParser from "body-parser"; -// import helmet from "helmet"; import http from "http"; declare global { @@ -21,7 +16,6 @@ export type ServerOptions = { host: string; production: boolean; serverInitLogging: boolean; - // errorHandler?: { (err: Error, req: Request, res: Response, next: NextFunction): any }; jsonBody: boolean; server: http.Server; app: Application; @@ -48,7 +42,6 @@ export class Server { if (!opts.host) opts.host = "0.0.0.0"; if (opts.production == null) opts.production = false; if (opts.serverInitLogging == null) opts.serverInitLogging = true; - // if (opts.errorHandler == null) opts.errorHandler = this.errorHandler; if (opts.jsonBody == null) opts.jsonBody = true; if (opts.server) this.http = opts.server; @@ -58,40 +51,6 @@ export class Server { else this.app = express(); } - // protected secureExpress() { - // this.app.use(helmet.contentSecurityPolicy()); - // this.app.use(helmet.expectCt); - // this.app.use(helmet.originAgentCluster()); - // this.app.use(helmet.referrerPolicy({ policy: "same-origin" })); - // this.app.use(helmet.hidePoweredBy()); - // this.app.use(helmet.noSniff()); - // this.app.use(helmet.dnsPrefetchControl({ allow: true })); - // this.app.use(helmet.ieNoOpen()); - // this.app.use(helmet.frameguard({ action: "deny" })); - // this.app.use(helmet.permittedCrossDomainPolicies({ permittedPolicies: "none" })); - // } - - public errorHandler = (error: Error, req: Request, res: Response, next: NextFunction) => { - try { - let code; - let message = error?.toString(); - - if (error instanceof HTTPError && error.code) code = error.code || 400; - else { - console.error(error); - if (this.options.production) { - message = "Internal Server Error"; - } - code = 500; - } - - res.status(code).json({ success: false, code: code, error: true, message }); - } catch (e) { - console.error(e); - return res.status(500).json({ success: false, code: 500, error: true, message: "Internal Server Error" }); - } - }; - async start() { const server = this.http || this.app; if (!server.listening) { @@ -102,18 +61,6 @@ export class Server { } } - async registerRoutes(root: string) { - this.app.use((req, res, next) => { - req.server = this; - next(); - }); - if (this.options.jsonBody) this.app.use(bodyParser.json()); - const result = await traverseDirectory({ dirname: root, recursive: true }, this.registerRoute.bind(this, root)); - // if (this.options.errorHandler) this.app.use(this.options.errorHandler); - // if (this.options.production) this.secureExpress(); - return result; - } - registerRoute(root: string, file: string): Router | undefined { if (root.endsWith("/") || root.endsWith("\\")) root = root.slice(0, -1); // removes slash at the end of the root dir let path = file.replace(root, ""); // remove root from path and @@ -128,10 +75,9 @@ export class Server { if (router.default) router = router.default; if (!router || router?.prototype?.constructor?.name !== "router") throw `File doesn't export any default router`; - // if (this.options.errorHandler) router.use(this.options.errorHandler); this.app.use(path, router); - if (this.options.serverInitLogging) console.log(`[Server] Route ${path} registered`); + if (this.options.serverInitLogging && process.env.LOG_ROUTES !== "false") console.log(`[Server] Route ${path} registered`); return router; } catch (error) { diff --git a/src/util/util/lambert-server/Utils.ts b/src/util/util/lambert-server/Utils.ts index 32f4b16cb..07f8eadca 100644 --- a/src/util/util/lambert-server/Utils.ts +++ b/src/util/util/lambert-server/Utils.ts @@ -16,7 +16,7 @@ export async function traverseDirectory(options: traverseDirectoryOptions, ac const routes = fs.readdirSync(options.dirname); const promises = []>routes - .sort((a, b) => (a.startsWith("#") ? 1 : -1)) // load #parameter routes last + .sort((a, _) => (a.startsWith("#") ? 1 : -1)) // load #parameter routes last .map(async (file) => { const path = options.dirname + file; const stat = fs.lstatSync(path); diff --git a/src/util/util/lambert-server/check.ts b/src/util/util/lambert-server/check.ts index 864e5fac0..561a584ce 100644 --- a/src/util/util/lambert-server/check.ts +++ b/src/util/util/lambert-server/check.ts @@ -1,6 +1,3 @@ -import { NextFunction, Request, Response } from "express"; -import { HTTPError } from "."; - const OPTIONAL_PREFIX = "$"; const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; diff --git a/src/webrtc/Server.ts b/src/webrtc/Server.ts index 312a97c9b..ff98fe57e 100644 --- a/src/webrtc/Server.ts +++ b/src/webrtc/Server.ts @@ -21,7 +21,7 @@ import { closeDatabase, Config, initDatabase, initEvent } from "@spacebar/util"; import http from "http"; import ws from "ws"; import { Connection } from "./events/Connection"; -import { loadWebRtcLibrary, mediaServer, WRTC_PORT_MAX, WRTC_PORT_MIN, WRTC_PUBLIC_IP } from "./util/MediaServer"; +import { loadWebRtcLibrary, mediaServer, WRTC_PORT_MAX, WRTC_PORT_MIN, WRTC_PUBLIC_IP } from "./util"; import { green, yellow } from "picocolors"; export class Server { @@ -66,12 +66,12 @@ export class Server { // try to load webrtc library, if failed just don't start webrtc endpoint try { await loadWebRtcLibrary(); + await mediaServer.start(WRTC_PUBLIC_IP, WRTC_PORT_MIN, WRTC_PORT_MAX); } catch (e) { console.log(`[WebRTC] ${yellow("WEBRTC disabled")}`); return; } - await mediaServer.start(WRTC_PUBLIC_IP, WRTC_PORT_MIN, WRTC_PORT_MAX); if (!this.server.listening) { this.server.listen(this.port); console.log(`[WebRTC] ${green(`online on 0.0.0.0:${this.port}`)}`); diff --git a/src/webrtc/events/Message.ts b/src/webrtc/events/Message.ts index 461d76e5c..b5455d977 100644 --- a/src/webrtc/events/Message.ts +++ b/src/webrtc/events/Message.ts @@ -17,17 +17,9 @@ */ import { CLOSECODES } from "@spacebar/gateway"; -import { Tuple } from "lambert-server"; import OPCodeHandlers from "../opcodes"; import { VoiceOPCodes, VoicePayload, WebRtcWebSocket } from "../util"; -const PayloadSchema = { - op: Number, - $d: new Tuple(Object, Number), // or number for heartbeat sequence - $s: Number, - $t: String, -}; - export async function onMessage(this: WebRtcWebSocket, buffer: Buffer) { try { const data: VoicePayload = JSON.parse(buffer.toString()); diff --git a/src/webrtc/opcodes/BackendVersion.ts b/src/webrtc/opcodes/BackendVersion.ts index d9172a7f2..7e6673e72 100644 --- a/src/webrtc/opcodes/BackendVersion.ts +++ b/src/webrtc/opcodes/BackendVersion.ts @@ -18,7 +18,7 @@ import { VoiceOPCodes, VoicePayload, WebRtcWebSocket, Send } from "../util"; -export async function onBackendVersion(this: WebRtcWebSocket, data: VoicePayload) { +export async function onBackendVersion(this: WebRtcWebSocket, _: VoicePayload) { await Send(this, { op: VoiceOPCodes.VOICE_BACKEND_VERSION, d: { voice: "0.8.43", rtc_worker: "0.3.26" }, diff --git a/src/webrtc/opcodes/Identify.ts b/src/webrtc/opcodes/Identify.ts index 509767949..e62fc7afc 100644 --- a/src/webrtc/opcodes/Identify.ts +++ b/src/webrtc/opcodes/Identify.ts @@ -25,6 +25,7 @@ import { subscribeToProducers } from "./Video"; export async function onIdentify(this: WebRtcWebSocket, data: VoicePayload) { clearTimeout(this.readyTimeout); + // noinspection JSUnusedLocalSymbols - TODO: use video? const { server_id, user_id, session_id, token, streams, video } = validateSchema("VoiceIdentifySchema", data.d) as VoiceIdentifySchema; // server_id can be one of the following: a unique id for a GO Live stream, a channel id for a DM voice call, or a guild id for a guild voice channel @@ -91,7 +92,7 @@ export async function onIdentify(this: WebRtcWebSocket, data: VoicePayload) { }); // the server generates a unique ssrc for the audio and video stream. Must be unique among users connected to same server - // UDP clients will respect this ssrc, but websocket clients will generate and replace it with their own + // UDP clients will respect this ssrc, but webrtc clients will generate and replace it with their own const generatedSsrc: SSRCs = { audio_ssrc: generateSsrc(), video_ssrc: generateSsrc(), diff --git a/src/webrtc/opcodes/Video.ts b/src/webrtc/opcodes/Video.ts index a7426198f..38eb419ba 100644 --- a/src/webrtc/opcodes/Video.ts +++ b/src/webrtc/opcodes/Video.ts @@ -53,7 +53,7 @@ export async function onVideo(this: WebRtcWebSocket, payload: VoicePayload) { if (wantsToProduceAudio) { try { await Promise.race([ - new Promise((resolve, reject) => { + new Promise((resolve, _) => { this.webRtcClient?.emitter.once("connected", () => resolve()); }), new Promise((resolve, reject) => { diff --git a/src/webrtc/start.ts b/src/webrtc/start.ts index 14b4ce3a5..54190436d 100644 --- a/src/webrtc/start.ts +++ b/src/webrtc/start.ts @@ -35,4 +35,4 @@ const server = new Server({ if (fs.existsSync("/proc/self/comm")) fs.writeFileSync("/proc/self/comm", `spacebar-wrtc-${cluster.worker ? cluster.worker.id : port}`); process.title = `sb-wrtc-${cluster.worker ? cluster.worker.id : port}`; -server.start(); +server.start().catch((e) => console.error("Failed to start WebRTC server:", e)); diff --git a/src/webrtc/util/MediaServer.ts b/src/webrtc/util/MediaServer.ts index 0f64bc04d..eb1663220 100644 --- a/src/webrtc/util/MediaServer.ts +++ b/src/webrtc/util/MediaServer.ts @@ -52,7 +52,7 @@ export const loadWebRtcLibrary = async () => { console.log(`[WebRTC] ${green(`Succesfully loaded ${selectedWrtcLibrary}`)}`); return Promise.resolve(); } catch (error) { - console.log(`[WebRTC] ${red(`Failed to import ${selectedWrtcLibrary}: ${error instanceof NoConfiguredLibraryError ? error.message : ""}`)}`); + console.log(`[WebRTC] ${red(`Failed to import ${selectedWrtcLibrary}: ${error instanceof NoConfiguredLibraryError ? error.message : (error as Error).message}`)}`); return Promise.reject(); }