import { createRoom, registerMessageInRoom, getRoomType, findRoomsFromMessage, hasSameMembersAsParent, registerThread, registerMember, getAllMembers, getThreadInfo, getThreadInfoOnId, } from "../../db/message/saveMessage-db"; import { findRoomByOwner, getAddressId, getUserIdOfMailbox } from "../../db/utils/mail"; import { nbMembers } from "../utils/envelopeUtils"; import logger from "../../system/Logger"; import { Attrs, Envelope, User } from "../../interfaces/mail/attrs.interface"; /** * take object address and join mailbox and host to return mailbox@host */ function createAddress(elt: User): string { return `${elt.mailbox}@${elt.host}`; } export enum RoomType { ROOM = 0, CHANNEL = 1, GROUP = 2, DM = 3, THREAD = 4, } export default class RegisterMessageInApp { messageId: number; attrs: Attrs; envelope: Envelope; messageID?: string; boxId: number; isSeen: boolean; ownerId: number; userId: number; inReplyTo: string; constructor(_messageId: number, _attrs: Attrs, _boxId: number) { this.messageId = _messageId; this.attrs = _attrs; if (!this.attrs.envelope) throw new Error("Envelope must exist in attributes"); this.envelope = this.attrs.envelope; this.messageID = this.envelope?.messageId; this.boxId = _boxId; this.isSeen = this.attrs.flags.includes("\\Seen"); this.ownerId = -1; this.userId = -1; this.inReplyTo = ""; } async init() { if (this.envelope.from) { this.ownerId = await getAddressId(createAddress(this.envelope.from[0])); // todo use sender or from ? } else { throw new Error("Envelope must have a 'from' field"); } } isDm = () => nbMembers(this.envelope) == 2; async isFromUs() { if (this.userId == -1) { await getUserIdOfMailbox(this.boxId).then((res) => { this.userId = res[0]?.user_id; }); } return this.ownerId == this.userId; } async registerMembers(roomId: number) { getAllMembers(this.messageId).then((res) => { if (res.lenght == 0) return; const data = res[0].id.split(","); data.forEach(async (memberId: number) => { await registerMember(roomId, memberId); }); }); } async initiateRoom(owner: number, roomType: RoomType) { try { const roomId = await createRoom(this.envelope.subject, owner, this.messageId, roomType); await registerMessageInRoom(this.messageId, roomId, this.envelope.date); await this.registerMembers(roomId); return roomId; } catch (err) { logger.err(err); } } async createOrRegisterOnExistence(owner: number, roomType: RoomType) { await findRoomByOwner(owner).then(async (res) => { if (res.length == 0) { // first message with this sender await this.initiateRoom(owner, roomType); } else { // not a reply, add to the list of message if this sender await registerMessageInRoom(this.messageId, res[0].room_id, this.envelope.date); } }); } async initiateThread() { await createRoom(this.envelope.subject, this.ownerId, this.messageId, RoomType.THREAD).then( async (threadId: number) => { // find parent room infos let roomId: number; let root_id: number; await getThreadInfo(this.inReplyTo).then(async (room) => { // todo room not lenght, reply to transfer ? roomId = room[0].room_id; root_id = room[0].root_id; if (root_id === undefined) root_id = roomId; await registerThread(threadId, roomId, root_id); }); // impl register previous message or go back await registerMessageInRoom(this.messageId, threadId, this.envelope.date); await this.registerMembers(threadId); }, ); } async createOrRegisterOnMembers(roomId: number, isThread?: boolean) { const hasSameMembers = await hasSameMembersAsParent(this.messageId, this.inReplyTo); if (hasSameMembers) { await registerMessageInRoom(this.messageId, roomId, this.envelope.date); if (isThread) { await getThreadInfoOnId(roomId).then(async (res) => { let root_id = res[0].root_id; if (root_id == undefined) root_id = res[0].room_id; }); } } else { await this.initiateThread(); } } async save() { await this.init(); if (this.envelope.inReplyTo) { this.inReplyTo = this.envelope.inReplyTo; await this.saveReply(); } else { if (await this.isFromUs()) { if (this.isDm()) { // create or add new message to DM if (!this.envelope.to) throw new Error("Who send a DM and put the recipient in cc ?"); const userTo = await getAddressId(createAddress(this.envelope.to[0])); await this.createOrRegisterOnExistence(userTo, RoomType.DM); } else { // it is not a reply and not a dm // so it is a channel, which can be possibly a group // this version is considered to be for personnal use // so by default it will be a group await this.initiateRoom(this.ownerId, RoomType.GROUP); } } else { // todo if contains reply in recipent then is channel await this.createOrRegisterOnExistence(this.ownerId, RoomType.ROOM); } } } async saveReply() { await findRoomsFromMessage(this.inReplyTo).then(async (rooms) => { if (rooms.length < 1) { // no rooms, so is a transfer // todo test if members of transferred message are included } else if (rooms.length === 1) { // only one room so message is only in a room and not in a thread // as a thread is associated to a room const roomType = (await getRoomType(rooms[0].room_id))[0].room_type; if (roomType == RoomType.GROUP || roomType == RoomType.THREAD) { await this.createOrRegisterOnMembers(rooms[0].room_id, roomType == RoomType.THREAD); } else { // reply from CHANNEL or DM or ROOM await this.initiateThread(); // todo // if (sender == owner) { // correction from the original sender // // leave in the same channel // } } } else if (rooms.length > 1) { // get the lowest thread (order by room_id) const roomId = rooms[rooms.length - 1].room_id; await this.createOrRegisterOnMembers(roomId); } }); } }