1
0
mirror of https://github.com/spacebarchat/server.git synced 2024-09-20 01:31:34 +02:00
This commit is contained in:
Puyodead1 2023-03-23 11:56:17 -04:00
parent 3335f16ad1
commit 4a7811a25c
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
21 changed files with 128384 additions and 1282 deletions

File diff suppressed because it is too large Load Diff

View File

@ -16,18 +16,18 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import {
Channel,
ChannelDeleteEvent,
ChannelModifySchema,
ChannelType,
ChannelUpdateEvent,
emitEvent,
Recipient,
emitEvent,
handleFile,
ChannelModifySchema,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { route } from "@spacebar/api";
const router: Router = Router();
// TODO: delete channel
@ -35,7 +35,15 @@ const router: Router = Router();
router.get(
"/",
route({ permission: "VIEW_CHANNEL" }),
route({
permission: "VIEW_CHANNEL",
responses: {
200: {
body: "Channel",
},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;
@ -49,7 +57,15 @@ router.get(
router.delete(
"/",
route({ permission: "MANAGE_CHANNELS" }),
route({
permission: "MANAGE_CHANNELS",
responses: {
200: {
body: "Channel",
},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;
@ -90,7 +106,19 @@ router.delete(
router.patch(
"/",
route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
route({
body: "ChannelModifySchema",
permission: "MANAGE_CHANNELS",
responses: {
200: {
body: "Channel",
},
404: {},
400: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const payload = req.body as ChannelModifySchema;
const { channel_id } = req.params;

View File

@ -16,19 +16,18 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Request, Response } from "express";
import { HTTPError } from "lambert-server";
import { route } from "@spacebar/api";
import { random } from "@spacebar/api";
import { random, route } from "@spacebar/api";
import {
Channel,
Guild,
Invite,
InviteCreateEvent,
emitEvent,
User,
Guild,
PublicInviteRelation,
User,
emitEvent,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { isTextChannel } from "./messages";
const router: Router = Router();
@ -39,6 +38,15 @@ router.post(
body: "InviteCreateSchema",
permission: "CREATE_INSTANT_INVITE",
right: "CREATE_INVITES",
responses: {
201: {
body: "Invite",
},
404: {},
400: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { user_id } = req;
@ -84,7 +92,15 @@ router.post(
router.get(
"/",
route({ permission: "MANAGE_CHANNELS" }),
route({
permission: "MANAGE_CHANNELS",
responses: {
200: {
body: "ChannelInvitesResponse",
},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({

View File

@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import {
emitEvent,
getPermission,
@ -23,7 +24,6 @@ import {
ReadState,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { route } from "@spacebar/api";
const router = Router();
@ -33,7 +33,13 @@ const router = Router();
router.post(
"/",
route({ body: "MessageAcknowledgeSchema" }),
route({
body: "MessageAcknowledgeSchema",
responses: {
200: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id, message_id } = req.params;

View File

@ -16,14 +16,21 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Response, Request } from "express";
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
const router = Router();
router.post(
"/",
route({ permission: "MANAGE_MESSAGES" }),
route({
permission: "MANAGE_MESSAGES",
responses: {
200: {
body: "Message",
},
},
}),
(req: Request, res: Response) => {
// TODO:
res.json({

View File

@ -19,24 +19,23 @@
import {
Attachment,
Channel,
emitEvent,
SpacebarApiErrors,
getPermission,
getRights,
Message,
MessageCreateEvent,
MessageCreateSchema,
MessageDeleteEvent,
MessageEditSchema,
MessageUpdateEvent,
Snowflake,
SpacebarApiErrors,
emitEvent,
getPermission,
getRights,
uploadFile,
MessageCreateSchema,
MessageEditSchema,
} from "@spacebar/util";
import { Router, Response, Request } from "express";
import multer from "multer";
import { route } from "@spacebar/api";
import { handleMessage, postHandleMessage } from "@spacebar/api";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import multer from "multer";
import { handleMessage, postHandleMessage, route } from "../../../../../util";
const router = Router();
// TODO: message content/embed string length limit
@ -56,6 +55,16 @@ router.patch(
body: "MessageEditSchema",
permission: "SEND_MESSAGES",
right: "SEND_MESSAGES",
responses: {
200: {
body: "Message",
},
400: {
body: "APIErrorResponse",
},
403: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
@ -146,6 +155,16 @@ router.put(
body: "MessageCreateSchema",
permission: "SEND_MESSAGES",
right: "SEND_BACKDATED_EVENTS",
responses: {
200: {
body: "Message",
},
400: {
body: "APIErrorResponse",
},
403: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id, message_id } = req.params;
@ -230,7 +249,19 @@ router.put(
router.get(
"/",
route({ permission: "VIEW_CHANNEL" }),
route({
permission: "VIEW_CHANNEL",
responses: {
200: {
body: "Message",
},
400: {
body: "APIErrorResponse",
},
403: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
@ -252,38 +283,54 @@ router.get(
},
);
router.delete("/", route({}), async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
const message = await Message.findOneOrFail({ where: { id: message_id } });
const rights = await getRights(req.user_id);
if (message.author_id !== req.user_id) {
if (!rights.has("MANAGE_MESSAGES")) {
const permission = await getPermission(
req.user_id,
channel.guild_id,
channel_id,
);
permission.hasThrow("MANAGE_MESSAGES");
}
} else rights.hasThrow("SELF_DELETE_MESSAGES");
await Message.delete({ id: message_id });
await emitEvent({
event: "MESSAGE_DELETE",
channel_id,
data: {
id: message_id,
channel_id,
guild_id: channel.guild_id,
router.delete(
"/",
route({
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
404: {},
},
} as MessageDeleteEvent);
}),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
res.sendStatus(204);
});
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
});
const message = await Message.findOneOrFail({
where: { id: message_id },
});
const rights = await getRights(req.user_id);
if (message.author_id !== req.user_id) {
if (!rights.has("MANAGE_MESSAGES")) {
const permission = await getPermission(
req.user_id,
channel.guild_id,
channel_id,
);
permission.hasThrow("MANAGE_MESSAGES");
}
} else rights.hasThrow("SELF_DELETE_MESSAGES");
await Message.delete({ id: message_id });
await emitEvent({
event: "MESSAGE_DELETE",
channel_id,
data: {
id: message_id,
channel_id,
guild_id: channel.guild_id,
},
} as MessageDeleteEvent);
res.sendStatus(204);
},
);
export default router;

View File

@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import {
Channel,
emitEvent,
@ -32,8 +33,7 @@ import {
PublicUserProjection,
User,
} from "@spacebar/util";
import { route } from "@spacebar/api";
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { In } from "typeorm";
@ -57,7 +57,17 @@ function getEmoji(emoji: string): PartialEmoji {
router.delete(
"/",
route({ permission: "MANAGE_MESSAGES" }),
route({
permission: "MANAGE_MESSAGES",
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
@ -83,7 +93,17 @@ router.delete(
router.delete(
"/:emoji",
route({ permission: "MANAGE_MESSAGES" }),
route({
permission: "MANAGE_MESSAGES",
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
const emoji = getEmoji(req.params.emoji);
@ -120,7 +140,19 @@ router.delete(
router.get(
"/:emoji",
route({ permission: "VIEW_CHANNEL" }),
route({
permission: "VIEW_CHANNEL",
responses: {
200: {
body: "UserPublic",
},
400: {
body: "APIErrorResponse",
},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { message_id, channel_id } = req.params;
const emoji = getEmoji(req.params.emoji);
@ -148,7 +180,18 @@ router.get(
router.put(
"/:emoji/:user_id",
route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }),
route({
permission: "READ_MESSAGE_HISTORY",
right: "SELF_ADD_REACTIONS",
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { message_id, channel_id, user_id } = req.params;
if (user_id !== "@me") throw new HTTPError("Invalid user");
@ -219,7 +262,16 @@ router.put(
router.delete(
"/:emoji/:user_id",
route({}),
route({
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
let { user_id } = req.params;
const { message_id, channel_id } = req.params;

View File

@ -16,18 +16,18 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Response, Request } from "express";
import { route } from "@spacebar/api";
import {
Channel,
Config,
emitEvent,
getPermission,
getRights,
MessageDeleteBulkEvent,
Message,
MessageDeleteBulkEvent,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { route } from "@spacebar/api";
const router: Router = Router();
@ -38,7 +38,17 @@ export default router;
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
router.post(
"/",
route({ body: "BulkDeleteSchema" }),
route({
body: "BulkDeleteSchema",
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
403: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;
const channel = await Channel.findOneOrFail({

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Response, Request } from "express";
import { handleMessage, postHandleMessage, route } from "@spacebar/api";
import {
Attachment,
Channel,
@ -26,19 +26,19 @@ import {
emitEvent,
FieldErrors,
getPermission,
Member,
Message,
MessageCreateEvent,
Snowflake,
uploadFile,
Member,
MessageCreateSchema,
Reaction,
ReadState,
Rights,
Reaction,
Snowflake,
uploadFile,
User,
} from "@spacebar/util";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { handleMessage, postHandleMessage, route } from "@spacebar/api";
import multer from "multer";
import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm";
import { URL } from "url";
@ -73,108 +73,123 @@ export function isTextChannel(type: ChannelType): boolean {
// https://discord.com/developers/docs/resources/channel#create-message
// get messages
router.get("/", route({}), async (req: Request, res: Response) => {
const channel_id = req.params.channel_id;
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
});
if (!channel) throw new HTTPError("Channel not found", 404);
router.get(
"/",
route({
responses: {
200: {
body: "ChannelMessagesResponse",
},
400: {
body: "APIErrorResponse",
},
403: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const channel_id = req.params.channel_id;
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
});
if (!channel) throw new HTTPError("Channel not found", 404);
isTextChannel(channel.type);
const around = req.query.around ? `${req.query.around}` : undefined;
const before = req.query.before ? `${req.query.before}` : undefined;
const after = req.query.after ? `${req.query.after}` : undefined;
const limit = Number(req.query.limit) || 50;
if (limit < 1 || limit > 100)
throw new HTTPError("limit must be between 1 and 100", 422);
isTextChannel(channel.type);
const around = req.query.around ? `${req.query.around}` : undefined;
const before = req.query.before ? `${req.query.before}` : undefined;
const after = req.query.after ? `${req.query.after}` : undefined;
const limit = Number(req.query.limit) || 50;
if (limit < 1 || limit > 100)
throw new HTTPError("limit must be between 1 and 100", 422);
const halfLimit = Math.floor(limit / 2);
const halfLimit = Math.floor(limit / 2);
const permissions = await getPermission(
req.user_id,
channel.guild_id,
channel_id,
);
permissions.hasThrow("VIEW_CHANNEL");
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
const permissions = await getPermission(
req.user_id,
channel.guild_id,
channel_id,
);
permissions.hasThrow("VIEW_CHANNEL");
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
const query: FindManyOptions<Message> & {
where: { id?: FindOperator<string> | FindOperator<string>[] };
} = {
order: { timestamp: "DESC" },
take: limit,
where: { channel_id },
relations: [
"author",
"webhook",
"application",
"mentions",
"mention_roles",
"mention_channels",
"sticker_items",
"attachments",
],
};
const query: FindManyOptions<Message> & {
where: { id?: FindOperator<string> | FindOperator<string>[] };
} = {
order: { timestamp: "DESC" },
take: limit,
where: { channel_id },
relations: [
"author",
"webhook",
"application",
"mentions",
"mention_roles",
"mention_channels",
"sticker_items",
"attachments",
],
};
if (after) {
if (BigInt(after) > BigInt(Snowflake.generate()))
return res.status(422);
query.where.id = MoreThan(after);
} else if (before) {
if (BigInt(before) < BigInt(req.params.channel_id))
return res.status(422);
query.where.id = LessThan(before);
} else if (around) {
query.where.id = [
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
LessThan((BigInt(around) + BigInt(halfLimit)).toString()),
];
if (after) {
if (BigInt(after) > BigInt(Snowflake.generate()))
return res.status(422);
query.where.id = MoreThan(after);
} else if (before) {
if (BigInt(before) < BigInt(req.params.channel_id))
return res.status(422);
query.where.id = LessThan(before);
} else if (around) {
query.where.id = [
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
LessThan((BigInt(around) + BigInt(halfLimit)).toString()),
];
return res.json([]); // TODO: fix around
}
return res.json([]); // TODO: fix around
}
const messages = await Message.find(query);
const endpoint = Config.get().cdn.endpointPublic;
const messages = await Message.find(query);
const endpoint = Config.get().cdn.endpointPublic;
return res.json(
messages.map((x: Partial<Message>) => {
(x.reactions || []).forEach((y: Partial<Reaction>) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
if ((y.user_ids || []).includes(req.user_id)) y.me = true;
delete y.user_ids;
});
if (!x.author)
x.author = User.create({
id: "4",
discriminator: "0000",
username: "Spacebar Ghost",
public_flags: 0,
return res.json(
messages.map((x: Partial<Message>) => {
(x.reactions || []).forEach((y: Partial<Reaction>) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
if ((y.user_ids || []).includes(req.user_id)) y.me = true;
delete y.user_ids;
});
if (!x.author)
x.author = User.create({
id: "4",
discriminator: "0000",
username: "Fosscord Ghost",
public_flags: 0,
});
x.attachments?.forEach((y: Attachment) => {
// dynamically set attachment proxy_url in case the endpoint changed
const uri = y.proxy_url.startsWith("http")
? y.proxy_url
: `https://example.org${y.proxy_url}`;
y.proxy_url = `${endpoint == null ? "" : endpoint}${
new URL(uri).pathname
}`;
});
x.attachments?.forEach((y: Attachment) => {
// dynamically set attachment proxy_url in case the endpoint changed
const uri = y.proxy_url.startsWith("http")
? y.proxy_url
: `https://example.org${y.proxy_url}`;
y.proxy_url = `${endpoint == null ? "" : endpoint}${
new URL(uri).pathname
}`;
});
/**
/**
Some clients ( discord.js ) only check if a property exists within the response,
which causes errors when, say, the `application` property is `null`.
**/
// for (var curr in x) {
// if (x[curr] === null)
// delete x[curr];
// }
// for (var curr in x) {
// if (x[curr] === null)
// delete x[curr];
// }
return x;
}),
);
});
return x;
}),
);
},
);
// TODO: config max upload size
const messageUpload = multer({
@ -208,6 +223,16 @@ router.post(
body: "MessageCreateSchema",
permission: "SEND_MESSAGES",
right: "SEND_MESSAGES",
responses: {
200: {
body: "Message",
},
400: {
body: "APIErrorResponse",
},
403: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;

View File

@ -19,13 +19,13 @@
import {
Channel,
ChannelPermissionOverwrite,
ChannelPermissionOverwriteSchema,
ChannelUpdateEvent,
emitEvent,
Member,
Role,
ChannelPermissionOverwriteSchema,
} from "@spacebar/util";
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { route } from "@spacebar/api";
@ -38,6 +38,12 @@ router.put(
route({
body: "ChannelPermissionOverwriteSchema",
permission: "MANAGE_ROLES",
responses: {
204: {},
404: {},
501: {},
400: { body: "APIErrorResponse" },
},
}),
async (req: Request, res: Response) => {
const { channel_id, overwrite_id } = req.params;
@ -92,7 +98,7 @@ router.put(
// TODO: check permission hierarchy
router.delete(
"/:overwrite_id",
route({ permission: "MANAGE_ROLES" }),
route({ permission: "MANAGE_ROLES", responses: { 204: {}, 404: {} } }),
async (req: Request, res: Response) => {
const { channel_id, overwrite_id } = req.params;

View File

@ -16,23 +16,33 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import {
Channel,
ChannelPinsUpdateEvent,
Config,
DiscordApiErrors,
emitEvent,
Message,
MessageUpdateEvent,
DiscordApiErrors,
} from "@spacebar/util";
import { Router, Request, Response } from "express";
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
const router: Router = Router();
router.put(
"/:message_id",
route({ permission: "VIEW_CHANNEL" }),
route({
permission: "VIEW_CHANNEL",
responses: {
204: {},
403: {},
404: {},
400: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { channel_id, message_id } = req.params;
@ -74,7 +84,17 @@ router.put(
router.delete(
"/:message_id",
route({ permission: "VIEW_CHANNEL" }),
route({
permission: "VIEW_CHANNEL",
responses: {
204: {},
403: {},
404: {},
400: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { channel_id, message_id } = req.params;
@ -114,7 +134,17 @@ router.delete(
router.get(
"/",
route({ permission: ["READ_MESSAGE_HISTORY"] }),
route({
permission: ["READ_MESSAGE_HISTORY"],
responses: {
200: {
body: "ChannelPinsResponse",
},
400: {
body: "APIErrorResponse",
},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;

View File

@ -16,20 +16,20 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { HTTPError } from "lambert-server";
import { route } from "@spacebar/api";
import { isTextChannel } from "./messages";
import { FindManyOptions, Between, Not, FindOperator } from "typeorm";
import {
Channel,
emitEvent,
getPermission,
getRights,
Message,
MessageDeleteBulkEvent,
PurgeSchema,
emitEvent,
getPermission,
getRights,
} from "@spacebar/util";
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { Between, FindManyOptions, FindOperator, Not } from "typeorm";
import { isTextChannel } from "./messages";
const router: Router = Router();
@ -42,6 +42,14 @@ router.post(
"/",
route({
/*body: "PurgeSchema",*/
responses: {
204: {},
400: {
body: "APIErrorResponse",
},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;

View File

@ -16,7 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Request, Response, Router } from "express";
import { route } from "@spacebar/api";
import {
Channel,
ChannelRecipientAddEvent,
@ -28,80 +28,98 @@ import {
Recipient,
User,
} from "@spacebar/util";
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
const router: Router = Router();
router.put("/:user_id", route({}), async (req: Request, res: Response) => {
const { channel_id, user_id } = req.params;
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
relations: ["recipients"],
});
router.put(
"/:user_id",
route({
responses: {
201: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id, user_id } = req.params;
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
relations: ["recipients"],
});
if (channel.type !== ChannelType.GROUP_DM) {
const recipients = [
...(channel.recipients?.map((r) => r.user_id) || []),
user_id,
].unique();
if (channel.type !== ChannelType.GROUP_DM) {
const recipients = [
...(channel.recipients?.map((r) => r.user_id) || []),
user_id,
].unique();
const new_channel = await Channel.createDMChannel(
recipients,
req.user_id,
);
return res.status(201).json(new_channel);
} else {
if (channel.recipients?.map((r) => r.user_id).includes(user_id)) {
const new_channel = await Channel.createDMChannel(
recipients,
req.user_id,
);
return res.status(201).json(new_channel);
} else {
if (channel.recipients?.map((r) => r.user_id).includes(user_id)) {
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
}
channel.recipients?.push(
Recipient.create({ channel_id: channel_id, user_id: user_id }),
);
await channel.save();
await emitEvent({
event: "CHANNEL_CREATE",
data: await DmChannelDTO.from(channel, [user_id]),
user_id: user_id,
});
await emitEvent({
event: "CHANNEL_RECIPIENT_ADD",
data: {
channel_id: channel_id,
user: await User.findOneOrFail({
where: { id: user_id },
select: PublicUserProjection,
}),
},
channel_id: channel_id,
} as ChannelRecipientAddEvent);
return res.sendStatus(204);
}
},
);
router.delete(
"/:user_id",
route({
responses: {
204: {},
404: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id, user_id } = req.params;
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
relations: ["recipients"],
});
if (
!(
channel.type === ChannelType.GROUP_DM &&
(channel.owner_id === req.user_id || user_id === req.user_id)
)
)
throw DiscordApiErrors.MISSING_PERMISSIONS;
if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) {
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
}
channel.recipients?.push(
Recipient.create({ channel_id: channel_id, user_id: user_id }),
);
await channel.save();
await Channel.removeRecipientFromChannel(channel, user_id);
await emitEvent({
event: "CHANNEL_CREATE",
data: await DmChannelDTO.from(channel, [user_id]),
user_id: user_id,
});
await emitEvent({
event: "CHANNEL_RECIPIENT_ADD",
data: {
channel_id: channel_id,
user: await User.findOneOrFail({
where: { id: user_id },
select: PublicUserProjection,
}),
},
channel_id: channel_id,
} as ChannelRecipientAddEvent);
return res.sendStatus(204);
}
});
router.delete("/:user_id", route({}), async (req: Request, res: Response) => {
const { channel_id, user_id } = req.params;
const channel = await Channel.findOneOrFail({
where: { id: channel_id },
relations: ["recipients"],
});
if (
!(
channel.type === ChannelType.GROUP_DM &&
(channel.owner_id === req.user_id || user_id === req.user_id)
)
)
throw DiscordApiErrors.MISSING_PERMISSIONS;
if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) {
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
}
await Channel.removeRecipientFromChannel(channel, user_id);
return res.sendStatus(204);
});
},
);
export default router;

View File

@ -16,15 +16,22 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Channel, emitEvent, Member, TypingStartEvent } from "@spacebar/util";
import { route } from "@spacebar/api";
import { Router, Request, Response } from "express";
import { Channel, emitEvent, Member, TypingStartEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
const router: Router = Router();
router.post(
"/",
route({ permission: "SEND_MESSAGES" }),
route({
permission: "SEND_MESSAGES",
responses: {
204: {},
404: {},
403: {},
},
}),
async (req: Request, res: Response) => {
const { channel_id } = req.params;
const user_id = req.user_id;

View File

@ -16,34 +16,56 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Router, Response, Request } from "express";
import { route } from "@spacebar/api";
import {
Channel,
Config,
handleFile,
trimSpecial,
DiscordApiErrors,
User,
Webhook,
WebhookCreateSchema,
WebhookType,
handleFile,
trimSpecial,
} from "@spacebar/util";
import crypto from "crypto";
import { Request, Response, Router } from "express";
import { HTTPError } from "lambert-server";
import { isTextChannel } from "./messages/index";
import { DiscordApiErrors } from "@spacebar/util";
import crypto from "crypto";
const router: Router = Router();
//TODO: implement webhooks
router.get("/", route({}), async (req: Request, res: Response) => {
res.json([]);
});
router.get(
"/",
route({
responses: {
200: {
body: "ChannelWebhooksResponse",
},
},
}),
async (req: Request, res: Response) => {
res.json([]);
},
);
// TODO: use Image Data Type for avatar instead of String
router.post(
"/",
route({ body: "WebhookCreateSchema", permission: "MANAGE_WEBHOOKS" }),
route({
body: "WebhookCreateSchema",
permission: "MANAGE_WEBHOOKS",
responses: {
200: {
body: "WebhookCreateResponse",
},
400: {
body: "APIErrorResponse",
},
403: {},
},
}),
async (req: Request, res: Response) => {
const channel_id = req.params.channel_id;
const channel = await Channel.findOneOrFail({

View File

@ -0,0 +1,3 @@
import { Invite } from "../../entities";
export type ChannelInvitesResponse = Invite[];

View File

@ -0,0 +1,3 @@
import { Message } from "../../entities";
export type ChannelMessagesResponse = Message[];

View File

@ -0,0 +1,3 @@
import { Message } from "../../entities";
export type ChannelPinsResponse = Message[];

View File

@ -0,0 +1,3 @@
import { Webhook } from "../../entities";
export type ChannelWebhooksResponse = Webhook[];

View File

@ -0,0 +1,6 @@
import { User, Webhook } from "../../entities";
export interface WebhookCreateResponse {
user: User;
hook: Webhook;
}

View File

@ -6,6 +6,10 @@ export * from "./ApplicationSkusResponse";
export * from "./ApplicationsResponse";
export * from "./BackupCodesChallengeResponse";
export * from "./CaptchaRequiredResponse";
export * from "./ChannelInvitesResponse";
export * from "./ChannelPinsResponse";
export * from "./ChannelWebhooksResponse";
export * from "./GenerateRegistrationTokensResponse";
export * from "./LocationMetadataResponse";
export * from "./TokenResponse";
export * from "./WebhookCreateResponse";