Work on add contact to contact list
This commit is contained in:
@@ -1,17 +1,17 @@
|
|||||||
package org.to.telegramfinalproject.Client;
|
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.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
|
||||||
import org.to.telegramfinalproject.Models.SearchRequestModel;
|
|
||||||
|
|
||||||
public class ActionHandler {
|
public class ActionHandler {
|
||||||
private final PrintWriter out;
|
private final PrintWriter out;
|
||||||
@@ -26,41 +26,43 @@ public class ActionHandler {
|
|||||||
|
|
||||||
public void loginHandler() {
|
public void loginHandler() {
|
||||||
System.out.println("Login form: \n");
|
System.out.println("Login form: \n");
|
||||||
System.out.println("Username: ");
|
System.out.print("Username: ");
|
||||||
String username = this.scanner.nextLine();
|
String username = this.scanner.nextLine();
|
||||||
System.out.println("Password: ");
|
System.out.print("Password: ");
|
||||||
String password = this.scanner.nextLine();
|
String password = this.scanner.nextLine();
|
||||||
|
|
||||||
JSONObject request = new JSONObject();
|
JSONObject request = new JSONObject();
|
||||||
request.put("action", "login");
|
request.put("action", "login");
|
||||||
request.put("user_id", JSONObject.NULL);
|
request.put("user_id", JSONObject.NULL);
|
||||||
request.put("username", username);
|
request.put("username", username);
|
||||||
request.put("password", password);
|
request.put("password", password);
|
||||||
request.put("profile_name", JSONObject.NULL);
|
request.put("profile_name", JSONObject.NULL);
|
||||||
|
|
||||||
this.send(request);
|
this.send(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void register() {
|
public void register() {
|
||||||
System.out.println("Register form: \n");
|
System.out.println("Register form: \n");
|
||||||
System.out.println("Username: ");
|
System.out.print("Username: ");
|
||||||
String username = this.scanner.nextLine();
|
String username = this.scanner.nextLine();
|
||||||
System.out.println("User id: ");
|
System.out.print("User id: ");
|
||||||
String user_id = this.scanner.nextLine();
|
String user_id = this.scanner.nextLine();
|
||||||
System.out.println("Password: ");
|
System.out.print("Password: ");
|
||||||
String password = this.scanner.nextLine();
|
String password = this.scanner.nextLine();
|
||||||
System.out.println("Profile name: ");
|
System.out.print("Profile name: ");
|
||||||
String profile_name = this.scanner.nextLine();
|
String profile_name = this.scanner.nextLine();
|
||||||
|
|
||||||
JSONObject request = new JSONObject();
|
JSONObject request = new JSONObject();
|
||||||
request.put("action", "register");
|
request.put("action", "register");
|
||||||
request.put("user_id", user_id);
|
request.put("user_id", user_id);
|
||||||
request.put("username", username);
|
request.put("username", username);
|
||||||
request.put("password", password);
|
request.put("password", password);
|
||||||
request.put("profile_name", profile_name);
|
request.put("profile_name", profile_name);
|
||||||
|
|
||||||
this.send(request);
|
this.send(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void search() {
|
public void search() {
|
||||||
|
|
||||||
System.out.print("Enter keyword to search: ");
|
System.out.print("Enter keyword to search: ");
|
||||||
String keyword = scanner.nextLine();
|
String keyword = scanner.nextLine();
|
||||||
|
|
||||||
@@ -71,10 +73,27 @@ public class ActionHandler {
|
|||||||
|
|
||||||
String userId = Session.currentUser.getString("user_id");
|
String userId = Session.currentUser.getString("user_id");
|
||||||
SearchRequestModel model = new SearchRequestModel("search", keyword, userId);
|
SearchRequestModel model = new SearchRequestModel("search", keyword, userId);
|
||||||
|
|
||||||
send(model.toJson());
|
send(model.toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addContact(UUID contactId) {
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "add_contact");
|
||||||
|
req.put("user_id", Session.currentUser.getString("user_id"));
|
||||||
|
req.put("contact_id", contactId.toString());
|
||||||
|
send(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void joinGroupOrChannel(String type, String id) {
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "join_" + type);
|
||||||
|
req.put("user_id", Session.currentUser.getString("user_id"));
|
||||||
|
req.put("id", id);
|
||||||
|
send(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void send(JSONObject request) {
|
private void send(JSONObject request) {
|
||||||
try {
|
try {
|
||||||
if (!request.has("action") || request.isNull("action")) {
|
if (!request.has("action") || request.isNull("action")) {
|
||||||
@@ -83,17 +102,21 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String action = request.getString("action");
|
String action = request.getString("action");
|
||||||
|
|
||||||
this.out.println(request.toString());
|
this.out.println(request.toString());
|
||||||
|
|
||||||
String responseText = this.in.readLine();
|
String responseText = this.in.readLine();
|
||||||
|
if (responseText == null) {
|
||||||
|
System.out.println("No response from server.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (responseText != null) {
|
|
||||||
JSONObject response = new JSONObject(responseText);
|
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 (status.equals("success") && response.has("data") && !response.isNull("data")) {
|
String status = response.getString("status");
|
||||||
|
if (!"success".equals(status) || !response.has("data") || response.isNull("data"))
|
||||||
|
return;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "login":
|
case "login":
|
||||||
case "register":
|
case "register":
|
||||||
@@ -108,7 +131,8 @@ public class ActionHandler {
|
|||||||
chat.getString("name"),
|
chat.getString("name"),
|
||||||
chat.getString("image_url"),
|
chat.getString("image_url"),
|
||||||
chat.getString("type"),
|
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"))
|
||||||
);
|
);
|
||||||
chatList.add(entry);
|
chatList.add(entry);
|
||||||
}
|
}
|
||||||
@@ -123,73 +147,77 @@ public class ActionHandler {
|
|||||||
System.out.println("No results found.");
|
System.out.println("No results found.");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("\nSearch Results:");
|
System.out.println("\nSearch Results:");
|
||||||
for (Object obj : results) {
|
for (int i = 0; i < results.length(); i++) {
|
||||||
JSONObject item = (JSONObject) obj;
|
JSONObject item = results.getJSONObject(i);
|
||||||
if (item.getString("type").equals("message")) {
|
String type = item.getString("type");
|
||||||
System.out.println("- [message] \"" + item.getString("content") + "\""
|
if (type.equals("message")) {
|
||||||
+ " (from: " + item.getString("sender") + ", at: " + item.getString("time") + ")");
|
System.out.println((i + 1) + ". [message] \"" + item.getString("content") + "\""
|
||||||
|
+ " (from: " + item.optString("sender", "N/A") + ", at: " + item.getString("time") + ")");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("- [" + item.getString("type") + "] "
|
System.out.println((i + 1) + ". [" + type + "] "
|
||||||
+ item.getString("name") + " (ID: " + item.getString("id") + ")");
|
+ item.getString("name") + " (ID: " + item.getString("id") + ")");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.print("Select a result number to interact: ");
|
||||||
|
int index = Integer.parseInt(scanner.nextLine()) - 1;
|
||||||
|
if (index < 0 || index >= results.length()) return;
|
||||||
|
|
||||||
|
JSONObject selected = results.getJSONObject(index);
|
||||||
|
String type = selected.getString("type");
|
||||||
|
switch (type) {
|
||||||
|
case "user" -> {
|
||||||
|
UUID contactId = UUID.fromString(selected.getString("uuid")); // ✅ درست
|
||||||
|
addContact(contactId);
|
||||||
|
}
|
||||||
|
|
||||||
|
case "group", "channel" -> {
|
||||||
|
joinGroupOrChannel(selected.getString("type"), selected.getString("uuid")); // ✅
|
||||||
|
}
|
||||||
|
|
||||||
|
default -> System.out.println("No interaction available for type: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
case "get_messages":
|
case "get_messages":
|
||||||
|
// Optional: handle later
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
System.out.println("No response from server.");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("Error while communicating with server: " + e.getMessage());
|
System.err.println("Error communicating with server: " + e.getMessage());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println("Client error: " + e.getMessage());
|
System.err.println("Client error: " + e.getMessage());
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void userMenu(UUID internal_uuid) {
|
||||||
|
|
||||||
|
|
||||||
public void userMenu() {
|
|
||||||
while (true) {
|
while (true) {
|
||||||
System.out.println("\nUser Menu:");
|
System.out.println("\nUser Menu:");
|
||||||
System.out.println("1. Show chat list");
|
System.out.println("1. Show chat list");
|
||||||
System.out.println("2. Search");
|
System.out.println("2. Search");
|
||||||
System.out.println("3. Logout");
|
System.out.println("3. Add contact");
|
||||||
|
System.out.println("4. Logout");
|
||||||
System.out.print("Choose an option: ");
|
System.out.print("Choose an option: ");
|
||||||
String choice = scanner.nextLine();
|
String choice = scanner.nextLine();
|
||||||
|
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
|
case "1" -> showChatListAndSelect();
|
||||||
case "1":
|
case "2" -> search();
|
||||||
showChatListAndSelect();
|
case "3" -> addContact(internal_uuid);
|
||||||
break;
|
case "4" -> {
|
||||||
case "2" :
|
|
||||||
search();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "3":
|
|
||||||
|
|
||||||
logout();
|
logout();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
default:
|
}
|
||||||
System.out.println("Invalid choice.");
|
default -> System.out.println("Invalid choice.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void showChatListAndSelect() {
|
public void showChatListAndSelect() {
|
||||||
if (Session.chatList == null || Session.chatList.isEmpty()) {
|
if (Session.chatList == null || Session.chatList.isEmpty()) {
|
||||||
System.out.println("No chats available.");
|
System.out.println("No chats available.");
|
||||||
@@ -199,8 +227,11 @@ public class ActionHandler {
|
|||||||
System.out.println("\nYour Chats:");
|
System.out.println("\nYour Chats:");
|
||||||
for (int i = 0; i < Session.chatList.size(); i++) {
|
for (int i = 0; i < Session.chatList.size(); i++) {
|
||||||
ChatEntry entry = Session.chatList.get(i);
|
ChatEntry entry = Session.chatList.get(i);
|
||||||
String time = entry.getLastMessageTime() == null ? "No messages yet" : entry.getLastMessageTime().toString();
|
String time = (entry.getLastMessageTime() == null)
|
||||||
System.out.println((i + 1) + ". [" + entry.getType() + "] " + entry.getName() + " - Last: " + time);
|
? "No messages yet"
|
||||||
|
: entry.getLastMessageTime().toString();
|
||||||
|
System.out.println((i + 1) + ". [" + entry.getType() + "] " +
|
||||||
|
entry.getName() + " - Last: " + time);
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.print("Select a chat by number: ");
|
System.out.print("Select a chat by number: ");
|
||||||
@@ -215,24 +246,20 @@ public class ActionHandler {
|
|||||||
openChat(selected);
|
openChat(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void openChat(ChatEntry chat) {
|
private void openChat(ChatEntry chat) {
|
||||||
JSONObject request = new JSONObject();
|
JSONObject request = new JSONObject();
|
||||||
request.put("action", "get_messages");
|
request.put("action", "get_messages");
|
||||||
request.put("receiver_id", chat.getId());
|
request.put("receiver_id", chat.getId());
|
||||||
request.put("receiver_type", chat.getType());
|
request.put("receiver_type", chat.getType());
|
||||||
|
|
||||||
send(request);
|
send(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void logout() {
|
public void logout() {
|
||||||
if (Session.currentUser != null && Session.currentUser.has("user_id")) {
|
if (Session.currentUser != null && Session.currentUser.has("user_id")) {
|
||||||
JSONObject request = new JSONObject();
|
JSONObject request = new JSONObject();
|
||||||
String userId = Session.currentUser.getString("internalUUID");
|
|
||||||
request.put("action", "logout");
|
request.put("action", "logout");
|
||||||
request.put("user_id",userId);
|
request.put("user_id", Session.currentUser.getString("internalUUID"));
|
||||||
|
|
||||||
send(request);
|
send(request);
|
||||||
Session.currentUser = null;
|
Session.currentUser = null;
|
||||||
Session.chatList = null;
|
Session.chatList = null;
|
||||||
@@ -241,5 +268,6 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package org.to.telegramfinalproject.Client;
|
||||||
|
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
|
||||||
|
public class IncomingMessageListener implements Runnable {
|
||||||
|
private final BufferedReader in;
|
||||||
|
|
||||||
|
public IncomingMessageListener(BufferedReader in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
String line;
|
||||||
|
while ((line = in.readLine()) != null) {
|
||||||
|
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 "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(">> ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("🔴 Listener stopped: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,10 +8,11 @@ import java.io.InputStreamReader;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class TelegramClient {
|
public class TelegramClient {
|
||||||
private static final String SERVER_HOST = "localhost";
|
private static final String SERVER_HOST = "localhost";
|
||||||
private static final int SERVER_PORT = 12345;
|
private static final int SERVER_PORT = 8000;
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private BufferedReader in;
|
private BufferedReader in;
|
||||||
private PrintWriter out;
|
private PrintWriter out;
|
||||||
@@ -51,7 +52,11 @@ public class TelegramClient {
|
|||||||
this.handler.loginHandler();
|
this.handler.loginHandler();
|
||||||
if (Session.currentUser != null) {
|
if (Session.currentUser != null) {
|
||||||
System.out.println("Login successful.");
|
System.out.println("Login successful.");
|
||||||
this.handler.userMenu();
|
UUID internalId = UUID.fromString(Session.currentUser.getString("internalUUID"));
|
||||||
|
//Thread listenerThread = new Thread(new IncomingMessageListener(in));
|
||||||
|
//listenerThread.setDaemon(true);
|
||||||
|
//listenerThread.start();
|
||||||
|
this.handler.userMenu(internalId);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
System.out.println("Login failed.");
|
System.out.println("Login failed.");
|
||||||
|
|||||||
@@ -75,4 +75,115 @@ public class ChannelDatabase {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static List<UUID> getSubscriberUUIDs(UUID channelInternalUUID) {
|
||||||
|
List<UUID> subscriberIds = new ArrayList<>();
|
||||||
|
String sql = "SELECT user_id FROM channel_subscribe WHERE channel_id = ?";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, channelInternalUUID);
|
||||||
|
|
||||||
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
subscriberIds.add((UUID) rs.getObject("user_id"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return subscriberIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Channel findByChannelId(String channelId) {
|
||||||
|
String sql = "SELECT * FROM channels WHERE channel_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
stmt.setString(1, channelId);
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
|
||||||
|
if (rs.next()) {
|
||||||
|
Channel channel = new Channel();
|
||||||
|
channel.setInternal_uuid(UUID.fromString(rs.getString("internal_uuid")));
|
||||||
|
channel.setChannel_id(rs.getString("channel_id"));
|
||||||
|
channel.setChannel_name(rs.getString("channel_name"));
|
||||||
|
channel.setImage_url(rs.getString("image_url"));
|
||||||
|
channel.setCreator_id(UUID.fromString(rs.getString("creator_id")));
|
||||||
|
channel.setDescription(rs.getString("description"));
|
||||||
|
channel.setCreated_at(rs.getTimestamp("created_at").toLocalDateTime());
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isUserSubscribed(UUID userId, UUID channelInternalId) {
|
||||||
|
String sql = "SELECT * FROM channel_subscribe WHERE user_id = ? AND channel_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, userId);
|
||||||
|
stmt.setObject(2, channelInternalId);
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
return rs.next();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addSubscriber(UUID channelInternalId, UUID userId) {
|
||||||
|
String sql = "INSERT INTO channel_subscribe (channel_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
||||||
|
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, channelInternalId);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static UUID findInternalUUIDByChannelId(String channelId) {
|
||||||
|
String sql = "SELECT internal_uuid FROM channels WHERE channel_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setString(1, channelId);
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return (UUID) rs.getObject("internal_uuid");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean addSubscriberToChannel(UUID userId, UUID channelUUID) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO channel_subscribers (channel_id, user_id)
|
||||||
|
VALUES (?, ?)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, channelUUID);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
|
return stmt.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,17 @@ public class ContactDatabase {
|
|||||||
return ConnectionDb.connect();
|
return ConnectionDb.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean addContact(UUID userId, UUID contactId) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO contacts (user_id, contact_id)
|
||||||
|
VALUES (?, ?)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
""";
|
||||||
|
|
||||||
public boolean addContact(UUID user_id, UUID contact_id) {
|
try (Connection conn = ConnectionDb.connect();
|
||||||
String sql = "INSERT INTO contacts (user_id, contact_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
try (Connection connection = getConnection()) {
|
stmt.setObject(1, userId);
|
||||||
PreparedStatement stmt = connection.prepareStatement(sql);
|
stmt.setObject(2, contactId);
|
||||||
stmt.setObject(1, user_id);
|
|
||||||
stmt.setObject(2,contact_id);
|
|
||||||
return stmt.executeUpdate() > 0;
|
return stmt.executeUpdate() > 0;
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -31,6 +35,7 @@ public class ContactDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public boolean removeContact(UUID user_id, UUID contact_id) {
|
public boolean removeContact(UUID user_id, UUID contact_id) {
|
||||||
String sql = "DELETE FROM contacts WHERE user_id = ? AND contact_id = ?";
|
String sql = "DELETE FROM contacts WHERE user_id = ? AND contact_id = ?";
|
||||||
try (Connection connection = getConnection()) {
|
try (Connection connection = getConnection()) {
|
||||||
@@ -94,7 +99,7 @@ public class ContactDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean existsContact(UUID user_id, UUID contact_id) {
|
public static boolean existsContact(UUID user_id, UUID contact_id) {
|
||||||
String sql = "SELECT 1 FROM contacts WHERE user_id = ? AND contact_id = ? LIMIT 1"; // stop searching when find the first item in DB(LIMIT 1)
|
String sql = "SELECT 1 FROM contacts WHERE user_id = ? AND contact_id = ? LIMIT 1"; // stop searching when find the first item in DB(LIMIT 1)
|
||||||
try (Connection connection = getConnection()) {
|
try (Connection connection = getConnection()) {
|
||||||
PreparedStatement stmt = connection.prepareStatement(sql);
|
PreparedStatement stmt = connection.prepareStatement(sql);
|
||||||
@@ -155,4 +160,6 @@ public class ContactDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,5 +76,111 @@ public class GroupDatabase {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<UUID> getMemberUUIDs(UUID groupInternalUUID) {
|
||||||
|
List<UUID> memberIds = new ArrayList<>();
|
||||||
|
String sql = "SELECT user_id FROM group_members WHERE group_id = ?";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, groupInternalUUID);
|
||||||
|
|
||||||
|
try (ResultSet rs = stmt.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
memberIds.add((UUID) rs.getObject("user_id"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return memberIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static Group findByGroupId(String groupId) {
|
||||||
|
String sql = "SELECT * FROM groups WHERE group_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
stmt.setString(1, groupId);
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
|
||||||
|
if (rs.next()) {
|
||||||
|
Group group = new Group();
|
||||||
|
group.setInternal_uuid(UUID.fromString(rs.getString("internal_uuid")));
|
||||||
|
group.setGroup_id(rs.getString("group_id"));
|
||||||
|
group.setGroup_name(rs.getString("group_name"));
|
||||||
|
group.setImage_url(rs.getString("image_url"));
|
||||||
|
group.setCreator_id(UUID.fromString(rs.getString("creator_id")));
|
||||||
|
group.setDescription(rs.getString("description"));
|
||||||
|
group.setCreated_at(rs.getTimestamp("created_at").toLocalDateTime());
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isUserInGroup(UUID userId, UUID groupInternalId) {
|
||||||
|
String sql = "SELECT * FROM group_members WHERE user_id = ? AND group_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, userId);
|
||||||
|
stmt.setObject(2, groupInternalId);
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
return rs.next();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void addMember(UUID groupInternalId, UUID userId) {
|
||||||
|
String sql = "INSERT INTO group_members (group_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
||||||
|
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, groupInternalId);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
|
stmt.executeUpdate();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static UUID findInternalUUIDByGroupId(String groupId) {
|
||||||
|
String sql = "SELECT internal_uuid FROM groups WHERE group_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setString(1, groupId);
|
||||||
|
ResultSet rs = stmt.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return (UUID) rs.getObject("internal_uuid");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean addMemberToGroup(UUID userId, UUID groupUUID) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO group_members (group_id, user_id)
|
||||||
|
VALUES (?, ?)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setObject(1, groupUUID);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
|
return stmt.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,41 @@ import java.util.stream.Collectors;
|
|||||||
public class MessageDatabase {
|
public class MessageDatabase {
|
||||||
|
|
||||||
|
|
||||||
|
public static void save(Message message) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO messages (
|
||||||
|
message_id, sender_id, receiver_type, receiver_id, content,
|
||||||
|
message_type, file_url, send_at, status,
|
||||||
|
reply_to_id, is_edited, original_message_id, forwarded_by, forwarded_from
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
stmt.setObject(1, message.getMessage_id());
|
||||||
|
stmt.setObject(2, message.getSender_id());
|
||||||
|
stmt.setString(3, message.getReceiver_type());
|
||||||
|
stmt.setObject(4, message.getReceiver_id());
|
||||||
|
stmt.setString(5, message.getContent());
|
||||||
|
stmt.setString(6, message.getMessage_type());
|
||||||
|
stmt.setString(7, message.getFile_url());
|
||||||
|
stmt.setObject(8, message.getSend_at());
|
||||||
|
stmt.setString(9, message.getStatus());
|
||||||
|
stmt.setObject(10, message.getReply_to_id());
|
||||||
|
stmt.setBoolean(11, message.isIs_edited());
|
||||||
|
stmt.setObject(12, message.getOriginal_message_id());
|
||||||
|
stmt.setObject(13, message.getForwarded_by());
|
||||||
|
stmt.setObject(14, message.getForwarded_from());
|
||||||
|
|
||||||
|
stmt.executeUpdate();
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
System.err.println("❌ Error saving message: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void markMessageAsRead(UUID messageId, UUID userId) {
|
public void markMessageAsRead(UUID messageId, UUID userId) {
|
||||||
String sql = "INSERT INTO message_receipts (message_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
String sql = "INSERT INTO message_receipts (message_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ public class userDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static User findByInternalUUID(UUID internalUuid) {
|
public static User findByInternalUUID(UUID internalUuid) {
|
||||||
String sql = "SELECT * FROM users WHERE internal_uuid = ?";
|
String sql = "SELECT * FROM users WHERE internal_uuid = ?";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.to.telegramfinalproject.Models;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class ContactRequestModel {
|
||||||
|
private String event;
|
||||||
|
private String contactId;
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
public ContactRequestModel(String event, String contactId, String userId) {
|
||||||
|
this.event = event;
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.userId = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject toJson() {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("event", event);
|
||||||
|
json.put("contact_id", contactId);
|
||||||
|
json.put("user_id", userId);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.to.telegramfinalproject.Models;
|
||||||
|
|
||||||
|
public class SearchResultModel {
|
||||||
|
private final String type;
|
||||||
|
private final String id; // ← UUID واقعی برای عملیات
|
||||||
|
private final String displayId; // ← user_id یا group_id برای نمایش
|
||||||
|
private String name;
|
||||||
|
private final String content;
|
||||||
|
private final String sender;
|
||||||
|
private final String time;
|
||||||
|
|
||||||
|
public SearchResultModel(String type, String id, String displayId,
|
||||||
|
String content, String sender, String time) {
|
||||||
|
this.type = type;
|
||||||
|
this.id = id;
|
||||||
|
this.displayId = displayId;
|
||||||
|
this.content = content;
|
||||||
|
this.sender = sender;
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// فقط در صورت نیاز برای user/group/channel
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayId() {
|
||||||
|
return displayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSender() {
|
||||||
|
return sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "[" + type.toUpperCase() + "] " + name + " (ID: " + displayId + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,20 +69,12 @@ public class ClientHandler implements Runnable {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
User user = authService.login(request.getUsername(), request.getPassword());
|
User user = authService.login(request.getUsername(), request.getPassword());
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
response = new ResponseModel("error", "Login failed.");
|
response = new ResponseModel("error", "Login failed.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.currentUser = user;
|
this.currentUser = user;
|
||||||
|
|
||||||
if (SessionManager.contains(user.getInternal_uuid())) {
|
|
||||||
response = new ResponseModel("error", "You are already logged in from another device.");
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SessionManager.addUser(user.getInternal_uuid(), this.socket);
|
SessionManager.addUser(user.getInternal_uuid(), this.socket);
|
||||||
userDatabase.updateUserStatus(user.getInternal_uuid(), "online");
|
userDatabase.updateUserStatus(user.getInternal_uuid(), "online");
|
||||||
List<Contact> contacts = ContactDatabase.getContacts(user.getInternal_uuid());
|
List<Contact> contacts = ContactDatabase.getContacts(user.getInternal_uuid());
|
||||||
@@ -126,21 +118,16 @@ public class ClientHandler implements Runnable {
|
|||||||
|
|
||||||
case "logout": {
|
case "logout": {
|
||||||
String user_Id = requestJson.optString("user_id");
|
String user_Id = requestJson.optString("user_id");
|
||||||
if (user_Id != null && !user_Id.isEmpty()) {
|
if (userId != null && !user_Id.isEmpty()) {
|
||||||
try {
|
|
||||||
UUID uuid = UUID.fromString(user_Id);
|
UUID uuid = UUID.fromString(user_Id);
|
||||||
userDatabase.updateUserStatus(uuid, "offline");
|
userDatabase.updateUserStatus(uuid, "offline");
|
||||||
userDatabase.updateLastSeen(uuid);
|
userDatabase.updateLastSeen(uuid);
|
||||||
SessionManager.removeUser(uuid);
|
SessionManager.removeUser(uuid);
|
||||||
response = new ResponseModel("success", "Logged out.");
|
response = new ResponseModel("success", "Logged out.");
|
||||||
} catch (IllegalArgumentException ex) {
|
|
||||||
response = new ResponseModel("error", "Invalid UUID format for user_id.");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
response = new ResponseModel("error", "Invalid user_id for logout.");
|
response = new ResponseModel("error", "Invalid user_id for logout.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case "search": {
|
case "search": {
|
||||||
@@ -154,6 +141,7 @@ public class ClientHandler implements Runnable {
|
|||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("type", "user");
|
obj.put("type", "user");
|
||||||
obj.put("id", u.getUser_id());
|
obj.put("id", u.getUser_id());
|
||||||
|
obj.put("uuid", u.getInternal_uuid().toString()); // ✅ اضافه شود
|
||||||
obj.put("name", u.getProfile_name());
|
obj.put("name", u.getProfile_name());
|
||||||
results.add(obj);
|
results.add(obj);
|
||||||
}
|
}
|
||||||
@@ -161,7 +149,8 @@ public class ClientHandler implements Runnable {
|
|||||||
for (Group g : GroupDatabase.searchGroups(keyword)) {
|
for (Group g : GroupDatabase.searchGroups(keyword)) {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("type", "group");
|
obj.put("type", "group");
|
||||||
obj.put("id", g.getGroup_id());
|
obj.put("id", g.getGroup_id()); // قابل نمایش
|
||||||
|
obj.put("uuid", g.getInternal_uuid().toString()); // برای عملیات
|
||||||
obj.put("name", g.getGroup_name());
|
obj.put("name", g.getGroup_name());
|
||||||
results.add(obj);
|
results.add(obj);
|
||||||
}
|
}
|
||||||
@@ -169,7 +158,8 @@ public class ClientHandler implements Runnable {
|
|||||||
for (Channel c : ChannelDatabase.searchChannels(keyword)) {
|
for (Channel c : ChannelDatabase.searchChannels(keyword)) {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("type", "channel");
|
obj.put("type", "channel");
|
||||||
obj.put("id", c.getChannel_id());
|
obj.put("id", c.getChannel_id()); // قابل نمایش
|
||||||
|
obj.put("uuid", c.getInternal_uuid().toString()); // برای عملیات
|
||||||
obj.put("name", c.getChannel_name());
|
obj.put("name", c.getChannel_name());
|
||||||
results.add(obj);
|
results.add(obj);
|
||||||
}
|
}
|
||||||
@@ -224,6 +214,40 @@ public class ClientHandler implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "add_contact": {
|
||||||
|
UUID userUUID = new userDatabase().findByUserId(requestJson.getString("user_id")).getInternal_uuid();
|
||||||
|
UUID contactUUID = UUID.fromString(requestJson.getString("contact_id"));
|
||||||
|
|
||||||
|
boolean success = ContactDatabase.addContact(userUUID, contactUUID);
|
||||||
|
response = success
|
||||||
|
? new ResponseModel("success", "Contact added successfully.")
|
||||||
|
: new ResponseModel("error", "Failed to add contact. Maybe already exists.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "join_group": {
|
||||||
|
UUID userUUID = new userDatabase().findByUserId(requestJson.getString("user_id")).getInternal_uuid();
|
||||||
|
UUID groupUUID = GroupDatabase.findInternalUUIDByGroupId(requestJson.getString("id"));
|
||||||
|
|
||||||
|
boolean joined = GroupDatabase.addMemberToGroup(userUUID, groupUUID);
|
||||||
|
response = joined
|
||||||
|
? new ResponseModel("success", "Joined group.")
|
||||||
|
: new ResponseModel("error", "Failed to join group.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "join_channel": {
|
||||||
|
UUID userUUID = new userDatabase().findByUserId(requestJson.getString("user_id")).getInternal_uuid();
|
||||||
|
UUID channelUUID = ChannelDatabase.findInternalUUIDByChannelId(requestJson.getString("id"));
|
||||||
|
|
||||||
|
boolean joined = ChannelDatabase.addSubscriberToChannel(userUUID, channelUUID);
|
||||||
|
response = joined
|
||||||
|
? new ResponseModel("success", "Joined channel.")
|
||||||
|
: new ResponseModel("error", "Failed to join channel.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
response = new ResponseModel("error", "Unknown action: " + action);
|
response = new ResponseModel("error", "Unknown action: " + action);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.to.telegramfinalproject.Server;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.to.telegramfinalproject.Database.userDatabase;
|
||||||
|
import org.to.telegramfinalproject.Database.ContactDatabase;
|
||||||
|
public class ContactService {
|
||||||
|
public static boolean addContact(UUID userId, UUID contactId) {
|
||||||
|
if (userId.equals(contactId)) return false;
|
||||||
|
if (userDatabase.findByInternalUUID(contactId) == null) return false;
|
||||||
|
if (userDatabase.findByInternalUUID(userId) == null) return false;
|
||||||
|
if (ContactDatabase.existsContact(userId, contactId)) return false;
|
||||||
|
|
||||||
|
return ContactDatabase.addContact(userId, contactId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import java.net.ServerSocket;
|
|||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
|
||||||
public class MainServer {
|
public class MainServer {
|
||||||
private static final int PORT = 12345;
|
private static final int PORT = 8000;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
|
try (ServerSocket serverSocket = new ServerSocket(PORT)) {
|
||||||
|
|||||||
@@ -0,0 +1,248 @@
|
|||||||
|
package org.to.telegramfinalproject.Server;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Database.ChannelDatabase;
|
||||||
|
import org.to.telegramfinalproject.Database.GroupDatabase;
|
||||||
|
import org.to.telegramfinalproject.Models.Message;
|
||||||
|
import org.to.telegramfinalproject.Models.User;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class RealTimeEventDispatcher {
|
||||||
|
|
||||||
|
public static void sendToUser(UUID userId, JSONObject data) {
|
||||||
|
Socket socket = SessionManager.getUserSocket(userId);
|
||||||
|
|
||||||
|
|
||||||
|
if (socket != null && !socket.isClosed()) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
|
||||||
|
out.println(data.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("❌ Error sending to user: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println("⚠️ User " + userId + " is offline. Skipping real-time send.");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void broadcastToUsers(List<UUID> userIds, JSONObject data) {
|
||||||
|
for (UUID userId : userIds) {
|
||||||
|
sendToUser(userId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JSONObject buildEvent(String action, JSONObject payload) {
|
||||||
|
JSONObject json = new JSONObject();
|
||||||
|
json.put("action", action);
|
||||||
|
json.put("data", payload);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyNewMessage(Message msg, User sender) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("sender", sender.getUser_id());
|
||||||
|
data.put("receiver_type", msg.getReceiver_type());
|
||||||
|
data.put("receiver_id", msg.getReceiver_id());
|
||||||
|
data.put("content", msg.getContent());
|
||||||
|
data.put("time", msg.getSend_at().toString());
|
||||||
|
|
||||||
|
JSONObject event = buildEvent("new_message", data);
|
||||||
|
|
||||||
|
switch (msg.getReceiver_type()) {
|
||||||
|
case "private" -> RealTimeEventDispatcher.sendToUser(msg.getReceiver_id(), event);
|
||||||
|
case "group" -> {
|
||||||
|
List<UUID> memberIds = GroupDatabase.getMemberUUIDs(msg.getReceiver_id());
|
||||||
|
memberIds.remove(sender.getInternal_uuid());
|
||||||
|
RealTimeEventDispatcher.broadcastToUsers(memberIds, event);
|
||||||
|
}
|
||||||
|
case "channel" -> {
|
||||||
|
List<UUID> subscriberIds = ChannelDatabase.getSubscriberUUIDs(msg.getReceiver_id());
|
||||||
|
subscriberIds.remove(sender.getInternal_uuid());
|
||||||
|
subscriberIds.remove(sender.getInternal_uuid());
|
||||||
|
RealTimeEventDispatcher.broadcastToUsers(subscriberIds, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyMessageEdited(UUID messageId, String newContent, List<UUID> receivers) {
|
||||||
|
|
||||||
|
String editTime = LocalDateTime.now().toString();
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("message_id", messageId.toString());
|
||||||
|
data.put("new_content", newContent);
|
||||||
|
data.put("edited_at", editTime);
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "edit_message");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(receivers, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyMessageDeleted(UUID messageId, List<UUID> receivers) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("message_id", messageId.toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "delete_message");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(receivers, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyUserUpdated(UUID userId, String newProfileName, String newImageUrl, List<UUID> contactIds) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("user_id", userId.toString());
|
||||||
|
data.put("new_name", newProfileName);
|
||||||
|
data.put("new_image_url", newImageUrl);
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "update_user");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(contactIds, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyChatDeleted(String type, UUID id, List<UUID> affectedUsers) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_type", type); // private, group, channel
|
||||||
|
data.put("chat_id", id.toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "chat_deleted");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(affectedUsers, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyGroupOrChannelUpdated(String type, UUID id, String newName, String newImageUrl, List<UUID> affectedUsers) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_type", type); // "group" or "channel"
|
||||||
|
data.put("chat_id", id.toString());
|
||||||
|
data.put("new_name", newName);
|
||||||
|
data.put("new_image_url", newImageUrl);
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "update_group_or_channel");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(affectedUsers, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyMediaMessage(Message msg, User sender) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("sender", sender.getUser_id());
|
||||||
|
data.put("receiver_type", msg.getReceiver_type());
|
||||||
|
data.put("receiver_id", msg.getReceiver_id());
|
||||||
|
data.put("file_url", msg.getFile_url());
|
||||||
|
data.put("file_type", msg.getMessage_type()); // IMAGE, FILE, VIDEO...
|
||||||
|
data.put("time", msg.getSend_at().toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "new_media");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
switch (msg.getReceiver_type()) {
|
||||||
|
case "private" -> sendToUser(msg.getReceiver_id(), event);
|
||||||
|
case "group" -> {
|
||||||
|
List<UUID> members = GroupDatabase.getMemberUUIDs(msg.getReceiver_id());
|
||||||
|
members.remove(sender.getInternal_uuid());
|
||||||
|
broadcastToUsers(members, event);
|
||||||
|
}
|
||||||
|
case "channel" -> {
|
||||||
|
List<UUID> subs = ChannelDatabase.getSubscriberUUIDs(msg.getReceiver_id());
|
||||||
|
subs.remove(sender.getInternal_uuid());
|
||||||
|
broadcastToUsers(subs, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_id", chatId.toString());
|
||||||
|
data.put("chat_name", chatName);
|
||||||
|
data.put("image_url", imageUrl);
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", type.equals("group") ? "added_to_group" : "added_to_channel");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
sendToUser(userId, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyRemovedFromChat(String type, UUID chatId, UUID userId) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_type", type);
|
||||||
|
data.put("chat_id", chatId.toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", type.equals("group") ? "removed_from_group" : "removed_from_channel");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
sendToUser(userId, event);
|
||||||
|
}
|
||||||
|
public static void notifyMessageSeen(UUID messageId, UUID senderId) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("message_id", messageId.toString());
|
||||||
|
data.put("seen_at", LocalDateTime.now().toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "message_seen");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
sendToUser(senderId, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyBlocked(UUID blockerId, UUID blockedUserId) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("blocker_id", blockerId.toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "blocked_by_user");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
sendToUser(blockedUserId, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyUnblocked(UUID unblockerId, UUID unblockedUserId) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("unblocker_id", unblockerId.toString());
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "unblocked_by_user");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
sendToUser(unblockedUserId, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyUserStatusChanged(UUID userId, String status, List<UUID> contacts) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("user_id", userId.toString());
|
||||||
|
data.put("status", status); // online | offline
|
||||||
|
data.put("time", LocalDateTime.now().toString());
|
||||||
|
|
||||||
|
JSONObject event = buildEvent("user_status_changed", data);
|
||||||
|
|
||||||
|
broadcastToUsers(contacts, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.image.Image?>
|
||||||
|
<?import javafx.scene.image.ImageView?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
|
||||||
|
<HBox xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.to.telegramfinalproject.ContactCell"
|
||||||
|
spacing="10.0" alignment="CENTER_LEFT"
|
||||||
|
prefHeight="60.0" style="-fx-padding: 10;">
|
||||||
|
|
||||||
|
<ImageView fx:id="profileImage" fitHeight="40.0" fitWidth="40.0" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/Icons/default_user.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
|
||||||
|
<VBox spacing="4.0">
|
||||||
|
<Label fx:id="nameLabel" text="profile_name"
|
||||||
|
style="-fx-font-size: 14px; -fx-font-weight: bold;" />
|
||||||
|
<Label fx:id="statusLabel" text="● Online"
|
||||||
|
style="-fx-font-size: 12px; -fx-text-fill: green;" />
|
||||||
|
</VBox>
|
||||||
|
</HBox>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.image.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<ScrollPane prefHeight="983.0" prefWidth="859.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="org.to.telegramfinalproject.MainPage">
|
||||||
|
<content>
|
||||||
|
<AnchorPane prefHeight="977.0" prefWidth="845.0">
|
||||||
|
<children>
|
||||||
|
<AnchorPane layoutX="7.0" layoutY="-8.0" prefHeight="977.0" prefWidth="845.0">
|
||||||
|
<children>
|
||||||
|
<Label layoutX="-7.0" layoutY="-6.0" prefHeight="78.0" prefWidth="859.0" style="-fx-background-color: #1882da;" text=" Telegram" textFill="WHITE">
|
||||||
|
<font>
|
||||||
|
<Font name="Arial Bold" size="36.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
<Button contentDisplay="GRAPHIC_ONLY" layoutX="6.0" layoutY="10.0" mnemonicParsing="false" style="-fx-background-color: #1882da;">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitHeight="39.0" fitWidth="63.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../../Icons/menu.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button contentDisplay="GRAPHIC_ONLY" layoutX="703.0" layoutY="9.0" mnemonicParsing="false" prefHeight="54.0" prefWidth="135.0" style="-fx-background-color: #1882da;" text="" textFill="#d6d6d6">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitHeight="47.0" fitWidth="54.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../../Icons/search.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button contentDisplay="GRAPHIC_ONLY" layoutX="547.0" mnemonicParsing="false" prefHeight="66.0" prefWidth="114.0" style="-fx-background-color: #1882da;" text="" />
|
||||||
|
<ImageView fitHeight="50.0" fitWidth="110.0" layoutX="586.0" layoutY="14.0" pickOnBounds="true" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@../../../Icons/time.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
<ListView fx:id="contactListView" layoutX="1.0" layoutY="73.0" prefHeight="907.0" prefWidth="851.0" />
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
|
</content>
|
||||||
|
</ScrollPane>
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.image.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.text.Font?>
|
||||||
|
|
||||||
|
<AnchorPane prefHeight="800.0" prefWidth="802.0"
|
||||||
|
xmlns="http://javafx.com/javafx/8"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="org.to.telegramfinalproject.SearchingView">
|
||||||
|
|
||||||
|
<children>
|
||||||
|
|
||||||
|
<StackPane alignment="CENTER_LEFT" layoutX="0" layoutY="0" prefHeight="90.0" prefWidth="802.0">
|
||||||
|
<children>
|
||||||
|
|
||||||
|
<Label prefHeight="90.0" prefWidth="802.0"
|
||||||
|
style="-fx-background-color: #1882da;"
|
||||||
|
text=" Search" textFill="#c9c9c9">
|
||||||
|
<font>
|
||||||
|
<Font name="System Bold" size="24.0" />
|
||||||
|
</font>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<HBox spacing="5.0" alignment="CENTER_LEFT">
|
||||||
|
<StackPane.margin>
|
||||||
|
<Insets left="100.0" />
|
||||||
|
</StackPane.margin>
|
||||||
|
|
||||||
|
<TextField fx:id="searchField"
|
||||||
|
promptText="Search..."
|
||||||
|
prefHeight="40.0" prefWidth="250.0"
|
||||||
|
style="-fx-background-color: #ffffff; -fx-background-radius: 20; -fx-border-radius: 20; -fx-border-color: transparent; -fx-font-size: 14;" />
|
||||||
|
|
||||||
|
<Button fx:id="searchButton"
|
||||||
|
contentDisplay="GRAPHIC_ONLY"
|
||||||
|
prefWidth="32.0" prefHeight="32.0"
|
||||||
|
style="-fx-background-color: #ffffff; -fx-background-radius: 50; -fx-border-color: transparent;">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitHeight="16.0" fitWidth="16.0" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/Icons/search.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button fx:id="exitButton"
|
||||||
|
contentDisplay="GRAPHIC_ONLY"
|
||||||
|
prefWidth="32.0" prefHeight="32.0"
|
||||||
|
style="-fx-background-color: #ffffff; -fx-background-radius: 50; -fx-border-color: transparent;">
|
||||||
|
<graphic>
|
||||||
|
<ImageView fitHeight="16.0" fitWidth="16.0" preserveRatio="true">
|
||||||
|
<image>
|
||||||
|
<Image url="@/Icons/remove.png" />
|
||||||
|
</image>
|
||||||
|
</ImageView>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
</children>
|
||||||
|
</StackPane>
|
||||||
|
|
||||||
|
</children>
|
||||||
|
</AnchorPane>
|
||||||
Reference in New Issue
Block a user