From ee037532a1a2ddeb1d59dcb3be65f0f7a66663d5 Mon Sep 17 00:00:00 2001 From: Partow Roshani Date: Mon, 30 Jun 2025 22:22:08 +0330 Subject: [PATCH] RealTime and openChat --- src/main/java/module-info.java | 2 + .../Client/ActionHandler.java | 906 ++++++++++++++++-- .../Client/EventProcessorThread.java | 20 + .../Client/IncomingMessageListener.java | 149 +-- .../Client/RealTimeBuffer.java | 12 + .../Client/TelegramClient.java | 83 +- .../Database/ChannelDatabase.java | 149 ++- .../Database/ContactDatabase.java | 114 ++- .../Database/GroupDatabase.java | 221 +++++ .../Database/MessageDatabase.java | 74 ++ .../Database/userDatabase.java | 2 +- .../Models/ChatEntry.java | 73 +- .../telegramfinalproject/Models/JsonUtil.java | 12 +- .../Models/PrivateChat.java | 2 +- .../Server/ChannelService.java | 2 +- .../Server/ClientHandler.java | 588 ++++++++++-- .../Server/GroupService.java | 5 +- .../Server/PrivateChatService.java | 22 + .../Server/RealTimeEventDispatcher.java | 2 + .../Utils/GroupPermissionUtil.java | 59 ++ 20 files changed, 2222 insertions(+), 275 deletions(-) create mode 100644 src/main/java/org/to/telegramfinalproject/Client/EventProcessorThread.java create mode 100644 src/main/java/org/to/telegramfinalproject/Client/RealTimeBuffer.java create mode 100644 src/main/java/org/to/telegramfinalproject/Server/PrivateChatService.java create mode 100644 src/main/java/org/to/telegramfinalproject/Utils/GroupPermissionUtil.java diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index f20151f..0024ec0 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -13,4 +13,6 @@ module org.to.telegramfinalproject { requires java.sql; opens org.to.telegramfinalproject to javafx.fxml; exports org.to.telegramfinalproject; + exports org.to.telegramfinalproject.Client; + opens org.to.telegramfinalproject.Client to javafx.fxml; } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index 25b639b..ade0bfb 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -3,12 +3,9 @@ package org.to.telegramfinalproject.Client; import org.json.JSONArray; import org.json.JSONObject; import org.to.telegramfinalproject.Models.ChatEntry; -import org.to.telegramfinalproject.Models.ContactRequestModel; import org.to.telegramfinalproject.Models.SearchRequestModel; -import org.to.telegramfinalproject.Models.SearchResultModel; import java.io.BufferedReader; -import java.io.IOException; import java.io.PrintWriter; import java.time.LocalDateTime; import java.util.*; @@ -39,6 +36,7 @@ public class ActionHandler { request.put("profile_name", JSONObject.NULL); this.send(request); + } public void register() { @@ -76,6 +74,20 @@ public class ActionHandler { send(model.toJson()); } + + public void searchInUsers(){ + System.out.println("Enter keyword to search: "); + String keyword = scanner.nextLine(); + if (Session.currentUser == null || !Session.currentUser.has("user_id")) { + System.out.println("You must be logged in to search."); + return; + } + String userId = Session.currentUser.getString("user_id"); + SearchRequestModel model = new SearchRequestModel("searchInUsers", keyword, userId); + send(model.toJson()); + + } + private void addContact(UUID contactId) { JSONObject req = new JSONObject(); req.put("action", "add_contact"); @@ -88,8 +100,8 @@ public class ActionHandler { private void joinGroupOrChannel(String type, String uuid) { JSONObject req = new JSONObject(); req.put("action", "join_" + type); - req.put("user_id", Session.currentUser.getString("internalUUID")); // ✅ internal_uuid لازم است - req.put("id", uuid); // ✅ این هم internal_uuid است + req.put("user_id", Session.getUserUUID()); + req.put("id", uuid); send(req); } @@ -103,25 +115,35 @@ public class ActionHandler { out.println(req.toString()); try { - String responseText = in.readLine(); - if (responseText != null) { - JSONObject response = new JSONObject(responseText); - if (response.getString("status").equals("success")) { - JSONObject data = response.getJSONObject("data"); - return new ChatEntry( - receiverId, - data.getString("name"), - data.optString("image_url", ""), - receiverType, - null - ); - } + JSONObject response = TelegramClient.responseQueue.take(); + if (response != null && response.getString("status").equals("success")) { + JSONObject data = response.getJSONObject("data"); + + return new ChatEntry( + UUID.fromString(data.getString("internal_id")), + receiverId, + data.getString("name"), + data.optString("image_url", ""), + receiverType, + null, + data.optBoolean("is_owner", false), + data.optBoolean("is_admin", false) + ); + } } catch (Exception e) { System.err.println("Error fetching chat info: " + e.getMessage()); } - return new ChatEntry(receiverId, "[Unknown " + receiverType + "]", "", receiverType, null); + // اگر شکست خورد، internalId نامعتبر می‌سازیم (برای جلوگیری از null) + return new ChatEntry( + UUID.randomUUID(), // ساخت یک UUID موقت (ولی اشتباه) + receiverId, + "[Unknown " + receiverType + "]", + "", + receiverType, + null + ); } @@ -132,37 +154,44 @@ public class ActionHandler { out.println(req.toString()); try { - String responseText = in.readLine(); - if (responseText != null) { - JSONObject response = new JSONObject(responseText); + JSONObject response = TelegramClient.responseQueue.take(); + if (response != null) { if (response.getString("status").equals("success")) { JSONArray chatListJson = response.getJSONObject("data").getJSONArray("chat_list"); List chatList = new ArrayList<>(); for (Object obj : chatListJson) { JSONObject chat = (JSONObject) obj; + ChatEntry entry = new ChatEntry( + UUID.fromString(chat.getString("internal_id")), chat.getString("id"), chat.getString("name"), chat.optString("image_url", ""), chat.getString("type"), - chat.isNull("last_message_time") - ? null - : LocalDateTime.parse(chat.getString("last_message_time")) + chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")), + chat.optBoolean("is_owner", false), + chat.optBoolean("is_admin", false) ); + chatList.add(entry); } Session.chatList = chatList; System.out.println("✅ Chat list updated."); + } else { + System.out.println("❌ Failed to refresh chat list: " + response.getString("message")); } } } catch (Exception e) { - System.err.println("❌ Failed to refresh chat list: " + e.getMessage()); + System.err.println("❌ Error during refreshChatList: " + e.getMessage()); + e.printStackTrace(); } } + + public void createGroup() { System.out.print("Enter group ID: "); String groupId = scanner.nextLine(); @@ -174,7 +203,7 @@ public class ActionHandler { JSONObject req = new JSONObject(); req.put("action", "create_group"); req.put("user_id", Session.getUserUUID()); - req.put("group_id", groupId); // ID قابل نمایش + req.put("group_id", groupId); req.put("group_name", groupName); req.put("image_url", imageUrl.isBlank() ? JSONObject.NULL : imageUrl); @@ -201,7 +230,14 @@ public class ActionHandler { send(req); } - + public void addMember(UUID memberId){ + JSONObject req = new JSONObject(); + req.put("action", "add_member"); + req.put("user_id", Session.currentUser.getString("user_id")); + //req.put("group/channel",) + req.put("member_id", memberId.toString()); + send(req); + } @@ -209,21 +245,21 @@ public class ActionHandler { private void send(JSONObject request) { try { if (!request.has("action") || request.isNull("action")) { - System.err.println("Error: Request does not contain 'action'."); + System.err.println("❌ Invalid request: missing action."); return; } String action = request.getString("action"); this.out.println(request.toString()); - String responseText = this.in.readLine(); - if (responseText == null) { - System.out.println("No response from server."); + JSONObject response = TelegramClient.responseQueue.take(); + + if (response == null) { + System.out.println("⚠️ No response received."); return; } - JSONObject response = new JSONObject(responseText); - System.out.println("Server response: " + response.getString("message")); + System.out.println("✅ Server Response: " + response.getString("message")); String status = response.getString("status"); if (!"success".equals(status) || !response.has("data") || response.isNull("data")) @@ -233,26 +269,59 @@ public class ActionHandler { case "login": case "register": Session.currentUser = response.getJSONObject("data"); + JSONArray chatListJson = Session.currentUser.getJSONArray("chat_list"); List chatList = new ArrayList<>(); for (Object obj : chatListJson) { JSONObject chat = (JSONObject) obj; + ChatEntry entry = new ChatEntry( + UUID.fromString(chat.getString("internal_id")), chat.getString("id"), chat.getString("name"), chat.optString("image_url", ""), chat.getString("type"), - chat.isNull("last_message_time") ? null : - LocalDateTime.parse(chat.getString("last_message_time")) + chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")), + chat.optBoolean("is_owner", false), + chat.optBoolean("is_admin", false) ); + + + chatList.add(entry); } + Session.chatList = chatList; break; - case "search": + case "searchInUsers": + JSONArray result = response.getJSONObject("data").getJSONArray("results"); + if (result.isEmpty()){ + System.out.println("No results found."); + } + else{ + System.out.println("Search Results:"); + for (int i = 0 ; i < result.length(); i++){ + JSONObject item = result.getJSONObject(i); + String type = item.getString("type"); + if(type.equals("user")){ + System.out.println((i + 1) + ". [" + type + "] " + + item.getString("name") + " (ID: " + item.getString("id") + ")"); + } + } + System.out.print("Select a result number to interact(0 for return): "); + int index = Integer.parseInt(scanner.nextLine()) - 1; + if (index < 0 || index >= result.length()) return; + JSONObject selected = result.getJSONObject(index); + String userId = selected.getString("id"); + String uuid = selected.getString("uuid"); + UUID memberId = UUID.fromString(uuid); + addMember(memberId); + } + break; + case "search" : JSONArray results = response.getJSONObject("data").getJSONArray("results"); if (results.isEmpty()) { @@ -262,6 +331,7 @@ public class ActionHandler { for (int i = 0; i < results.length(); i++) { JSONObject item = results.getJSONObject(i); String type = item.getString("type"); + if (type.equals("message")) { System.out.println((i + 1) + ". [message] \"" + item.getString("content") + "\"" + " (from: " + item.optString("sender", "N/A") + ", at: " + item.getString("time") + ")"); @@ -271,61 +341,92 @@ public class ActionHandler { } } - System.out.print("Select a result number to interact: "); + System.out.print("Select a result number to interact (or 0 to exit): "); int index = Integer.parseInt(scanner.nextLine()) - 1; - if (index < 0 || index >= results.length()) return; + if (index == -1) { + System.out.println("Exit..."); + return; + } + if (index < -1 || index >= results.length()) return; JSONObject selected = results.getJSONObject(index); String type = selected.getString("type"); switch (type) { case "user" -> { - String userId = selected.getString("id"); - String uuid = selected.getString("uuid"); + String uuidStr = selected.getString("uuid"); + UUID uuid = UUID.fromString(uuidStr); + + System.out.println("🔍 Looking for chat with internal_id='" + uuid + "' type='private'"); ChatEntry existing = Session.chatList.stream() - .filter(c -> c.getId().equals(userId) && c.getType().equals("private")) + .filter(c -> c.getId().equals(uuid) && c.getType().equals("private")) .findFirst() .orElse(null); if (existing != null) { openChat(existing); } else { - UUID contactId = UUID.fromString(uuid); - addContact(contactId); - ChatEntry newChat = fetchChatInfo(contactId.toString(), "private"); + System.out.println("ℹ Trying to add contact..."); + addContact(uuid); + refreshChatList(); - openChat(newChat); + System.out.println("🔄 Rechecking chat list..."); + + existing = Session.chatList.stream() + .filter(c -> c.getId().equals(uuid) && c.getType().equals("private")) + .findFirst() + .orElse(null); + + if (existing != null) { + openChat(existing); + } else { + System.out.println("❌ Failed to open chat. Try again later."); + } } } case "group", "channel" -> { - String id = selected.getString("id"); - String uuid = selected.getString("uuid"); + String uuidStr = selected.getString("uuid"); + UUID uuid = UUID.fromString(uuidStr); + + System.out.println("🔍 Looking for chat with internal_id='" + uuid + "' type='" + type + "'"); ChatEntry existing = Session.chatList.stream() - .filter(c -> c.getId().equals(id) && c.getType().equals(type)) + .filter(c -> c.getId().equals(uuid) && c.getType().equals(type)) .findFirst() .orElse(null); if (existing != null) { openChat(existing); } else { - joinGroupOrChannel(type, uuid); + System.out.println("ℹ Trying to join " + type + "..."); + joinGroupOrChannel(type, uuidStr); - ChatEntry newChat = fetchChatInfo(id, type); refreshChatList(); - openChat(newChat); + System.out.println("🔄 Rechecking chat list..."); + + existing = Session.chatList.stream() + .filter(c -> c.getId().equals(uuid) && c.getType().equals(type)) + .findFirst() + .orElse(null); + + if (existing != null) { + openChat(existing); + } else { + System.out.println("❌ Failed to open " + type + ". Try again later."); + } } } - case "message" -> { String receiverId = selected.getString("receiver_id"); String receiverType = selected.getString("receiver_type"); + System.out.println("🔍 Looking for chat with displayId='" + receiverId + "' type='" + receiverType + "'"); + ChatEntry chat = Session.chatList.stream() - .filter(c -> c.getId().equals(receiverId) && c.getType().equals(receiverType)) + .filter(c -> c.getDisplayId().equals(receiverId) && c.getType().equals(receiverType)) .findFirst() .orElseGet(() -> fetchChatInfo(receiverId, receiverType)); @@ -334,44 +435,68 @@ public class ActionHandler { default -> System.out.println("No interaction available for type: " + type); } - } - break; + + + + + break; case "create_group": case "create_channel": - if (status.equals("success") && response.has("data")) { - JSONObject chatJson = response.getJSONObject("data"); + if (response.has("data")) { + JSONObject chatJson = response.getJSONObject("data"); + ChatEntry chat = new ChatEntry( + UUID.fromString(chatJson.getString("internal_id")), chatJson.getString("id"), chatJson.getString("name"), chatJson.optString("image_url", ""), chatJson.getString("type"), - null + null, + chatJson.optBoolean("is_owner", false), + chatJson.optBoolean("is_admin", false) ); + + refreshChatList(); - System.out.println("✅ Created and opening chat..."); - openChat(chat); + System.out.println("✅ Created and opening chat..."); + openChat(chat); + } + + break; + + case "get_messages": + JSONArray messages = response.getJSONObject("data").getJSONArray("messages"); + System.out.println("\n🔓 Messages fetched:"); + System.out.println("─────────────────────────────────────────────"); + for (int i = 0; i < messages.length(); i++) { + JSONObject m = messages.getJSONObject(i); + String senderId = m.getString("sender_id"); + String content = m.getString("content"); + String time = m.getString("send_at"); + + String label = senderId.equals(Session.currentUser.getString("internal_uuid")) ? "You" : "Other"; + System.out.println("[" + time + "] " + label + ": " + content); } + System.out.println("─────────────────────────────────────────────"); break; - - - case "get_messages": - // Optional: handle later + case "logout": + Session.currentUser = null; + Session.chatList = null; break; } - } catch (IOException e) { - System.err.println("Error communicating with server: " + e.getMessage()); } catch (Exception e) { - System.err.println("Client error: " + e.getMessage()); + System.err.println("❌ Error during send(): " + e.getMessage()); e.printStackTrace(); } } + public void userMenu(UUID internal_uuid) { while (true) { System.out.println("\nUser Menu:"); @@ -416,7 +541,11 @@ public class ActionHandler { System.out.print("Select a chat by number: "); int choice = Integer.parseInt(scanner.nextLine()) - 1; - if (choice < 0 || choice >= Session.chatList.size()) { + if(choice == -1){ + System.out.println("Exit..."); + return; + } + if (choice < -1 || choice >= Session.chatList.size()) { System.out.println("Invalid selection."); return; } @@ -426,18 +555,499 @@ public class ActionHandler { } private void openChat(ChatEntry chat) { - JSONObject request = new JSONObject(); - request.put("action", "get_messages"); - request.put("receiver_id", chat.getId()); - request.put("receiver_type", chat.getType()); - send(request); + JSONObject req = new JSONObject(); + req.put("action", "get_messages"); + req.put("receiver_id", chat.getId()); + req.put("receiver_type", chat.getType()); + send(req); + + boolean stayInChat = true; + + while (stayInChat) { + switch (chat.getType().trim().toLowerCase()) { + case "private" -> stayInChat = showPrivateChatMenu(chat); + case "group" -> stayInChat = showGroupChatMenu(chat); + case "channel" -> stayInChat = showChannelChatMenu(chat); + default -> { + System.out.println("❗ Unknown chat type: " + chat.getType()); + stayInChat = false; + } + } + } } + + + + + private boolean showPrivateChatMenu(ChatEntry chat) { + System.out.println("1. Send message"); + System.out.println("2. Block/Unblock"); + System.out.println("3. Delete chat (one-sided)"); + System.out.println("4. Delete chat (both sides)"); + System.out.println("5. Back"); + + + String input = scanner.nextLine(); + switch (input) { + case "1" -> sendMessageTo(chat.getId(), "private"); + case "2" -> toggleBlock(chat.getId()); + case "3" -> { + deleteChat(chat.getId(), false); + return false; + } + case "4" -> { + deleteChat(chat.getId(), true); + return false; + } + case "5" -> { + return false; + } + default -> System.out.println("Invalid choice."); + } + return true; + } + + private JSONObject getGroupPermissions(UUID groupId) { + JSONObject req = new JSONObject(); + req.put("action", "get_group_permissions"); + req.put("group_id", groupId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null || !res.getString("status").equals("success")) { + return new JSONObject(); // پیش‌فرض خالی یعنی هیچ پرمیشنی + } + return res.getJSONObject("data"); + } + + + + private boolean showGroupChatMenu(ChatEntry chat) { + boolean isAdmin = chat.isAdmin(); + boolean isOwner = chat.isOwner(); + JSONObject perms = getGroupPermissions(chat.getId()); + + System.out.println("\n--- Group Chat Menu ---"); + System.out.println("1. Send Message"); + System.out.println("2. View Members"); + + if (isOwner || (isAdmin && perms.optBoolean("can_add_members", false))) { + System.out.println("3. Add Member"); + } + if (isOwner || (isAdmin && perms.optBoolean("can_edit_group", false))) { + System.out.println("4. Edit Group Info"); + } + if (isOwner || (isAdmin && perms.optBoolean("can_add_admins", false))) { + System.out.println("5. Add Admin"); + } + if (isOwner || (isAdmin && perms.optBoolean("can_remove_admins", false))) { + System.out.println("6. Remove Admin"); + } + if (isOwner) { + System.out.println("7. Delete Group"); + } + System.out.println("8. Leave Group"); + System.out.println("0. Back to Chat List"); + + String input = scanner.nextLine(); + switch (input) { + case "1" -> sendMessageTo(chat.getId(), "group"); + case "2" -> viewGroupMembers(chat.getId()); + case "3" -> { + if (isOwner || (isAdmin && perms.optBoolean("can_add_members", false))) addMemberToGroup(chat.getId()); + else System.out.println("❌ You don't have permission."); + } + case "4" -> { + if (isOwner || (isAdmin && perms.optBoolean("can_edit_group", false))) editGroupInfo(chat.getId()); + else System.out.println("❌ You don't have permission."); + } + case "5" -> { + if (isOwner || (isAdmin && perms.optBoolean("can_add_admins", false))) addAdminToGroup(chat.getId()); + else System.out.println("❌ You don't have permission."); + } + case "6" -> { + if (isOwner || (isAdmin && perms.optBoolean("can_remove_admins", false))) removeAdminFromGroup(chat.getId()); + else System.out.println("❌ You don't have permission."); + } + case "7" -> { + if (isOwner) deleteGroup(chat.getId()); + else System.out.println("❌ You don't have permission."); + return false; + } + case "8" -> { + leaveChat(chat.getId(), "group"); + return false; + } + case "0" -> { + return false; + } + default -> System.out.println("Invalid choice."); + } + return true; + } + + + + + private boolean showChannelChatMenu(ChatEntry chat) { + boolean isAdmin = chat.isAdmin(); + boolean isOwner = chat.isOwner(); + + System.out.println("\n--- Channel Menu ---"); + + if (isOwner || isAdmin) + System.out.println("1. Send Post"); + + System.out.println("2. View Subscribers"); + + if (isOwner || isAdmin) { + System.out.println("3. Add Admin"); + System.out.println("4. Edit Channel Info"); + } + + System.out.println("5. Unsubscribe"); + System.out.println("0. Back to Chat List"); + + String input = scanner.nextLine(); + switch (input) { + case "1" -> { + if (isOwner || isAdmin) sendMessageTo(chat.getId(), "channel"); + else System.out.println("❌ You are not allowed to post."); + } + case "2" -> viewChannelSubscribers(chat.getId()); + case "3" -> { + if (isOwner) addAdminToChannel(chat.getId()); + else System.out.println("❌ Only owner can add admins."); + } + case "4" -> { + if (isOwner || isAdmin) editChannelInfo(chat.getId()); + else System.out.println("❌ You don't have permission."); + } + case "5" -> { + leaveChat(chat.getId(), "channel"); + return false; // خروج کامل از چت + } + case "0" -> { + return false; // بازگشت به لیست چت‌ها + } + default -> System.out.println("Invalid choice."); + } + return true; // همچنان در منو باقی بمان + } + + + + + + + + + + private void addAdminToGroup(UUID groupId) { + JSONObject req = new JSONObject(); + req.put("action", "view_group_members"); + req.put("group_id", groupId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null || !res.getString("status").equals("success")) { + System.out.println("❌ Failed to fetch members."); + return; + } + + JSONArray members = res.getJSONObject("data").getJSONArray("members"); + List eligible = new ArrayList<>(); + + System.out.println("\n--- Members List ---"); + for (int i = 0; i < members.length(); i++) { + JSONObject m = members.getJSONObject(i); + if (m.getString("role").equals("member")) { + eligible.add(m); + System.out.println((eligible.size()) + ". " + m.getString("profile_name") + " (" + m.getString("user_id") + ")"); + } + } + + if (eligible.isEmpty()) { + System.out.println("⚠️ No eligible members."); + return; + } + + System.out.print("Select a member to promote: "); + int choice = Integer.parseInt(scanner.nextLine()) - 1; + if (choice < 0 || choice >= eligible.size()) { + System.out.println("❌ Invalid selection."); + return; + } + + JSONObject selected = eligible.get(choice); + String targetInternalUUID = selected.getString("internal_uuid"); // دقت کن internal_uuid + + JSONObject permissions = new JSONObject(); + System.out.print("Can add members? (true/false): "); + permissions.put("can_add_members", Boolean.parseBoolean(scanner.nextLine())); + System.out.print("Can remove members? (true/false): "); + permissions.put("can_remove_members", Boolean.parseBoolean(scanner.nextLine())); + System.out.print("Can add admins? (true/false): "); + permissions.put("can_add_admins", Boolean.parseBoolean(scanner.nextLine())); + System.out.print("Can remove admins? (true/false): "); + permissions.put("can_remove_admins", Boolean.parseBoolean(scanner.nextLine())); + System.out.print("Can edit group info? (true/false): "); + permissions.put("can_edit_group", Boolean.parseBoolean(scanner.nextLine())); + + JSONObject promoteReq = new JSONObject(); + promoteReq.put("action", "add_admin_to_group"); + promoteReq.put("group_id", groupId.toString()); + promoteReq.put("user_id", targetInternalUUID); // ارسال internal_uuid واقعی + promoteReq.put("permissions", permissions); + + JSONObject promoteRes = sendWithResponse(promoteReq); + if (promoteRes != null) + System.out.println(promoteRes.getString("message")); + } + + + private void viewGroupMembers(UUID groupId) { + JSONObject req = new JSONObject(); + req.put("action", "view_group_members"); + req.put("group_id", groupId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null) return; + + if (res.getString("status").equals("success")) { + JSONArray members = res.getJSONObject("data").getJSONArray("members"); + + System.out.println("\n--- Group Members ---"); + for (int i = 0; i < members.length(); i++) { + JSONObject m = members.getJSONObject(i); + String name = m.getString("profile_name"); + String userId = m.getString("user_id"); + String role = m.getString("role"); + System.out.printf("- %s (ID: %s) [%s]\n", name, userId, role); + } + } else { + System.out.println("❌ Failed to fetch members."); + } + } + + + + private void removeAdminFromGroup(UUID groupId) { + System.out.print("Enter user_id to remove from admin: "); + String userId = scanner.nextLine().trim(); + + JSONObject req = new JSONObject(); + req.put("action", "remove_admin_from_group"); + req.put("group_id", groupId.toString()); + req.put("user_id", userId); + + JSONObject res = sendWithResponse(req); + if (res != null) + System.out.println(res.getString("message")); + } + + + private void deleteGroup(UUID groupId) { + System.out.print("Are you sure you want to delete the group? (yes/no): "); + String confirm = scanner.nextLine().trim().toLowerCase(); + + if (!confirm.equals("yes")) { + System.out.println("❌ Delete cancelled."); + return; + } + + JSONObject req = new JSONObject(); + req.put("action", "delete_group"); + req.put("group_id", groupId.toString()); + + JSONObject res = sendWithResponse(req); + if (res != null && res.getString("status").equals("success")) { + System.out.println("✅ Group deleted successfully."); + refreshChatList(); + } else { + System.out.println("❌ Failed to delete group."); + } + } + + private void deleteChat(UUID targetId, boolean both) { + JSONObject req = new JSONObject(); + req.put("action", "delete_private_chat"); + req.put("target_id", targetId.toString()); + req.put("both", both); + send(req); + + JSONObject resp = sendWithResponse(req); + if (resp.getString("status").equals("success")) { + System.out.println("✅ Chat deleted successfully."); + } else { + System.out.println("⚠️ Failed to delete chat."); + } + } + + + + private void toggleBlock(UUID userId) { + JSONObject req = new JSONObject(); + req.put("action", "toggle_block"); + req.put("user_id", Session.getUserUUID()); + req.put("target_id", userId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null) return; + + String status = res.getString("status"); + String message = res.getString("message"); + + if (status.equals("success")) { + System.out.println("🔒 " + message); + } else { + System.out.println("❌ " + message); + } + } + + + private void leaveChat(UUID id, String type) { + JSONObject req = new JSONObject(); + req.put("action", "leave_chat"); + req.put("user_id", Session.getUserUUID()); + req.put("chat_id", id.toString()); + req.put("chat_type", type); + + JSONObject res = sendWithResponse(req); + if (res == null) return; + + if (res.getBoolean("success")) { + System.out.println("✅ You left the chat."); + } else { + System.out.println("❌ " + res.getString("message")); + } + } + + private void sendMessageTo(UUID id, String type) { + System.out.print("Enter message: "); + String text = scanner.nextLine().trim(); + + if (text.isEmpty()) { + System.out.println("Message cannot be empty."); + return; + } + + JSONObject req = new JSONObject(); + req.put("action", "send_message"); + req.put("sender_id", Session.getUserUUID()); + req.put("receiver_id", id.toString()); + req.put("receiver_type", type); + req.put("text", text); + + JSONObject res = sendWithResponse(req); + if (res == null) return; + + if (res.getBoolean("success")) { + System.out.println("✅ Message sent."); + } else { + System.out.println("❌ Failed to send message."); + } + } + + private void addMemberToGroup(UUID groupId) { + System.out.print("Enter user_id to add: "); + String userId = scanner.nextLine().trim(); + + JSONObject req = new JSONObject(); + req.put("action", "add_member_to_group"); + req.put("group_id", groupId.toString()); + req.put("user_id", userId); + + JSONObject res = sendWithResponse(req); + if (res != null) + System.out.println(res.getString("message")); + } + + private void editGroupInfo(UUID groupId) { + System.out.print("Enter new group name: "); + String newName = scanner.nextLine().trim(); + + System.out.print("Enter new group description: "); + String newDesc = scanner.nextLine().trim(); + + JSONObject req = new JSONObject(); + req.put("action", "edit_group_info"); + req.put("group_id", groupId.toString()); + req.put("name", newName); + req.put("description", newDesc); + + JSONObject res = sendWithResponse(req); + if (res != null) + System.out.println(res.getString("message")); + } + + + private void viewChannelSubscribers(UUID channelId) { + JSONObject req = new JSONObject(); + req.put("action", "view_channel_subscribers"); + req.put("channel_id", channelId.toString()); + + JSONObject res = sendWithResponse(req); + if (res == null) return; + + if (res.getBoolean("success")) { + JSONArray subs = res.getJSONArray("subscribers"); + System.out.println("--- Subscribers ---"); + for (int i = 0; i < subs.length(); i++) { + JSONObject s = subs.getJSONObject(i); + System.out.println("- " + s.getString("profile_name")); + } + } else { + System.out.println("❌ Failed to load subscribers."); + } + } + + private void addAdminToChannel(UUID channelId) { + System.out.print("Enter user_id to promote: "); + String userId = scanner.nextLine().trim(); + + JSONObject req = new JSONObject(); + req.put("action", "add_admin_to_channel"); + req.put("channel_id", channelId.toString()); + req.put("user_id", userId); + + JSONObject res = sendWithResponse(req); + if (res != null) + System.out.println(res.getString("message")); + } + + private void editChannelInfo(UUID channelId) { + System.out.print("Enter new channel name: "); + String name = scanner.nextLine().trim(); + + System.out.print("Enter new description: "); + String desc = scanner.nextLine().trim(); + + JSONObject req = new JSONObject(); + req.put("action", "edit_channel_info"); + req.put("channel_id", channelId.toString()); + req.put("name", name); + req.put("description", desc); + + JSONObject res = sendWithResponse(req); + if (res != null) + System.out.println(res.getString("message")); + } + + + private JSONObject getResponse() { + try { + return TelegramClient.responseQueue.take(); + } catch (InterruptedException e) { + throw new RuntimeException("Failed to get server response"); + } + } + + public void logout() { if (Session.currentUser != null && Session.currentUser.has("user_id")) { JSONObject request = new JSONObject(); request.put("action", "logout"); - request.put("user_id", Session.currentUser.getString("internalUUID")); + request.put("user_id", Session.currentUser.getString("internal_uuid")); send(request); Session.currentUser = null; @@ -449,4 +1059,150 @@ public class ActionHandler { + public void processIncomingEvents() { + try { + while (in.ready()) { + String line = in.readLine(); + if (line == null) continue; + + JSONObject response = new JSONObject(line); + if (!response.has("action")) continue; + + String action = response.getString("action"); + + switch (action) { + case "new_message" -> { + JSONObject msg = response.getJSONObject("data"); + System.out.println("\n🔔 New Message:"); + System.out.println("From: " + msg.getString("sender")); + System.out.println("Time: " + msg.getString("time")); + System.out.println("Content: " + msg.getString("content")); + System.out.print(">> "); + } + + case "user_status_changed" -> { + JSONObject msg = response.getJSONObject("data"); + System.out.println("\n🔄 User Status Changed:"); + System.out.println("User: " + msg.getString("user_id")); + System.out.println("Status: " + msg.getString("status")); + System.out.print(">> "); + } + + case "update_group_or_channel" -> { + JSONObject data = response.getJSONObject("data"); + System.out.println("\n📢 " + data.getString("chat_type") + " updated: " + data.getString("new_name")); + System.out.print(">> "); + } + + + default -> { + if (!action.equals("search")) { + System.out.println("\n❓ Unknown action received: " + action); + System.out.print(">> "); + } + } + } + } + } catch (Exception e) { + System.out.println("🔴 Failed to process event: " + e.getMessage()); + } + } + + + public void addAdminToEntity(String type, UUID entityId) { + System.out.print("Enter user ID to promote to admin: "); + String targetUserId = scanner.nextLine().trim(); + + JSONObject permissions = new JSONObject(); + System.out.print("Can send messages? (true/false): "); + permissions.put("can_send", Boolean.parseBoolean(scanner.nextLine())); + System.out.print("Can edit info? (true/false): "); + permissions.put("can_edit", Boolean.parseBoolean(scanner.nextLine())); + + JSONObject req = new JSONObject(); + req.put("action", type.equals("group") ? "add_admin_to_group" : "add_admin_to_channel"); + req.put(type + "_id", entityId.toString()); + req.put("target_user_id", targetUserId); + req.put("permissions", permissions); + + send(req); + JSONObject res = getResponse(); + System.out.println(res.getString("message")); + } + + + public void editAdminPermissions(String type, UUID entityId) { + System.out.print("Enter user ID of the admin to edit: "); + String targetUserId = scanner.nextLine().trim(); + + JSONObject permissions = new JSONObject(); + System.out.print("Can send messages? (true/false): "); + permissions.put("can_send", Boolean.parseBoolean(scanner.nextLine())); + System.out.print("Can edit info? (true/false): "); + permissions.put("can_edit", Boolean.parseBoolean(scanner.nextLine())); + + JSONObject req = new JSONObject(); + req.put("action", type.equals("group") ? "edit_group_admin_permissions" : "edit_channel_admin_permissions"); + req.put(type + "_id", entityId.toString()); + req.put("target_user_id", targetUserId); + req.put("permissions", permissions); + + send(req); + JSONObject res = getResponse(); + System.out.println(res.getString("message")); + } + + + public void viewAdmins(String type, UUID entityId) { + JSONObject req = new JSONObject(); + req.put("action", type.equals("group") ? "view_group_admins" : "view_channel_admins"); + req.put(type + "_id", entityId.toString()); + + send(req); + JSONObject res = getResponse(); + if (res.getString("status").equals("success")) { + JSONArray admins = res.getJSONObject("data").getJSONArray("admins"); + System.out.println("📋 Admins in this " + type + ":"); + for (int i = 0; i < admins.length(); i++) { + JSONObject admin = admins.getJSONObject(i); + System.out.printf("- %s (%s)\n", admin.getString("name"), admin.getString("role")); + } + } else { + System.out.println("❌ " + res.getString("message")); + } + } + + + + + + + private JSONObject sendWithResponse(JSONObject request) { + try { + if (!request.has("action") || request.isNull("action")) { + System.err.println("❌ Invalid request: missing action."); + return null; + } + + String action = request.getString("action"); + this.out.println(request.toString()); + + JSONObject response = TelegramClient.responseQueue.take(); + + if (response == null) { + System.out.println("⚠️ No response received."); + return null; + } + + System.out.println("✅ Server Response: " + response.getString("message")); + return response; + + } catch (Exception e) { + System.err.println("❌ Error during sendWithResponse(): " + e.getMessage()); + e.printStackTrace(); + return null; + } + } + + } diff --git a/src/main/java/org/to/telegramfinalproject/Client/EventProcessorThread.java b/src/main/java/org/to/telegramfinalproject/Client/EventProcessorThread.java new file mode 100644 index 0000000..ff50959 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Client/EventProcessorThread.java @@ -0,0 +1,20 @@ +package org.to.telegramfinalproject.Client; + +public class EventProcessorThread extends Thread { + private final ActionHandler handler; + + public EventProcessorThread(ActionHandler handler) { + this.handler = handler; + setDaemon(true); + } + + @Override + public void run() { + while (true) { + try { + Thread.sleep(2000); + handler.processIncomingEvents(); + } catch (InterruptedException ignored) {} + } + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Client/IncomingMessageListener.java b/src/main/java/org/to/telegramfinalproject/Client/IncomingMessageListener.java index 4de32f5..30ca3a0 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/IncomingMessageListener.java +++ b/src/main/java/org/to/telegramfinalproject/Client/IncomingMessageListener.java @@ -15,72 +15,107 @@ public class IncomingMessageListener implements Runnable { @Override public void run() { try { + System.out.println("👂 Real-Time Listener started."); + String line; while ((line = in.readLine()) != null) { JSONObject response = new JSONObject(line); + System.out.println("📥 Received raw line: " + line); - if (!response.has("action")) continue; - String action = response.getString("action"); - - switch (action) { - case "new_message" -> { - JSONObject msg = response.getJSONObject("data"); - System.out.println("\n🔔 New Message:"); - System.out.println("From: " + msg.getString("sender")); - System.out.println("Time: " + msg.getString("time")); - System.out.println("Content: " + msg.getString("content")); - System.out.print(">> "); + if (response.has("action")) { + String action = response.getString("action"); + if (isRealTimeEvent(action)) { + handleRealTimeEvent(response); + } else { + TelegramClient.responseQueue.put(response); } - - case "message_edited" -> { - JSONObject msg = response.getJSONObject("data"); - System.out.println("\n✏️ Message Edited:"); - System.out.println("ID: " + msg.getString("message_id")); - System.out.println("New Content: " + msg.getString("new_content")); - System.out.println("Edit Time: " + msg.getString("edit_time")); - System.out.print(">> "); - } - - case "message_deleted" -> { - JSONObject msg = response.getJSONObject("data"); - System.out.println("\n🗑️ Message Deleted:"); - System.out.println("Message ID: " + msg.getString("message_id")); - System.out.print(">> "); - } - - case "status_change" -> { - JSONObject msg = response.getJSONObject("data"); - System.out.println("\n🔄 User Status Changed:"); - System.out.println("User: " + msg.getString("user_id")); - System.out.println("Status: " + msg.getString("status")); - System.out.print(">> "); - } - - case "system_notification" -> { - JSONObject msg = response.getJSONObject("data"); - System.out.println("\n⚠️ System Notification:"); - System.out.println(msg.getString("content")); - System.out.print(">> "); - } - - case "contact_added" ->{ - System.out.println("\n🔔 You were added by a new contact: " + response.getString("user_id")); - System.out.print(">> "); - } - - - - default -> { - if (!action.equals("search")) { // ignore action: search - System.out.println("\n❓ Unknown action received: " + action); - System.out.print(">> "); - } - } - + } else if (response.has("status") && response.has("message")) { + TelegramClient.responseQueue.put(response); + } else { + TelegramClient.responseQueue.put(response); } } + } catch (Exception e) { System.out.println("🔴 Listener stopped: " + e.getMessage()); } } + + private boolean isRealTimeEvent(String action) { + return switch (action) { + case "new_message", "message_edited", "message_deleted", + "user_status_changed", "added_to_group", "added_to_channel", + "update_group_or_channel", "chat_deleted", + "blocked_by_user", "unblocked_by_user", "message_seen" -> true; + default -> false; + }; + } + + private void handleRealTimeEvent(JSONObject response) { + String action = response.getString("action"); + JSONObject msg = response.getJSONObject("data"); + + switch (action) { + case "new_message" -> { + System.out.println("\n🔔 New Message:"); + System.out.println("From: " + msg.getString("sender")); + System.out.println("Time: " + msg.getString("time")); + System.out.println("Content: " + msg.getString("content")); + } + + case "message_edited" -> { + System.out.println("\n✏️ Message Edited:"); + System.out.println("ID: " + msg.getString("message_id")); + System.out.println("New Content: " + msg.getString("new_content")); + System.out.println("Edit Time: " + msg.getString("edited_at")); + } + + case "message_deleted" -> { + System.out.println("\n🗑️ Message Deleted:"); + System.out.println("Message ID: " + msg.getString("message_id")); + } + + case "user_status_changed" -> { + System.out.println("\n🔄 User Status Changed:"); + System.out.println("User: " + msg.getString("user_id")); + System.out.println("Status: " + msg.getString("status")); + } + + case "added_to_group" -> { + System.out.println("\n👥 You were added to a group: " + msg.getString("chat_name")); + } + + case "added_to_channel" -> { + System.out.println("\n📢 You were added to a channel: " + msg.getString("chat_name")); + } + + case "update_group_or_channel" -> { + System.out.println("\n🔄 Group/Channel updated: " + msg.getString("new_name")); + } + + case "chat_deleted" -> { + System.out.println("\n🗑️ Chat deleted: " + msg.getString("chat_id")); + } + + case "blocked_by_user" -> { + System.out.println("\n⛔ You were blocked by user: " + msg.getString("blocker_id")); + } + + case "unblocked_by_user" -> { + System.out.println("\n✅ You were unblocked by user: " + msg.getString("unblocker_id")); + } + + case "message_seen" -> { + System.out.println("\n👁️ Your message was seen:"); + System.out.println("Message ID: " + msg.getString("message_id")); + System.out.println("Seen at: " + msg.getString("seen_at")); + } + + default -> { + System.out.println("\n❓ Unknown real-time action: " + action); + } + } + + System.out.print(">> "); + } } diff --git a/src/main/java/org/to/telegramfinalproject/Client/RealTimeBuffer.java b/src/main/java/org/to/telegramfinalproject/Client/RealTimeBuffer.java new file mode 100644 index 0000000..0b66a4c --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Client/RealTimeBuffer.java @@ -0,0 +1,12 @@ +package org.to.telegramfinalproject.Client; + + +import org.json.JSONObject; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class RealTimeBuffer { + public static final BlockingQueue incomingEvents = new LinkedBlockingQueue<>(); +} + diff --git a/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java b/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java index ba614c0..16ae524 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java +++ b/src/main/java/org/to/telegramfinalproject/Client/TelegramClient.java @@ -9,6 +9,8 @@ import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; public class TelegramClient { private static final String SERVER_HOST = "localhost"; @@ -18,6 +20,7 @@ public class TelegramClient { private PrintWriter out; private final Scanner scanner; ActionHandler handler = null; + public static BlockingQueue responseQueue = new LinkedBlockingQueue<>(); public TelegramClient() { this.scanner = new Scanner(System.in); @@ -28,76 +31,52 @@ public class TelegramClient { this.socket = new Socket(SERVER_HOST, SERVER_PORT); this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); this.out = new PrintWriter(this.socket.getOutputStream(), true); - System.out.println(" Connected to Telegram Server"); + System.out.println("✅ Connected to Telegram Server"); this.handler = new ActionHandler(this.out, this.in, this.scanner); - this.showMainMenu(); - } catch (IOException e) { - System.err.println("Error connecting to server: " + e.getMessage()); - } + // 👂 فقط این ترد مجاز به خواندن از in است + Thread listenerThread = new Thread(new IncomingMessageListener(in)); + listenerThread.setDaemon(true); + listenerThread.start(); + + showMainMenu(); + + } catch (IOException e) { + System.err.println("❌ Error connecting to server: " + e.getMessage()); + } } private void showMainMenu() { - while(true) { + while (true) { System.out.println("Main Menu:"); System.out.println("1. Register"); System.out.println("2. Login"); System.out.println("3. Exit"); System.out.print("Choose an option: "); - switch (this.scanner.nextLine()) { - case "1": - this.handler.register(); - break; - case "2": - this.handler.loginHandler(); + String choice = scanner.nextLine(); + + switch (choice) { + case "1" -> handler.register(); + case "2" -> { + handler.loginHandler(); if (Session.currentUser != null) { - System.out.println("Login successful."); - UUID internalId = UUID.fromString(Session.currentUser.getString("internalUUID")); - //Thread listenerThread = new Thread(new IncomingMessageListener(in)); - //listenerThread.setDaemon(true); - //listenerThread.start(); - this.handler.userMenu(internalId); - + System.out.println("✅ Login successful."); + UUID internalId = UUID.fromString(Session.currentUser.getString("internal_uuid")); + handler.userMenu(internalId); } else { - System.out.println("Login failed."); + System.out.println("❌ Login failed."); } - break; - case "3": - System.out.println("Disconnecting..."); - - if (Session.currentUser != null && Session.currentUser.has("internalUUID")) { - try { - JSONObject logoutRequest = new JSONObject(); - logoutRequest.put("action", "logout"); - logoutRequest.put("user_id", Session.currentUser.getString("internalUUID")); - out.println(logoutRequest.toString()); - in.readLine(); - } catch (Exception e) { - System.err.println("Failed to notify server on logout: " + e.getMessage()); - } - } - - try { - if (socket != null) socket.close(); - if (in != null) in.close(); - if (out != null) out.close(); - System.out.println("Disconnected."); - } catch (IOException e) { - System.err.println("Error closing connection: " + e.getMessage()); - } - + } + case "3" -> { + System.out.println("Exiting..."); return; - - - default: - System.out.println("Invalid choice. Please try again."); + } + default -> System.out.println("Invalid choice."); } } } public static void main(String[] args) { - TelegramClient client = new TelegramClient(); - client.start(); + new TelegramClient().start(); } } - diff --git a/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java index 3e9783b..01ac6b4 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/ChannelDatabase.java @@ -1,5 +1,6 @@ package org.to.telegramfinalproject.Database; +import org.json.JSONObject; import org.to.telegramfinalproject.Models.Channel; import org.to.telegramfinalproject.Models.Group; @@ -81,7 +82,7 @@ public class ChannelDatabase { public static List getSubscriberUUIDs(UUID channelInternalUUID) { List subscriberIds = new ArrayList<>(); - String sql = "SELECT user_id FROM channel_subscribe WHERE channel_id = ?"; + String sql = "SELECT user_id FROM channel_subscribers WHERE channel_id = ?"; try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { @@ -128,7 +129,7 @@ public class ChannelDatabase { public static boolean isUserSubscribed(UUID userId, UUID channelInternalId) { - String sql = "SELECT * FROM channel_subscribe WHERE user_id = ? AND channel_id = ?"; + String sql = "SELECT * FROM channel_subscribers WHERE user_id = ? AND channel_id = ?"; try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setObject(1, userId); stmt.setObject(2, channelInternalId); @@ -240,12 +241,14 @@ public class ChannelDatabase { } } - public static void addSubscriber(UUID channelId, UUID userId) { - String sql = "INSERT INTO channel_subscribers (channel_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING"; + public static void addSubscriber(UUID channelId, UUID userId, String role) { + String sql = "INSERT INTO channel_subscribers (channel_id, user_id, role) VALUES (?, ?, ?) ON CONFLICT DO NOTHING"; try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setObject(1, channelId); stmt.setObject(2, userId); + stmt.setString(3, role); + stmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); @@ -279,4 +282,142 @@ public class ChannelDatabase { } + public static boolean addOwnerToChannel(UUID channelId, UUID userId) { + String sql = "INSERT INTO channel_subscribers (channel_id, user_id, role) VALUES (?, ?, 'owner')"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + stmt.setObject(2, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public static boolean addAdminToChannel(UUID channelId, UUID userId, JSONObject permissions) { + String sql = "UPDATE channel_subscribers SET role = 'admin', permissions = ?::jsonb WHERE channel_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, permissions.toString()); + stmt.setObject(2, channelId); + stmt.setObject(3, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public static String getChannelRole(UUID channelId, UUID userId) { + String sql = "SELECT role FROM channel_subscribers WHERE channel_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + return rs.getString("role"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return "subscriber"; // پیش‌فرض + } + + + + public static JSONObject getChannelPermissions(UUID channelId, UUID userId) { + String sql = "SELECT permissions FROM channel_subscribers WHERE channel_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + return new JSONObject(rs.getString("permissions")); + } + } catch (Exception e) { + e.printStackTrace(); + } + return new JSONObject(); + } + + + + public static boolean updateChannelAdminPermissions(UUID channelId, UUID userId, JSONObject permissions) { + String sql = "UPDATE channel_subscribers SET permissions = ?::jsonb WHERE channel_id = ? AND user_id = ? AND role = 'admin'"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, permissions.toString()); + stmt.setObject(2, channelId); + stmt.setObject(3, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + + public static List getChannelAdminsAndOwner(UUID channelId) { + String sql = "SELECT user_id, role, permissions FROM channel_subscribers WHERE channel_id = ? AND role IN ('owner', 'admin')"; + List admins = new ArrayList<>(); + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + JSONObject obj = new JSONObject(); + obj.put("user_id", rs.getObject("user_id").toString()); + obj.put("role", rs.getString("role")); + obj.put("permissions", new JSONObject(rs.getString("permissions"))); + admins.add(obj); + } + } catch (Exception e) { + e.printStackTrace(); + } + return admins; + } + + + public static boolean isOwner(UUID channelId, UUID userId) { + String sql = "SELECT role FROM channel_subscribers WHERE channel_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + return "owner".equals(rs.getString("role")); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + public static boolean isAdmin(UUID channelId, UUID userId) { + String sql = "SELECT role FROM channel_subscribers WHERE channel_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + String role = rs.getString("role"); + return "admin".equals(role) || "owner".equals(role); // owner هم admin هست + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + + } diff --git a/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java index a890995..220f551 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/ContactDatabase.java @@ -49,19 +49,47 @@ public class ContactDatabase { } } - public boolean blockContact(UUID user_id, UUID contact_id) { - String sql = "UPDATE contacts SET is_blocked = TRUE WHERE user_id = ? AND contact_id = ?"; - try (Connection connection = getConnection()) { - PreparedStatement stmt = connection.prepareStatement(sql); - stmt.setObject(1, user_id); - stmt.setObject(2, contact_id); - return stmt.executeUpdate() > 0; + public static boolean toggleBlock(UUID userId, UUID targetId) { + String selectSql = "SELECT is_blocked FROM contacts WHERE user_id = ? AND contact_id = ?"; + String updateSql = "UPDATE contacts SET is_blocked = ? WHERE user_id = ? AND contact_id = ?"; + + try (Connection conn = getConnection(); + PreparedStatement selectStmt = conn.prepareStatement(selectSql)) { + + selectStmt.setObject(1, userId); + selectStmt.setObject(2, targetId); + + ResultSet rs = selectStmt.executeQuery(); + if (rs.next()) { + boolean currentlyBlocked = rs.getBoolean("is_blocked"); + + try (PreparedStatement updateStmt = conn.prepareStatement(updateSql)) { + updateStmt.setBoolean(1, !currentlyBlocked); + updateStmt.setObject(2, userId); + updateStmt.setObject(3, targetId); + updateStmt.executeUpdate(); + } + return !currentlyBlocked; + } else { + // اگر رابطه وجود نداره، اول باید کاربر رو به contact ها اضافه کنیم + String insertSql = "INSERT INTO contacts (user_id, contact_id, is_blocked) VALUES (?, ?, ?)"; + try (PreparedStatement insertStmt = conn.prepareStatement(insertSql)) { + insertStmt.setObject(1, userId); + insertStmt.setObject(2, targetId); + insertStmt.setBoolean(3, true); + insertStmt.executeUpdate(); + } + return true; + } + } catch (SQLException e) { e.printStackTrace(); - return false; } + return false; } + + public boolean unblockContact(UUID user_id, UUID contact_id) { String sql = "UPDATE contacts SET is_blocked = FALSE WHERE user_id = ? AND contact_id = ?"; try (Connection connection = getConnection()) { @@ -160,6 +188,76 @@ public class ContactDatabase { } + public static boolean deleteChatOneSide(UUID currentUserId, UUID otherUserId) { + String sql = """ + UPDATE private_chat + SET user1_deleted = CASE WHEN user1_id = ? THEN TRUE ELSE user1_deleted END, + user2_deleted = CASE WHEN user2_id = ? THEN TRUE ELSE user2_deleted END + WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?) + """; + + try (Connection conn = getConnection(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, currentUserId); + stmt.setObject(2, currentUserId); + stmt.setObject(3, currentUserId); + stmt.setObject(4, otherUserId); + stmt.setObject(5, otherUserId); + stmt.setObject(6, currentUserId); + + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public static boolean deleteChatBoth(UUID currentUserId, UUID otherUserId) { + String sqlDeleteMessages = """ + DELETE FROM messages + WHERE receiver_type = 'private' AND ( + (sender_id = ? AND receiver_id = ?) OR + (sender_id = ? AND receiver_id = ?) + ) + """; + + String sqlDeleteChat = """ + DELETE FROM private_chat + WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?) + """; + + try (Connection conn = getConnection()) { + conn.setAutoCommit(false); + + try (PreparedStatement stmtMsg = conn.prepareStatement(sqlDeleteMessages); + PreparedStatement stmtChat = conn.prepareStatement(sqlDeleteChat)) { + + stmtMsg.setObject(1, currentUserId); + stmtMsg.setObject(2, otherUserId); + stmtMsg.setObject(3, otherUserId); + stmtMsg.setObject(4, currentUserId); + stmtMsg.executeUpdate(); + + stmtChat.setObject(1, currentUserId); + stmtChat.setObject(2, otherUserId); + stmtChat.setObject(3, otherUserId); + stmtChat.setObject(4, currentUserId); + stmtChat.executeUpdate(); + + conn.commit(); + return true; + + } catch (SQLException e) { + conn.rollback(); + e.printStackTrace(); + return false; + } + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } } diff --git a/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java index 1cbc98d..d8313a4 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/GroupDatabase.java @@ -1,5 +1,7 @@ package org.to.telegramfinalproject.Database; +import org.json.JSONArray; +import org.json.JSONObject; import org.to.telegramfinalproject.Models.Group; import java.sql.Connection; @@ -284,4 +286,223 @@ public class GroupDatabase { } + public static boolean addOwnerToGroup(UUID groupId, UUID userId) { + String sql = "INSERT INTO group_members (group_id, user_id, role) VALUES (?, ?, 'owner')"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public static boolean addAdminToGroup(UUID groupId, UUID userId, JSONObject permissions) { + String sql = """ + UPDATE group_members + SET role = 'admin', + permissions = ?::jsonb + WHERE group_id = ? AND user_id = ? AND role = 'member' + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setString(1, permissions.toString()); + stmt.setObject(2, groupId); + stmt.setObject(3, userId); + + return stmt.executeUpdate() > 0; + + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + + public static String getGroupRole(UUID groupId, UUID userId) { + String sql = "SELECT role FROM group_members WHERE group_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + return rs.getString("role"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return "member"; // پیش‌فرض + } + + + + + + public static boolean updateGroupAdminPermissions(UUID groupId, UUID userId, JSONObject permissions) { + String sql = """ + UPDATE group_members + SET permissions = ?::jsonb + WHERE group_id = ? AND user_id = ? AND role = 'admin' + """; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, permissions.toString()); + stmt.setObject(2, groupId); + stmt.setObject(3, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + + public static List getGroupAdminsAndOwner(UUID groupId) { + String sql = "SELECT user_id, role, permissions FROM group_members WHERE group_id = ? AND role IN ('owner', 'admin')"; + List admins = new ArrayList<>(); + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + JSONObject obj = new JSONObject(); + obj.put("user_id", rs.getObject("user_id").toString()); + obj.put("role", rs.getString("role")); + obj.put("permissions", new JSONObject(rs.getString("permissions"))); + admins.add(obj); + } + } catch (Exception e) { + e.printStackTrace(); + } + return admins; + } + + + public static boolean isOwner(UUID groupId, UUID userId) { + String sql = "SELECT 1 FROM group_members WHERE group_id = ? AND user_id = ? AND role = 'owner'"; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + + ResultSet rs = stmt.executeQuery(); + return rs.next(); // اگر رکوردی پیدا شد یعنی owner است + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + public static boolean isAdmin(UUID groupId, UUID userId) { + String sql = "SELECT role FROM group_members WHERE group_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + String role = rs.getString("role"); + return "admin".equals(role) || "owner".equals(role); // owner هم admin هست + } + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + public static JSONObject getGroupPermissions(UUID groupId, UUID userId) { + String sql = "SELECT permissions FROM group_members WHERE group_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + ResultSet rs = stmt.executeQuery(); + if (rs.next()) { + String permissions = rs.getString("permissions"); + if (permissions != null && !permissions.isBlank()) { + return new JSONObject(permissions); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return new JSONObject(); + } + + + + public static JSONArray getGroupMembers(UUID groupId) { + String sql = """ + SELECT u.profile_name, u.user_id, u.internal_uuid, gm.role, gm.permissions + FROM group_members gm + JOIN users u ON gm.user_id = u.internal_uuid + WHERE gm.group_id = ? + """; + + JSONArray members = new JSONArray(); + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + + stmt.setObject(1, groupId); + ResultSet rs = stmt.executeQuery(); + + while (rs.next()) { + JSONObject member = new JSONObject(); + member.put("profile_name", rs.getString("profile_name")); + member.put("user_id", rs.getString("user_id")); // آیدی قابل نمایش + member.put("internal_uuid", rs.getObject("internal_uuid").toString()); + member.put("role", rs.getString("role")); + + String permissions = rs.getString("permissions"); + if (permissions != null && !permissions.isBlank()) { + member.put("permissions", new JSONObject(permissions)); + } + + members.put(member); + } + + return members; + + } catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + + public static boolean demoteAdminToMember(UUID groupId, UUID userId) { + String sql = "UPDATE group_members SET role = 'member', permissions = '{}'::jsonb WHERE group_id = ? AND user_id = ? AND role = 'admin'"; + try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + public static boolean removeMemberFromGroup(UUID groupId, UUID userId) { + String sql = "DELETE FROM group_members WHERE group_id = ? AND user_id = ?"; + try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + stmt.setObject(2, userId); + return stmt.executeUpdate() > 0; + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } + + } \ No newline at end of file diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java index 957b61d..22ef33b 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java @@ -217,6 +217,80 @@ public class MessageDatabase { return result; } + public static List privateChatHistory(UUID user1, UUID user2) { + List result = new ArrayList<>(); + String sql = """ + SELECT * FROM messages + WHERE receiver_type = 'private' + AND ( + (sender_id = ? AND receiver_id = ?) + OR (sender_id = ? AND receiver_id = ?) + ) + ORDER BY send_at + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, user1); + stmt.setObject(2, user2); + stmt.setObject(3, user2); + stmt.setObject(4, user1); + + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + result.add(extractMessage(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } + + + + public static List groupChatHistory(UUID groupId) { + List result = new ArrayList<>(); + String sql = """ + SELECT * FROM messages + WHERE receiver_type = 'group' AND receiver_id = ? + ORDER BY send_at + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, groupId); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + result.add(extractMessage(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } + + + public static List channelChatHistory(UUID channelId) { + List result = new ArrayList<>(); + String sql = """ + SELECT * FROM messages + WHERE receiver_type = 'channel' AND receiver_id = ? + ORDER BY send_at + """; + + try (Connection conn = ConnectionDb.connect(); + PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setObject(1, channelId); + ResultSet rs = stmt.executeQuery(); + while (rs.next()) { + result.add(extractMessage(rs)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return result; + } + public static List searchMessagesInGroups(List groupIds, String keyword) { List result = new ArrayList<>(); diff --git a/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java index e5f09b1..a330da4 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/userDatabase.java @@ -21,7 +21,7 @@ public class userDatabase { try { User var6; - try (Connection conn = this.getConnection()) { + try (Connection conn = ConnectionDb.connect()) { try (PreparedStatement stmt = conn.prepareStatement(query)) { stmt.setString(1, userId); ResultSet rs = stmt.executeQuery(); diff --git a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java index 52d3916..cbd0940 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java +++ b/src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java @@ -1,28 +1,70 @@ package org.to.telegramfinalproject.Models; +import org.json.JSONObject; + import java.time.LocalDateTime; +import java.util.UUID; public class ChatEntry { - private final String name; - private final String id; - private final String imageUrl; - private final String type; // "private", "group", "channel" - private final LocalDateTime lastMessageTime; + private UUID internalId; + private String displayId; + private String name; + private String imageUrl; + private String type; + private LocalDateTime lastMessageTime; - public ChatEntry(String name, String id, String imageUrl, String type, LocalDateTime lastMessageTime) { + // 🔹 نقش‌ها + private boolean isOwner = false; + private boolean isAdmin = false; + private JSONObject permissions; + + + public ChatEntry(UUID internalId, String displayId, String name, String imageUrl, String type, LocalDateTime lastMessageTime) { + this.internalId = internalId; + this.displayId = displayId; this.name = name; - this.id = id; this.imageUrl = imageUrl; this.type = type; this.lastMessageTime = lastMessageTime; } - public String getName() { - return name; + // ✅ کانستراکتور اضافه‌شده برای پشتیبانی از نقش‌ها (اختیاری، برای استفاده‌های جدید) + public ChatEntry(UUID internalId, String displayId, String name, String imageUrl, String type, LocalDateTime lastMessageTime, boolean isOwner, boolean isAdmin) { + this(internalId, displayId, name, imageUrl, type, lastMessageTime); + this.isOwner = isOwner; + this.isAdmin = isAdmin; + this.permissions = permissions; + } - public String getId() { - return id; + // 🟩 گتر و ستر جدید + public boolean isOwner() { + return isOwner; + } + + public void setOwner(boolean owner) { + isOwner = owner; + } + + public boolean isAdmin() { + return isAdmin; + } + + public void setAdmin(boolean admin) { + isAdmin = admin; + } + + // سایر گترها + public UUID getId() { + return internalId; + } + + public String getDisplayId() { + return displayId; + } + + public String getName() { + return name; } public String getImageUrl() { @@ -36,4 +78,13 @@ public class ChatEntry { public LocalDateTime getLastMessageTime() { return lastMessageTime; } + + + public JSONObject getPermissions() { + return permissions; + } + + public void setPermissions(JSONObject permissions) { + this.permissions = permissions; + } } diff --git a/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java b/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java index eba775f..a9ed199 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java +++ b/src/main/java/org/to/telegramfinalproject/Models/JsonUtil.java @@ -28,7 +28,7 @@ public class JsonUtil { public static JSONObject userToJson(User user) { JSONObject obj = new JSONObject(); - obj.put("internalUUID", user.getInternal_uuid().toString()); + obj.put("internal_uuid", user.getInternal_uuid().toString()); obj.put("user_id", user.getUser_id() != null ?user.getUser_id().toString() :JSONObject.NULL); obj.put("username", user.getUsername()); obj.put("profile_name", user.getProfile_name()); @@ -154,11 +154,15 @@ public class JsonUtil { for (ChatEntry entry : chatList) { JSONObject obj = new JSONObject(); - obj.put("id", entry.getId() != null ?entry.getId() :JSONObject.NULL); + obj.put("internal_id", entry.getId().toString()); + obj.put("id", entry.getDisplayId()); obj.put("name", entry.getName()); - obj.put("image_url", entry.getImageUrl() != null ?entry.getImageUrl() :JSONObject.NULL); + obj.put("image_url", entry.getImageUrl()); obj.put("type", entry.getType()); - obj.put("last_message_time", entry.getLastMessageTime() != null ? entry.getLastMessageTime().toString() : JSONObject.NULL); + obj.put("last_message_time", entry.getLastMessageTime() == null ? JSONObject.NULL : entry.getLastMessageTime().toString()); + obj.put("is_owner", entry.isOwner()); + obj.put("is_admin", entry.isAdmin()); + jsonArray.put(obj); } diff --git a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java index 9334717..372600b 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java +++ b/src/main/java/org/to/telegramfinalproject/Models/PrivateChat.java @@ -4,7 +4,7 @@ import java.time.LocalDateTime; import java.util.UUID; public class PrivateChat { - private UUID chat_id; + private final UUID chat_id; private UUID user1_id; private UUID user2_id; private LocalDateTime created_at; diff --git a/src/main/java/org/to/telegramfinalproject/Server/ChannelService.java b/src/main/java/org/to/telegramfinalproject/Server/ChannelService.java index a8f7a33..7901d62 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ChannelService.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ChannelService.java @@ -14,7 +14,7 @@ public class ChannelService { boolean inserted = ChannelDatabase.insertChannel(internalUUID, channelId, channelName, creatorUUID, imageUrl, now); if (inserted) { - ChannelDatabase.addSubscriber(internalUUID, creatorUUID); // اضافه کردن سازنده + ChannelDatabase.addSubscriber(internalUUID, creatorUUID,"owner"); return true; } return false; diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index 8fe3943..6bee449 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -4,6 +4,7 @@ import org.json.JSONArray; import org.json.JSONObject; import org.to.telegramfinalproject.Database.*; import org.to.telegramfinalproject.Models.*; +import org.to.telegramfinalproject.Utils.GroupPermissionUtil; import java.io.*; import java.net.Socket; @@ -92,17 +93,54 @@ public class ClientHandler implements Runnable { User target = userDatabase.findByInternalUUID(contact.getContact_id()); if (target == null) continue; LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(user.getInternal_uuid(), target.getInternal_uuid(), "private"); - chatList.add(new ChatEntry(target.getUser_id(), target.getProfile_name(), target.getImage_url(), "private", last)); + + chatList.add(new ChatEntry( + target.getInternal_uuid(), // internal UUID + target.getUser_id(), // public display ID + target.getProfile_name(), + target.getImage_url(), + "private", + last, + false, + false + )); } + for (Group group : groups) { LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group"); - chatList.add(new ChatEntry(group.getGroup_id(), group.getGroup_name(), group.getImage_url(), "group", last)); + boolean isOwner = GroupDatabase.isOwner(group.getInternal_uuid(), user.getInternal_uuid()); + boolean isAdmin = GroupDatabase.isAdmin(group.getInternal_uuid(), user.getInternal_uuid()); + + chatList.add(new ChatEntry( + group.getInternal_uuid(), + group.getGroup_id(), + group.getGroup_name(), + group.getImage_url(), + "group", + last, + isOwner, + isAdmin + )); } + for (Channel channel : channels) { LocalDateTime last = MessageDatabase.getLastMessageTime(channel.getInternal_uuid(), "channel"); - chatList.add(new ChatEntry(channel.getChannel_id(), channel.getChannel_name(), channel.getImage_url(), "channel", last)); + boolean isOwner = ChannelDatabase.isOwner(channel.getInternal_uuid(), user.getInternal_uuid()); + boolean isAdmin = ChannelDatabase.isAdmin(channel.getInternal_uuid(), user.getInternal_uuid()); + + chatList.add(new ChatEntry( + channel.getInternal_uuid(), + channel.getChannel_id(), + channel.getChannel_name(), + channel.getImage_url(), + "channel", + last, + isOwner, + isAdmin + )); } + chatList.sort((a, b) -> { if (a.getLastMessageTime() == null) return 1; if (b.getLastMessageTime() == null) return -1; @@ -115,21 +153,46 @@ public class ClientHandler implements Runnable { } break; } - case "logout": { String user_Id = requestJson.optString("user_id"); - if (userId != null && !user_Id.isEmpty()) { - UUID uuid = UUID.fromString(user_Id); - userDatabase.updateUserStatus(uuid, "offline"); - userDatabase.updateLastSeen(uuid); - SessionManager.removeUser(uuid); - response = new ResponseModel("success", "Logged out."); + if (user_Id != null && !user_Id.isEmpty()) { + try { + UUID uuid = UUID.fromString(user_Id); + userDatabase.updateUserStatus(uuid, "offline"); + userDatabase.updateLastSeen(uuid); + SessionManager.removeUser(uuid); + response = new ResponseModel("success", "Logged out."); + } catch (IllegalArgumentException e) { + response = new ResponseModel("error", "Invalid UUID format."); + } } else { response = new ResponseModel("error", "Invalid user_id for logout."); } break; } + + case "searchInUsers":{ + String keyword = requestJson.optString("keyword"); + List results = new ArrayList<>(); + String user_Id = requestJson.getString("user_id"); + User currentUser = new userDatabase().findByUserId(user_Id); + UUID currentUserUUID = currentUser.getInternal_uuid(); + + for (User u : new userDatabase().searchUsers(keyword, currentUserUUID)) { + JSONObject obj = new JSONObject(); + obj.put("type", "user"); + obj.put("id", u.getUser_id()); + obj.put("uuid", u.getInternal_uuid().toString()); + obj.put("name", u.getProfile_name()); + results.add(obj); + } + JSONObject data = new JSONObject(); + data.put("results", new JSONArray(results)); + response = new ResponseModel("success", "Search results found", data); + break; + } + case "search": { String keyword = requestJson.optString("keyword"); List results = new ArrayList<>(); @@ -228,7 +291,7 @@ public class ClientHandler implements Runnable { } case "join_group": { - UUID userUUID = UUID.fromString(requestJson.getString("user_id")); // مستقیم internalUUID دریافت می‌کنیم + UUID userUUID = UUID.fromString(requestJson.getString("user_id")); Group group = GroupDatabase.findByInternalUUID(UUID.fromString(requestJson.getString("id"))); if (group == null) { response = new ResponseModel("error", "Group not found."); @@ -244,7 +307,7 @@ public class ClientHandler implements Runnable { } case "join_channel": { - UUID userUUID = UUID.fromString(requestJson.getString("user_id")); // مستقیم internalUUID دریافت می‌کنیم + UUID userUUID = UUID.fromString(requestJson.getString("user_id")); Channel channel = ChannelDatabase.findByInternalUUID(UUID.fromString(requestJson.getString("id"))); if (channel == null) { response = new ResponseModel("error", "Channel not found."); @@ -261,57 +324,88 @@ public class ClientHandler implements Runnable { case "get_chat_info": { - String id = requestJson.getString("receiver_id"); - String type = requestJson.getString("receiver_type"); - JSONObject data = new JSONObject(); + try { + String id = requestJson.getString("receiver_id"); + String type = requestJson.getString("receiver_type"); + JSONObject data = new JSONObject(); - switch (type) { - case "private" -> { - User u = new userDatabase().findByUserId(id); - if (u != null) { - data.put("name", u.getProfile_name()); - data.put("image_url", u.getImage_url()); - } else { - response = new ResponseModel("error", "User not found."); - break; + switch (type) { + case "private" -> { + userDatabase userDatabase = new userDatabase(); + User u = userDatabase.findByUserId(id); + if (u == null) { + try { + UUID uuid = UUID.fromString(id); + u = userDatabase.findByInternalUUID(uuid); + } catch (IllegalArgumentException ignored) {} + } + + if (u != null) { + data.put("internal_id", u.getInternal_uuid().toString()); + data.put("name", u.getProfile_name()); + data.put("image_url", u.getImage_url()); + data.put("type", "private"); + data.put("id", u.getUser_id()); + } else { + response = new ResponseModel("error", "User not found."); + break; + } } - } - case "group" -> { - Group g = GroupDatabase.findByGroupId(id); - if (g != null) { - data.put("name", g.getGroup_name()); - data.put("image_url", g.getImage_url()); - } else { - response = new ResponseModel("error", "Group not found."); - break; + case "group" -> { + Group group = GroupDatabase.findByInternalUUID(UUID.fromString(id)); + if (group != null) { + data.put("internal_id", group.getInternal_uuid().toString()); + data.put("name", group.getGroup_name()); + data.put("image_url", group.getImage_url()); + data.put("type", "group"); + data.put("id", group.getGroup_id()); + + // اضافه کردن owner و admin بودن + boolean isOwner = GroupDatabase.isOwner(group.getInternal_uuid(), currentUser.getInternal_uuid()); + boolean isAdmin = GroupDatabase.isAdmin(group.getInternal_uuid(), currentUser.getInternal_uuid()); + + data.put("is_owner", isOwner); + data.put("is_admin", isAdmin); + } else { + response = new ResponseModel("error", "Group not found."); + break; + } } - } - case "channel" -> { - Channel c = ChannelDatabase.findByChannelId(id); - if (c != null) { - data.put("name", c.getChannel_name()); - data.put("image_url", c.getImage_url()); - } else { - response = new ResponseModel("error", "Channel not found."); + + + case "channel" -> { + Channel channel = ChannelDatabase.findByChannelId(id); + if (channel != null) { + data.put("internal_id", channel.getInternal_uuid().toString()); + data.put("name", channel.getChannel_name()); + data.put("image_url", channel.getImage_url()); + data.put("type", "channel"); + data.put("id", channel.getChannel_id()); + } else { + response = new ResponseModel("error", "Channel not found."); + break; + } + } + + default -> { + response = new ResponseModel("error", "Unknown type."); break; } } - default -> { - response = new ResponseModel("error", "Unknown type."); - break; + if (response == null) { + response = new ResponseModel("success", "Chat info fetched", data); } + + } catch (Exception e) { + response = new ResponseModel("error", "Error fetching chat info: " + e.getMessage()); } - - if (data.has("name")) { - response = new ResponseModel("success", "Chat info fetched", data); - } - break; } + case "get_chat_list": { String userIdStr = requestJson.getString("user_id"); User user = new userDatabase().findByUserId(userIdStr); @@ -321,25 +415,58 @@ public class ClientHandler implements Runnable { List channels = ChannelDatabase.getChannelsByUser(user.getInternal_uuid()); List chatList = new ArrayList<>(); - for (Contact contact : contacts) { User target = userDatabase.findByInternalUUID(contact.getContact_id()); if (target == null) continue; LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(user.getInternal_uuid(), target.getInternal_uuid(), "private"); - chatList.add(new ChatEntry(target.getUser_id(), target.getProfile_name(), target.getImage_url(), "private", last)); + chatList.add(new ChatEntry( + target.getInternal_uuid(), // internal UUID + target.getUser_id(), // public display ID + target.getProfile_name(), + target.getImage_url(), + "private", + last, + false, + false + )); } for (Group group : groups) { LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group"); - chatList.add(new ChatEntry(group.getGroup_id(), group.getGroup_name(), group.getImage_url(), "group", last)); + boolean isOwner = GroupDatabase.isOwner(group.getInternal_uuid(), user.getInternal_uuid()); + boolean isAdmin = GroupDatabase.isAdmin(group.getInternal_uuid(), user.getInternal_uuid()); + + chatList.add(new ChatEntry( + group.getInternal_uuid(), + group.getGroup_id(), + group.getGroup_name(), + group.getImage_url(), + "group", + last, + isOwner, + isAdmin + )); } for (Channel channel : channels) { LocalDateTime last = MessageDatabase.getLastMessageTime(channel.getInternal_uuid(), "channel"); - chatList.add(new ChatEntry(channel.getChannel_id(), channel.getChannel_name(), channel.getImage_url(), "channel", last)); + boolean isOwner = ChannelDatabase.isOwner(channel.getInternal_uuid(), user.getInternal_uuid()); + boolean isAdmin = ChannelDatabase.isAdmin(channel.getInternal_uuid(), user.getInternal_uuid()); + + chatList.add(new ChatEntry( + channel.getInternal_uuid(), + channel.getChannel_id(), + channel.getChannel_name(), + channel.getImage_url(), + "channel", + last, + isOwner, + isAdmin + )); } + chatList.sort((a, b) -> { if (a.getLastMessageTime() == null) return 1; if (b.getLastMessageTime() == null) return -1; @@ -354,7 +481,7 @@ public class ClientHandler implements Runnable { } case "create_group": { try { - String groupId = requestJson.getString("group_id"); // ID نمایشی + String groupId = requestJson.getString("group_id"); String groupName = requestJson.getString("group_name"); String userIdStr = requestJson.getString("user_id"); String imageUrl = requestJson.optString("image_url", null); @@ -363,16 +490,30 @@ public class ClientHandler implements Runnable { boolean created = GroupService.createGroup(groupId, groupName, creatorUUID, imageUrl); - response = created - ? new ResponseModel("success", "Group created.") - : new ResponseModel("error", "Group creation failed."); + if (created) { + Group createdGroup = GroupDatabase.findByGroupId(groupId); + if (createdGroup != null) { + JSONObject data = new JSONObject(); + data.put("internal_id", createdGroup.getInternal_uuid().toString()); + data.put("id", createdGroup.getGroup_id()); + data.put("name", createdGroup.getGroup_name()); + data.put("image_url", createdGroup.getImage_url()); + data.put("type", "group"); + response = new ResponseModel("success", "Group created.", data); + } else { + response = new ResponseModel("error", "Group created but not found."); + } + } else { + response = new ResponseModel("error", "Group creation failed."); + } } catch (Exception e) { response = new ResponseModel("error", "Error creating group: " + e.getMessage()); } break; } + case "create_channel": { try { String channelId = requestJson.getString("channel_id"); @@ -384,9 +525,23 @@ public class ClientHandler implements Runnable { boolean created = ChannelService.createChannel(channelId, channelName, creatorUUID, imageUrl); - response = created - ? new ResponseModel("success", "Channel created.") - : new ResponseModel("error", "Channel creation failed."); + if (created) { + Channel createdChannel = ChannelDatabase.findByChannelId(channelId); + if (createdChannel != null) { + JSONObject data = new JSONObject(); + data.put("internal_id", createdChannel.getInternal_uuid().toString()); + data.put("id", createdChannel.getChannel_id()); + data.put("name", createdChannel.getChannel_name()); + data.put("image_url", createdChannel.getImage_url()); + data.put("type", "channel"); + + response = new ResponseModel("success", "Channel created.", data); + } else { + response = new ResponseModel("error", "Channel created but not found."); + } + } else { + response = new ResponseModel("error", "Channel creation failed."); + } } catch (Exception e) { response = new ResponseModel("error", "Error creating channel: " + e.getMessage()); @@ -395,6 +550,319 @@ public class ClientHandler implements Runnable { } + case "add_admin_to_channel": { + UUID channelId = UUID.fromString(requestJson.getString("channel_id")); + UUID targetUserId = UUID.fromString(requestJson.getString("target_user_id")); + JSONObject permissions = requestJson.optJSONObject("permissions"); + + //if (!ChannelPermissionUtil.canAddAdmins(channelId, currentUser.getInternal_uuid())) { + //response = new ResponseModel("error", "You are not allowed to add admins to the channel."); + //break; + //} + + + boolean success = ChannelDatabase.addAdminToChannel(channelId, targetUserId, permissions); + response = success + ? new ResponseModel("success", "Admin added to channel.") + : new ResponseModel("error", "Failed to add admin."); + break; + } + + + + case "edit_channel_admin_permissions": { + UUID channelId = UUID.fromString(requestJson.getString("channel_id")); + UUID targetUserId = UUID.fromString(requestJson.getString("target_user_id")); + JSONObject permissions = requestJson.optJSONObject("permissions"); + + String role = ChannelDatabase.getChannelRole(channelId, currentUser.getInternal_uuid()); + if (!role.equals("owner")) { + response = new ResponseModel("error", "Only owner can update admin permissions."); + break; + } + + boolean success = ChannelDatabase.updateChannelAdminPermissions(channelId, targetUserId, permissions); + response = success + ? new ResponseModel("success", "Permissions updated.") + : new ResponseModel("error", "Failed to update permissions."); + break; + } + + + case "view_channel_admins": { + UUID channelId = UUID.fromString(requestJson.getString("channel_id")); + + //if (!ChannelPermissionUtil.canAddAdmins(channelId, currentUser.getInternal_uuid())) { + // response = new ResponseModel("error", "You are not allowed to add admins to the channel."); + //break; + //} + + + List admins = ChannelDatabase.getChannelAdminsAndOwner(channelId); + JSONObject data = new JSONObject(); + data.put("admins", new JSONArray(admins)); + response = new ResponseModel("success", "Admins fetched.", data); + break; + } + + + + case "add_admin_to_group": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + UUID targetUserId = UUID.fromString(requestJson.getString("user_id")); + JSONObject permissions = requestJson.optJSONObject("permissions"); + + if (!GroupPermissionUtil.canAddAdmins(groupId, currentUser.getInternal_uuid())) { + response = new ResponseModel("error", "You are not allowed to add admins."); + break; + } + + + boolean success = GroupDatabase.addAdminToGroup(groupId, targetUserId, permissions); + response = success + ? new ResponseModel("success", "Admin added to group.") + : new ResponseModel("error", "Failed to add admin."); + break; + } + + + case "edit_group_admin_permissions": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + UUID targetUserId = UUID.fromString(requestJson.getString("target_user_id")); + JSONObject permissions = requestJson.optJSONObject("permissions"); + + if (!GroupPermissionUtil.canAddAdmins(groupId, currentUser.getInternal_uuid())) { + response = new ResponseModel("error", "You are not allowed to edit admin permissions."); + break; + } + + boolean success = GroupDatabase.updateGroupAdminPermissions(groupId, targetUserId, permissions); + response = success + ? new ResponseModel("success", "Permissions updated.") + : new ResponseModel("error", "Failed to update permissions."); + break; + } + + + case "remove_admin_from_group": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + String targetUserIdStr = requestJson.getString("user_id"); + + // تبدیل user_id نمایشی به internal_uuid واقعی + User targetUser = new userDatabase().findByUserId(targetUserIdStr); + if (targetUser == null) { + response = new ResponseModel("error", "User not found."); + break; + } + + UUID targetUserUUID = targetUser.getInternal_uuid(); + + if (!GroupPermissionUtil.canRemoveAdmins(groupId, currentUser.getInternal_uuid())) { + response = new ResponseModel("error", "You are not allowed to remove admins."); + break; + } + + String targetRole = GroupDatabase.getGroupRole(groupId, targetUserUUID); + if (targetRole.equals("owner")) { + response = new ResponseModel("error", "You cannot remove the owner."); + break; + } + if (!targetRole.equals("admin")) { + response = new ResponseModel("error", "Target user is not an admin."); + break; + } + + boolean success = GroupDatabase.demoteAdminToMember(groupId, targetUserUUID); + response = success + ? new ResponseModel("success", "Admin removed successfully.") + : new ResponseModel("error", "Failed to remove admin."); + break; + } + + + + case "add_member_to_group": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + UUID targetUserId = UUID.fromString(requestJson.getString("user_id")); + + if (!GroupPermissionUtil.canAddMembers(groupId, currentUser.getInternal_uuid())) { + response = new ResponseModel("error", "You are not allowed to add members."); + break; + } + + if (GroupDatabase.isUserInGroup(targetUserId, groupId)) { + response = new ResponseModel("error", "User is already a member."); + break; + } + + boolean success = GroupDatabase.addMemberToGroup(targetUserId, groupId); + response = success + ? new ResponseModel("success", "Member added to group.") + : new ResponseModel("error", "Failed to add member."); + break; + } + + + case "remove_member_from_group": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + UUID targetUserId = UUID.fromString(requestJson.getString("user_id")); + + if (!GroupPermissionUtil.canRemoveMembers(groupId, currentUser.getInternal_uuid())) { + response = new ResponseModel("error", "You are not allowed to remove members."); + break; + } + + String targetRole = GroupDatabase.getGroupRole(groupId, targetUserId); + if (targetRole.equals("owner") || targetRole.equals("admin")) { + response = new ResponseModel("error", "You cannot remove admins or owner this way."); + break; + } + + boolean success = GroupDatabase.removeMemberFromGroup(groupId, targetUserId); + response = success + ? new ResponseModel("success", "Member removed from group.") + : new ResponseModel("error", "Failed to remove member."); + break; + } + + + case "view_group_admins": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + + String role = GroupDatabase.getGroupRole(groupId, currentUser.getInternal_uuid()); + if (!role.equals("owner") && !role.equals("admin")) { + response = new ResponseModel("error", "You are not authorized to view admins."); + break; + } + + List admins = GroupDatabase.getGroupAdminsAndOwner(groupId); + JSONObject data = new JSONObject(); + data.put("admins", new JSONArray(admins)); + response = new ResponseModel("success", "Admins fetched.", data); + break; + } + + + case "get_messages": { + try { + String receiverId = requestJson.getString("receiver_id"); + String receiverType = requestJson.getString("receiver_type"); + + List messages = new ArrayList<>(); + + switch (receiverType) { + case "private" -> { + // تبدیل user_id به internal_uuid + User otherUser = new userDatabase().findByInternalUUID(UUID.fromString(receiverId)); + if (otherUser == null) { + response = new ResponseModel("error", "User not found."); + break; + } + messages = MessageDatabase.privateChatHistory(currentUser.getInternal_uuid(), otherUser.getInternal_uuid()); + } + case "group" -> { + Group group = GroupDatabase.findByInternalUUID(UUID.fromString(receiverId)); + if (group == null) { + response = new ResponseModel("error", "Group not found."); + break; + } + messages = MessageDatabase.groupChatHistory(group.getInternal_uuid()); + } + + + case "channel" -> { + Channel channel = ChannelDatabase.findByChannelId(receiverId); + if (channel == null) { + response = new ResponseModel("error", "Channel not found."); + break; + } + messages = MessageDatabase.channelChatHistory(channel.getInternal_uuid()); + } + default -> { + response = new ResponseModel("error", "Invalid receiver type."); + break; + } + } + + if (response == null) { + JSONArray messageArray = new JSONArray(); + for (Message m : messages) { + JSONObject obj = new JSONObject(); + obj.put("id", m.getMessage_id().toString()); + obj.put("sender_id", m.getSender_id().toString()); + obj.put("receiver_id", m.getReceiver_id().toString()); + obj.put("receiver_type", m.getReceiver_type()); + obj.put("content", m.getContent()); + obj.put("send_at", m.getSend_at().toString()); + messageArray.put(obj); + } + + JSONObject data = new JSONObject(); + data.put("messages", messageArray); + + response = new ResponseModel("success", "Messages fetched.", data); + } + + } catch (Exception e) { + response = new ResponseModel("error", "Error fetching messages: " + e.getMessage()); + } + break; + } + + case "toggle_block": { + try { + UUID userUUID = UUID.fromString(requestJson.getString("user_id")); + UUID targetUUID = UUID.fromString(requestJson.getString("target_id")); + + boolean isBlocked = ContactDatabase.toggleBlock(userUUID, targetUUID); + + String message = isBlocked ? "🔒 User blocked successfully." : "🔓 User unblocked successfully."; + response = new ResponseModel("success", message); + + } catch (Exception e) { + response = new ResponseModel("error", "Error processing block/unblock: " + e.getMessage()); + } + break; + } + + + + case "view_group_members" : { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + + JSONArray members = GroupDatabase.getGroupMembers(groupId); + + if (members != null) { + JSONObject data = new JSONObject(); + data.put("members", members); + response = new ResponseModel("success", "Members fetched successfully.", data); + } else { + response = new ResponseModel("error", "Failed to fetch members."); + } + break; + } + + + + case "delete_private_chat" : { + UUID targetId = UUID.fromString(requestJson.getString("target_id")); + boolean both = requestJson.getBoolean("both"); + response = PrivateChatService.deletePrivateChat(currentUser.getInternal_uuid(), targetId, both); + break; + } + + + case "get_group_permissions": { + UUID groupId = UUID.fromString(requestJson.getString("group_id")); + userId = currentUser.getInternal_uuid(); + + JSONObject permissions = GroupDatabase.getGroupPermissions(groupId, userId); + + response = new ResponseModel("success", "Permissions fetched.", permissions); + break; + } + + diff --git a/src/main/java/org/to/telegramfinalproject/Server/GroupService.java b/src/main/java/org/to/telegramfinalproject/Server/GroupService.java index 31da28b..1d79728 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/GroupService.java +++ b/src/main/java/org/to/telegramfinalproject/Server/GroupService.java @@ -14,10 +14,13 @@ public class GroupService { boolean inserted = GroupDatabase.insertGroup(internalUUID, groupId, groupName, creatorUUID, imageUrl, now); if (inserted) { - GroupDatabase.addMember(internalUUID, creatorUUID, "owner"); // سازنده owner می‌شود + GroupDatabase.addMember(internalUUID, creatorUUID, "owner"); return true; } return false; } + + + } diff --git a/src/main/java/org/to/telegramfinalproject/Server/PrivateChatService.java b/src/main/java/org/to/telegramfinalproject/Server/PrivateChatService.java new file mode 100644 index 0000000..190da7c --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Server/PrivateChatService.java @@ -0,0 +1,22 @@ +package org.to.telegramfinalproject.Server; + + +import org.to.telegramfinalproject.Database.ContactDatabase; +import org.to.telegramfinalproject.Models.ResponseModel; + +import java.util.UUID; + +public class PrivateChatService { + + public static ResponseModel deletePrivateChat(UUID currentUserId, UUID targetUserId, boolean both) { + boolean success = both ? + ContactDatabase.deleteChatBoth(currentUserId, targetUserId) : + ContactDatabase.deleteChatOneSide(currentUserId, targetUserId); + + if (success) { + return new ResponseModel("success", "Chat deleted successfully"); + } else { + return new ResponseModel("error", "Chat not found or failed to delete"); + } + } +} diff --git a/src/main/java/org/to/telegramfinalproject/Server/RealTimeEventDispatcher.java b/src/main/java/org/to/telegramfinalproject/Server/RealTimeEventDispatcher.java index d2a341f..68e89ba 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/RealTimeEventDispatcher.java +++ b/src/main/java/org/to/telegramfinalproject/Server/RealTimeEventDispatcher.java @@ -24,6 +24,8 @@ public class RealTimeEventDispatcher { PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + System.out.println("🚀 Sending to user: " + userId + " → " + data); + out.println(data.toString()); } catch (IOException e) { System.err.println("❌ Error sending to user: " + e.getMessage()); diff --git a/src/main/java/org/to/telegramfinalproject/Utils/GroupPermissionUtil.java b/src/main/java/org/to/telegramfinalproject/Utils/GroupPermissionUtil.java new file mode 100644 index 0000000..8bcb3f5 --- /dev/null +++ b/src/main/java/org/to/telegramfinalproject/Utils/GroupPermissionUtil.java @@ -0,0 +1,59 @@ +package org.to.telegramfinalproject.Utils; + +import org.json.JSONObject; +import org.to.telegramfinalproject.Database.GroupDatabase; + +import java.util.UUID; + +public class GroupPermissionUtil { + + public static boolean canAddMembers(UUID groupId, UUID userId) { + String role = GroupDatabase.getGroupRole(groupId, userId); + if (role.equals("owner")) return true; + if (role.equals("admin")) { + JSONObject permissions = GroupDatabase.getGroupPermissions(groupId, userId); + return permissions.optBoolean("can_add_members", false); + } + return false; + } + + public static boolean canRemoveMembers(UUID groupId, UUID userId) { + String role = GroupDatabase.getGroupRole(groupId, userId); + if (role.equals("owner")) return true; + if (role.equals("admin")) { + JSONObject permissions = GroupDatabase.getGroupPermissions(groupId, userId); + return permissions.optBoolean("can_remove_members", false); + } + return false; + } + + public static boolean canAddAdmins(UUID groupId, UUID userId) { + String role = GroupDatabase.getGroupRole(groupId, userId); + if (role.equals("owner")) return true; + if (role.equals("admin")) { + JSONObject permissions = GroupDatabase.getGroupPermissions(groupId, userId); + return permissions.optBoolean("can_add_admins", false); + } + return false; + } + + public static boolean canRemoveAdmins(UUID groupId, UUID userId) { + String role = GroupDatabase.getGroupRole(groupId, userId); + if (role.equals("owner")) return true; + if (role.equals("admin")) { + JSONObject permissions = GroupDatabase.getGroupPermissions(groupId, userId); + return permissions.optBoolean("can_remove_admins", false); + } + return false; + } + + public static boolean canEditGroup(UUID groupId, UUID userId) { + String role = GroupDatabase.getGroupRole(groupId, userId); + if (role.equals("owner")) return true; + if (role.equals("admin")) { + JSONObject permissions = GroupDatabase.getGroupPermissions(groupId, userId); + return permissions.optBoolean("can_edit_group", false); + } + return false; + } +}