mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-10 20:52:42 +01:00
Implemented DMs and group DMs
This commit is contained in:
parent
0506d40aa1
commit
d630f09f80
@ -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
|
||||
@ -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 {
|
||||
|
@ -26,7 +26,7 @@ router.post("/", route({ body: "MessageAcknowledgeSchema" }), async (req: Reques
|
||||
data: {
|
||||
channel_id,
|
||||
message_id,
|
||||
version: 496
|
||||
version: 3763
|
||||
}
|
||||
} as MessageAckEvent);
|
||||
|
||||
|
@ -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";
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -151,10 +150,12 @@ router.post(
|
||||
return res.status(400).json(error);
|
||||
}
|
||||
}
|
||||
//TODO querying the DB at every message post should be avoided, caching maybe?
|
||||
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,
|
||||
@ -162,9 +163,36 @@ router.post(
|
||||
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);
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 }),
|
||||
}
|
||||
}))
|
||||
|
||||
res.json(aa);
|
||||
});
|
||||
|
||||
export interface DmChannelCreateSchema {
|
||||
@ -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;
|
||||
|
@ -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"] });
|
||||
|
||||
return res.json(user.relationships);
|
||||
//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(related_users);
|
||||
});
|
||||
|
||||
export interface RelationshipPutSchema {
|
||||
@ -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
|
||||
);
|
||||
|
@ -88,20 +88,17 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
const user_guild_settings_entries = members.map((x) => x.settings);
|
||||
|
||||
const recipients = await Recipient.find({
|
||||
where: { user_id: this.user_id },
|
||||
where: { user_id: this.user_id, closed: false },
|
||||
relations: ["channel", "channel.recipients", "channel.recipients.user"],
|
||||
// TODO: public user selection
|
||||
});
|
||||
const channels = recipients.map((x) => {
|
||||
// @ts-ignore
|
||||
x.channel.recipients = x.channel.recipients?.map((x) => x.user);
|
||||
// @ts-ignore
|
||||
users = users.concat(x.channel.recipients);
|
||||
if (x.channel.type === ChannelType.DM) {
|
||||
x.channel.recipients = [
|
||||
// @ts-ignore
|
||||
x.channel.recipients.find((x) => x.id !== this.user_id),
|
||||
];
|
||||
//TODO is this needed? check if users in group dm that are not friends are sent in the READY event
|
||||
//users = users.concat(x.channel.recipients);
|
||||
if (x.channel.isDm()) {
|
||||
x.channel.recipients = x.channel.recipients!.filter((x) => x.id !== this.user_id);
|
||||
}
|
||||
return x.channel;
|
||||
});
|
||||
@ -111,16 +108,19 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
});
|
||||
if (!user) return this.close(CLOSECODES.Authentication_failed);
|
||||
|
||||
const public_user = {
|
||||
username: user.username,
|
||||
discriminator: user.discriminator,
|
||||
id: user.id,
|
||||
public_flags: user.public_flags,
|
||||
avatar: user.avatar,
|
||||
bot: user.bot,
|
||||
bio: user.bio,
|
||||
};
|
||||
users.push(public_user);
|
||||
for (let relation of user.relationships) {
|
||||
const related_user = relation.to
|
||||
const public_related_user = {
|
||||
username: related_user.username,
|
||||
discriminator: related_user.discriminator,
|
||||
id: related_user.id,
|
||||
public_flags: related_user.public_flags,
|
||||
avatar: related_user.avatar,
|
||||
bot: related_user.bot,
|
||||
bio: related_user.bio,
|
||||
};
|
||||
users.push(public_related_user);
|
||||
}
|
||||
|
||||
const session_id = genSessionId();
|
||||
this.session_id = session_id; //Set the session of the WebSocket object
|
||||
@ -201,7 +201,7 @@ export async function onIdentify(this: WebSocket, data: Payload) {
|
||||
// @ts-ignore
|
||||
experiments: experiments, // TODO
|
||||
guild_join_requests: [], // TODO what is this?
|
||||
users: users.unique(), // TODO
|
||||
users: users.unique(),
|
||||
merged_members: merged_members,
|
||||
// shard // TODO: only for bots sharding
|
||||
// application // TODO for applications
|
||||
|
@ -21,5 +21,6 @@ export default {
|
||||
8: onRequestGuildMembers,
|
||||
// 9: Invalid Session
|
||||
// 10: Hello
|
||||
// 13: Dm_update
|
||||
14: onLazyRequest,
|
||||
};
|
||||
|
35
util/src/dtos/DmChannelDTO.ts
Normal file
35
util/src/dtos/DmChannelDTO.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { MinimalPublicUserDTO } from "./UserDTO";
|
||||
import { Channel, PublicUserProjection, User } from "../entities";
|
||||
|
||||
export class DmChannelDTO {
|
||||
icon: string | null;
|
||||
id: string;
|
||||
last_message_id: string | null;
|
||||
name: string | null;
|
||||
origin_channel_id: string | null;
|
||||
owner_id?: string;
|
||||
recipients: MinimalPublicUserDTO[];
|
||||
type: number;
|
||||
|
||||
static async from(channel: Channel, excluded_recipients: string[] = [], origin_channel_id?: string) {
|
||||
const obj = new DmChannelDTO()
|
||||
obj.icon = channel.icon || null
|
||||
obj.id = channel.id
|
||||
obj.last_message_id = channel.last_message_id || null
|
||||
obj.name = channel.name || null
|
||||
obj.origin_channel_id = origin_channel_id || null
|
||||
obj.owner_id = channel.owner_id
|
||||
obj.type = channel.type
|
||||
obj.recipients = (await Promise.all(channel.recipients!.filter(r => !excluded_recipients.includes(r.user_id)).map(async r => {
|
||||
return await User.findOneOrFail({ where: { id: r.user_id }, select: PublicUserProjection })
|
||||
}))).map(u => new MinimalPublicUserDTO(u))
|
||||
return obj
|
||||
}
|
||||
|
||||
excludedRecipients(excluded_recipients: string[]): DmChannelDTO {
|
||||
return {
|
||||
...this,
|
||||
recipients: this.recipients.filter(r => !excluded_recipients.includes(r.id))
|
||||
}
|
||||
}
|
||||
}
|
17
util/src/dtos/UserDTO.ts
Normal file
17
util/src/dtos/UserDTO.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { User } from "../entities";
|
||||
|
||||
export class MinimalPublicUserDTO {
|
||||
avatar?: string | null;
|
||||
discriminator: string;
|
||||
id: string;
|
||||
public_flags: number;
|
||||
username: string;
|
||||
|
||||
constructor(user: User) {
|
||||
this.avatar = user.avatar
|
||||
this.discriminator = user.discriminator
|
||||
this.id = user.id
|
||||
this.public_flags = user.public_flags
|
||||
this.username = user.username
|
||||
}
|
||||
}
|
2
util/src/dtos/index.ts
Normal file
2
util/src/dtos/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./DmChannelDTO";
|
||||
export * from "./UserDTO";
|
@ -1,7 +1,6 @@
|
||||
import { Column, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToMany, RelationId } from "typeorm";
|
||||
import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";
|
||||
import { BaseClass } from "./BaseClass";
|
||||
import { Guild } from "./Guild";
|
||||
import { Message } from "./Message";
|
||||
import { User } from "./User";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { emitEvent, getPermission, Snowflake } from "../util";
|
||||
@ -31,6 +30,9 @@ export class Channel extends BaseClass {
|
||||
@Column({ nullable: true })
|
||||
name?: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@Column({ type: "simple-enum", enum: ChannelType })
|
||||
type: ChannelType;
|
||||
|
||||
@ -38,13 +40,8 @@ export class Channel extends BaseClass {
|
||||
recipients?: Recipient[];
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((channel: Channel) => channel.last_message)
|
||||
last_message_id: string;
|
||||
|
||||
@JoinColumn({ name: "last_message_id" })
|
||||
@ManyToOne(() => Message)
|
||||
last_message?: Message;
|
||||
|
||||
@Column({ nullable: true })
|
||||
@RelationId((channel: Channel) => channel.guild)
|
||||
guild_id?: string;
|
||||
@ -162,6 +159,10 @@ export class Channel extends BaseClass {
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
isDm() {
|
||||
return this.type === ChannelType.DM || this.type === ChannelType.GROUP_DM
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChannelPermissionOverwrite {
|
||||
|
@ -19,5 +19,8 @@ export class Recipient extends BaseClass {
|
||||
@ManyToOne(() => require("./User").User)
|
||||
user: import("./User").User;
|
||||
|
||||
@Column({ default: false })
|
||||
closed: boolean;
|
||||
|
||||
// TODO: settings/mute/nick/added at/encryption keys/read_state
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ export class User extends BaseClass {
|
||||
flags: string; // UserFlags
|
||||
|
||||
@Column()
|
||||
public_flags: string;
|
||||
public_flags: number;
|
||||
|
||||
@JoinColumn({ name: "relationship_ids" })
|
||||
@OneToMany(() => Relationship, (relationship: Relationship) => relationship.from)
|
||||
|
@ -4,6 +4,8 @@ import "reflect-metadata";
|
||||
export * from "./util/index";
|
||||
export * from "./interfaces/index";
|
||||
export * from "./entities/index";
|
||||
export * from "./services/index";
|
||||
export * from "./dtos/index";
|
||||
|
||||
// import Config from "../util/Config";
|
||||
// import db, { MongooseCache, toObject } from "./util/Database";
|
||||
|
@ -127,6 +127,22 @@ export interface ChannelPinsUpdateEvent extends Event {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChannelRecipientAddEvent extends Event {
|
||||
event: "CHANNEL_RECIPIENT_ADD";
|
||||
data: {
|
||||
channel_id: string;
|
||||
user: User;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChannelRecipientRemoveEvent extends Event {
|
||||
event: "CHANNEL_RECIPIENT_REMOVE";
|
||||
data: {
|
||||
channel_id: string;
|
||||
user: User;
|
||||
};
|
||||
}
|
||||
|
||||
export interface GuildCreateEvent extends Event {
|
||||
event: "GUILD_CREATE";
|
||||
data: Guild & {
|
||||
@ -436,6 +452,8 @@ export type EventData =
|
||||
| ChannelUpdateEvent
|
||||
| ChannelDeleteEvent
|
||||
| ChannelPinsUpdateEvent
|
||||
| ChannelRecipientAddEvent
|
||||
| ChannelRecipientRemoveEvent
|
||||
| GuildCreateEvent
|
||||
| GuildUpdateEvent
|
||||
| GuildDeleteEvent
|
||||
@ -482,6 +500,8 @@ export enum EVENTEnum {
|
||||
ChannelUpdate = "CHANNEL_UPDATE",
|
||||
ChannelDelete = "CHANNEL_DELETE",
|
||||
ChannelPinsUpdate = "CHANNEL_PINS_UPDATE",
|
||||
ChannelRecipientAdd = "CHANNEL_RECIPIENT_ADD",
|
||||
ChannelRecipientRemove = "CHANNEL_RECIPIENT_REMOVE",
|
||||
GuildCreate = "GUILD_CREATE",
|
||||
GuildUpdate = "GUILD_UPDATE",
|
||||
GuildDelete = "GUILD_DELETE",
|
||||
@ -525,6 +545,8 @@ export type EVENT =
|
||||
| "CHANNEL_UPDATE"
|
||||
| "CHANNEL_DELETE"
|
||||
| "CHANNEL_PINS_UPDATE"
|
||||
| "CHANNEL_RECIPIENT_ADD"
|
||||
| "CHANNEL_RECIPIENT_REMOVE"
|
||||
| "GUILD_CREATE"
|
||||
| "GUILD_UPDATE"
|
||||
| "GUILD_DELETE"
|
||||
|
88
util/src/services/ChannelService.ts
Normal file
88
util/src/services/ChannelService.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import { Channel, ChannelType, PublicUserProjection, Recipient, User } from "../entities";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { emitEvent, trimSpecial } from "../util";
|
||||
import { DmChannelDTO } from "../dtos";
|
||||
import { ChannelRecipientRemoveEvent } from "../interfaces";
|
||||
|
||||
export function checker(arr: any[], target: any[]) {
|
||||
return target.every(v => arr.includes(v));
|
||||
}
|
||||
|
||||
export class ChannelService {
|
||||
public static async createDMChannel(recipients: string[], creator_user_id: string, name?: string) {
|
||||
recipients = recipients.unique().filter((x) => x !== creator_user_id);
|
||||
const otherRecipientsUsers = await User.find({ where: recipients.map((x) => ({ id: x })) });
|
||||
|
||||
if (otherRecipientsUsers.length !== recipients.length) {
|
||||
throw new HTTPError("Recipient/s not found");
|
||||
}
|
||||
|
||||
const type = recipients.length === 1 ? ChannelType.DM : ChannelType.GROUP_DM;
|
||||
|
||||
let channel = null;
|
||||
|
||||
const channelRecipients = [...recipients, creator_user_id]
|
||||
|
||||
const userRecipients = await Recipient.find({ where: { user_id: creator_user_id }, relations: ["channel", "channel.recipients"] })
|
||||
|
||||
for (let ur of userRecipients) {
|
||||
let re = ur.channel.recipients!.map(r => r.user_id)
|
||||
if (re.length === channelRecipients.length) {
|
||||
if (checker(re, channelRecipients)) {
|
||||
if (channel == null) {
|
||||
channel = ur.channel
|
||||
await ur.assign({ closed: false }).save()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel == null) {
|
||||
name = trimSpecial(name);
|
||||
|
||||
channel = await new Channel({
|
||||
name,
|
||||
type,
|
||||
owner_id: (type === ChannelType.DM ? undefined : creator_user_id),
|
||||
created_at: new Date(),
|
||||
last_message_id: null,
|
||||
recipients: channelRecipients.map((x) => new Recipient({ user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })),
|
||||
}).save();
|
||||
}
|
||||
|
||||
|
||||
const channel_dto = await DmChannelDTO.from(channel)
|
||||
|
||||
if (type === ChannelType.GROUP_DM) {
|
||||
|
||||
for (let recipient of channel.recipients!) {
|
||||
await emitEvent({
|
||||
event: "CHANNEL_CREATE",
|
||||
data: channel_dto.excludedRecipients([recipient.user_id]),
|
||||
user_id: recipient.user_id
|
||||
})
|
||||
}
|
||||
} else {
|
||||
await emitEvent({ event: "CHANNEL_CREATE", data: channel_dto, user_id: creator_user_id });
|
||||
}
|
||||
|
||||
return channel_dto.excludedRecipients([creator_user_id])
|
||||
}
|
||||
|
||||
public static async removeRecipientFromChannel(channel: Channel, user_id: string) {
|
||||
await Recipient.delete({ channel_id: channel.id, user_id: user_id })
|
||||
|
||||
await emitEvent({
|
||||
event: "CHANNEL_DELETE",
|
||||
data: await DmChannelDTO.from(channel, [user_id]),
|
||||
user_id: user_id
|
||||
});
|
||||
|
||||
await emitEvent({
|
||||
event: "CHANNEL_RECIPIENT_REMOVE", data: {
|
||||
channel_id: channel.id,
|
||||
user: await User.findOneOrFail({ where: { id: user_id }, select: PublicUserProjection })
|
||||
}, channel_id: channel.id
|
||||
} as ChannelRecipientRemoveEvent);
|
||||
}
|
||||
}
|
1
util/src/services/index.ts
Normal file
1
util/src/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./ChannelService";
|
Loading…
Reference in New Issue
Block a user