mirror of
https://github.com/spacebarchat/server.git
synced 2026-05-04 22:35:22 +00:00
more changes
This commit is contained in:
@@ -92,6 +92,16 @@ router.post(
|
||||
},
|
||||
author_id: user.id,
|
||||
});
|
||||
sendMessage({
|
||||
channel_id: channel.id,
|
||||
type: MessageType.THREAD_CREATED,
|
||||
content: thread.name,
|
||||
message_reference: {
|
||||
channel_id: channel.id,
|
||||
guild_id: channel.guild_id,
|
||||
},
|
||||
author_id: user.id,
|
||||
});
|
||||
await Promise.all([
|
||||
emitEvent({
|
||||
event: "THREAD_CREATE",
|
||||
|
||||
@@ -270,10 +270,10 @@ router.get(
|
||||
// polyfill message references for old messages
|
||||
await Promise.all(
|
||||
ret
|
||||
.filter((msg) => msg.message_reference && !msg.referenced_message?.id)
|
||||
.filter((msg) => msg.message_reference && !msg.referenced_message?.id && msg.message_reference.message_id)
|
||||
.map(async (msg) => {
|
||||
const whereOptions: { id: string; guild_id?: string; channel_id?: string } = {
|
||||
id: msg.message_reference!.message_id,
|
||||
id: msg.message_reference!.message_id as string,
|
||||
};
|
||||
if (msg.message_reference!.guild_id) whereOptions.guild_id = msg.message_reference!.guild_id;
|
||||
if (msg.message_reference!.channel_id) whereOptions.channel_id = msg.message_reference!.channel_id;
|
||||
@@ -290,7 +290,7 @@ router.get(
|
||||
);
|
||||
|
||||
// TODO: config max upload size
|
||||
const messageUpload = multer({
|
||||
export const messageUpload = multer({
|
||||
limits: {
|
||||
fileSize: Config.get().limits.message.maxAttachmentSize,
|
||||
fields: 10,
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { handleMessage, postHandleMessage, route, sendMessage } from "@spacebar/api";
|
||||
import { Message, Channel, emitEvent, User, MessageUpdateEvent, Recipient, uploadFile, Attachment, Member, ReadState, MessageCreateEvent } from "@spacebar/util";
|
||||
import { MessageThreadCreationSchema, ChannelType, MessageType, ThreadCreationSchema, MessageCreateAttachment, MessageCreateCloudAttachment } from "@spacebar/schemas";
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { messageUpload } from "./messages";
|
||||
|
||||
const router = Router({ mergeParams: true });
|
||||
|
||||
// TODO: public read receipts & privacy scoping
|
||||
// TODO: send read state event to all channel members
|
||||
// TODO: advance-only notification cursor
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
messageUpload.any(),
|
||||
(req, res, next) => {
|
||||
if (req.body.payload_json) {
|
||||
req.body = JSON.parse(req.body.payload_json);
|
||||
}
|
||||
|
||||
next();
|
||||
},
|
||||
route({
|
||||
requestBody: "ThreadCreationSchema",
|
||||
permission: "CREATE_PUBLIC_THREADS",
|
||||
responses: {
|
||||
200: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO: check for differences with https://github.com/spacebarchat/server/pull/876/files#diff-95be9c4cdfd8ba6f67361cd40b9abc8226b35d83e2bb44bf5b4682f1d66155e9
|
||||
const { channel_id } = req.params;
|
||||
const body = req.body as ThreadCreationSchema;
|
||||
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
|
||||
const thread = await Channel.createChannel(
|
||||
{
|
||||
owner: user,
|
||||
parent: channel,
|
||||
guild: channel.guild,
|
||||
member_count: 1,
|
||||
message_count: body.message ? 1 : 0,
|
||||
total_message_sent: body.message ? 1 : 0,
|
||||
name: body.name,
|
||||
guild_id: channel.guild_id,
|
||||
rate_limit_per_user: body.rate_limit_per_user,
|
||||
type: body.type,
|
||||
recipients: [],
|
||||
thread_metadata: {
|
||||
archived: false,
|
||||
auto_archive_duration: body.auto_archive_duration || channel.default_auto_archive_duration || 4320,
|
||||
archive_timestamp: new Date().toISOString(),
|
||||
locked: false,
|
||||
create_timestamp: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
void 0,
|
||||
{ skipPermissionCheck: true, keepId: true, skipEventEmit: true },
|
||||
);
|
||||
const recipient = Recipient.create({ channel_id: channel.id, user });
|
||||
|
||||
await recipient.save();
|
||||
|
||||
await Promise.all([
|
||||
emitEvent({
|
||||
event: "THREAD_CREATE",
|
||||
channel_id,
|
||||
data: {
|
||||
...thread.toJSON(),
|
||||
newly_created: true,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
if (body.type !== ChannelType.GUILD_PRIVATE_THREAD)
|
||||
sendMessage({
|
||||
channel_id: channel.id,
|
||||
type: MessageType.THREAD_CREATED,
|
||||
content: thread.name,
|
||||
message_reference: {
|
||||
channel_id: channel.id,
|
||||
guild_id: channel.guild_id,
|
||||
},
|
||||
author_id: user.id,
|
||||
});
|
||||
if (body.message) {
|
||||
const files = (req.files as Express.Multer.File[]) ?? [];
|
||||
const attachments: (Attachment | MessageCreateAttachment | MessageCreateCloudAttachment)[] = body.message.attachments ?? [];
|
||||
for (const currFile of files) {
|
||||
try {
|
||||
const file = await uploadFile(`/attachments/${channel.id}`, currFile);
|
||||
attachments.push(Attachment.create({ ...file, proxy_url: file.url }));
|
||||
} catch (error) {
|
||||
return res.status(400).json({ message: error?.toString() });
|
||||
}
|
||||
}
|
||||
const embeds = body.message.embeds || [];
|
||||
const message = await handleMessage({
|
||||
...body,
|
||||
type: 0,
|
||||
pinned: false,
|
||||
author_id: req.user_id,
|
||||
embeds,
|
||||
channel_id,
|
||||
attachments,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore wrong type but idk why it's mad
|
||||
message.edited_timestamp = null;
|
||||
if (message.guild_id) {
|
||||
// handleMessage will fetch the Member, but only if they are not guild owner.
|
||||
// have to fetch ourselves otherwise.
|
||||
if (!message.member) {
|
||||
message.member = await Member.findOneOrFail({
|
||||
where: { id: req.user_id, guild_id: message.guild_id },
|
||||
relations: { roles: true },
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
message.member.roles = message.member.roles.filter((x) => x.id != x.guild_id).map((x) => x.id);
|
||||
}
|
||||
let read_state = await ReadState.findOne({
|
||||
where: { user_id: req.user_id, channel_id },
|
||||
});
|
||||
if (!read_state) read_state = ReadState.create({ user_id: req.user_id, channel_id });
|
||||
read_state.last_message_id = message.id;
|
||||
//It's a little more complicated than this but this'll do
|
||||
read_state.mention_count = 0;
|
||||
|
||||
await Promise.all([
|
||||
read_state.save(),
|
||||
message.save(),
|
||||
emitEvent({
|
||||
event: "MESSAGE_CREATE",
|
||||
channel_id: channel_id,
|
||||
data: message,
|
||||
} as MessageCreateEvent),
|
||||
message.guild_id ? Member.update({ id: req.user_id, guild_id: message.guild_id }, { last_message_id: message.id }) : null,
|
||||
]);
|
||||
postHandleMessage(message).catch((e) => console.error("[Message] post-message handler failed", e));
|
||||
}
|
||||
|
||||
return res.json(thread.toJSON());
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -248,38 +248,40 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
}
|
||||
|
||||
message.message_reference = opts.message_reference;
|
||||
message.referenced_message = await Message.findOneOrFail({
|
||||
where: {
|
||||
id: opts.message_reference.message_id,
|
||||
},
|
||||
relations: {
|
||||
author: true,
|
||||
webhook: true,
|
||||
application: true,
|
||||
mentions: true,
|
||||
mention_roles: true,
|
||||
mention_channels: true,
|
||||
sticker_items: true,
|
||||
attachments: true,
|
||||
},
|
||||
});
|
||||
if (message.message_reference.message_id) {
|
||||
message.referenced_message = await Message.findOneOrFail({
|
||||
where: {
|
||||
id: opts.message_reference.message_id,
|
||||
},
|
||||
relations: {
|
||||
author: true,
|
||||
webhook: true,
|
||||
application: true,
|
||||
mentions: true,
|
||||
mention_roles: true,
|
||||
mention_channels: true,
|
||||
sticker_items: true,
|
||||
attachments: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
message.referenced_message.channel_id &&
|
||||
message.referenced_message.channel_id !== opts.message_reference.channel_id &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
)
|
||||
throw new HTTPError("Referenced message not found in the specified channel", 404);
|
||||
if (
|
||||
message.referenced_message.guild_id &&
|
||||
message.referenced_message.guild_id !== opts.message_reference.guild_id &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
)
|
||||
throw new HTTPError("Referenced message not found in the specified channel", 404);
|
||||
if (
|
||||
message.referenced_message.channel_id &&
|
||||
message.referenced_message.channel_id !== opts.message_reference.channel_id &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
)
|
||||
throw new HTTPError("Referenced message not found in the specified channel", 404);
|
||||
if (
|
||||
message.referenced_message.guild_id &&
|
||||
message.referenced_message.guild_id !== opts.message_reference.guild_id &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
)
|
||||
throw new HTTPError("Referenced message not found in the specified channel", 404);
|
||||
}
|
||||
}
|
||||
/** Q: should be checked if the referenced message exists? ANSWER: NO
|
||||
otherwise backfilling won't work **/
|
||||
if (MessageType.THREAD_STARTER_MESSAGE !== message.type) message.type = MessageType.REPLY;
|
||||
if (MessageType.THREAD_STARTER_MESSAGE !== message.type && MessageType.THREAD_CREATED !== message.type) message.type = MessageType.REPLY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface MessageActivity {
|
||||
type: 1 | 3 | 5;
|
||||
session_id?: string;
|
||||
party_id?: string;
|
||||
name_override?: string;
|
||||
icon_override?: string;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ export interface MessageCreateSchema {
|
||||
replied_user?: boolean;
|
||||
};
|
||||
message_reference?: {
|
||||
message_id: string;
|
||||
message_id?: string;
|
||||
channel_id?: string;
|
||||
guild_id?: string;
|
||||
fail_if_not_exists?: boolean;
|
||||
|
||||
@@ -20,4 +20,6 @@ export interface MessageThreadCreationSchema {
|
||||
auto_archive_duration?: number;
|
||||
rate_limit_per_user?: number;
|
||||
name: string;
|
||||
location?: string; //ignore it
|
||||
type?: number;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ActionRowComponent, ChannelType, Embed } from "#schemas/api";
|
||||
import { MessageActivity } from "./MessageActivity";
|
||||
import { MessageCreateAttachment, MessageCreateCloudAttachment } from "./MessageCreateSchema";
|
||||
|
||||
export interface ThreadCreationSchema {
|
||||
auto_archive_duration?: number;
|
||||
rate_limit_per_user?: number;
|
||||
name: string;
|
||||
type: ChannelType.GUILD_PUBLIC_THREAD | ChannelType.GUILD_PRIVATE_THREAD;
|
||||
invitable?: boolean;
|
||||
applied_tags?: string[];
|
||||
location?: string; //Ignore it
|
||||
message?: {
|
||||
content?: string;
|
||||
embeds?: Embed[];
|
||||
allowed_mentions?: {
|
||||
parse?: string[];
|
||||
roles?: string[];
|
||||
users?: string[];
|
||||
replied_user?: boolean;
|
||||
};
|
||||
components?: ActionRowComponent[] | null;
|
||||
sticker_ids?: string[];
|
||||
activity?: MessageActivity;
|
||||
application_id?: string;
|
||||
flags?: number;
|
||||
attachments?: (MessageCreateAttachment | MessageCreateCloudAttachment)[];
|
||||
};
|
||||
}
|
||||
@@ -92,3 +92,5 @@ export * from "./WebhookExecuteSchema";
|
||||
export * from "./WebhookUpdateSchema";
|
||||
export * from "./WidgetModifySchema";
|
||||
export * from "./MessageThreadCreationSchema";
|
||||
export * from "./ThreadCreationSchema";
|
||||
export * from "./MessageActivity";
|
||||
|
||||
@@ -173,7 +173,7 @@ export class Message extends BaseClass {
|
||||
|
||||
@Column({ type: "simple-json", nullable: true })
|
||||
message_reference?: {
|
||||
message_id: string;
|
||||
message_id?: string;
|
||||
channel_id?: string;
|
||||
guild_id?: string;
|
||||
type?: number; // 0 = DEFAULT, 1 = FORWARD
|
||||
|
||||
Reference in New Issue
Block a user