mirror of
https://github.com/spacebarchat/server.git
synced 2026-09-01 20:09:01 +00:00
thread API start
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
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 { route, sendMessage } from "@spacebar/api";
|
||||
import { Message, Channel, emitEvent, User, MessageUpdateEvent } from "@spacebar/util";
|
||||
import { MessageThreadCreationSchema, ChannelType, MessageType } from "@spacebar/schemas";
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
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(
|
||||
"/",
|
||||
route({
|
||||
requestBody: "MessageThreadCreationSchema",
|
||||
responses: {
|
||||
200: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
const body = req.body as MessageThreadCreationSchema;
|
||||
const message = await Message.findOneOrFail({
|
||||
where: { id: message_id, channel_id },
|
||||
relations: ["guild"],
|
||||
});
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
const thread = await Channel.createChannel(
|
||||
{
|
||||
id: message.id,
|
||||
owner: user,
|
||||
parent: channel,
|
||||
guild: channel.guild,
|
||||
member_count: 1,
|
||||
message_count: 1,
|
||||
total_message_sent: 1,
|
||||
name: body.name,
|
||||
guild_id: channel.guild_id,
|
||||
rate_limit_per_user: body.rate_limit_per_user,
|
||||
type: ChannelType.GUILD_PUBLIC_THREAD,
|
||||
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 },
|
||||
);
|
||||
message.thread = thread;
|
||||
message.flags ||= 1 << 5;
|
||||
await sendMessage({
|
||||
channel_id: thread.id,
|
||||
type: MessageType.THREAD_STARTER_MESSAGE,
|
||||
message_reference: {
|
||||
message_id: message.id,
|
||||
channel_id: channel.id,
|
||||
guild_id: channel.guild_id,
|
||||
},
|
||||
author_id: user.id,
|
||||
});
|
||||
await Promise.all([
|
||||
emitEvent({
|
||||
event: "THREAD_CREATE",
|
||||
channel_id,
|
||||
data: {
|
||||
...thread.toJSON(),
|
||||
newly_created: true,
|
||||
},
|
||||
}),
|
||||
message.save(),
|
||||
emitEvent({
|
||||
event: "MESSAGE_UPDATE",
|
||||
channel_id: message.channel_id,
|
||||
data: message.toJSON(),
|
||||
} as MessageUpdateEvent),
|
||||
]);
|
||||
|
||||
return res.json(thread.toJSON());
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
@@ -137,6 +137,7 @@ router.get(
|
||||
sticker_items: true,
|
||||
attachments: true,
|
||||
},
|
||||
thread: true,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -234,7 +234,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
|
||||
if (opts.message_reference.type != 1) {
|
||||
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 (opts.message_reference.channel_id !== opts.channel_id && opts.type !== MessageType.THREAD_STARTER_MESSAGE)
|
||||
throw new HTTPError("You can only reference messages from this channel");
|
||||
}
|
||||
|
||||
message.message_reference = opts.message_reference;
|
||||
@@ -254,14 +255,22 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
},
|
||||
});
|
||||
|
||||
if (message.referenced_message.channel_id && message.referenced_message.channel_id !== opts.message_reference.channel_id)
|
||||
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)
|
||||
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 **/
|
||||
message.type = MessageType.REPLY;
|
||||
if (MessageType.THREAD_STARTER_MESSAGE !== message.type) message.type = MessageType.REPLY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +283,8 @@ export async function handleMessage(opts: MessageOptions): Promise<Message> {
|
||||
!opts.sticker_ids?.length &&
|
||||
!opts.poll &&
|
||||
!opts.components?.length &&
|
||||
opts.message_reference?.type != 1
|
||||
opts.message_reference?.type != 1 &&
|
||||
opts.type !== MessageType.THREAD_STARTER_MESSAGE
|
||||
) {
|
||||
console.log("[Message] Rejecting empty message:", opts, message);
|
||||
throw new HTTPError("Empty messages are not allowed", 50006);
|
||||
|
||||
Reference in New Issue
Block a user