Merge branch 'develop' into ArchivedChats
# Conflicts: # src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java # src/main/java/org/to/telegramfinalproject/Client/Session.java # src/main/java/org/to/telegramfinalproject/Models/ChatEntry.java # src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,38 @@
|
||||
package org.to.telegramfinalproject.Client;
|
||||
|
||||
public class EventProcessorThread extends Thread {
|
||||
private final ActionHandler handler;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public EventProcessorThread(ActionHandler handler) {
|
||||
this.handler = handler;
|
||||
setDaemon(true);
|
||||
}
|
||||
import java.io.BufferedReader;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
handler.processIncomingEvents();
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
//public class EventProcessorThread extends Thread {
|
||||
// private final ActionHandler handler;
|
||||
// private final BufferedReader in;
|
||||
//
|
||||
// public EventProcessorThread(ActionHandler handler, BufferedReader in) {
|
||||
// this.handler = handler;
|
||||
// this.in = in;
|
||||
// setDaemon(true);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void run() {
|
||||
// try {
|
||||
// System.out.println("👂 Real-Time Listener started.");
|
||||
// String line;
|
||||
// while ((line = in.readLine()) != null) {
|
||||
// JSONObject json = new JSONObject(line);
|
||||
// System.out.println("📥 Received raw line: " + line);
|
||||
//
|
||||
// if (json.has("action")) {
|
||||
// // پیام real-time
|
||||
// handler.processIncomingEvent(json);
|
||||
// } else {
|
||||
// // پیام پاسخ معمولی
|
||||
// TelegramClient.responseQueue.put(json);
|
||||
// }
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// System.err.println("❌ Error in EventProcessorThread: " + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package org.to.telegramfinalproject.Client;
|
||||
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
public class IncomingMessageListener implements Runnable {
|
||||
private final BufferedReader in;
|
||||
@@ -19,25 +23,52 @@ public class IncomingMessageListener implements Runnable {
|
||||
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
|
||||
|
||||
JSONObject response = new JSONObject(line);
|
||||
System.out.println("📥 Received raw line: " + line);
|
||||
|
||||
//if it has reqID answer
|
||||
if (response.has("request_id")) {
|
||||
String requestId = response.getString("request_id");
|
||||
System.out.println("📬 Response with request_id: " + requestId);
|
||||
System.out.println("📬 Full response: " + response.toString(2));
|
||||
|
||||
BlockingQueue<JSONObject> queue = TelegramClient.pendingResponses.get(requestId);
|
||||
if (queue != null) {
|
||||
queue.put(response);
|
||||
} else {
|
||||
System.out.println("⚠️ No pending queue for request_id = " + requestId + ". Putting in responseQueue...");
|
||||
TelegramClient.responseQueue.put(response);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//if it has action check it
|
||||
if (response.has("action")) {
|
||||
String action = response.getString("action");
|
||||
System.out.println("🎯 [Listener] Action received: " + response.toString(2));
|
||||
System.out.println("🎯 Received action: " + action);
|
||||
|
||||
if (isRealTimeEvent(action)) {
|
||||
handleRealTimeEvent(response);
|
||||
} else {
|
||||
TelegramClient.responseQueue.put(response);
|
||||
}
|
||||
|
||||
} else if (response.has("status") && response.has("message")) {
|
||||
TelegramClient.responseQueue.put(response);
|
||||
TelegramClient.responseQueue.put(response); // general answer
|
||||
} else {
|
||||
TelegramClient.responseQueue.put(response);
|
||||
TelegramClient.responseQueue.put(response); // fallback
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("🔴 Listener stopped: " + e.getMessage());
|
||||
System.out.println("🔴 [Listener] Crashed due to: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,15 +77,157 @@ public class IncomingMessageListener implements Runnable {
|
||||
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;
|
||||
"blocked_by_user", "unblocked_by_user", "message_seen",
|
||||
"removed_from_group", "removed_from_channel",
|
||||
"became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated" -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private void handleRealTimeEvent(JSONObject response) {
|
||||
void handleRealTimeEvent(JSONObject response) throws IOException {
|
||||
String action = response.getString("action");
|
||||
JSONObject msg = response.getJSONObject("data");
|
||||
|
||||
|
||||
switch (action) {
|
||||
case "added_to_group", "added_to_channel",
|
||||
"removed_from_group", "removed_from_channel", "chat_deleted" -> {
|
||||
System.out.println("🔄 Chat list changed. Updating...");
|
||||
Session.forceRefreshChatList = true;
|
||||
System.out.println("🧪 Calling requestChatList() after being added");
|
||||
|
||||
String chatId = msg.getString("chat_id");
|
||||
String chatType = msg.getString("chat_type");
|
||||
ActionHandler.requestChatInfo(chatId, chatType);
|
||||
|
||||
if (action.equals("removed_from_group") || action.equals("removed_from_channel") || action.equals("chat_deleted")) {
|
||||
System.out.println("🚫 You were removed from the chat or chat was deleted. Exiting...");
|
||||
ActionHandler.forceExitChat = true;
|
||||
}
|
||||
}
|
||||
|
||||
case "chat_updated" -> {
|
||||
System.out.println("\n🔄 Group/Channel info updated.");
|
||||
new Thread(() -> {
|
||||
try {
|
||||
handleAdminRoleChanged(msg);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
case "became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated" -> {
|
||||
System.out.println("🧩 Detected admin/owner role change. Calling handler...");
|
||||
new Thread(() -> {
|
||||
try {
|
||||
handleAdminRoleChanged(msg); //new thread
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
default -> displayRealTimeMessage(action, msg);
|
||||
}
|
||||
|
||||
System.out.print(">> ");
|
||||
}
|
||||
|
||||
private void handleAdminRoleChanged(JSONObject data) throws IOException {
|
||||
String chatType = data.getString("chat_type");
|
||||
String chatId = data.optString("group_id", data.optString("channel_id", data.optString("chat_id", null)));
|
||||
|
||||
if (chatId == null) {
|
||||
System.out.println("⚠️ No valid ID found in real-time data: " + data.toString(2));
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("\n🔄 Your admin status changed. Updating chat info...");
|
||||
|
||||
try {
|
||||
// 1. get chat info
|
||||
JSONObject chatInfoReq = new JSONObject();
|
||||
chatInfoReq.put("action", "get_chat_info");
|
||||
chatInfoReq.put("receiver_id", chatId);
|
||||
chatInfoReq.put("receiver_type", chatType);
|
||||
System.out.println("📤 Sending get_chat_info: " + chatInfoReq);
|
||||
JSONObject chatInfoResp = ActionHandler.sendWithResponse(chatInfoReq);
|
||||
JSONObject chatData = chatInfoResp.getJSONObject("data");
|
||||
|
||||
UUID chatUUID = UUID.fromString(chatData.getString("internal_id"));
|
||||
|
||||
Optional<ChatEntry> entry = Session.chatList.stream()
|
||||
.filter(e -> e.getId().equals(chatUUID))
|
||||
.findFirst();
|
||||
|
||||
if (entry.isEmpty()) {
|
||||
System.out.println("❌ Chat not found in session.");
|
||||
return;
|
||||
}
|
||||
|
||||
entry.ifPresent(chat -> {
|
||||
chat.setAdmin(chatData.optBoolean("is_admin", false));
|
||||
chat.setOwner(chatData.optBoolean("is_owner", false));
|
||||
chat.setName(chatData.optString("name", ""));
|
||||
chat.setDisplayId(chatData.optString("id", ""));
|
||||
chat.setImageUrl(chatData.optString("image_url", ""));
|
||||
chat.setType(chatData.optString("type", ""));
|
||||
Session.currentChatEntry = chat;
|
||||
});
|
||||
|
||||
// 2. get permission
|
||||
JSONObject permissionReq = new JSONObject();
|
||||
if (chatType.equalsIgnoreCase("group")) {
|
||||
permissionReq.put("action", "get_group_permissions");
|
||||
permissionReq.put("group_id", chatId);
|
||||
} else {
|
||||
permissionReq.put("action", "get_channel_permissions");
|
||||
permissionReq.put("channel_id", chatId);
|
||||
}
|
||||
|
||||
JSONObject permissionResp = ActionHandler.sendWithResponse(permissionReq);
|
||||
JSONObject perm = permissionResp.getJSONObject("data");
|
||||
entry.ifPresent(chat -> chat.setPermissions(perm));
|
||||
|
||||
// 3. set currentChatId
|
||||
Session.currentChatId = chatUUID.toString();
|
||||
|
||||
System.out.println("🧪 Checking refresh conditions...");
|
||||
System.out.println("🔹 inChatMenu: " + Session.inChatMenu);
|
||||
System.out.println("🔹 currentChatId: " + Session.currentChatId);
|
||||
System.out.println("🔹 chatUUID: " + chatUUID);
|
||||
|
||||
if (Session.inChatMenu && Session.currentChatId != null && Session.currentChatId.equals(chatUUID.toString())) {
|
||||
synchronized (Session.class) {
|
||||
Session.refreshCurrentChatMenu = true;
|
||||
}
|
||||
System.out.println("✅ Admin status updated. Refreshing menu...");
|
||||
} else {
|
||||
System.out.println("❌ Refresh conditions not met.");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("❌ Exception while handling admin role change: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void displayRealTimeMessage(String action, JSONObject msg) {
|
||||
switch (action) {
|
||||
case "new_message" -> {
|
||||
System.out.println("\n🔔 New Message:");
|
||||
@@ -62,60 +235,36 @@ public class IncomingMessageListener implements Runnable {
|
||||
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.println(msg.toString(2));
|
||||
}
|
||||
}
|
||||
|
||||
System.out.print(">> ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.to.telegramfinalproject.Client;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||
|
||||
@@ -11,8 +12,18 @@ import java.util.UUID;
|
||||
// method for save data from server response
|
||||
public class Session {
|
||||
public static JSONObject currentUser;
|
||||
public static List<ChatEntry> chatList;
|
||||
public static List<UUID> archivedChatIds = new ArrayList<>();
|
||||
public static List<ChatEntry> chatList = new ArrayList<>();
|
||||
public static volatile boolean forceRefreshChatList = false;
|
||||
public static volatile boolean backToChatList = false;
|
||||
public static boolean inChatListMenu = false;
|
||||
public static String currentChatType = null;
|
||||
public static volatile boolean inChatMenu = false;
|
||||
public static volatile boolean refreshCurrentChatMenu = false;
|
||||
public static String currentChatId = null;
|
||||
public static ChatEntry currentChatEntry = null;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static String getUserUUID() {
|
||||
@@ -22,4 +33,26 @@ public class Session {
|
||||
throw new RuntimeException("❌ No UUID found in currentUser!");
|
||||
}
|
||||
|
||||
public static void updateChatList(JSONArray chatArray) {
|
||||
chatList.clear();
|
||||
for (int i = 0; i < chatArray.length(); i++) {
|
||||
JSONObject obj = chatArray.getJSONObject(i);
|
||||
ChatEntry entry = new ChatEntry(
|
||||
UUID.fromString(obj.getString("internal_id")),
|
||||
obj.optString("id", ""), // displayId
|
||||
obj.optString("name", ""), // name
|
||||
obj.optString("image_url", ""),
|
||||
obj.getString("type"),
|
||||
null, // last message time (if needed, parse it)
|
||||
obj.optBoolean("is_owner", false),
|
||||
obj.optBoolean("is_admin", false)
|
||||
);
|
||||
entry.setPermissions(obj.optJSONObject("permissions")); // اگر permissions وجود داره
|
||||
chatList.add(entry);
|
||||
}
|
||||
}
|
||||
public static List<ChatEntry> getChatList() {
|
||||
return chatList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,32 +7,44 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
public class TelegramClient {
|
||||
private static final String SERVER_HOST = "localhost";
|
||||
private static final int SERVER_PORT = 8000;
|
||||
private Socket socket;
|
||||
private static Socket socket;
|
||||
private BufferedReader in;
|
||||
private PrintWriter out;
|
||||
private final Scanner scanner;
|
||||
ActionHandler handler = null;
|
||||
private ActionHandler handler;
|
||||
public static BlockingQueue<JSONObject> responseQueue = new LinkedBlockingQueue<>();
|
||||
public static UUID loggedInUserId = null;
|
||||
public static final Map<String, BlockingQueue<JSONObject>> pendingResponses = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
private static TelegramClient instance;
|
||||
|
||||
public TelegramClient() {
|
||||
this.scanner = new Scanner(System.in);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
public static TelegramClient getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
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);
|
||||
socket = new Socket(SERVER_HOST, SERVER_PORT);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintWriter(socket.getOutputStream(), true);
|
||||
System.out.println("✅ Connected to Telegram Server");
|
||||
this.handler = new ActionHandler(this.out, this.in, this.scanner);
|
||||
handler = new ActionHandler(out, in, scanner);
|
||||
|
||||
Thread listenerThread = new Thread(new IncomingMessageListener(in));
|
||||
listenerThread.setDaemon(true);
|
||||
@@ -45,7 +57,7 @@ public class TelegramClient {
|
||||
}
|
||||
}
|
||||
|
||||
private void showMainMenu() {
|
||||
private void showMainMenu() throws IOException {
|
||||
while (true) {
|
||||
System.out.println("Main Menu:");
|
||||
System.out.println("1. Register");
|
||||
@@ -60,7 +72,13 @@ public class TelegramClient {
|
||||
handler.loginHandler();
|
||||
if (Session.currentUser != null) {
|
||||
System.out.println("✅ Login successful.");
|
||||
new Thread(new ActionHandler.ChatStateMonitor(out)).start();
|
||||
// new Thread(new ActionHandler.CurrentChatMenuRefresher(this.handler)).start();
|
||||
|
||||
|
||||
UUID internalId = UUID.fromString(Session.currentUser.getString("internal_uuid"));
|
||||
loggedInUserId = internalId;
|
||||
|
||||
handler.userMenu(internalId);
|
||||
} else {
|
||||
System.out.println("❌ Login failed.");
|
||||
@@ -75,7 +93,30 @@ public class TelegramClient {
|
||||
}
|
||||
}
|
||||
|
||||
public static void send(JSONObject req) {
|
||||
try {
|
||||
responseQueue.clear(); // optional: clear old responses
|
||||
getInstance().out.println(req.toString());
|
||||
System.out.println("📤 [SEND] " + req.toString(2));
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ Error sending request: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Socket getSocket() {
|
||||
return socket;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
new TelegramClient().start();
|
||||
}
|
||||
|
||||
public PrintWriter getOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -576,7 +576,7 @@ public class ChannelDatabase {
|
||||
|
||||
|
||||
public static boolean transferOwnership(UUID channelId, UUID newOwnerUUID) {
|
||||
String sql = """
|
||||
String updateRoles = """
|
||||
UPDATE channel_subscribers
|
||||
SET role = CASE
|
||||
WHEN user_id = ? THEN 'owner'
|
||||
@@ -586,15 +586,64 @@ public class ChannelDatabase {
|
||||
WHERE channel_id = ?
|
||||
""";
|
||||
|
||||
String clearPermissions = """
|
||||
UPDATE channel_subscribers
|
||||
SET permissions = '{}'::jsonb
|
||||
WHERE channel_id = ? AND user_id = ?
|
||||
""";
|
||||
|
||||
try (Connection conn = ConnectionDb.connect()) {
|
||||
conn.setAutoCommit(false);
|
||||
|
||||
try (PreparedStatement roleStmt = conn.prepareStatement(updateRoles);
|
||||
PreparedStatement clearPermsStmt = conn.prepareStatement(clearPermissions)) {
|
||||
|
||||
roleStmt.setObject(1, newOwnerUUID);
|
||||
roleStmt.setObject(2, channelId);
|
||||
roleStmt.executeUpdate();
|
||||
|
||||
clearPermsStmt.setObject(1, channelId);
|
||||
clearPermsStmt.setObject(2, newOwnerUUID);
|
||||
clearPermsStmt.executeUpdate();
|
||||
|
||||
conn.commit();
|
||||
return true;
|
||||
|
||||
} catch (SQLException e) {
|
||||
conn.rollback();
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static List<UUID> getChannelSubscriberUUIDs(UUID channelId) {
|
||||
List<UUID> subscriberIds = new ArrayList<>();
|
||||
try (Connection conn = ConnectionDb.connect();
|
||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||
PreparedStatement stmt = conn.prepareStatement("SELECT user_id FROM channel_subscribers WHERE channel_id = ?")) {
|
||||
stmt.setObject(1, channelId);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
subscriberIds.add(UUID.fromString(rs.getString("user_id")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return subscriberIds;
|
||||
}
|
||||
|
||||
stmt.setObject(1, newOwnerUUID);
|
||||
public static boolean updateAdminPermissions(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.executeUpdate();
|
||||
return true;
|
||||
|
||||
stmt.setObject(3, userId);
|
||||
return stmt.executeUpdate() > 0;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
|
||||
@@ -34,6 +34,15 @@ public class ContactDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<UUID> getContactUUIDs(UUID userId) {
|
||||
List<Contact> contacts = getContacts(userId);
|
||||
List<UUID> contactIds = new ArrayList<>();
|
||||
for (Contact c : contacts) {
|
||||
contactIds.add(c.getContact_id());
|
||||
}
|
||||
return contactIds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public boolean removeContact(UUID user_id, UUID contact_id) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.to.telegramfinalproject.Database;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Models.Group;
|
||||
import org.to.telegramfinalproject.Models.User;
|
||||
|
||||
import java.sql.*;
|
||||
import java.time.LocalDateTime;
|
||||
@@ -377,7 +378,7 @@ public class GroupDatabase {
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "member"; // پیشفرض
|
||||
return "member";
|
||||
}
|
||||
|
||||
|
||||
@@ -556,7 +557,7 @@ public class GroupDatabase {
|
||||
|
||||
public static boolean transferOwnership(UUID groupId, UUID newOwnerId) {
|
||||
String demoteOldOwner = "UPDATE group_members SET role = 'admin' WHERE group_id = ? AND role = 'owner'";
|
||||
String promoteNewOwner = "UPDATE group_members SET role = 'owner' WHERE group_id = ? AND user_id = ?";
|
||||
String promoteNewOwner = "UPDATE group_members SET role = 'owner', permissions = '{}'::jsonb WHERE group_id = ? AND user_id = ?";
|
||||
|
||||
try (Connection conn = ConnectionDb.connect()) {
|
||||
conn.setAutoCommit(false);
|
||||
@@ -600,4 +601,82 @@ public class GroupDatabase {
|
||||
}
|
||||
|
||||
|
||||
public static List<UUID> getGroupMemberUUIDs(UUID groupId) {
|
||||
List<UUID> memberIds = new ArrayList<>();
|
||||
try (Connection conn = ConnectionDb.connect();
|
||||
PreparedStatement stmt = conn.prepareStatement("SELECT user_id FROM group_members WHERE group_id = ?")) {
|
||||
stmt.setObject(1, groupId);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
while (rs.next()) {
|
||||
memberIds.add(UUID.fromString(rs.getString("user_id")));
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return memberIds;
|
||||
}
|
||||
|
||||
public static boolean updateAdminPermissions(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 boolean isMember(UUID groupId, UUID userId) {
|
||||
String sql = "SELECT 1 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();
|
||||
return rs.next();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// public static List<User> searchGroupMembers(UUID groupId, String keyword) {
|
||||
// List<User> result = new ArrayList<>();
|
||||
// String sql = """
|
||||
// SELECT u.*
|
||||
// FROM users u
|
||||
// JOIN group_members gm ON gm.user_id = u.internal_uuid
|
||||
// WHERE gm.group_id = ?
|
||||
// AND (
|
||||
// LOWER(u.profile_name) LIKE ?
|
||||
// OR LOWER(u.username) LIKE ?
|
||||
// OR LOWER(u.user_id) LIKE ?
|
||||
// )
|
||||
// """;
|
||||
//
|
||||
// try (Connection conn = ConnectionDb.connect();
|
||||
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||
//
|
||||
// stmt.setObject(1, groupId);
|
||||
// String likePattern = "%" + keyword.toLowerCase() + "%";
|
||||
// stmt.setString(2, likePattern);
|
||||
// stmt.setString(3, likePattern);
|
||||
// stmt.setString(4, likePattern);
|
||||
//
|
||||
// ResultSet rs = stmt.executeQuery();
|
||||
// while (rs.next()) {
|
||||
// User user = User.fromResultSet(rs);
|
||||
// result.add(user);
|
||||
// }
|
||||
//
|
||||
// } catch (SQLException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -9,13 +9,58 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class userDatabase {
|
||||
|
||||
|
||||
public userDatabase() {
|
||||
}
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
public static boolean isUserOnline(UUID userId) {
|
||||
String sql = "SELECT status FROM users WHERE internal_uuid = ?";
|
||||
|
||||
try (Connection conn = getConnection();
|
||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||
stmt.setObject(1, userId);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
String status = rs.getString("status");
|
||||
return "online".equalsIgnoreCase(status);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static Connection getConnection() throws SQLException {
|
||||
return ConnectionDb.connect();
|
||||
}
|
||||
|
||||
public static String getLastSeen(UUID userId) {
|
||||
String sql = "SELECT last_seen FROM users WHERE internal_uuid = ?";
|
||||
|
||||
try (Connection conn = getConnection();
|
||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||
stmt.setObject(1, userId);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
|
||||
if (rs.next()) {
|
||||
Timestamp lastSeen = rs.getTimestamp("last_seen");
|
||||
if (lastSeen != null) {
|
||||
return lastSeen.toLocalDateTime().toString();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
|
||||
public User findByUserId(String userId) {
|
||||
String query = "SELECT * FROM users WHERE user_id = ?";
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ public class ChatEntry {
|
||||
|
||||
}
|
||||
|
||||
public ChatEntry() {
|
||||
|
||||
}
|
||||
|
||||
// 🟩 گتر و ستر جدید
|
||||
public boolean isOwner() {
|
||||
return isOwner;
|
||||
@@ -54,7 +58,6 @@ public class ChatEntry {
|
||||
isAdmin = admin;
|
||||
}
|
||||
|
||||
// سایر گترها
|
||||
public UUID getId() {
|
||||
return internalId;
|
||||
}
|
||||
@@ -88,14 +91,21 @@ public class ChatEntry {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
private boolean archived = false;
|
||||
|
||||
public boolean isArchived() {
|
||||
return archived;
|
||||
public void setName(String name) {this.name = name;
|
||||
}
|
||||
|
||||
public void setArchived(boolean archived) {
|
||||
this.archived = archived;
|
||||
public void setDisplayId(String id) {this.displayId = id;
|
||||
}
|
||||
|
||||
public void setImageUrl(String image_url) {this.imageUrl = image_url;
|
||||
}
|
||||
|
||||
public void setType(String type) {this.type =type;
|
||||
}
|
||||
|
||||
public void setId(String internalId) {this.internalId = UUID.fromString(internalId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ public class ResponseModel {
|
||||
private String status;
|
||||
private String message;
|
||||
private JSONObject data;
|
||||
private String requestId;
|
||||
|
||||
|
||||
|
||||
public ResponseModel(String status, String message) {
|
||||
@@ -29,4 +31,20 @@ public class ResponseModel {
|
||||
}
|
||||
|
||||
public JSONObject getData() {return this.data;}
|
||||
|
||||
public void setRequestId(String requestId) {
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
|
||||
public JSONObject toJson() {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("status", this.status);
|
||||
json.put("message", this.message);
|
||||
json.put("data", this.data != null ? this.data : JSONObject.NULL);
|
||||
if (this.requestId != null) {
|
||||
json.put("request_id", this.requestId);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -176,7 +176,7 @@ public class RealTimeEventDispatcher {
|
||||
|
||||
public static void notifyAddedToChat(String type, UUID chatId, String chatName, String imageUrl, UUID userId) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("chat_type", type); // group یا channel
|
||||
data.put("chat_type", type);
|
||||
data.put("chat_id", chatId.toString());
|
||||
data.put("chat_name", chatName);
|
||||
data.put("image_url", imageUrl);
|
||||
@@ -246,5 +246,63 @@ public class RealTimeEventDispatcher {
|
||||
broadcastToUsers(contacts, event);
|
||||
}
|
||||
|
||||
public static void sendGroupOrChannelUpdate(String type, UUID chatId, String name, String imageUrl, String description, List<UUID> affectedUsers) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("chat_type", type);
|
||||
data.put("chat_id", chatId.toString());
|
||||
data.put("name", name);
|
||||
data.put("image_url", imageUrl);
|
||||
data.put("description", description != null ? description : "");
|
||||
|
||||
JSONObject event = new JSONObject();
|
||||
event.put("action", "chat_updated");
|
||||
event.put("data", data);
|
||||
|
||||
broadcastToUsers(affectedUsers, event);
|
||||
}
|
||||
|
||||
|
||||
public static void notifyBecameAdmin(String type, UUID chatId, String chatName, String imageUrl, UUID userId) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("chat_type", type); // group or channel
|
||||
data.put("chat_id", chatId.toString());
|
||||
data.put("chat_name", chatName);
|
||||
data.put("image_url", imageUrl);
|
||||
|
||||
JSONObject event = new JSONObject();
|
||||
event.put("action", "became_admin");
|
||||
event.put("data", data);
|
||||
|
||||
sendToUser(userId, event);
|
||||
}
|
||||
|
||||
|
||||
public static void notifyRemovedAdminFromChat(String type, UUID chatId, String chatName, String imageUrl, UUID userId) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("chat_type", type);
|
||||
data.put("chat_id", chatId.toString());
|
||||
data.put("chat_name", chatName);
|
||||
data.put("image_url", imageUrl);
|
||||
|
||||
JSONObject event = new JSONObject();
|
||||
event.put("action", "removed_admin");
|
||||
event.put("data", data);
|
||||
|
||||
sendToUser(userId, event);
|
||||
}
|
||||
|
||||
|
||||
public static void sendOwnershipTransferred(String type, UUID chatId, String chatName, List<UUID> affectedUsers) {
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("chat_type", type); // "group" or "channel"
|
||||
data.put("chat_id", chatId.toString());
|
||||
data.put("chat_name", chatName);
|
||||
|
||||
JSONObject event = new JSONObject();
|
||||
event.put("action", "ownership_transferred");
|
||||
event.put("data", data);
|
||||
|
||||
broadcastToUsers(affectedUsers, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.to.telegramfinalproject.UI;
|
||||
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Stage;
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Client.ClientConnection;
|
||||
import org.to.telegramfinalproject.Database.userDatabase;
|
||||
import org.to.telegramfinalproject.Models.User;
|
||||
import org.to.telegramfinalproject.Security.PasswordHashing;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class LoginForm {
|
||||
|
||||
@FXML
|
||||
private Button loginButton;
|
||||
@FXML
|
||||
private Button backButton;
|
||||
@FXML private TextField usernameField;
|
||||
@FXML private PasswordField passwordField;
|
||||
|
||||
private ClientConnection connection;
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
|
||||
try {
|
||||
connection = new ClientConnection("localhost", 8000);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not connect to server: " + e.getMessage());
|
||||
}
|
||||
|
||||
loginButton.setOnAction(e -> {
|
||||
String username = usernameField.getText();
|
||||
String password = passwordField.getText();
|
||||
|
||||
JSONObject request = new JSONObject();
|
||||
request.put("action", "login");
|
||||
request.put("user_id", JSONObject.NULL);
|
||||
request.put("username", username);
|
||||
request.put("password", password);
|
||||
request.put("profile_name", JSONObject.NULL);
|
||||
if (connection!=null) {
|
||||
connection.send(request.toString());
|
||||
}
|
||||
userDatabase userDb = new userDatabase();
|
||||
User user = userDb.findByUsername(username);
|
||||
|
||||
if(!userDb.existsByUsername(username)){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid username");
|
||||
alert.show();
|
||||
}
|
||||
else if(!PasswordHashing.verify(password,user.getPassword())){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid password");
|
||||
alert.show();
|
||||
}
|
||||
else if(!PasswordHashing.verify(password,user.getPassword()) && !userDb.existsByUsername(username)){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Invalid password and username");
|
||||
alert.show();
|
||||
}
|
||||
else{
|
||||
try {
|
||||
String responseStr = connection.receive();
|
||||
JSONObject response = new JSONObject(responseStr);
|
||||
System.out.println("Status: " + response.getString("status"));
|
||||
System.out.println("Message: " + response.getString("message"));
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION, " Message: " + response.getString("message"));
|
||||
alert.show();
|
||||
} catch (Exception ex) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Error receiving response: " + ex.getMessage());
|
||||
alert.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
backButton.setOnAction(e -> {
|
||||
switchScene("login_view.fxml");
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void switchScene(String fxmlFile) {
|
||||
try {
|
||||
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/" + fxmlFile));
|
||||
Parent root = loader.load();
|
||||
|
||||
|
||||
Stage stage = (Stage) backButton.getScene().getWindow();
|
||||
stage.setScene(new Scene(root));
|
||||
stage.show();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.to.telegramfinalproject.UI;
|
||||
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.stage.Stage;
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Client.ClientConnection;
|
||||
import org.to.telegramfinalproject.Database.userDatabase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RegisterForm {
|
||||
@FXML
|
||||
private Button submitButton;
|
||||
@FXML
|
||||
private Button backButton;
|
||||
@FXML private TextField userIdField;
|
||||
@FXML private TextField usernameField;
|
||||
@FXML private TextField profileNameField;
|
||||
@FXML private PasswordField passwordField;
|
||||
@FXML private PasswordField confirmPasswordField;
|
||||
|
||||
|
||||
|
||||
private ClientConnection connection;
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
|
||||
try {
|
||||
connection = new ClientConnection("localhost", 8000);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Could not connect to server: " + e.getMessage());
|
||||
}
|
||||
|
||||
submitButton.setOnAction(e -> {
|
||||
String userID = userIdField.getText();
|
||||
String username = usernameField.getText();
|
||||
String profile_name = profileNameField.getText();
|
||||
String password = passwordField.getText();
|
||||
String confirmPass =confirmPasswordField.getText();
|
||||
JSONObject request = new JSONObject();
|
||||
String passwordRegex = "\\b(?=[^\\s]*[A-Z])(?=[^\\s]*[a-z])(?=[^\\s]*\\d)(?=[^\\s]*[!@#$%^&*])[^\\s]{8,}\\b";
|
||||
|
||||
userDatabase userDb = new userDatabase();
|
||||
if(password.equals(confirmPass) && password.matches(passwordRegex) && !userDb.existsByUserId(userID)&& !userDb.existsByUsername(username)){
|
||||
try {
|
||||
request.put("action", "register");
|
||||
request.put("user_id", userID);
|
||||
request.put("username", username);
|
||||
request.put("password", password);
|
||||
request.put("profile_name", profile_name);
|
||||
connection.send(request.toString());
|
||||
Alert alert = new Alert(Alert.AlertType.INFORMATION, "Registration is successful");
|
||||
alert.show();
|
||||
|
||||
} catch (Exception ex) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Error receiving response: " + ex.getMessage());
|
||||
alert.show();
|
||||
}
|
||||
|
||||
}
|
||||
else if(!password.equals(confirmPass) && password.matches(passwordRegex)) {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Password doesn't match");
|
||||
alert.show();
|
||||
}
|
||||
else if(userDb.existsByUserId(userID))
|
||||
{
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "User ID is already exist");
|
||||
alert.show();
|
||||
}
|
||||
else if(userDb.existsByUsername(username)){
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Username is already exist");
|
||||
alert.show();
|
||||
}
|
||||
else {
|
||||
Alert alert = new Alert(Alert.AlertType.ERROR, "Password isn't Strong enough");
|
||||
alert.show();
|
||||
}
|
||||
});
|
||||
|
||||
backButton.setOnAction(e -> {
|
||||
switchScene("login_view.fxml");
|
||||
});
|
||||
}
|
||||
|
||||
private void switchScene(String fxmlFile) {
|
||||
try {
|
||||
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/to/telegramfinalproject/" + fxmlFile));
|
||||
Parent root = loader.load();
|
||||
|
||||
|
||||
Stage stage = (Stage) backButton.getScene().getWindow();
|
||||
stage.setScene(new Scene(root));
|
||||
stage.show();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user