From 764d2801a683e343de0ff88e46053952f8a8ab46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sat, 23 Apr 2022 16:37:16 +0300 Subject: [PATCH 01/38] self commands and /me messages --- util/src/entities/Message.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index b32bbd94..8bd50455 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -39,11 +39,13 @@ export enum MessageType { 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, ENCRYPTED = 16, REPLY = 19, - APPLICATION_COMMAND = 20, + APPLICATION_COMMAND = 20, // application command or self command invocation + SELF_COMMAND_SCRIPT = 21, // self command scripts ROUTE_ADDED = 41, // custom message routing: new route affecting that channel ROUTE_DISABLED = 42, // custom message routing: given route no longer affecting that channel ENCRYPTION = 50, From e69c8a31a5f051c75a1c5544d210b8c0ae76e9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sat, 23 Apr 2022 16:40:54 +0300 Subject: [PATCH 02/38] Update Message.ts --- util/src/entities/Message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/entities/Message.ts b/util/src/entities/Message.ts index 8bd50455..e18cf691 100644 --- a/util/src/entities/Message.ts +++ b/util/src/entities/Message.ts @@ -45,9 +45,9 @@ export enum MessageType { ENCRYPTED = 16, REPLY = 19, APPLICATION_COMMAND = 20, // application command or self command invocation - SELF_COMMAND_SCRIPT = 21, // self command scripts ROUTE_ADDED = 41, // custom message routing: new route affecting that channel ROUTE_DISABLED = 42, // custom message routing: given route no longer affecting that channel + SELF_COMMAND_SCRIPT = 43, // self command scripts ENCRYPTION = 50, CUSTOM_START = 63, UNHANDLED = 255 From 2e84840213afe7682af56beedfd817ed6854248c Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Sun, 24 Apr 2022 16:09:38 +1000 Subject: [PATCH 03/38] While backfilling, message ids must now be valid snowflakes, cannot be in the future, and cannot overwrite existing messages --- .../#channel_id/messages/#message_id/index.ts | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts index 6d2bf185..8d2bd5cb 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts @@ -16,6 +16,8 @@ import multer from "multer"; import { route } from "@fosscord/api"; import { handleMessage, postHandleMessage } from "@fosscord/api"; import { MessageCreateSchema } from "../index"; +import { Snowflake } from "@fosscord/util"; +import { HTTPError } from "lambert-server"; const router = Router(); // TODO: message content/embed string length limit @@ -91,6 +93,22 @@ router.put( var body = req.body as MessageCreateSchema; const attachments: Attachment[] = []; + // regex to check if message contains anything other than numerals ( also no decimals ) + if (!message_id.match(/^\+?\d+$/)) { + throw new HTTPError("Message IDs must be positive integers") + } + + const snowflake = Snowflake.deconstruct(message_id) + if (Date.now() < snowflake.timestamp) { + // message is in the future + throw new HTTPError("You cannot backfill messages in the future", 400); + } + + const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id }}); + if (exists) { + throw new HTTPError("Cannot backfill to message ID that already exists", 400); + } + if (req.file) { try { const file = await uploadFile(`/attachments/${req.params.channel_id}`, req.file); @@ -100,8 +118,6 @@ router.put( } } const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] }); - - // TODO: check the ID is not from the future, to prevent future-faking of channel histories const embeds = body.embeds || []; if (body.embed) embeds.push(body.embed); @@ -115,11 +131,9 @@ router.put( channel_id, attachments, edited_timestamp: undefined, - timestamp: undefined, // FIXME: calculate timestamp from snowflake + timestamp: new Date(snowflake.timestamp), }); - channel.last_message_id = message.id; - //Fix for the client bug delete message.member From 122abacf17d8508c471f7139b0607d297d1f6357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 09:23:52 +0300 Subject: [PATCH 04/38] Backfilling privilege does not imply right to post messages --- .../channels/#channel_id/messages/#message_id/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts index 8d2bd5cb..cf25f916 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts @@ -92,10 +92,13 @@ router.put( const { channel_id, message_id } = req.params; var body = req.body as MessageCreateSchema; const attachments: Attachment[] = []; + + const rights = getRights(req.user_id); + rights.hasThrow("SEND_MESSAGES"); // regex to check if message contains anything other than numerals ( also no decimals ) if (!message_id.match(/^\+?\d+$/)) { - throw new HTTPError("Message IDs must be positive integers") + throw new HTTPError("Message IDs must be positive integers", 400); } const snowflake = Snowflake.deconstruct(message_id) @@ -106,7 +109,7 @@ router.put( const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id }}); if (exists) { - throw new HTTPError("Cannot backfill to message ID that already exists", 400); + throw new HTTPError("Cannot backfill to message ID that already exists", 409); } if (req.file) { From c6a79005f4ced55128e4552c827c3af61282bd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 09:48:45 +0300 Subject: [PATCH 05/38] API response codes for backfill errors --- util/src/util/Constants.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts index 8d61b9b4..9ceebf30 100644 --- a/util/src/util/Constants.ts +++ b/util/src/util/Constants.ts @@ -742,6 +742,8 @@ export const FosscordApiErrors = { DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061), FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006), MISSING_RIGHTS: new ApiError("You lack rights to perform that action ({})", 50013, undefined, [""]), + CANNOT_REPLACE_BY_BACKFILL: new APIError("Cannot backfill to message ID that already exists", 55002), + CANNOT_BACKFILL_TO_THE_FUTURE: new APIError("You cannot backfill messages in the future", 55003), CANNOT_GRANT_PERMISSIONS_EXCEEDING_RIGHTS: new ApiError("You cannot grant permissions exceeding your own rights", 50050), ROUTES_LOOPING: new ApiError("Loops in the route definition ({})", 50060, undefined, [""]), CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061), From ddf0626a9a667e8fa70f8fcb11ee7254062d05dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 09:57:00 +0300 Subject: [PATCH 06/38] refine error codes for backfill constraint violations --- util/src/util/Constants.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts index 9ceebf30..54287af6 100644 --- a/util/src/util/Constants.ts +++ b/util/src/util/Constants.ts @@ -727,23 +727,23 @@ export const DiscordApiErrors = { * An error encountered while performing an API request (Fosscord only). Here are the potential errors: */ export const FosscordApiErrors = { - MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1), + MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1, 500), PREMIUM_DISABLED_FOR_GUILD: new ApiError("This guild cannot be boosted", 25001), NO_FURTHER_PREMIUM: new ApiError("This guild does not receive further boosts", 25002), - GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003), + GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003, 403), CANNOT_FRIEND_SELF: new ApiError("Cannot friend oneself", 25009), USER_SPECIFIC_INVITE_WRONG_RECIPIENT: new ApiError("This invite is not meant for you", 25010), USER_SPECIFIC_INVITE_FAILED: new ApiError("Failed to invite user", 25011), - CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050), + CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050, 403), CANNOT_REMOVE_SELF_FROM_GROUP: new ApiError("This user cannot remove oneself from user group", 25051), CANNOT_BAN_OPERATOR: new ApiError("Non-OPERATOR cannot ban OPERATOR from instance", 25052), - CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059), - EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060), - DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061), - FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006), + CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059, 403), + EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060, 403), + DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061, 403), + FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006, 501), MISSING_RIGHTS: new ApiError("You lack rights to perform that action ({})", 50013, undefined, [""]), - CANNOT_REPLACE_BY_BACKFILL: new APIError("Cannot backfill to message ID that already exists", 55002), - CANNOT_BACKFILL_TO_THE_FUTURE: new APIError("You cannot backfill messages in the future", 55003), + CANNOT_REPLACE_BY_BACKFILL: new ApiError("Cannot backfill to message ID that already exists", 55002, 409), + CANNOT_BACKFILL_TO_THE_FUTURE: new ApiError("You cannot backfill messages in the future", 55003), CANNOT_GRANT_PERMISSIONS_EXCEEDING_RIGHTS: new ApiError("You cannot grant permissions exceeding your own rights", 50050), ROUTES_LOOPING: new ApiError("Loops in the route definition ({})", 50060, undefined, [""]), CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061), From 6a1e2edd8ddc7e5aef8ed2847431673eb5b0084b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 11:40:24 +0300 Subject: [PATCH 07/38] use return codes to allow for automation --- .../channels/#channel_id/messages/#message_id/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts index cf25f916..958954b6 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts @@ -2,13 +2,16 @@ import { Attachment, Channel, Embed, + DiscordApiErrors, emitEvent, + FosscordApiErrors, getPermission, getRights, Message, MessageCreateEvent, MessageDeleteEvent, MessageUpdateEvent, + Snowflake, uploadFile } from "@fosscord/util"; import { Router, Response, Request } from "express"; @@ -16,7 +19,6 @@ import multer from "multer"; import { route } from "@fosscord/api"; import { handleMessage, postHandleMessage } from "@fosscord/api"; import { MessageCreateSchema } from "../index"; -import { Snowflake } from "@fosscord/util"; import { HTTPError } from "lambert-server"; const router = Router(); @@ -104,12 +106,12 @@ router.put( const snowflake = Snowflake.deconstruct(message_id) if (Date.now() < snowflake.timestamp) { // message is in the future - throw new HTTPError("You cannot backfill messages in the future", 400); + throw FosscordApiErrors.CANNOT_BACKFILL_TO_THE_FUTURE; } const exists = await Message.findOne({ where: { id: message_id, channel_id: channel_id }}); if (exists) { - throw new HTTPError("Cannot backfill to message ID that already exists", 409); + throw FosscordApiErrors.CANNOT_REPLACE_BY_BACKFILL; } if (req.file) { From 037bd43d16b11c85d3a521c7ad364b0ffe7c231a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 14:57:26 +0300 Subject: [PATCH 08/38] Punitive rate limiting --- api/src/middlewares/RateLimit.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index 1a38cfcf..8368d14a 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -53,12 +53,12 @@ export default function rateLimit(opts: { if (opts.GET && ["GET", "OPTIONS", "HEAD"].includes(req.method)) max_hits = opts.GET; else if (opts.MODIFY && ["POST", "DELETE", "PATCH", "PUT"].includes(req.method)) max_hits = opts.MODIFY; - const offender = Cache.get(executor_id + bucket_id); + let offender = Cache.get(executor_id + bucket_id); if (offender) { - const reset = offender.expires_at.getTime(); - const resetAfterMs = reset - Date.now(); - const resetAfterSec = resetAfterMs / 1000; + let reset = offender.expires_at.getTime(); + let resetAfterMs = reset - Date.now(); + let resetAfterSec = (resetAfterMs + 999) / 1000; if (resetAfterMs <= 0) { offender.hits = 0; @@ -70,6 +70,10 @@ export default function rateLimit(opts: { if (offender.blocked) { const global = bucket_id === "global"; + reset = reset + opts.window * 1000; // each block violation pushes the expiry one full window further + offender.expires_at += opts.window * 1000; + resetAfterMs = reset - Date.now(); + resetAfterSec = (resetAfterMs + 999) / 1000; console.log("blocked bucket: " + bucket_id, { resetAfterMs }); return ( From 8769bb2868d1a48a7da34fc0d7f6152a51ea37c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 17:35:09 +0300 Subject: [PATCH 09/38] fix the seconds rounding logic --- api/src/middlewares/RateLimit.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index 8368d14a..b4f32131 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -58,7 +58,7 @@ export default function rateLimit(opts: { if (offender) { let reset = offender.expires_at.getTime(); let resetAfterMs = reset - Date.now(); - let resetAfterSec = (resetAfterMs + 999) / 1000; + let resetAfterSec = Math.ceil(resetAfterMs / 1000); if (resetAfterMs <= 0) { offender.hits = 0; @@ -73,7 +73,7 @@ export default function rateLimit(opts: { reset = reset + opts.window * 1000; // each block violation pushes the expiry one full window further offender.expires_at += opts.window * 1000; resetAfterMs = reset - Date.now(); - resetAfterSec = (resetAfterMs + 999) / 1000; + resetAfterSec = Math.ceil(resetAfterMs / 1000); console.log("blocked bucket: " + bucket_id, { resetAfterMs }); return ( From ea1f188ccec325e2128d155fe6595e431b6aea5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 21:49:04 +0300 Subject: [PATCH 10/38] Update RateLimit.ts --- api/src/middlewares/RateLimit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index b4f32131..f31aa5da 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -71,7 +71,7 @@ export default function rateLimit(opts: { if (offender.blocked) { const global = bucket_id === "global"; reset = reset + opts.window * 1000; // each block violation pushes the expiry one full window further - offender.expires_at += opts.window * 1000; + offender.expires_at = offender.expires_at + opts.window * 1000; resetAfterMs = reset - Date.now(); resetAfterSec = Math.ceil(resetAfterMs / 1000); From 6f031c2839c85f8d43aff211b18949403fcca383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 22:40:36 +0300 Subject: [PATCH 11/38] Update Token.ts --- util/src/util/Token.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/src/util/Token.ts b/util/src/util/Token.ts index 7c4cc61d..500ace45 100644 --- a/util/src/util/Token.ts +++ b/util/src/util/Token.ts @@ -6,7 +6,12 @@ export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] }; export function checkToken(token: string, jwtSecret: string): Promise { return new Promise((res, rej) => { - token = token.replace("Bot ", ""); // TODO: proper bot support + token = token.replace("Bot ", ""); + /** + in fosscord, even with instances that have bot distinction; we won't enforce "Bot" prefix, + as we don't really have separate pathways for bots + **/ + jwt.verify(token, jwtSecret, JWTOptions, async (err, decoded: any) => { if (err || !decoded) return rej("Invalid Token"); From aacf99d82a2b2dce609e7426396743c8cbcb7318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 23:04:55 +0300 Subject: [PATCH 12/38] Update RateLimit.ts --- api/src/middlewares/RateLimit.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index f31aa5da..4bbef520 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -70,8 +70,9 @@ export default function rateLimit(opts: { if (offender.blocked) { const global = bucket_id === "global"; - reset = reset + opts.window * 1000; // each block violation pushes the expiry one full window further - offender.expires_at = offender.expires_at + opts.window * 1000; + // each block violation pushes the expiry one full window further + reset = new Date(reset.getTime() + opts.window * 1000); + offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000); resetAfterMs = reset - Date.now(); resetAfterSec = Math.ceil(resetAfterMs / 1000); From 9bd5fce30c83aacb4175b4e301529592d4dcdd75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 24 Apr 2022 23:07:25 +0300 Subject: [PATCH 13/38] eventually fix those errors --- api/src/middlewares/RateLimit.ts | 2 +- util/src/util/Constants.ts | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index 4bbef520..81668034 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -71,7 +71,7 @@ export default function rateLimit(opts: { if (offender.blocked) { const global = bucket_id === "global"; // each block violation pushes the expiry one full window further - reset = new Date(reset.getTime() + opts.window * 1000); + reset += opts.window * 1000; offender.expires_at = new Date(offender.expires_at.getTime() + opts.window * 1000); resetAfterMs = reset - Date.now(); resetAfterSec = Math.ceil(resetAfterMs / 1000); diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts index 8d61b9b4..a5d3fcd2 100644 --- a/util/src/util/Constants.ts +++ b/util/src/util/Constants.ts @@ -727,21 +727,23 @@ export const DiscordApiErrors = { * An error encountered while performing an API request (Fosscord only). Here are the potential errors: */ export const FosscordApiErrors = { - MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1), + MANUALLY_TRIGGERED_ERROR: new ApiError("This is an artificial error", 1, 500), PREMIUM_DISABLED_FOR_GUILD: new ApiError("This guild cannot be boosted", 25001), NO_FURTHER_PREMIUM: new ApiError("This guild does not receive further boosts", 25002), - GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003), + GUILD_PREMIUM_DISABLED_FOR_YOU: new ApiError("This guild cannot be boosted by you", 25003, 403), CANNOT_FRIEND_SELF: new ApiError("Cannot friend oneself", 25009), USER_SPECIFIC_INVITE_WRONG_RECIPIENT: new ApiError("This invite is not meant for you", 25010), USER_SPECIFIC_INVITE_FAILED: new ApiError("Failed to invite user", 25011), - CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050), + CANNOT_MODIFY_USER_GROUP: new ApiError("This user cannot manipulate this group", 25050, 403), CANNOT_REMOVE_SELF_FROM_GROUP: new ApiError("This user cannot remove oneself from user group", 25051), CANNOT_BAN_OPERATOR: new ApiError("Non-OPERATOR cannot ban OPERATOR from instance", 25052), - CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059), - EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060), - DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061), - FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006), + CANNOT_LEAVE_GUILD: new ApiError("You are not allowed to leave guilds that you joined by yourself", 25059, 403), + EDITS_DISABLED: new ApiError("You are not allowed to edit your own messages", 25060, 403), + DELETE_MESSAGE_DISABLED: new ApiError("You are not allowed to delete your own messages", 25061, 403), + FEATURE_PERMANENTLY_DISABLED: new ApiError("This feature has been disabled server-side", 45006, 501), MISSING_RIGHTS: new ApiError("You lack rights to perform that action ({})", 50013, undefined, [""]), + CANNOT_REPLACE_BY_BACKFILL: new ApiError("Cannot backfill to message ID that already exists", 55002, 409), + CANNOT_BACKFILL_TO_THE_FUTURE: new ApiError("You cannot backfill messages in the future", 55003), CANNOT_GRANT_PERMISSIONS_EXCEEDING_RIGHTS: new ApiError("You cannot grant permissions exceeding your own rights", 50050), ROUTES_LOOPING: new ApiError("Loops in the route definition ({})", 50060, undefined, [""]), CANNOT_REMOVE_ROUTE: new ApiError("Cannot remove message route while it is in effect and being used", 50061), @@ -787,3 +789,4 @@ function keyMirror(arr: string[]) { for (const value of arr) tmp[value] = value; return tmp; } + From 265f603b0af7f57763d3442d64cfad93aa3f0ad5 Mon Sep 17 00:00:00 2001 From: Thesourtimes Date: Tue, 26 Apr 2022 21:34:15 +0300 Subject: [PATCH 14/38] Do stuff for npm workspace --- api/LICENSE | 14 ------------ api/crowdin.yml | 3 --- api/package.json | 2 +- cdn/CONTRIBUTE.md | 18 --------------- cdn/package.json | 4 ++-- dashboard/LICENSE | 14 ------------ dashboard/package.json | 4 ++-- gateway/LICENSE | 14 ------------ gateway/client.js | 51 ------------------------------------------ gateway/package.json | 2 +- package.json | 17 ++++++++++++++ rtc/LICENSE | 14 ------------ util/LICENSE | 14 ------------ util/package.json | 2 +- webrtc/LICENSE | 14 ------------ webrtc/package.json | 4 ++-- 16 files changed, 26 insertions(+), 165 deletions(-) delete mode 100644 api/LICENSE delete mode 100644 api/crowdin.yml delete mode 100644 cdn/CONTRIBUTE.md delete mode 100644 dashboard/LICENSE delete mode 100644 gateway/LICENSE delete mode 100644 gateway/client.js create mode 100644 package.json delete mode 100644 rtc/LICENSE delete mode 100644 util/LICENSE delete mode 100644 webrtc/LICENSE diff --git a/api/LICENSE b/api/LICENSE deleted file mode 100644 index f19bf520..00000000 --- a/api/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2021 Fosscord and 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 . \ No newline at end of file diff --git a/api/crowdin.yml b/api/crowdin.yml deleted file mode 100644 index 7228117f..00000000 --- a/api/crowdin.yml +++ /dev/null @@ -1,3 +0,0 @@ -files: - - source: /locales/en/*.json - translation: /locales/%two_letters_code%/%original_file_name% diff --git a/api/package.json b/api/package.json index c586c9fe..65472522 100644 --- a/api/package.json +++ b/api/package.json @@ -30,7 +30,7 @@ "discord-open-source" ], "author": "Fosscord", - "license": "GPLV3", + "license": "AGPLV3", "bugs": { "url": "https://github.com/fosscord/fosscord-server/issues" }, diff --git a/cdn/CONTRIBUTE.md b/cdn/CONTRIBUTE.md deleted file mode 100644 index 7cc673d9..00000000 --- a/cdn/CONTRIBUTE.md +++ /dev/null @@ -1,18 +0,0 @@ -# CONTRIBUTE - -### Setup: - -``` -npm i -npm start -``` - -### Run tests: - -``` -npm test -``` - -#### common errors: - -- db not connecting --> start mongod in a seperate terminal window diff --git a/cdn/package.json b/cdn/package.json index 7a1f43c9..f1d12ba5 100644 --- a/cdn/package.json +++ b/cdn/package.json @@ -14,8 +14,8 @@ "url": "git+https://github.com/fosscord/fosscord-server.git" }, "keywords": [], - "author": "", - "license": "GPLV3", + "author": "Fosscord", + "license": "AGPLV3", "bugs": { "url": "https://github.com/fosscord/fosscord-server/issues" }, diff --git a/dashboard/LICENSE b/dashboard/LICENSE deleted file mode 100644 index f19bf520..00000000 --- a/dashboard/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2021 Fosscord and 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 . \ No newline at end of file diff --git a/dashboard/package.json b/dashboard/package.json index 1009d658..e71de793 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -14,8 +14,8 @@ "url": "git+https://github.com/fosscord/fosscord-server.git" }, "keywords": [], - "author": "", - "license": "GPLV3", + "author": "Fosscord", + "license": "AGPLV3", "bugs": { "url": "https://github.com/fosscord/fosscord-server/issues" }, diff --git a/gateway/LICENSE b/gateway/LICENSE deleted file mode 100644 index f19bf520..00000000 --- a/gateway/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2021 Fosscord and 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 . \ No newline at end of file diff --git a/gateway/client.js b/gateway/client.js deleted file mode 100644 index c841c6a0..00000000 --- a/gateway/client.js +++ /dev/null @@ -1,51 +0,0 @@ -require("missing-native-js-functions"); -const WebSocket = require("ws"); -const Constants = require("./dist/util/Constants"); - -// const ws = new WebSocket("ws://127.0.0.1:8080"); -const ws = new WebSocket("wss://dev.fosscord.com"); - -ws.on("open", () => { - // ws.send(JSON.stringify({ req_type: "new_auth" })); - // ws.send(JSON.stringify({ req_type: "check_auth", token: "" })); - // op: 0, - // d: {}, - // s: 42, - // t: "GATEWAY_EVENT_NAME", -}); - -function send(data) { - ws.send(JSON.stringify(data)); -} - -ws.on("message", (buffer) => { - let data = JSON.parse(buffer.toString()); - console.log(data); - - switch (data.op) { - case 10: - setIntervalNow(() => { - send({ op: 1 }); - }, data.d.heartbeat_interval); - - // send({ - // op: 2, - // d: { - // token: - // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjgxMTY0MjkxNzQzMjA2NjA0OCIsImlhdCI6MTYxMzU4MTE1MX0.7Qj_z2lYIgJ0rc7NfGtpW5DKGqecQfv1mLpoBUQHKDc", - // intents: 0n, - // properties: {}, - // }, - // }); - - send({ - op: 6, - }); - - break; - } -}); - -ws.on("close", (code, reason) => { - console.log(code, reason, Constants.CLOSECODES[code]); -}); diff --git a/gateway/package.json b/gateway/package.json index 6d0d2d1c..92d812b4 100644 --- a/gateway/package.json +++ b/gateway/package.json @@ -13,7 +13,7 @@ }, "keywords": [], "author": "Fosscord", - "license": "GPLV3", + "license": "AGPLV3", "devDependencies": { "@types/amqplib": "^0.8.1", "@types/jsonwebtoken": "^8.5.0", diff --git a/package.json b/package.json new file mode 100644 index 00000000..0fa46c7a --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "fosscord-server", + "version": "1.0.0", + "description": "A Fosscord server written in Node.js", + "workspaces": ["api", "bundle", "cdn", "gateway", "webrtc"], + "scripts": {}, + "repository": { + "type": "git", + "url": "git+https://github.com/fosscord/fosscord-server.git" + }, + "author": "Fosscord", + "license": "AGPLV3", + "bugs": { + "url": "https://github.com/fosscord/fosscord-server/issues" + }, + "homepage": "https://fosscord.com" +} diff --git a/rtc/LICENSE b/rtc/LICENSE deleted file mode 100644 index f19bf520..00000000 --- a/rtc/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2021 Fosscord and 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 . \ No newline at end of file diff --git a/util/LICENSE b/util/LICENSE deleted file mode 100644 index f19bf520..00000000 --- a/util/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2021 Fosscord and 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 . \ No newline at end of file diff --git a/util/package.json b/util/package.json index d7baed9a..7251d3e4 100644 --- a/util/package.json +++ b/util/package.json @@ -23,7 +23,7 @@ "discord-open-source" ], "author": "Fosscord", - "license": "GPLV3", + "license": "AGPLV3", "bugs": { "url": "https://github.com/fosscord/fosscord-server/issues" }, diff --git a/webrtc/LICENSE b/webrtc/LICENSE deleted file mode 100644 index f19bf520..00000000 --- a/webrtc/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (C) 2021 Fosscord and 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 . \ No newline at end of file diff --git a/webrtc/package.json b/webrtc/package.json index 0f700728..ab9e7486 100644 --- a/webrtc/package.json +++ b/webrtc/package.json @@ -9,8 +9,8 @@ "start": "npm run build && node dist/start.js" }, "keywords": [], - "author": "", - "license": "ISC", + "author": "Fosscord", + "license": "AGPLV3", "devDependencies": { "@types/node": "^15.6.1", "@types/ws": "^7.4.4", From cb4325f15d12629af7de4139043b2cf12e53a2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 27 Apr 2022 21:35:36 +0300 Subject: [PATCH 15/38] Forum and guild directory types --- util/src/entities/Channel.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index 98766a9d..69c08be7 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -28,6 +28,8 @@ export enum ChannelType { GUILD_PUBLIC_THREAD = 11, // a temporary sub-channel within a GUILD_TEXT channel GUILD_PRIVATE_THREAD = 12, // a temporary sub-channel within a GUILD_TEXT channel that is only viewable by those invited and those with the MANAGE_THREADS permission GUILD_STAGE_VOICE = 13, // a voice channel for hosting events with an audience + DIRECTORY = 14, // guild directory listing channel + GUILD_FORUM = 15, // forum composed of IM threads TICKET_TRACKER = 33, // ticket tracker, individual ticket items shall have type 12 KANBAN = 34, // confluence like kanban board VOICELESS_WHITEBOARD = 35, // whiteboard but without voice (whiteboard + voice is the same as stage) From a6bc97167e7c6c25052937caa266381c1587b4e7 Mon Sep 17 00:00:00 2001 From: Kuna <65683493+kunamech@users.noreply.github.com> Date: Wed, 27 Apr 2022 23:09:01 +0300 Subject: [PATCH 16/38] Hotfix for workspace (#739) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fa46c7a..3395122e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "fosscord-server", "version": "1.0.0", "description": "A Fosscord server written in Node.js", - "workspaces": ["api", "bundle", "cdn", "gateway", "webrtc"], + "workspaces": ["api", "cdn", "gateway"], "scripts": {}, "repository": { "type": "git", From 2e451d8fd0d85394cc219d7675f3537b8cc9ee90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Thu, 28 Apr 2022 21:30:41 +0300 Subject: [PATCH 17/38] exempt users logic resolves #396 --- api/src/middlewares/RateLimit.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index 81668034..7d5c51e2 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -1,4 +1,4 @@ -import { Config, listenEvent } from "@fosscord/util"; +import { Config, getRights, listenEvent, Rights } from "@fosscord/util"; import { NextFunction, Request, Response, Router } from "express"; import { getIpAdress } from "@fosscord/api"; import { API_PREFIX_TRAILING_SLASH } from "./Authentication"; @@ -9,6 +9,7 @@ import { API_PREFIX_TRAILING_SLASH } from "./Authentication"; /* ? bucket limit? Max actions/sec per bucket? +(ANSWER: a small fosscord instance might not need a complex rate limiting system) TODO: delay database requests to include multiple queries TODO: different for methods (GET/POST) @@ -44,9 +45,12 @@ export default function rateLimit(opts: { onlyIp?: boolean; }): any { return async (req: Request, res: Response, next: NextFunction): Promise => { + // exempt user? if so, immediately short circuit + if (getRights(req.user_id).has("BYPASS_RATE_LIMITS")) return; + const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); var executor_id = getIpAdress(req); - if (!opts.onlyIp && req.user_id) executor_id = req.user_id; + if (!opts.onlyIp && req.user_id) executor_id = req.user_id; var max_hits = opts.count; if (opts.bot && req.user_bot) max_hits = opts.bot; From f09daaa3de902641ca9772089b8af676e05543c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Thu, 28 Apr 2022 21:38:39 +0300 Subject: [PATCH 18/38] needs to be async --- api/src/middlewares/RateLimit.ts | 3 ++- .../channels/#channel_id/messages/#message_id/index.ts | 2 +- bundle/package-lock.json | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/api/src/middlewares/RateLimit.ts b/api/src/middlewares/RateLimit.ts index 7d5c51e2..ca6de98f 100644 --- a/api/src/middlewares/RateLimit.ts +++ b/api/src/middlewares/RateLimit.ts @@ -46,7 +46,8 @@ export default function rateLimit(opts: { }): any { return async (req: Request, res: Response, next: NextFunction): Promise => { // exempt user? if so, immediately short circuit - if (getRights(req.user_id).has("BYPASS_RATE_LIMITS")) return; + const rights = await getRights(req.user_id); + if (rights.has("BYPASS_RATE_LIMITS")) return; const bucket_id = opts.bucket || req.originalUrl.replace(API_PREFIX_TRAILING_SLASH, ""); var executor_id = getIpAdress(req); diff --git a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts index 958954b6..63fee9b9 100644 --- a/api/src/routes/channels/#channel_id/messages/#message_id/index.ts +++ b/api/src/routes/channels/#channel_id/messages/#message_id/index.ts @@ -95,7 +95,7 @@ router.put( var body = req.body as MessageCreateSchema; const attachments: Attachment[] = []; - const rights = getRights(req.user_id); + const rights = await getRights(req.user_id); rights.hasThrow("SEND_MESSAGES"); // regex to check if message contains anything other than numerals ( also no decimals ) diff --git a/bundle/package-lock.json b/bundle/package-lock.json index 4742b4a4..6fbd6978 100644 --- a/bundle/package-lock.json +++ b/bundle/package-lock.json @@ -101,7 +101,7 @@ "name": "@fosscord/api", "version": "1.0.0", "hasInstallScript": true, - "license": "GPLV3", + "license": "AGPLV3", "dependencies": { "@babel/preset-env": "^7.15.8", "@babel/preset-typescript": "^7.15.0", @@ -164,7 +164,7 @@ "../cdn": { "name": "@fosscord/cdn", "version": "1.0.0", - "license": "GPLV3", + "license": "AGPLV3", "dependencies": { "@aws-sdk/client-s3": "^3.36.1", "@aws-sdk/node-http-handler": "^3.36.0", @@ -208,7 +208,7 @@ "name": "@fosscord/gateway", "version": "1.0.0", "hasInstallScript": true, - "license": "GPLV3", + "license": "AGPLV3", "dependencies": { "@fosscord/util": "file:../util", "amqplib": "^0.8.0", From 3343df5c235382910bf3e0e5ebd06e754bc00157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Thu, 28 Apr 2022 21:51:35 +0300 Subject: [PATCH 19/38] Update prune.ts --- api/src/routes/guilds/#guild_id/prune.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/src/routes/guilds/#guild_id/prune.ts b/api/src/routes/guilds/#guild_id/prune.ts index 0dd4d610..0e587d22 100644 --- a/api/src/routes/guilds/#guild_id/prune.ts +++ b/api/src/routes/guilds/#guild_id/prune.ts @@ -11,6 +11,10 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n //Snowflake should have `generateFromTime` method? Or similar? var minId = BigInt(date.valueOf() - Snowflake.EPOCH) << BigInt(22); + /** + idea: ability to customise the cutoff variable + possible candidates: public read receipt, last presence, last VC leave + **/ var members = await Member.find({ where: [ { @@ -47,7 +51,7 @@ export const inactiveMembers = async (guild_id: string, user_id: string, days: n return members; }; -router.get("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => { +router.get("/", route({}), async (req: Request, res: Response) => { const days = parseInt(req.query.days as string); var roles = req.query.include_roles; @@ -65,7 +69,7 @@ export interface PruneSchema { days: number; } -router.post("/", route({ permission: "KICK_MEMBERS" }), async (req: Request, res: Response) => { +router.post("/", route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }), async (req: Request, res: Response) => { const days = parseInt(req.body.days); var roles = req.query.include_roles; From cec8cea01a1fd189464061785c1b6cb0dcff740b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Thu, 28 Apr 2022 22:31:21 +0300 Subject: [PATCH 20/38] introduce the purge endpoint closes #281 --- .../channels/#channel_id/messages/index.ts | 4 +- api/src/routes/channels/#channel_id/purge.ts | 75 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 api/src/routes/channels/#channel_id/purge.ts diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 34cc5ff8..a77b459b 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -30,6 +30,8 @@ export function isTextChannel(type: ChannelType): boolean { case ChannelType.GUILD_VOICE: case ChannelType.GUILD_STAGE_VOICE: case ChannelType.GUILD_CATEGORY: + case ChannelType.GUILD_FORUM: + case ChannelType.DIRECTORY: throw new HTTPError("not a text channel", 400); case ChannelType.DM: case ChannelType.GROUP_DM: @@ -155,7 +157,7 @@ const messageUpload = multer({ // https://discord.com/developers/docs/resources/channel#create-message // TODO: text channel slowdown // TODO: trim and replace message content and every embed field -// TODO: check allowed_mentions +// TODO: only dispatch mentions denoted in allowed_mentions // Send message router.post( diff --git a/api/src/routes/channels/#channel_id/purge.ts b/api/src/routes/channels/#channel_id/purge.ts new file mode 100644 index 00000000..e66034e4 --- /dev/null +++ b/api/src/routes/channels/#channel_id/purge.ts @@ -0,0 +1,75 @@ +import { HTTPError } from "lambert-server"; +import { route } from "@fosscord/api"; +import { isTextChannel } from "./messages"; +import { FindManyOptions, Between } from "typeorm"; +import { + Attachment, + Channel, + Config, + Embed, + DiscordApiErrors, + emitEvent, + FosscordApiErrors, + getPermission, + getRights, + Message, + MessageDeleteBulkEvent, + Snowflake, + uploadFile +} from "@fosscord/util"; +import { Router, Response, Request } from "express"; +import multer from "multer"; +import { handleMessage, postHandleMessage } from "@fosscord/api"; + +const router: Router = Router(); + +export default router; + +export interface PurgeSchema { + before: string; + after: string +} + +// TODO: should users be able to bulk delete messages or only bots? +// TODO: should this request fail, if you provide messages older than 14 days/invalid ids? +// https://discord.com/developers/docs/resources/channel#bulk-delete-messages +router.post("/", route({ body: "PurgeSchema", right: "SELF_DELETE_MESSAGES" }), async (req: Request, res: Response) => { + const { channel_id } = req.params; + const channel = await Channel.findOneOrFail({ id: channel_id }); + + if (!channel.guild_id) throw new HTTPError("Can't purge dm channels", 400); + isTextChannel(channel.type); + + const rights = await getRights(req.user_id); + if (!rights.has("MANAGE_MESSAGES")) { + const permissions = await getPermission(req.user_id, channel.guild_id, channel_id); + permissions.hasThrow("MANAGE_MESSAGES"); + permissions.hasThrow("MANAGE_CHANNELS"); + } + + const { before, after } = req.body as PurgeSchema; + + // TODO: send the deletion event bite-by-bite to prevent client stress + var query: FindManyOptions & { where: { id?: any; }; } = { + order: { id: "ASC" }, + // take: limit, + where: { + channel_id, + id: Between(after, before), // the right way around + }, + relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"] + }; + + const messages = await Message.find(query); + const endpoint = Config.get().cdn.endpointPublic; + + await Message.delete(messages.map((x) => ({ id: x }))); + + await emitEvent({ + event: "MESSAGE_DELETE_BULK", + channel_id, + data: { ids: messages.map(x => x.id), channel_id, guild_id: channel.guild_id } + } as MessageDeleteBulkEvent); + + res.sendStatus(204); +}); From 6950166ee86673ddc4880c588c15dee6a2f0e9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sat, 30 Apr 2022 00:39:44 +0300 Subject: [PATCH 21/38] patch for missing router schema, and also add purge route self-deletion checks --- api/src/routes/channels/#channel_id/purge.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/api/src/routes/channels/#channel_id/purge.ts b/api/src/routes/channels/#channel_id/purge.ts index e66034e4..8a87c379 100644 --- a/api/src/routes/channels/#channel_id/purge.ts +++ b/api/src/routes/channels/#channel_id/purge.ts @@ -1,7 +1,7 @@ import { HTTPError } from "lambert-server"; import { route } from "@fosscord/api"; import { isTextChannel } from "./messages"; -import { FindManyOptions, Between } from "typeorm"; +import { FindManyOptions, Between, Not } from "typeorm"; import { Attachment, Channel, @@ -33,7 +33,7 @@ export interface PurgeSchema { // TODO: should users be able to bulk delete messages or only bots? // TODO: should this request fail, if you provide messages older than 14 days/invalid ids? // https://discord.com/developers/docs/resources/channel#bulk-delete-messages -router.post("/", route({ body: "PurgeSchema", right: "SELF_DELETE_MESSAGES" }), async (req: Request, res: Response) => { +router.post("/", route({ /*body: "PurgeSchema",*/ }), async (req: Request, res: Response) => { const { channel_id } = req.params; const channel = await Channel.findOneOrFail({ id: channel_id }); @@ -50,18 +50,27 @@ router.post("/", route({ body: "PurgeSchema", right: "SELF_DELETE_MESSAGES" }), const { before, after } = req.body as PurgeSchema; // TODO: send the deletion event bite-by-bite to prevent client stress + var query: FindManyOptions & { where: { id?: any; }; } = { order: { id: "ASC" }, // take: limit, where: { channel_id, id: Between(after, before), // the right way around + author_id: rights.has("SELF_DELETE_MESSAGES") ? undefined : Not(req.user_id) + // if you lack the right of self-deletion, you can't delete your own messages, even in purges }, relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"] }; + const messages = await Message.find(query); const endpoint = Config.get().cdn.endpointPublic; + + if (messages.length == 0) { + res.sendStatus(304); + return; + } await Message.delete(messages.map((x) => ({ id: x }))); From 9711f9507224906c42ce167f7cc9f4c057777fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sat, 30 Apr 2022 07:44:44 +0300 Subject: [PATCH 22/38] messages before/after soundness check --- .../routes/channels/#channel_id/messages/index.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index a77b459b..9a56106a 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -11,6 +11,7 @@ import { getRights, Message, MessageCreateEvent, + Snowflake, uploadFile, Member } from "@fosscord/util"; @@ -86,7 +87,7 @@ router.get("/", async (req: Request, res: Response) => { const before = req.query.before ? `${req.query.before}` : undefined; const after = req.query.after ? `${req.query.after}` : undefined; const limit = Number(req.query.limit) || 50; - if (limit < 1 || limit > 100) throw new HTTPError("limit must be between 1 and 100"); + if (limit < 1 || limit > 100) throw new HTTPError("limit must be between 1 and 100", 422); var halfLimit = Math.floor(limit / 2); @@ -100,9 +101,16 @@ router.get("/", async (req: Request, res: Response) => { where: { channel_id }, relations: ["author", "webhook", "application", "mentions", "mention_roles", "mention_channels", "sticker_items", "attachments"] }; + - if (after) query.where.id = MoreThan(after); - else if (before) query.where.id = LessThan(before); + if (after) { + if (after > new Snowflake()) return res.status(422); + query.where.id = MoreThan(after); + } + else if (before) { + if (before < req.params.channel_id) return res.status(422); + query.where.id = LessThan(before); + } else if (around) { query.where.id = [ MoreThan((BigInt(around) - BigInt(halfLimit)).toString()), @@ -243,3 +251,4 @@ router.post( return res.json(message); } ); + From c9272ffd002bbbf006128dccd7e394e3d26ed17b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Fri, 6 May 2022 18:23:52 +0300 Subject: [PATCH 23/38] Remove more WAAO references --- api/client_test/index.html | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/api/client_test/index.html b/api/client_test/index.html index 39ff346d..b438b492 100644 --- a/api/client_test/index.html +++ b/api/client_test/index.html @@ -24,20 +24,20 @@ ASSET_ENDPOINT: "", MEDIA_PROXY_ENDPOINT: "https://media.discordapp.net", WIDGET_ENDPOINT: `//${location.host}/widget`, - INVITE_HOST: `${location.host}/invite`, - GUILD_TEMPLATE_HOST: "discord.new", - GIFT_CODE_HOST: "discord.gift", + INVITE_HOST: `${location.hostname}/invite`, + GUILD_TEMPLATE_HOST: "${location.host}", + GIFT_CODE_HOST: "${location.hostname}", RELEASE_CHANNEL: "stable", MARKETING_ENDPOINT: "//discord.com", BRAINTREE_KEY: "production_5st77rrc_49pp2rp4phym7387", STRIPE_KEY: "pk_live_CUQtlpQUF0vufWpnpUmQvcdi", NETWORKING_ENDPOINT: "//router.discordapp.net", - RTC_LATENCY_ENDPOINT: "//latency.discord.media/rtc", + RTC_LATENCY_ENDPOINT: "//${location.hostname}/rtc", PROJECT_ENV: "production", REMOTE_AUTH_ENDPOINT: "//localhost:3020", SENTRY_TAGS: { buildId: "75e36d9", buildType: "normal" }, - MIGRATION_SOURCE_ORIGIN: "https://discordapp.com", - MIGRATION_DESTINATION_ORIGIN: "https://discord.com", + MIGRATION_SOURCE_ORIGIN: "https://${location.hostname}", + MIGRATION_DESTINATION_ORIGIN: "https://${location.hostname}", HTML_TIMESTAMP: Date.now(), ALGOLIA_KEY: "aca0d7082e4e63af5ba5917d5e96bed0" }; From 69b5bb96ae2539971db5fac3bb8dec858dd455e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Tue, 10 May 2022 19:43:28 +0300 Subject: [PATCH 24/38] more event types --- util/src/entities/AuditLog.ts | 58 ++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index 4b81ed6a..be26374b 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -4,41 +4,91 @@ import { ChannelPermissionOverwrite } from "./Channel"; import { User } from "./User"; export enum AuditLogEvents { - GUILD_UPDATE = 1, - CHANNEL_CREATE = 10, + // guild level + GUILD_UPDATE = 1, + GUILD_IMPORT = 2, + GUILD_EXPORTED = 3, + GUILD_ARCHIVE = 4, + GUILD_UNARCHIVE = 5, + // join-leave + USER_JOIN = 6, + USER_LEAVE = 7, + // channels + CHANNEL_CREATE = 10, CHANNEL_UPDATE = 11, CHANNEL_DELETE = 12, - CHANNEL_OVERWRITE_CREATE = 13, + // permission overrides + CHANNEL_OVERWRITE_CREATE = 13, CHANNEL_OVERWRITE_UPDATE = 14, CHANNEL_OVERWRITE_DELETE = 15, - MEMBER_KICK = 20, + // kick and ban + MEMBER_KICK = 20, MEMBER_PRUNE = 21, MEMBER_BAN_ADD = 22, MEMBER_BAN_REMOVE = 23, + // member updates MEMBER_UPDATE = 24, MEMBER_ROLE_UPDATE = 25, MEMBER_MOVE = 26, MEMBER_DISCONNECT = 27, BOT_ADD = 28, + // roles ROLE_CREATE = 30, ROLE_UPDATE = 31, ROLE_DELETE = 32, + ROLE_SWAP = 33, + // invites INVITE_CREATE = 40, INVITE_UPDATE = 41, INVITE_DELETE = 42, + // webhooks WEBHOOK_CREATE = 50, WEBHOOK_UPDATE = 51, WEBHOOK_DELETE = 52, + WEBHOOK_SWAP = 53, + // custom emojis EMOJI_CREATE = 60, EMOJI_UPDATE = 61, EMOJI_DELETE = 62, + EMOJI_SWAP = 63, + // deletion + MESSAGE_CREATE = 70, // messages sent using non-primary seat of the user only + MESSAGE_EDIT = 71, // non-self edits only MESSAGE_DELETE = 72, MESSAGE_BULK_DELETE = 73, + // pinning MESSAGE_PIN = 74, MESSAGE_UNPIN = 75, + // integrations INTEGRATION_CREATE = 80, INTEGRATION_UPDATE = 81, INTEGRATION_DELETE = 82, + // stage actions + STAGE_INSTANCE_CREATE = 83, + STAGE_INSTANCE_UPDATE = 84, + STAGE_INSTANCE_DELETE = 85, + // stickers + STICKER_CREATE = 90, + STICKER_UPDATE = 91, + STICKER_DELETE = 92, + STICKER_SWAP = 93, + // threads + THREAD_CREATE = 110, + THREAD_UPDATE = 111, + THREAD_DELETE = 112, + // application commands + APPLICATION_COMMAND_PERMISSION_UPDATE = 121, + // automod + POLICY_CREATE = 140, + POLICY_UPDATE = 141, + // instance policies affecting the guild + GUILD_CROPPED_BY_POLICIES = 216, + // message moves + IN_GUILD_MESSAGE_MOVE = 223, + CROSS_GUILD_MESSAGE_MOVE = 224, + // message routing + ROUTE_CREATE = 225, + ROUTE_UPDATE = 226, } @Entity("audit_logs") From cf4a0fd5f35d9082e011a42a50d60cbd96e9c998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 11 May 2022 00:40:00 +0300 Subject: [PATCH 25/38] add more message flags --- util/src/util/MessageFlags.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/util/src/util/MessageFlags.ts b/util/src/util/MessageFlags.ts index c76be4c8..d8e7bc4f 100644 --- a/util/src/util/MessageFlags.ts +++ b/util/src/util/MessageFlags.ts @@ -1,5 +1,5 @@ -// https://github.com/discordjs/discord.js/blob/master/src/util/MessageFlags.js -// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah +// based on https://github.com/discordjs/discord.js/blob/master/src/util/MessageFlags.js +// Apache License Version 2.0 Copyright 2015 - 2021 Amish Shah, 2022 Erkin Alp Güney import { BitField } from "./BitField"; @@ -8,7 +8,12 @@ export class MessageFlags extends BitField { CROSSPOSTED: BigInt(1) << BigInt(0), IS_CROSSPOST: BigInt(1) << BigInt(1), SUPPRESS_EMBEDS: BigInt(1) << BigInt(2), - SOURCE_MESSAGE_DELETED: BigInt(1) << BigInt(3), + // SOURCE_MESSAGE_DELETED: BigInt(1) << BigInt(3), // fosscord will delete them from destination too, making this redundant URGENT: BigInt(1) << BigInt(4), + // HAS_THREAD: BigInt(1) << BigInt(5) // does not apply to fosscord due to infrastructural differences + PRIVATE_ROUTE: 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 + // FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: BigInt(1) << BigInt(8) + SCRIPT_WAIT: BigInt(1) << BigInt(24) // waiting for the self command to complete }; } From 4ee6b0cf7abeee3af3659d2892d8e6a8066d4add Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 11 May 2022 00:43:57 +0300 Subject: [PATCH 26/38] Update MessageFlags.ts --- util/src/util/MessageFlags.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/src/util/MessageFlags.ts b/util/src/util/MessageFlags.ts index d8e7bc4f..b8f8c857 100644 --- a/util/src/util/MessageFlags.ts +++ b/util/src/util/MessageFlags.ts @@ -14,6 +14,7 @@ export class MessageFlags extends BitField { PRIVATE_ROUTE: 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 // FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: BigInt(1) << BigInt(8) - SCRIPT_WAIT: BigInt(1) << BigInt(24) // waiting for the self command to complete + SCRIPT_WAIT: BigInt(1) << BigInt(24), // waiting for the self command to complete + IMPORT_WAIT: BigInt(1) << BigInt(25), // last message of a channel bulk guild import, waiting for the rest of the channel to be backfilled }; } From 8f5d673b840743c5762e7c90472a839ae63fa87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 11 May 2022 00:44:19 +0300 Subject: [PATCH 27/38] Update MessageFlags.ts --- util/src/util/MessageFlags.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/util/MessageFlags.ts b/util/src/util/MessageFlags.ts index b8f8c857..b59295c4 100644 --- a/util/src/util/MessageFlags.ts +++ b/util/src/util/MessageFlags.ts @@ -15,6 +15,6 @@ export class MessageFlags extends BitField { INTERACTION_WAIT: BigInt(1) << BigInt(7), // discord.com calls this LOADING // FAILED_TO_MENTION_SOME_ROLES_IN_THREAD: BigInt(1) << BigInt(8) SCRIPT_WAIT: BigInt(1) << BigInt(24), // waiting for the self command to complete - IMPORT_WAIT: BigInt(1) << BigInt(25), // last message of a channel bulk guild import, waiting for the rest of the channel to be backfilled + IMPORT_WAIT: BigInt(1) << BigInt(25), // latest message of a bulk import, waiting for the rest of the channel to be backfilled }; } From 0101e62075d5c24207598f74734706475394c8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 00:16:50 +0300 Subject: [PATCH 28/38] Update purge.ts --- api/src/routes/channels/#channel_id/purge.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/routes/channels/#channel_id/purge.ts b/api/src/routes/channels/#channel_id/purge.ts index 8a87c379..28b52b50 100644 --- a/api/src/routes/channels/#channel_id/purge.ts +++ b/api/src/routes/channels/#channel_id/purge.ts @@ -30,9 +30,9 @@ export interface PurgeSchema { after: string } -// TODO: should users be able to bulk delete messages or only bots? -// TODO: should this request fail, if you provide messages older than 14 days/invalid ids? -// https://discord.com/developers/docs/resources/channel#bulk-delete-messages +/** +TODO: apply the delete bit by bit to prevent client and database stress +**/ router.post("/", route({ /*body: "PurgeSchema",*/ }), async (req: Request, res: Response) => { const { channel_id } = req.params; const channel = await Channel.findOneOrFail({ id: channel_id }); From c5709acb9ee2400668c5abdb462153b9ddf22cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 00:23:17 +0300 Subject: [PATCH 29/38] Update bulk-delete.ts --- .../#channel_id/messages/bulk-delete.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/api/src/routes/channels/#channel_id/messages/bulk-delete.ts b/api/src/routes/channels/#channel_id/messages/bulk-delete.ts index 7a711cb0..6eacf249 100644 --- a/api/src/routes/channels/#channel_id/messages/bulk-delete.ts +++ b/api/src/routes/channels/#channel_id/messages/bulk-delete.ts @@ -1,5 +1,5 @@ import { Router, Response, Request } from "express"; -import { Channel, Config, emitEvent, getPermission, MessageDeleteBulkEvent, Message } from "@fosscord/util"; +import { Channel, Config, emitEvent, getPermission, getRights, MessageDeleteBulkEvent, Message } from "@fosscord/util"; import { HTTPError } from "lambert-server"; import { route } from "@fosscord/api"; import { In } from "typeorm"; @@ -12,22 +12,28 @@ export interface BulkDeleteSchema { messages: string[]; } -// TODO: should users be able to bulk delete messages or only bots? -// TODO: should this request fail, if you provide messages older than 14 days/invalid ids? +// should users be able to bulk delete messages or only bots? ANSWER: all users +// should this request fail, if you provide messages older than 14 days/invalid ids? ANSWER: NO // https://discord.com/developers/docs/resources/channel#bulk-delete-messages router.post("/", route({ body: "BulkDeleteSchema" }), async (req: Request, res: Response) => { const { channel_id } = req.params; const channel = await Channel.findOneOrFail({ id: channel_id }); if (!channel.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400); + const rights = await getRights(req.user_id); + rights.hasThrow("SELF_DELETE_MESSAGES"); + + let superuser = rights.has("MANAGE_MESSAGES"); const permission = await getPermission(req.user_id, channel?.guild_id, channel_id); - permission.hasThrow("MANAGE_MESSAGES"); - + const { maxBulkDelete } = Config.get().limits.message; const { messages } = req.body as { messages: string[] }; - if (messages.length < 2) throw new HTTPError("You must at least specify 2 messages to bulk delete"); - if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`); + if (messages.length === 0) throw new HTTPError("You must specify messages to bulk delete"); + if (!superuser) { + permission.hasThrow("MANAGE_MESSAGES"); + if (messages.length > maxBulkDelete) throw new HTTPError(`You cannot delete more than ${maxBulkDelete} messages`); + } await Message.delete(messages.map((x) => ({ id: x }))); From 41564c79561d68f00c37d3ca980bbd8e6405160d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 09:12:28 +0300 Subject: [PATCH 30/38] user groups --- util/src/entities/Group.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 util/src/entities/Group.ts diff --git a/util/src/entities/Group.ts b/util/src/entities/Group.ts new file mode 100644 index 00000000..b24d38cf --- /dev/null +++ b/util/src/entities/Group.ts @@ -0,0 +1,33 @@ +import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; + +import { BaseClass } from "./BaseClass"; + +@Entity("groups") +export class UserGroup extends BaseClass { + @Column({ nullable: true }) + parent?: BigInt; + + @Column() + color: number; + + @Column() + hoist: boolean; + + @Column() + mentionable: boolean; + + @Column() + name: string; + + @Column() + rights: BigInt; + + @Column() + position: number; + + @Column({ nullable: true }) + icon: BigInt; + + @Column({ nullable: true }) + unicode_emoji: BigInt; +} From 36ebef6eaf43c17c8b7d842946543c3c5896dd5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 16:28:57 +0300 Subject: [PATCH 31/38] Update AuditLog.ts --- util/src/entities/AuditLog.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/src/entities/AuditLog.ts b/util/src/entities/AuditLog.ts index be26374b..b003e7ba 100644 --- a/util/src/entities/AuditLog.ts +++ b/util/src/entities/AuditLog.ts @@ -81,8 +81,10 @@ export enum AuditLogEvents { // automod POLICY_CREATE = 140, POLICY_UPDATE = 141, + POLICY_DELETE = 142, + MESSAGE_BLOCKED_BY_POLICIES = 143, // in fosscord, blocked messages are stealth-dropped // instance policies affecting the guild - GUILD_CROPPED_BY_POLICIES = 216, + GUILD_AFFECTED_BY_POLICIES = 216, // message moves IN_GUILD_MESSAGE_MOVE = 223, CROSS_GUILD_MESSAGE_MOVE = 224, From 1653f2c50594fbb900fe4900d5f26174d16c14fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 17:43:30 +0300 Subject: [PATCH 32/38] Update index.ts --- .../channels/#channel_id/messages/index.ts | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/api/src/routes/channels/#channel_id/messages/index.ts b/api/src/routes/channels/#channel_id/messages/index.ts index 9a56106a..2d6a2977 100644 --- a/api/src/routes/channels/#channel_id/messages/index.ts +++ b/api/src/routes/channels/#channel_id/messages/index.ts @@ -71,7 +71,11 @@ export interface MessageCreateSchema { }; payload_json?: string; file?: any; - attachments?: any[]; //TODO we should create an interface for attachments + /** + TODO: we should create an interface for attachments + TODO: OpenWAAO<-->attachment-style metadata conversion + **/ + attachments?: any[]; sticker_ids?: string[]; } @@ -136,9 +140,12 @@ router.get("/", async (req: Request, res: Response) => { const uri = y.proxy_url.startsWith("http") ? y.proxy_url : `https://example.org${y.proxy_url}`; y.proxy_url = `${endpoint == null ? "" : endpoint}${new URL(uri).pathname}`; }); - - //Some clients ( discord.js ) only check if a property exists within the response, - //which causes erorrs when, say, the `application` property is `null`. + + /** + Some clients ( discord.js ) only check if a property exists within the response, + which causes erorrs when, say, the `application` property is `null`. + **/ + for (var curr in x) { if (x[curr] === null) delete x[curr]; @@ -158,15 +165,14 @@ const messageUpload = multer({ }, storage: multer.memoryStorage() }); // max upload 50 mb +/** + TODO: dynamically change limit of MessageCreateSchema with config -// TODO: dynamically change limit of MessageCreateSchema with config -// TODO: check: sum of all characters in an embed structure must not exceed instance limits - -// https://discord.com/developers/docs/resources/channel#create-message -// TODO: text channel slowdown -// TODO: trim and replace message content and every embed field -// TODO: only dispatch mentions denoted in allowed_mentions - + https://discord.com/developers/docs/resources/channel#create-message + TODO: text channel slowdown (per-user and across-users) + Q: trim and replace message content and every embed field A: NO, given this cannot be implemented in E2EE channels + TODO: only dispatch notifications for mentions denoted in allowed_mentions +**/ // Send message router.post( "/", @@ -233,8 +239,6 @@ router.post( }) ); } - - //Fix for the client bug delete message.member From 215df37be52b46fd3d5eb48e58e8d77cd9091c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 17:52:17 +0300 Subject: [PATCH 33/38] Extended settings --- util/src/entities/User.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index a5c4c136..558a4190 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -163,6 +163,10 @@ export class User extends BaseClass { @Column({ type: "simple-json", select: false }) settings: UserSettings; + + // workaround to prevent fossord-unaware clients from deleting settings not used by them + @Column({ type: "simple-json", select: false }) + extended_settings: UserSettings; @Column({ type: "simple-json" }) notes: { [key: string]: string }; //key is ID of user @@ -273,6 +277,7 @@ export class User extends BaseClass { valid_tokens_since: new Date(), }, settings: { ...defaultSettings, locale: language }, + extended_settings: {}, fingerprints: [], notes: {}, }); From 1573edb505fd70238239218fac04029f496c33e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sun, 15 May 2022 17:53:24 +0300 Subject: [PATCH 34/38] Extended settings will not be type checked --- util/src/entities/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts index 558a4190..9b1c494e 100644 --- a/util/src/entities/User.ts +++ b/util/src/entities/User.ts @@ -166,7 +166,7 @@ export class User extends BaseClass { // workaround to prevent fossord-unaware clients from deleting settings not used by them @Column({ type: "simple-json", select: false }) - extended_settings: UserSettings; + extended_settings: string; @Column({ type: "simple-json" }) notes: { [key: string]: string }; //key is ID of user From 3b57829cba9d8d2b347ae06b143609f561b10f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Thu, 19 May 2022 23:11:46 +0300 Subject: [PATCH 35/38] add guild policies --- util/src/util/Intents.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/src/util/Intents.ts b/util/src/util/Intents.ts index d9a60e4a..1e840b76 100644 --- a/util/src/util/Intents.ts +++ b/util/src/util/Intents.ts @@ -18,6 +18,8 @@ export class Intents extends BitField { DIRECT_MESSAGE_REACTIONS: BigInt(1) << BigInt(13), // DM or orphan channel message reactions DIRECT_MESSAGE_TYPING: BigInt(1) << BigInt(14), // DM typing notifications GUILD_MESSAGES_CONTENT: BigInt(1) << BigInt(15), // guild message content + GUILD_POLICIES: BigInt(1) << BigInt(20), // guild policies + GUILD_POLICY_EXECUTION: BigInt(1) << BigInt(21), // guild policy execution LIVE_MESSAGE_COMPOSITION: BigInt(1) << BigInt(32), // allow composing messages using the gateway GUILD_ROUTES: BigInt(1) << BigInt(41), // message routes affecting the guild DIRECT_MESSAGES_THREADS: BigInt(1) << BigInt(42), // direct message threads From efc670172e04251d1720cb9f5968f9031a979f09 Mon Sep 17 00:00:00 2001 From: FlumeZ <35439776+xFlum3@users.noreply.github.com> Date: Thu, 26 May 2022 22:56:51 +0300 Subject: [PATCH 36/38] Fixed Broken Translates and finished the file Enjoy :) --- api/locales/he/common.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/api/locales/he/common.json b/api/locales/he/common.json index 9e72e941..4101eac4 100644 --- a/api/locales/he/common.json +++ b/api/locales/he/common.json @@ -1,18 +1,18 @@ { "field": { - "BASE_TYPE_REQUIRED": "This field is required", - "BASE_TYPE_STRING": "This field must be a string", - "BASE_TYPE_NUMBER": "This field must be a number", - "BASE_TYPE_BIGINT": "This field must be a bigint", - "BASE_TYPE_BOOLEAN": "This field must be a boolean", - "BASE_TYPE_CHOICES": "This field must be one of ({{types}})", - "BASE_TYPE_CLASS": "This field must be an instance of {{type}}", + "BASE_TYPE_REQUIRED": "שדה זה חובה", + "BASE_TYPE_STRING": "שדה זה חייב להיות כטקסט", + "BASE_TYPE_NUMBER": "שדה זה חייב להיות מספר", + "BASE_TYPE_BIGINT": "השדה הזה חייב להיות ביגינט", + "BASE_TYPE_BOOLEAN": "השדה הזה חייב להיות בוליאני", + "BASE_TYPE_CHOICES": "({{types}}) שדה זה חייב להיות אחד מ", + "BASE_TYPE_CLASS": "{{type}} מסוג instance שדה זה חייב להיות", "BASE_TYPE_OBJECT": "שדה זה חייב להיות אובייקט", "BASE_TYPE_ARRAY": "שדה זה חייב להיות מערך", - "UNKOWN_FIELD": "מפתח לא ידוע: {{key}}", - "BASE_TYPE_CONSTANT": "שדה זה להיות {{value}}", + "UNKOWN_FIELD": "{{key}} :מפתח לא ידוע", + "BASE_TYPE_CONSTANT": "{{value}} שדה זה חייב להיות", "EMAIL_TYPE_INVALID_EMAIL": "כתובת דואר אלקטרוני לא חוקית", - "DATE_TYPE_PARSE": "לא ניתן לנתח {{date}}. צריך להיות ISO8601", - "BASE_TYPE_BAD_LENGTH": "האורך חייב להיות בין {{length}}" + "DATE_TYPE_PARSE": "ISO8601 אמור להיות {{date}} לא ניתן לאתר", + "BASE_TYPE_BAD_LENGTH": "{{length}} האורך חייב להיות בין" } } From 474363f31d57187bf8508446e54f0a4b80dfb7d6 Mon Sep 17 00:00:00 2001 From: FlumeZ <35439776+xFlum3@users.noreply.github.com> Date: Thu, 26 May 2022 23:02:19 +0300 Subject: [PATCH 37/38] Finished file translation Auth.json :D --- api/locales/he/auth.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/api/locales/he/auth.json b/api/locales/he/auth.json index e19547a0..b7296868 100644 --- a/api/locales/he/auth.json +++ b/api/locales/he/auth.json @@ -1,16 +1,16 @@ { "login": { - "INVALID_LOGIN": "E-Mail or Phone not found", - "INVALID_PASSWORD": "Invalid Password", - "ACCOUNT_DISABLED": "This account is disabled" + "INVALID_LOGIN": "מייל או מספר טלפון לא נמצאים במאגר", + "INVALID_PASSWORD": "סיסמא שגויה", + "ACCOUNT_DISABLED": "משתמש זה חסום / מבוטל" }, "register": { - "REGISTRATION_DISABLED": "New user registration is disabled", - "INVITE_ONLY": "You must be invited to register", - "EMAIL_INVALID": "Invalid Email", - "EMAIL_ALREADY_REGISTERED": "Email is already registered", - "DATE_OF_BIRTH_UNDERAGE": "You need to be {{years}} years or older", - "CONSENT_REQUIRED": "You must agree to the Terms of Service and Privacy Policy.", - "USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another" + "REGISTRATION_DISABLED": "לא ניתן לאפשר רישום משתמשים חדשים", + "INVITE_ONLY": "עליך להיות מוזמן בכדי להרשם", + "EMAIL_INVALID": "מייל שגוי", + "EMAIL_ALREADY_REGISTERED": "מייל זה כבר רשום", + "DATE_OF_BIRTH_UNDERAGE": "{{years}} עלייך להיות מעל גיל", + "CONSENT_REQUIRED": ".עליך להסכים לתנאי השירות ולמדיניות הפרטיות", + "USERNAME_TOO_MANY_USERS": "ליותר מדי משתמשים יש שם משתמש זהה, אנא נסה אחר" } } From f420cfd36c8ed263487c9aac2209a3052eecc3a7 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 30 May 2022 23:28:23 +1000 Subject: [PATCH 38/38] Respect register_dateOfBirth_required = false --- api/src/routes/auth/register.ts | 2 +- util/src/entities/Config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/routes/auth/register.ts b/api/src/routes/auth/register.ts index cd1bcb72..94dd6502 100644 --- a/api/src/routes/auth/register.ts +++ b/api/src/routes/auth/register.ts @@ -128,7 +128,7 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re throw FieldErrors({ date_of_birth: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED") } }); - } else if (register.dateOfBirth.minimum) { + } else if (register.dateOfBirth.required && register.dateOfBirth.minimum) { const minimum = new Date(); minimum.setFullYear(minimum.getFullYear() - register.dateOfBirth.minimum); body.date_of_birth = new Date(body.date_of_birth as Date); diff --git a/util/src/entities/Config.ts b/util/src/entities/Config.ts index 8d29b387..063a4d4d 100644 --- a/util/src/entities/Config.ts +++ b/util/src/entities/Config.ts @@ -324,7 +324,7 @@ export const DefaultConfigOptions: ConfigValue = { // domains: fs.readFileSync(__dirname + "/blockedEmailDomains.txt", { encoding: "utf8" }).split("\n"), }, dateOfBirth: { - required: false, + required: true, minimum: 13, }, disabled: false,