diff --git a/assets/openapi.json b/assets/openapi.json
index 84d02290..b5445287 100644
--- a/assets/openapi.json
+++ b/assets/openapi.json
@@ -14507,7 +14507,11 @@
"type": "boolean"
},
"type": {
- "type": "integer"
+ "enum": [
+ 0,
+ 1
+ ],
+ "type": "number"
}
}
},
diff --git a/assets/schemas.json b/assets/schemas.json
index 1590a7c3..3d4c30c5 100644
--- a/assets/schemas.json
+++ b/assets/schemas.json
@@ -15514,7 +15514,11 @@
"type": "boolean"
},
"type": {
- "type": "integer"
+ "enum": [
+ 0,
+ 1
+ ],
+ "type": "number"
}
},
"additionalProperties": false,
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts
index 0a59543d..0e7749f6 100644
--- a/src/api/util/handlers/Message.ts
+++ b/src/api/util/handlers/Message.ts
@@ -47,6 +47,10 @@ import {
MessageFlags,
FieldErrors,
getDatabase,
+ ElapsedTime,
+ TraceRoot,
+ Stopwatch,
+ TraceNode,
} from "@spacebar/util";
import { HTTPError } from "lambert-server";
import { In, Or, Equal, IsNull } from "typeorm";
@@ -66,6 +70,7 @@ import {
UnfurledMediaItem,
BaseMessageComponents,
v1CompTypes,
+ MessageReferenceType,
} from "@spacebar/schemas";
const allow_empty = false;
// TODO: check webhook, application, system author, stickers
@@ -655,22 +660,31 @@ export async function convertCloudAttachmentToAttachment(cAtt: MessageCreateClou
}
async function handleMessageMentionsAsync(message: Message) {
+ const sw = Stopwatch.startNew(),
+ totalSw = Stopwatch.startNew();
+ const trace: TraceNode = { micros: 0, calls: [] };
+ const traceRoot: TraceRoot = ["handleMessageMentionsAsync", trace];
+
const channel = await Channel.findOneOrFail({
where: { id: message.channel_id },
relations: { recipients: true },
});
+ trace.calls.push(`getChannel(${channel.id})`, { micros: sw.getElapsedAndReset().totalMicroseconds });
+
const permission = await getPermission(message.author_id ?? message.author?.id, channel.guild_id, channel);
+ trace.calls.push(`getPermissions`, { micros: sw.getElapsedAndReset().totalMicroseconds });
let content = message.content;
// TODO: sets
// root@Rory - 20/02/2023 - This breaks channel mentions in test client. We're not sure this was used in older clients.
//const mention_channel_ids = [] as string[];
- const mention_role_ids = [] as string[];
- const mention_user_ids = [] as string[];
let mention_everyone = false;
+ const mention_user_id_set = new Set<string>();
+ const mention_role_id_set = new Set<string>();
if (content) {
+ const contentTrace: TraceNode = { micros: 0, calls: [] };
// TODO: explicit-only mentions
// TODO: make mentions lazy
content = content.replace(/ *`[^)]*` */g, ""); // remove codeblocks
@@ -679,25 +693,33 @@ async function handleMessageMentionsAsync(message: Message) {
if (!mention_channel_ids.includes(mention))
mention_channel_ids.push(mention);
}*/
+ contentTrace.calls.push("filterCodeblocks", { micros: sw.getElapsedAndReset().totalMicroseconds });
- for (const [, mention] of content.matchAll(USER_MENTION)) {
- if (!mention_user_ids.includes(mention)) mention_user_ids.push(mention);
+ for (const [, mention] of content.matchAll(USER_MENTION)) mention_user_id_set.add(mention);
+ for (const [, mention] of content.matchAll(ROLE_MENTION)) mention_role_id_set.add(mention);
+ if (message.webhook?.id || message.webhook_id || permission?.has("MENTION_EVERYONE")) {
+ mention_everyone = !!content.match(EVERYONE_MENTION) || !!content.match(HERE_MENTION);
}
+ contentTrace.calls.push("parseMentions", { micros: sw.getElapsedAndReset().totalMicroseconds });
- await Promise.all(
- content.matchAll(ROLE_MENTION).map(async ([, mention]) => {
- const role = await Role.findOneOrFail({
- where: { id: mention, guild_id: channel.guild_id },
- });
- if (role.mentionable || message.webhook?.id || message.webhook_id || permission?.has("MANAGE_ROLES")) {
- mention_role_ids.push(mention);
- }
- }),
- );
+ let mentionedRoles = await Role.find({ where: { id: In(mention_role_id_set.values().toArray()), guild_id: channel.guild_id } });
+ contentTrace.calls.push("queryMentionRoles", { micros: sw.getElapsedAndReset().totalMicroseconds });
- if (message.webhook?.id || message.webhook_id || permission?.has("MENTION_EVERYONE")) {
- mention_everyone = !!content.match(EVERYONE_MENTION) || !!content.match(HERE_MENTION);
+ //TODO: should this throw at all?
+ if (mention_role_id_set.size != mentionedRoles.length) {
+ const missingRoles = mention_role_id_set
+ .values()
+ .filter((x) => !mentionedRoles.find((r) => r.id == x))
+ .toArray();
+ throw new HTTPError("Mentioned invalid roles: " + missingRoles.join(", "), 500);
+ }
+
+ if (!(message.webhook?.id || message.webhook_id || permission?.has("MANAGE_ROLES"))) {
+ mentionedRoles = mentionedRoles.filter((x) => x.mentionable);
+ mention_role_id_set.clear();
+ mentionedRoles.forEach((r) => mention_role_id_set.add(r.id));
}
+ contentTrace.calls.push("validateMentionRoles", { micros: sw.getElapsedAndReset().totalMicroseconds });
}
if (message.message_reference?.message_id) {
@@ -718,8 +740,7 @@ async function handleMessageMentionsAsync(message: Message) {
);
}
- // FORWARD
- if (message.message_reference.type === 1) {
+ if (message.message_reference.type === MessageReferenceType.FORWARD) {
message.type = MessageType.DEFAULT;
if (message.referenced_message) {
@@ -733,8 +754,8 @@ async function handleMessageMentionsAsync(message: Message) {
/*message.mention_channels = mention_channel_ids.map((x) =>
Channel.create({ id: x }),
);*/
- message.mention_roles = await Role.find({ where: { id: In(mention_role_ids), guild_id: channel.guild_id } });
- message.mentions = [...message.mentions, ...(await User.find({ where: { id: In(mention_user_ids) } }))];
+ message.mention_roles = await Role.find({ where: { id: In(mention_role_id_set.values().toArray()), guild_id: channel.guild_id } });
+ message.mentions = [...message.mentions, ...(await User.find({ where: { id: In(mention_user_id_set.values().toArray()) } }))];
message.mention_everyone = mention_everyone;
async function fillInMissingIDs(ids: string[]) {
diff --git a/src/schemas/api/messages/Message.ts b/src/schemas/api/messages/Message.ts
index b7eed17c..73d0e79b 100644
--- a/src/schemas/api/messages/Message.ts
+++ b/src/schemas/api/messages/Message.ts
@@ -238,5 +238,10 @@ export interface MessageReference {
channel_id?: string;
guild_id?: string;
fail_if_not_exists?: boolean;
- type?: number;
+ type?: MessageReferenceType;
+}
+
+export enum MessageReferenceType {
+ DEFAULT = 0,
+ FORWARD = 1,
}
|