Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct Messages and other fixes #369

Merged
merged 11 commits into from
Sep 18, 2021
28 changes: 21 additions & 7 deletions api/src/routes/channels/#channel_id/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChannelDeleteEvent, Channel, ChannelUpdateEvent, emitEvent, ChannelType, ChannelPermissionOverwriteType } from "@fosscord/util";
import { Router, Response, Request } from "express";
import { Channel, ChannelDeleteEvent, ChannelPermissionOverwriteType, ChannelService, ChannelType, ChannelUpdateEvent, emitEvent, Recipient } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";

const router: Router = Router();
// TODO: delete channel
// TODO: Get channel
Expand All @@ -16,14 +17,27 @@ router.get("/", route({ permission: "VIEW_CHANNEL" }), async (req: Request, res:
router.delete("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
const { channel_id } = req.params;

const channel = await Channel.findOneOrFail({ id: channel_id });
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients"] });

// TODO: Dm channel "close" not delete
const data = channel;
if (channel.type === ChannelType.DM) {
const recipient = await Recipient.findOneOrFail({ where: { channel_id: channel_id, user_id: req.user_id } })
recipient.closed = true
await Promise.all([
recipient.save(),
emitEvent({ event: "CHANNEL_DELETE", data: channel, user_id: req.user_id } as ChannelDeleteEvent)
]);

await Promise.all([emitEvent({ event: "CHANNEL_DELETE", data, channel_id } as ChannelDeleteEvent), Channel.delete({ id: channel_id })]);
} else if (channel.type === ChannelType.GROUP_DM) {
await ChannelService.removeRecipientFromChannel(channel, req.user_id)
} else {
//TODO messages in this channel should be deleted before deleting the channel
await Promise.all([
Channel.delete({ id: channel_id }),
emitEvent({ event: "CHANNEL_DELETE", data: channel, channel_id } as ChannelDeleteEvent)
]);
}

res.send(data);
res.send(channel);
});

export interface ChannelModifySchema {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ router.post("/", route({ body: "MessageAcknowledgeSchema" }), async (req: Reques
data: {
channel_id,
message_id,
version: 496
version: 3763
}
} as MessageAckEvent);

Expand Down
46 changes: 37 additions & 9 deletions api/src/routes/channels/#channel_id/messages/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Router, Response, Request } from "express";
import { Attachment, Channel, ChannelType, Embed, getPermission, Message } from "@fosscord/util";
import { Attachment, Channel, ChannelType, DmChannelDTO, Embed, emitEvent, getPermission, Message, MessageCreateEvent } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
import multer from "multer";
import { sendMessage } from "@fosscord/api";
import { uploadFile } from "@fosscord/api";
import { FindManyOptions, LessThan, MoreThan } from "typeorm";

Expand Down Expand Up @@ -62,9 +61,9 @@ router.get("/", async (req: Request, res: Response) => {
if (!channel) throw new HTTPError("Channel not found", 404);

isTextChannel(channel.type);
const around = `${req.query.around}`;
const before = `${req.query.before}`;
const after = `${req.query.after}`;
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");

Expand Down Expand Up @@ -151,20 +150,49 @@ router.post(
return res.status(400).json(error);
}
}
//TODO querying the DB at every message post should be avoided, caching maybe?
AlTech98 marked this conversation as resolved.
Show resolved Hide resolved
const channel = await Channel.findOneOrFail({ where: { id: channel_id }, relations: ["recipients", "recipients.user"] })

const embeds = [];
if (body.embed) embeds.push(body.embed);
const data = await sendMessage({
let message = await handleMessage({
...body,
type: 0,
pinned: false,
author_id: req.user_id,
embeds,
channel_id,
attachments,
edited_timestamp: undefined
edited_timestamp: undefined,
timestamp: new Date()
});

return res.json(data);
message = await message.save()

await channel.assign({ last_message_id: message.id }).save()

if (channel.isDm()) {
const channel_dto = await DmChannelDTO.from(channel)

for (let recipient of channel.recipients!) {
if (recipient.closed) {
await emitEvent({
event: "CHANNEL_CREATE",
data: channel_dto.excludedRecipients([recipient.user_id]),
user_id: recipient.user_id
})
}
}

await Promise.all(channel.recipients!.map(async r => {
r.closed = false;
return await r.save()
}));
}

await emitEvent({ event: "MESSAGE_CREATE", channel_id: channel_id, data: message } as MessageCreateEvent)
postHandleMessage(message).catch((e) => {}); // no await as it shouldnt block the message send function and silently catch error

return res.json(message);
}
);
56 changes: 54 additions & 2 deletions api/src/routes/channels/#channel_id/recipients.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
import { Router, Response, Request } from "express";
import { Request, Response, Router } from "express";
import { Channel, ChannelRecipientAddEvent, ChannelService, ChannelType, DiscordApiErrors, DmChannelDTO, emitEvent, PublicUserProjection, Recipient, User } from "@fosscord/util";

const router: Router = Router();
// TODO:

router.put("/:user_id", 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()

const new_channel = await ChannelService.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(new Recipient({ 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", 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 ChannelService.removeRecipientFromChannel(channel, user_id)

return res.sendStatus(204);
});

export default router;
18 changes: 18 additions & 0 deletions api/src/routes/sticker-packs/#id/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Request, Response, Router } from "express";

const router: Router = Router();

router.get("/", async (req: Request, res: Response) => {
//TODO
res.json({
id: "",
stickers: [],
name: "",
sku_id: "",
cover_sticker_id: "",
description: "",
banner_asset_id: ""
}).status(200);
});

export default router;
10 changes: 10 additions & 0 deletions api/src/routes/sticker-packs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Request, Response, Router } from "express";

const router: Router = Router();

router.get("/", async (req: Request, res: Response) => {
//TODO
res.json({ sticker_packs: [] }).status(200);
});

export default router;
1 change: 1 addition & 0 deletions api/src/routes/users/#id/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ router.get("/", route({ response: { body: "UserProfileResponse" } }), async (req
connected_accounts: user.connected_accounts,
premium_guild_since: null, // TODO
premium_since: null, // TODO
mutual_guilds: [], // TODO {id: "", nick: null} when ?with_mutual_guilds=true
user: {
username: user.username,
discriminator: user.discriminator,
Expand Down
43 changes: 13 additions & 30 deletions api/src/routes/users/@me/channels.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { Router, Request, Response } from "express";
import { Channel, ChannelCreateEvent, ChannelType, Snowflake, trimSpecial, User, emitEvent, Recipient } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { Request, Response, Router } from "express";
import { PublicUserProjection, Recipient, User, ChannelService } from "@fosscord/util";
import { route } from "@fosscord/api";
import { In } from "typeorm";

const router: Router = Router();

router.get("/", route({}), async (req: Request, res: Response) => {
const recipients = await Recipient.find({ where: { user_id: req.user_id }, relations: ["channel"] });
const recipients = await Recipient.find({ where: { user_id: req.user_id }, relations: ["channel", "user"] });

res.json(recipients.map((x) => x.channel));
//TODO check if this is right
const aa = await Promise.all(recipients.map(async (x) => {
return {
...(x.channel),
recipients: await User.findOneOrFail({ where: { id: x.user_id }, select: PublicUserProjection }),
AlTech98 marked this conversation as resolved.
Show resolved Hide resolved
}
}))

res.json(aa);
});

export interface DmChannelCreateSchema {
Expand All @@ -19,30 +25,7 @@ export interface DmChannelCreateSchema {

router.post("/", route({ body: "DmChannelCreateSchema" }), async (req: Request, res: Response) => {
const body = req.body as DmChannelCreateSchema;

body.recipients = body.recipients.filter((x) => x !== req.user_id).unique();

const recipients = await User.find({ where: body.recipients.map((x) => ({ id: x })) });

if (recipients.length !== body.recipients.length) {
throw new HTTPError("Recipient/s not found");
}

const type = body.recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM;
const name = trimSpecial(body.name);

const channel = await new Channel({
name,
type,
// owner_id only for group dm channels
created_at: new Date(),
last_message_id: null,
recipients: [...body.recipients.map((x) => new Recipient({ user_id: x })), new Recipient({ user_id: req.user_id })]
}).save();

await emitEvent({ event: "CHANNEL_CREATE", data: channel, user_id: req.user_id } as ChannelCreateEvent);

res.json(channel);
res.json(await ChannelService.createDMChannel(body.recipients, req.user_id, body.name));
});

export default router;
19 changes: 16 additions & 3 deletions api/src/routes/users/@me/relationships.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,19 @@ const router = Router();
const userProjection: (keyof User)[] = ["relationships", ...PublicUserProjection];

router.get("/", route({}), async (req: Request, res: Response) => {
const user = await User.findOneOrFail({ where: { id: req.user_id }, relations: ["relationships"] });
const user = await User.findOneOrFail({ where: { id: req.user_id }, relations: ["relationships", "relationships.to"] });

//TODO DTO
const related_users = user.relationships.map(r => {
return {
id: r.to.id,
type: r.type,
nickname: null,
user: r.to.toPublicUser(),
}
})

return res.json(user.relationships);
return res.json(related_users);
});

export interface RelationshipPutSchema {
Expand Down Expand Up @@ -48,7 +58,10 @@ router.post("/", route({ body: "RelationshipPostSchema" }), async (req: Request,
await User.findOneOrFail({
relations: ["relationships", "relationships.to"],
select: userProjection,
where: req.body as { discriminator: string; username: string }
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
}
}),
req.body.type
);
Expand Down
11 changes: 8 additions & 3 deletions gateway/src/listener/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async function consume(this: WebSocket, opts: EventOpts) {
.has("VIEW_CHANNEL")
)
return;
// TODO: check if user has permission to channel
break;
case "GUILD_CREATE":
this.events[id] = await listenEvent(id, consumer, listenOpts);
break;
Expand Down Expand Up @@ -193,11 +193,16 @@ async function consume(this: WebSocket, opts: EventOpts) {
break;
}

Send(this, {
let aa = {
op: OPCODES.Dispatch,
t: event,
d: data,
s: this.sequence++,
});
}

//TODO remove before PR merge
console.log(aa)

Send(this, aa);
opts.acknowledge?.();
}
Loading