import { RequestHandler } from "../entities/RequestHandler"; import { ConversationsHelper } from "../helpers/ConversationsHelper"; import { Conversation, BaseConversation } from "../entities/Conversation"; import { UserHelper } from "../helpers/UserHelper"; import { removeHTMLNodes } from "../utils/StringUtils"; import { ConversationMessage } from "../entities/ConversationMessage"; import { UnreadConversation } from "../entities/UnreadConversation"; /** * Conversations controller * * @author Pierre HUBERT */ export class ConversationsController { /** * Create a new conversation * * @param h Request handler */ public static async CreateConversation(h : RequestHandler) { const name = h.postString("name"); const members = h.postNumbersSet("users"); // Check if the users exists for (const userID of members) { if(!await UserHelper.Exists(userID)) h.error(404, "User " + userID + " not found!"); } if(!members.has(h.getUserId())) members.add(h.getUserId()); const conv : BaseConversation = { ownerID: h.getUserId(), name: name == "false" ? "" : removeHTMLNodes(name), following: h.postBool("follow"), members: members, } const convID = await ConversationsHelper.Create(conv); // Success h.send({ conversationID: convID, success: "The conversation was successfully created!" }) } /** * Get the list of conversations of the user * * @param handler */ public static async GetList(handler: RequestHandler) { const list = await ConversationsHelper.GetListUser(handler.getUserId()); handler.send(list.map(c => this.ConversationToAPI(c))); } /** * Get information about a single conversation * * @param handler */ public static async GetInfoSingle(handler: RequestHandler) { const conversationID = await this.GetPostConversationId("conversationID", handler); const conv = await ConversationsHelper.GetSingle(conversationID, handler.getUserId()); if(!conv) throw Error("Could not get information about the conversation (but it should have been found though) !!"); handler.send(this.ConversationToAPI(conv)); } /** * Update conversation settings * * @param h Request handler */ public static async UpdateSettings(h: RequestHandler) : Promise { const convID = await this.GetPostConversationId("conversationID", h); // Update following state, if required if(h.hasPostParameter("following")) { await ConversationsHelper.SetFollowing( h.getUserId(), convID, h.postBool("following") ); } // Change moderator settings if(h.hasPostParameter("members") || h.hasPostParameter("name")) { // Check if user is the moderator of the conversation if(!await ConversationsHelper.IsUserModerator(h.getUserId(), convID)) h.error(401, "You are not allowed to perform changes on this conversation !"); // Update conversation name (if required) if(h.hasPostParameter("name")) { const name = h.postString("name"); await ConversationsHelper.SetName(convID, name == "false" ? "" : removeHTMLNodes(name)); } // Update the list of members of the conversation (if required) if(h.hasPostParameter("members")) { const members = h.postNumbersSet("members"); // Make sure current user is on the list if(!members.has(h.getUserId())) members.add(h.getUserId()); await ConversationsHelper.SetMembers(convID, members); } } h.success("Conversation information successfully updated!"); } /** * Find, optionally create and return the ID of a privat conversation * * @param h Request handler */ public static async FindPrivate(h: RequestHandler) { const otherUser = await h.postUserId("otherUser"); const allowCreate = h.postBool("allowCreate", false); // Query the database const list = await ConversationsHelper.FindPrivate(h.getUserId(), otherUser); if(list.length == 0) { if(!allowCreate) h.error(404, "Not any private conversation was found. The server was not allowed to create a new one..."); // Create the conversation const convID = await ConversationsHelper.Create({ ownerID: h.getUserId(), name: "", following: true, members: new Set([h.getUserId(), otherUser]) }); list.push(convID); } h.send({"conversationsID": list}); } /** * Refresh current user conversations * * @param h Request handler */ public static async RefreshList(h: RequestHandler) { const list = {}; // Check for new conversations if(h.hasPostParameter("newConversations")) { for(const convID of h.postNumbersSet("newConversations", 0)) { if(!ConversationsHelper.DoesUsersBelongsTo(h.getUserId(), convID)) h.error(401, "You are not allowed to fetch the messages of this conversation ("+convID+")!"); list["conversation-" + convID] = (await ConversationsHelper.GetLastMessages(convID, 10)) .map(e => this.ConversationMessageToAPI(e)); // Mark the user has seen the messages await ConversationsHelper.MarkUserSeen(convID, h.getUserId()); } } // Check for refresh on some conversations if(h.hasPostParameter("toRefresh")) { const toRefresh = h.postJSON("toRefresh"); for (const key in toRefresh) { if (toRefresh.hasOwnProperty(key)) { const element = toRefresh[key]; // Extract conversation ID if(!key.startsWith("conversation-")) h.error(400, "Entries of 'toRefresh' should start with 'conversation-' !"); const convID = Number.parseInt(key.replace("conversation-", "")); // Extract last message ID if(!element.hasOwnProperty("last_message_id")) h.error(400, "Missing last_message_id for conversation " + convID + "!"); const lastMessageID = Number.parseInt(element.last_message_id); // Check user rights if(!ConversationsHelper.DoesUsersBelongsTo(h.getUserId(), convID)) h.error(401, "You are not allowed to fetch the messages of this conversation ("+convID+")!"); // Get the messages list["conversation-" + convID] = (await ConversationsHelper.GetNewMessages(convID, lastMessageID)) .map(e => this.ConversationMessageToAPI(e)); // Mark the user has seen the messages await ConversationsHelper.MarkUserSeen(convID, h.getUserId()); } } } h.send(list); } /** * Refresh the messages of a single conversation * * @param h Request handler */ public static async RefreshSingleConversation(h: RequestHandler) { const convID = await this.GetPostConversationId("conversationID", h); const lastMessageID = h.postInt("last_message_id"); const messages = lastMessageID == 0 ? // Get lastest messages (the user has no message yet) await ConversationsHelper.GetLastMessages(convID, 10) : // Get new messages await ConversationsHelper.GetNewMessages(convID, lastMessageID); // Specify the user has seen the last message await ConversationsHelper.MarkUserSeen(convID, h.getUserId()); h.send(messages.map(e => this.ConversationMessageToAPI(e))); } /** * Send a new message * * @param h Request handler */ public static async SendMessage(h: RequestHandler) { const convID = await this.GetPostConversationId("conversationID", h); const message = removeHTMLNodes(h.postString("message", 0)); let imagePath = ""; // Check for image if(h.hasFile("image")){ // Try to save image imagePath = await h.savePostImage("image", "conversations", 1200, 1200); } else if(message.length < 3) h.error(401, "Message is empty!"); // Send message await ConversationsHelper.SendMessage({ userID: h.getUserId(), convID: convID, message: message, imagePath: imagePath }); h.success("Conversation message was sent!"); } /** * Get the older messages of a conversation * * @param h Request handler */ public static async GetOlderMessages(h: RequestHandler) { const convID = await this.GetPostConversationId("conversationID", h); const maxID = h.postInt("oldest_message_id") - 1; let limit = h.postInt("limit"); if(limit < 1) limit = 1; else if(limit > 30) limit = 30; // Get the list of messages const messages = await ConversationsHelper.GetOlderMessage(convID, maxID, limit); h.send(messages.map(e => this.ConversationMessageToAPI(e))); } /** * Get the number of unread conversations for the user * * @param h Request handler */ public static async CountUnreadForUser(h: RequestHandler) { h.send({ nb_unread: await ConversationsHelper.CountUnreadForUser(h.getUserId()) }) } /** * Get the list of unread conversations of the user * * @param h Request handler */ public static async GetListUnread(h: RequestHandler) { const list = await ConversationsHelper.GetListUnread(h.getUserId()); h.send(list.map(e => this.UnreadConversationToAPI(e))); } /** * Request a conversation * * @param h Request handler */ public static async DeleteConversation(h: RequestHandler) { const convID = await this.GetPostConversationId("conversationID", h); await ConversationsHelper.RemoveUserFromConversation(h.getUserId(), convID); h.success("The conversation has been deleted."); } /** * Get and return safely a conversation ID specified in a $_POST Request * * @param name The name of the POST field containing the ID of the conversation * @param handler */ private static async GetPostConversationId(name : string, handler: RequestHandler) : Promise { const convID = handler.postInt(name); // Check out whether the user belongs to the conversation or not if(!await ConversationsHelper.DoesUsersBelongsTo(handler.getUserId(), convID)) handler.error(401, "You are not allowed to perform queries on this conversation!"); return convID; } /** * Turn a conversation object into an API entry * * @param c */ private static ConversationToAPI(c : Conversation) : any { return { ID: c.id, ID_owner: c.ownerID, last_active: c.lastActive, name: c.name.length > 0 ? c.name : false, following: c.following ? 1 : 0, saw_last_message: c.sawLastMessage ? 1 : 0, members: [...c.members] }; } /** * Turn a conversation message into an API object * * @param c Information about the conversation */ private static ConversationMessageToAPI(c: ConversationMessage) : any { return { ID: c.id, ID_user: c.userID, time_insert: c.timeSent, message: c.message, image_path: c.hasImage ? c.imageURL : null }; } /** * Turn an UnreadConversation object into an API entry * * @param c Target conversation */ private static UnreadConversationToAPI(c: UnreadConversation): any { return { id: c.id, conv_name: c.name, last_active: c.lastActive, userID: c.userID, message: c.message } } }