diff --git a/src/api/routes/auth/forgot.ts b/src/api/routes/auth/forgot.ts
index 26e1b353..69ca582b 100644
--- a/src/api/routes/auth/forgot.ts
+++ b/src/api/routes/auth/forgot.ts
@@ -20,6 +20,7 @@ import { route, verifyCaptcha } from "@spacebar/api";
import { Config, Email, User } from "@spacebar/util";
import { Request, Response, Router } from "express";
import { ForgotPasswordSchema } from "@spacebar/schemas";
+import { HTTPError } from "lambert-server*";
const router = Router({ mergeParams: true });
router.post(
@@ -66,11 +67,16 @@ router.post(
select: { username: true, id: true, email: true },
}).catch(() => {});
- if (user && user.email) {
- Email.sendResetPassword(user, user.email).catch((e) => {
- console.error(`Failed to send password reset email to ${user.username}#${user.discriminator} (${user.id}): ${e}`);
- });
- }
+ if (user && user.email)
+ return await Email.sendResetPassword(user, user.email)
+ .then(() => {
+ return res.sendStatus(204);
+ })
+ .catch((e) => {
+ console.error(`Failed to send password reset email to ${user.handle}: ${e}`);
+ throw new HTTPError("Failed to send password reset email", 500);
+ });
+ else throw new HTTPError("No user or email", 500);
},
);
diff --git a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
index 2aad157d..bf1f708d 100644
--- a/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
+++ b/src/api/routes/channels/#channel_id/messages/#message_id/crosspost.ts
@@ -43,6 +43,7 @@ router.post(
username: "",
avatar: "",
discriminator: "",
+ global_name: "",
public_flags: 64,
},
attachments: [],
diff --git a/src/api/routes/channels/#channel_id/messages/index.ts b/src/api/routes/channels/#channel_id/messages/index.ts
index 6b18e2c9..0ad522fc 100644
--- a/src/api/routes/channels/#channel_id/messages/index.ts
+++ b/src/api/routes/channels/#channel_id/messages/index.ts
@@ -192,11 +192,14 @@ router.get(
if ((y.user_ids || []).includes(req.user_id)) y.me = true;
delete y.user_ids;
});
+ const { pomeloEnabled } = Config.get().general;
if (!x.author)
x.author = User.create({
id: "4",
- discriminator: "0000",
+ discriminator: pomeloEnabled ? "0" : "0000",
username: "Spacebar Ghost",
+ global_name: "spacebarghost",
+ display_name: "Spacebar Ghost",
public_flags: 0,
});
x.attachments?.forEach((y: Attachment) => {
diff --git a/src/api/routes/guilds/#guild_id/bans.ts b/src/api/routes/guilds/#guild_id/bans.ts
index 2bd28ace..8f1b4444 100644
--- a/src/api/routes/guilds/#guild_id/bans.ts
+++ b/src/api/routes/guilds/#guild_id/bans.ts
@@ -61,6 +61,8 @@ router.get(
user: {
username: user.username,
discriminator: user.discriminator,
+ global_name: user.global_name,
+ display_name: user.display_name,
id: user.id,
avatar: user.avatar ?? null,
public_flags: user.public_flags,
diff --git a/src/api/routes/guilds/#guild_id/messages/search.ts b/src/api/routes/guilds/#guild_id/messages/search.ts
index 51c9b5a2..700071ef 100644
--- a/src/api/routes/guilds/#guild_id/messages/search.ts
+++ b/src/api/routes/guilds/#guild_id/messages/search.ts
@@ -123,6 +123,8 @@ router.get(
avatar: x.author?.avatar,
avatar_decoration: null,
discriminator: x.author?.discriminator,
+ global_name: x.author?.global_name,
+ display_name: x.author?.display_name,
public_flags: x.author?.public_flags,
},
attachments: x.attachments,
diff --git a/src/api/routes/oauth2/authorize.ts b/src/api/routes/oauth2/authorize.ts
index b989899a..ee84ccf5 100644
--- a/src/api/routes/oauth2/authorize.ts
+++ b/src/api/routes/oauth2/authorize.ts
@@ -75,7 +75,7 @@ router.get(
id: req.user_id,
bot: false,
},
- select: { id: true, username: true, avatar: true, discriminator: true, public_flags: true },
+ select: { id: true, username: true, avatar: true, discriminator: true, public_flags: true, global_name: true, display_name: true, public_flags: true },
});
const guilds = await Member.find({
@@ -122,6 +122,8 @@ router.get(
avatar: user.avatar,
avatar_decoration: null, // TODO
discriminator: user.discriminator,
+ global_name: user.global_name,
+ display_name: user.display_name,
public_flags: user.public_flags,
},
application: {
@@ -144,6 +146,8 @@ router.get(
avatar: bot.avatar,
avatar_decoration: null, // TODO
discriminator: bot.discriminator,
+ global_name: bot.global_name,
+ display_name: bot.display_name,
public_flags: bot.public_flags,
bot: true,
approximated_guild_count: 0, // TODO
@@ -220,4 +224,4 @@ router.post(
},
);
-export default router;
+export default router;
\ No newline at end of file
diff --git a/src/api/routes/users/#user_id/relationships.ts b/src/api/routes/users/#user_id/relationships.ts
index 93f11296..a9b91817 100644
--- a/src/api/routes/users/#user_id/relationships.ts
+++ b/src/api/routes/users/#user_id/relationships.ts
@@ -55,6 +55,8 @@ router.get(
username: relation_user.username,
avatar: relation_user.avatar,
discriminator: relation_user.discriminator,
+ global_name: relation_user.global_name,
+ display_name: relation_user.display_name,
public_flags: relation_user.public_flags,
});
}
diff --git a/src/api/routes/users/@me/index.ts b/src/api/routes/users/@me/index.ts
index bd1fa077..628dddd4 100644
--- a/src/api/routes/users/@me/index.ts
+++ b/src/api/routes/users/@me/index.ts
@@ -120,6 +120,7 @@ router.patch(
newToken = (await generateToken(user.id)) as string;
}
+ // TODO: pomelo: disallow if pomelo is enabled
if (body.username) {
const check_username = body?.username?.replace(/\s/g, "").trim();
if (!check_username) {
@@ -151,6 +152,7 @@ router.patch(
}
}
+ // TODO: pomelo: disallow if pomelo is enabled
if (body.discriminator) {
if (
await User.findOne({
diff --git a/src/api/routes/users/@me/relationships.ts b/src/api/routes/users/@me/relationships.ts
index 37d5e222..9cd138af 100644
--- a/src/api/routes/users/@me/relationships.ts
+++ b/src/api/routes/users/@me/relationships.ts
@@ -131,16 +131,26 @@ router.post(
},
}),
async (req: Request, res: Response) => {
+ const { pomeloEnabled } = Config.get().general;
+ const where = pomeloEnabled
+ ? {
+ // TODO: pomelo: should we use username or add global_name property to the request?
+ global_name: req.body.username,
+ }
+ : {
+ discriminator: String(req.body.discriminator).padStart(
+ 4,
+ "0",
+ ), //Discord send the discriminator as integer, we need to add leading zeroes
+ username: req.body.username,
+ };
return await updateRelationship(
req,
res,
await User.findOneOrFail({
relations: { relationships: { to: true } },
select: userProjection,
- where: {
- discriminator: String(req.body.discriminator).padStart(4, "0"), //Discord send the discriminator as integer, we need to add leading zeroes
- username: req.body.username,
- },
+ where,
}),
req.body.type,
);
diff --git a/src/connections/Discord/index.ts b/src/connections/Discord/index.ts
index ab44f4b3..c3d26119 100644
--- a/src/connections/Discord/index.ts
+++ b/src/connections/Discord/index.ts
@@ -25,6 +25,8 @@ interface UserResponse {
id: string;
username: string;
discriminator: string;
+ global_name: string;
+ display_name?: string;
avatar_url: string | null;
}
@@ -113,6 +115,7 @@ export default class DiscordConnection extends Connection {
if (exists) return null;
+ // TODO: pomelo
return await this.createConnection({
user_id: userId,
external_id: userInfo.id,
diff --git a/src/schemas/responses/GuildBansResponse.ts b/src/schemas/responses/GuildBansResponse.ts
index 5e3c3ed4..24f99ed8 100644
--- a/src/schemas/responses/GuildBansResponse.ts
+++ b/src/schemas/responses/GuildBansResponse.ts
@@ -21,6 +21,8 @@ export interface GuildBansResponse {
user: {
username: string;
discriminator: string;
+ global_name: string;
+ display_name: string | null;
id: string;
avatar: string | null;
public_flags: number;
diff --git a/src/schemas/responses/GuildWidgetJsonResponse.ts b/src/schemas/responses/GuildWidgetJsonResponse.ts
index a1c99dde..60e8e7cc 100644
--- a/src/schemas/responses/GuildWidgetJsonResponse.ts
+++ b/src/schemas/responses/GuildWidgetJsonResponse.ts
@@ -19,21 +19,23 @@
import { ClientStatus } from "@spacebar/util";
export interface GuildWidgetJsonResponse {
- id: string;
- name: string;
- instant_invite: string;
- channels: {
- id: string;
- name: string;
- position: number;
- }[];
- members: {
- id: string;
- username: string;
- discriminator: string;
- avatar: string | null;
- status: ClientStatus;
- avatar_url: string;
- }[];
- presence_count: number;
+ id: string;
+ name: string;
+ instant_invite: string;
+ channels: {
+ id: string;
+ name: string;
+ position: number;
+ }[];
+ members: {
+ id: string;
+ username: string;
+ global_name: string;
+ display_name: string | null;
+ discriminator: string;
+ avatar: string | null;
+ status: ClientStatus;
+ avatar_url: string;
+ }[];
+ presence_count: number;
}
diff --git a/src/schemas/responses/UserRelationsResponse.ts b/src/schemas/responses/UserRelationsResponse.ts
index 377cab09..59db277f 100644
--- a/src/schemas/responses/UserRelationsResponse.ts
+++ b/src/schemas/responses/UserRelationsResponse.ts
@@ -17,4 +17,10 @@
*/
import { User } from "@spacebar/util";
-export type UserRelationsResponse = (Pick<User, "id"> & Pick<User, "username"> & Pick<User, "discriminator"> & Pick<User, "avatar"> & Pick<User, "public_flags">)[];
+export type UserRelationsResponse = (Pick<User, "id"> &
+ Pick<User, "username"> &
+ Pick<User, "global_name"> &
+ Pick<User, "display_name"> &
+ Pick<User, "discriminator"> &
+ Pick<User, "avatar"> &
+ Pick<User, "public_flags">)[];
diff --git a/src/schemas/uncategorised/RelationshipPostSchema.ts b/src/schemas/uncategorised/RelationshipPostSchema.ts
index 5f2d9437..f0a5fc32 100644
--- a/src/schemas/uncategorised/RelationshipPostSchema.ts
+++ b/src/schemas/uncategorised/RelationshipPostSchema.ts
@@ -16,7 +16,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+// TODO: pomelo?
export interface RelationshipPostSchema {
- discriminator: string;
- username: string;
+ discriminator: string;
+ username: string;
}
diff --git a/src/util/config/types/GeneralConfiguration.ts b/src/util/config/types/GeneralConfiguration.ts
index f4f76441..1db15dfa 100644
--- a/src/util/config/types/GeneralConfiguration.ts
+++ b/src/util/config/types/GeneralConfiguration.ts
@@ -29,4 +29,5 @@ export class GeneralConfiguration {
image: string | null = null;
instanceId: string = Snowflake.generate();
autoCreateBotUsers: boolean = false;
+ pomeloEnabled: boolean = false;
}
diff --git a/src/util/dtos/UserDTO.ts b/src/util/dtos/UserDTO.ts
index 2a79f8f1..b34d6d8a 100644
--- a/src/util/dtos/UserDTO.ts
+++ b/src/util/dtos/UserDTO.ts
@@ -19,19 +19,21 @@
import { User } from "../entities";
export class MinimalPublicUserDTO {
- avatar?: string | null;
- discriminator: string;
- id: string;
- public_flags: number;
- username: string;
- badge_ids?: string[] | null;
+ avatar?: string | null;
+ discriminator: string;
+ id: string;
+ public_flags: number;
+ username: string;
+ global_name: string;
+ display_name?: string;
- constructor(user: User) {
- this.avatar = user.avatar;
- this.discriminator = user.discriminator;
- this.id = user.id;
- this.public_flags = user.public_flags;
- this.username = user.username;
- this.badge_ids = user.badge_ids;
- }
+ constructor(user: User) {
+ this.avatar = user.avatar;
+ this.discriminator = user.discriminator;
+ this.id = user.id;
+ this.public_flags = user.public_flags;
+ this.username = user.username;
+ this.global_name = user.global_name;
+ this.display_name = user.display_name;
+ }
}
diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts
index 84471e0d..e527598f 100644
--- a/src/util/entities/User.ts
+++ b/src/util/entities/User.ts
@@ -29,6 +29,25 @@ import { Session } from "./Session";
import { UserSettings } from "./UserSettings";
import { ChannelType, PrivateUserProjection, PublicUser, PublicUserProjection, UserPrivate } from "@spacebar/schemas";
+export enum PublicUserEnum {
+ username,
+ global_name,
+ display_name,
+ discriminator,
+ id,
+ public_flags,
+ avatar,
+ accent_color,
+ banner,
+ bio,
+ bot,
+ premium_since,
+ premium_type,
+ theme_colors,
+ pronouns,
+}
+export type PublicUserKeys = keyof typeof PublicUserEnum;
+
@Entity({
name: "users",
})
@@ -36,8 +55,14 @@ export class User extends BaseClass {
@Column()
username: string; // username max length 32, min 2 (should be configurable)
+ @Column({nullable: true})
+ global_name: string; // puyo: pomelo
+
+ @Column({nullable: true})
+ display_name?: string; // puyo: pomelo
+
@Column()
- discriminator: string; // opaque string: 4 digits on discord.com
+ discriminator: string; // opaque string: 4 digits on discord.com, 0 for pomelo
@Column({ nullable: true })
avatar?: string; // hash of the user avatar
@@ -245,6 +270,13 @@ export class User extends BaseClass {
}
}
+ public get handle(): string {
+ const {pomeloEnabled} = Config.get().general;
+
+ // if pomelo is enabled, global_name should be set
+ return pomeloEnabled ? this.global_name as string : `${this.username}#${this.discriminator}`;
+ }
+
static async register({
email,
username,
@@ -261,19 +293,25 @@ export class User extends BaseClass {
req?: Request;
bot?: boolean;
}) {
+ const {pomeloEnabled} = Config.get().general;
+
// trim special uf8 control characters -> Backspace, Newline, ...
username = trimSpecial(username);
- const discriminator = await User.generateDiscriminator(username);
- if (!discriminator) {
- // We've failed to generate a valid and unused discriminator
- throw FieldErrors({
- username: {
- code: "USERNAME_TOO_MANY_USERS",
+ let discriminator: string | undefined;
+ if(pomeloEnabled) discriminator = "0";
+ else {
+ discriminator = await User.generateDiscriminator(username);
+ if (!discriminator) {
+ // We've failed to generate a valid and unused discriminator
+ throw FieldErrors({
+ username: {
+ code: "USERNAME_TOO_MANY_USERS",
message: req?.t("auth:register.USERNAME_TOO_MANY_USERS") || "",
- },
- });
- }
+ },
+ });
+ }
+ }
// TODO: save date_of_birth
// apparently discord doesn't save the date of birth and just calculate if nsfw is allowed
@@ -286,6 +324,8 @@ export class User extends BaseClass {
const user = User.create({
username: username,
+ global_name: username, // TODO: convert to lowercase, strip special characters,etc???
+ // display_name: username, // TODO: how should we do this?
discriminator,
id: id || Snowflake.generate(),
email: email,
@@ -310,7 +350,7 @@ export class User extends BaseClass {
// send verification email if users aren't verified by default and we have an email
if (!Config.get().defaults.user.verified && email) {
await Email.sendVerifyEmail(user, email).catch((e) => {
- console.error(`Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`);
+ console.error(`Failed to send verification email to ${user.handle}: ${e}`);
});
}
diff --git a/src/util/util/email/index.ts b/src/util/util/email/index.ts
index ca87e279..08fd1478 100644
--- a/src/util/util/email/index.ts
+++ b/src/util/util/email/index.ts
@@ -100,9 +100,12 @@ export const Email: {
) {
const { instanceName } = Config.get().general;
+ // TODO: pomelo: display_name should take precedence over username if pomelo is enabled. maybe we should use global_name as the username?
const replacements = [
["{instanceName}", instanceName],
["{userUsername}", user.username],
+ ["{userGlobalName}", user.global_name],
+ ["{userDisplayName}", user.display_name],
["{userDiscriminator}", user.discriminator],
["{userId}", user.id],
["{phoneNumber}", user.phone?.slice(-4)],
|