From 64f0b1f1a16c89c7f47facf2d0a73400e377cbe2 Mon Sep 17 00:00:00 2001 From: Nobody Date: Wed, 9 Mar 2022 06:04:50 -0300 Subject: [PATCH 01/11] fix(api): working replies in dm channels --- api/src/util/handlers/Message.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/src/util/handlers/Message.ts b/api/src/util/handlers/Message.ts index 21664368..2d9f7032 100644 --- a/api/src/util/handlers/Message.ts +++ b/api/src/util/handlers/Message.ts @@ -82,10 +82,12 @@ export async function handleMessage(opts: MessageOptions): Promise { if (opts.message_reference) { permission.hasThrow("READ_MESSAGE_HISTORY"); // code below has to be redone when we add custom message routing and cross-channel replies - const guild = await Guild.findOneOrFail({ id: channel.guild_id }); - if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { - if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild"); - if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel"); + if (message.guild_id !== null) { + const guild = await Guild.findOneOrFail({ id: channel.guild_id }); + if (!guild.features.includes("CROSS_CHANNEL_REPLIES")) { + if (opts.message_reference.guild_id !== channel.guild_id) throw new HTTPError("You can only reference messages from this guild"); + if (opts.message_reference.channel_id !== opts.channel_id) throw new HTTPError("You can only reference messages from this channel"); + } } // TODO: should be checked if the referenced message exists? // @ts-ignore From 134d746ebf41fe0682d8525b20c10bf8bcde25f8 Mon Sep 17 00:00:00 2001 From: Nobody <17956512+n0bodysec@users.noreply.github.com> Date: Wed, 9 Mar 2022 11:35:19 -0300 Subject: [PATCH 02/11] fix(api): add vanity urls creation/update (#665) * fix(api): add vanity urls creation/update * refactor(api): multiple vanity urls Old vanty urls will not be updated, instead a new one will be created. * feat(api): add ALIASABLE_NAMES feature Reference: https://github.com/fosscord/fosscord-server/issues/407 --- api/src/routes/guilds/#guild_id/vanity-url.ts | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/api/src/routes/guilds/#guild_id/vanity-url.ts b/api/src/routes/guilds/#guild_id/vanity-url.ts index 63173345..29cd25e2 100644 --- a/api/src/routes/guilds/#guild_id/vanity-url.ts +++ b/api/src/routes/guilds/#guild_id/vanity-url.ts @@ -9,11 +9,19 @@ const InviteRegex = /\W/g; router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { const { guild_id } = req.params; + const guild = await Guild.findOneOrFail({ id: guild_id }); - const invite = await Invite.findOne({ where: { guild_id: guild_id, vanity_url: true } }); - if (!invite) return res.json({ code: null }); + if (!guild.features.includes("ALIASABLE_NAMES")) { + const invite = await Invite.findOne({ where: { guild_id: guild_id, vanity_url: true } }); + if (!invite) return res.json({ code: null }); - return res.json({ code: invite.code, uses: invite.uses }); + return res.json({ code: invite.code, uses: invite.uses }); + } else { + const invite = await Invite.find({ where: { guild_id: guild_id, vanity_url: true } }); + if (!invite || invite.length == 0) return res.json({ code: null }); + + return res.json(invite.map((x) => ({ code: x.code, uses: x.uses }))); + } }); export interface VanityUrlSchema { @@ -24,18 +32,33 @@ export interface VanityUrlSchema { code?: string; } -// TODO: check if guild is elgible for vanity url router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => { const { guild_id } = req.params; const body = req.body as VanityUrlSchema; const code = body.code?.replace(InviteRegex, ""); + const guild = await Guild.findOneOrFail({ id: guild_id }); + if (!guild.features.includes("VANITY_URL")) throw new HTTPError("Your guild doesn't support vanity urls"); + + if (!code || code.length === 0) throw new HTTPError("Code cannot be null or empty"); + const invite = await Invite.findOne({ code }); if (invite) throw new HTTPError("Invite already exists"); const { id } = await Channel.findOneOrFail({ guild_id, type: ChannelType.GUILD_TEXT }); - await Invite.update({ vanity_url: true, guild_id }, { code: code, channel_id: id }); + await new Invite({ + vanity_url: true, + code: code, + temporary: false, + uses: 0, + max_uses: 0, + max_age: 0, + created_at: new Date(), + expires_at: new Date(), + guild_id: guild_id, + channel_id: id + }).save(); return res.json({ code: code }); }); From 771e9c1cae0d59f5e443c38cbbb88eaa6cab25e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 9 Mar 2022 17:41:34 +0300 Subject: [PATCH 03/11] Update Rights.ts --- util/src/util/Rights.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/src/util/Rights.ts b/util/src/util/Rights.ts index 9a99d393..db5384d0 100644 --- a/util/src/util/Rights.ts +++ b/util/src/util/Rights.ts @@ -65,6 +65,8 @@ export class Rights extends BitField { // inverts the presence confidentiality default (OPERATOR's presence is not routed by default, others' are) for a given user SELF_ADD_DISCOVERABLE: BitFlag(36), // can mark discoverable guilds that they have permissions to mark as discoverable MANAGE_GUILD_DIRECTORY: BitFlag(37), // can change anything in the primary guild directory + POGGERS: BitFlag(38), // can send confetti, screenshake, random user mention (@someone) + USE_ACHIEVEMENTS: BitFlag(39), // can use achievements and cheers INITIATE_INTERACTIONS: BitFlag(40), // can initiate interactions RESPOND_TO_INTERACTIONS: BitFlag(41), // can respond to interactions SEND_BACKDATED_EVENTS: BitFlag(42), // can send backdated events From aa7a5b126be9e23a230d3ec8315ff5fb663df4de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Mon, 14 Mar 2022 19:45:25 +0300 Subject: [PATCH 04/11] close issue #684 --- api/assets/openapi.json | 4 ++-- api/assets/schemas.json | 4 ++-- util/src/entities/ConnectedAccount.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/api/assets/openapi.json b/api/assets/openapi.json index 03550323..8e2a54cf 100644 --- a/api/assets/openapi.json +++ b/api/assets/openapi.json @@ -2960,7 +2960,7 @@ "type": { "type": "string" }, - "verifie": { + "verified": { "type": "boolean" }, "visibility": { @@ -2980,7 +2980,7 @@ "type", "user", "user_id", - "verifie", + "verified", "visibility" ] }, diff --git a/api/assets/schemas.json b/api/assets/schemas.json index d531df21..441752ad 100644 --- a/api/assets/schemas.json +++ b/api/assets/schemas.json @@ -355,11 +355,11 @@ "type": { "type": "string" }, - "verifie": { + "verified": { "type": "boolean" } }, - "required": ["name", "type", "verifie"] + "required": ["name", "type", "verified"] } }, "$schema": "http://json-schema.org/draft-07/schema#" diff --git a/util/src/entities/ConnectedAccount.ts b/util/src/entities/ConnectedAccount.ts index b8aa2889..09ae30ab 100644 --- a/util/src/entities/ConnectedAccount.ts +++ b/util/src/entities/ConnectedAccount.ts @@ -2,7 +2,7 @@ import { Column, Entity, JoinColumn, ManyToOne, RelationId } from "typeorm"; import { BaseClass } from "./BaseClass"; import { User } from "./User"; -export interface PublicConnectedAccount extends Pick {} +export interface PublicConnectedAccount extends Pick {} @Entity("connected_accounts") export class ConnectedAccount extends BaseClass { @@ -35,7 +35,7 @@ export class ConnectedAccount extends BaseClass { type: string; @Column() - verifie: boolean; + verified: boolean; @Column({ select: false }) visibility: number; From 44ddb97e088c08b799ac27c70d1f0418bb59d4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Sat, 19 Mar 2022 15:17:11 +0300 Subject: [PATCH 05/11] add the necessary types for server assisted selfbotting --- util/src/interfaces/Interaction.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/src/interfaces/Interaction.ts b/util/src/interfaces/Interaction.ts index 3cafb2d5..5d3aae24 100644 --- a/util/src/interfaces/Interaction.ts +++ b/util/src/interfaces/Interaction.ts @@ -12,11 +12,13 @@ export interface Interaction { } export enum InteractionType { + SelfCommand = 0, Ping = 1, ApplicationCommand = 2, } export enum InteractionResponseType { + SelfCommandResponse = 0, Pong = 1, Acknowledge = 2, ChannelMessage = 3, From e20fd0454725d57d3fb1e7d7faca381991a7a6a0 Mon Sep 17 00:00:00 2001 From: SpacingBat3 Date: Tue, 1 Mar 2022 23:33:19 +0100 Subject: [PATCH 06/11] Fix `servers[0].url` in `openapi.json`. A tiny fix within `openapi.json`, that changes the URL in servers property to the correct ones. --- api/assets/openapi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/assets/openapi.json b/api/assets/openapi.json index 8e2a54cf..a8a657b2 100644 --- a/api/assets/openapi.json +++ b/api/assets/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.0", "servers": [ { - "url": "https://api.fosscord.com/v{version}", + "url": "https://api.fosscord.com/api/v{version}", "description": "Official fosscord instance", "variables": { "version": { From 1b087b134afce22d97ed694fa15d1f684b16378e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 23 Mar 2022 22:18:04 +0300 Subject: [PATCH 07/11] A few channels on channels - Added a field for retention policies (progress towards #164) - Allowed note to self channels - Added the UNHANDLED type --- util/src/entities/Channel.ts | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index 1cc4a538..f6b1a6b4 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -14,12 +14,12 @@ import { Webhook } from "./Webhook"; import { DmChannelDTO } from "../dtos"; export enum ChannelType { - GUILD_TEXT = 0, // a text channel within a server + GUILD_TEXT = 0, // a text channel within a guild DM = 1, // a direct message between users - GUILD_VOICE = 2, // a voice channel within a server + GUILD_VOICE = 2, // a voice channel within a guild GROUP_DM = 3, // a direct message between multiple users - GUILD_CATEGORY = 4, // an organizational category that contains up to 50 channels - GUILD_NEWS = 5, // a channel that users can follow and crosspost into their own server + GUILD_CATEGORY = 4, // an organizational category that contains zero or more channels + GUILD_NEWS = 5, // a channel that users can follow and crosspost into a guild or route GUILD_STORE = 6, // a channel in which game developers can sell their game on Discord ENCRYPTED = 7, // end-to-end encrypted channel ENCRYPTED_THREAD = 8, // end-to-end encrypted thread channel @@ -72,7 +72,7 @@ export class Channel extends BaseClass { @ManyToOne(() => Channel) parent?: Channel; - // only for group dms + // for group DMs and owned custom channel types @Column({ nullable: true }) @RelationId((channel: Channel) => channel.owner) owner_id: string; @@ -117,6 +117,9 @@ export class Channel extends BaseClass { }) invites?: Invite[]; + @Column({ nullable: true }) + retention_policy_id?: string; + @OneToMany(() => Message, (message: Message) => message.channel, { cascade: true, orphanedRowAction: "delete", @@ -140,7 +143,7 @@ export class Channel extends BaseClass { orphanedRowAction: "delete", }) webhooks?: Webhook[]; - + // TODO: DM channel static async createChannel( channel: Partial, @@ -182,6 +185,7 @@ export class Channel extends BaseClass { switch (channel.type) { case ChannelType.GUILD_TEXT: + case ChannelType.GUILD_NEWS: case ChannelType.GUILD_VOICE: if (channel.parent_id && !opts?.skipExistsCheck) { const exists = await Channel.findOneOrFail({ id: channel.parent_id }); @@ -191,25 +195,24 @@ export class Channel extends BaseClass { } break; case ChannelType.GUILD_CATEGORY: + case ChannelType.UNHANDLED: break; case ChannelType.DM: case ChannelType.GROUP_DM: throw new HTTPError("You can't create a dm channel in a guild"); - // TODO: check if guild is community server case ChannelType.GUILD_STORE: - case ChannelType.GUILD_NEWS: default: throw new HTTPError("Not yet supported"); } if (!channel.permission_overwrites) channel.permission_overwrites = []; - // TODO: auto generate position + // TODO: eagerly auto generate position of all guild channels channel = { ...channel, ...(!opts?.keepId && { id: Snowflake.generate() }), created_at: new Date(), - position: channel.position || 0, + position: ((channel.type === ChannelType.UNHANDLED) || channel.position) || 0, }; await Promise.all([ @@ -231,11 +234,13 @@ export class Channel extends BaseClass { const otherRecipientsUsers = await User.find({ where: recipients.map((x) => ({ id: x })) }); // TODO: check config for max number of recipients + /** if you want to disallow note to self channels, uncomment the conditional below if (otherRecipientsUsers.length !== recipients.length) { throw new HTTPError("Recipient/s not found"); } + **/ - const type = recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM; + const type = recipients.length > 1 ? ChannelType.DM : ChannelType.GROUP_DM; let channel = null; @@ -288,7 +293,8 @@ export class Channel extends BaseClass { await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id }); } - return channel_dto.excludedRecipients([creator_user_id]); + if (recipients.length === 1) return channel_dto; + else return channel_dto.excludedRecipients([creator_user_id]); } static async removeRecipientFromChannel(channel: Channel, user_id: string) { @@ -354,4 +360,5 @@ export interface ChannelPermissionOverwrite { export enum ChannelPermissionOverwriteType { role = 0, member = 1, + group = 2, } From 67011ccc3d4147e47cbc1f728deb73e3e45641c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkin=20Alp=20G=C3=BCney?= Date: Wed, 23 Mar 2022 22:25:46 +0300 Subject: [PATCH 08/11] fix the conditional --- util/src/entities/Channel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts index f6b1a6b4..08be1e02 100644 --- a/util/src/entities/Channel.ts +++ b/util/src/entities/Channel.ts @@ -212,7 +212,7 @@ export class Channel extends BaseClass { ...channel, ...(!opts?.keepId && { id: Snowflake.generate() }), created_at: new Date(), - position: ((channel.type === ChannelType.UNHANDLED) || channel.position) || 0, + position: (channel.type === ChannelType.UNHANDLED ? 0 : channel.position) || 0, }; await Promise.all([ From 850ddcf3940a9d41ec5851b01f2899c82f0f6644 Mon Sep 17 00:00:00 2001 From: Luna Alfien Date: Sun, 16 Jan 2022 22:20:33 -0800 Subject: [PATCH 09/11] Create fosscord-login.js --- api/assets/preload-plugins/fosscord-login.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 api/assets/preload-plugins/fosscord-login.js diff --git a/api/assets/preload-plugins/fosscord-login.js b/api/assets/preload-plugins/fosscord-login.js new file mode 100644 index 00000000..1a36f0bd --- /dev/null +++ b/api/assets/preload-plugins/fosscord-login.js @@ -0,0 +1,8 @@ +// Remove `` from header when we're not accessing `/login` or `/register` +// fosscord-login.css replaces discord's TOS tooltip with something more fitting for fosscord, which when included in the main app, causes other tooltips +// to be affected, which is potentially unwanted. + +var token = JSON.parse(localStorage.getItem("token")); +if (!token && location.pathname !== "/login" && location.pathname !== "/register") { + document.getElementById("logincss").remove(); +} From e8355db1f46b617e892a51cde1cb61892145ec4c Mon Sep 17 00:00:00 2001 From: Luna Alfien Date: Sun, 23 Jan 2022 21:54:40 -0800 Subject: [PATCH 10/11] Make comments more accurate to use-case --- api/assets/preload-plugins/fosscord-login.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/assets/preload-plugins/fosscord-login.js b/api/assets/preload-plugins/fosscord-login.js index 1a36f0bd..38f82200 100644 --- a/api/assets/preload-plugins/fosscord-login.js +++ b/api/assets/preload-plugins/fosscord-login.js @@ -1,6 +1,10 @@ // Remove `` from header when we're not accessing `/login` or `/register` // fosscord-login.css replaces discord's TOS tooltip with something more fitting for fosscord, which when included in the main app, causes other tooltips // to be affected, which is potentially unwanted. +// +// This script removes fosscord-login.css when a user reloads the page. From testing, it appears fosscord already properly removes +// fosscord-login.css after login is successful, but not if you reload the page after logging in. This script is to remove fosscord-login.css in +// that specific case. var token = JSON.parse(localStorage.getItem("token")); if (!token && location.pathname !== "/login" && location.pathname !== "/register") { From f1caebe98dfcdeab3c0237f31d77c54e32a12471 Mon Sep 17 00:00:00 2001 From: minhducsun2002 <32769878+minhducsun2002@users.noreply.github.com> Date: Sun, 20 Mar 2022 10:56:25 +0700 Subject: [PATCH 11/11] Make member.premium_since ISO8601 timestamp --- util/src/entities/Member.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index a246b891..928a25d7 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -85,8 +85,8 @@ export class Member extends BaseClassWithoutId { @Column() joined_at: Date; - @Column({ type: "bigint", nullable: true }) - premium_since?: number; + @Column() + premium_since?: Date; @Column() deaf: boolean; @@ -245,7 +245,7 @@ export class Member extends BaseClassWithoutId { nick: undefined, roles: [guild_id], // @everyone role joined_at: new Date(), - premium_since: (new Date()).getTime(), + premium_since: new Date(), deaf: false, mute: false, pending: false,