@@ -268,34 +268,45 @@ public class ActionHandler {
|
|||||||
if (response.has("data") && !response.isNull("data")) {
|
if (response.has("data") && !response.isNull("data")) {
|
||||||
JSONObject data = response.getJSONObject("data");
|
JSONObject data = response.getJSONObject("data");
|
||||||
|
|
||||||
//is chat list available
|
if ((!data.has("active_chat_list") || data.isNull("active_chat_list")) &&
|
||||||
if (!data.has("chat_list") || data.isNull("chat_list")) {
|
(!data.has("archived_chat_list") || data.isNull("archived_chat_list"))) {
|
||||||
System.out.println("❌ chat_list not found in response data.");
|
System.out.println("❌ No chat list found in response data.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONArray chatListJson = data.getJSONArray("chat_list");
|
List<ChatEntry> activeChats = new ArrayList<>();
|
||||||
List<ChatEntry> chatList = new ArrayList<>();
|
List<ChatEntry> archivedChats = new ArrayList<>();
|
||||||
|
|
||||||
for (Object obj : chatListJson) {
|
// 📁 Parse active chat list
|
||||||
|
if (data.has("active_chat_list") && !data.isNull("active_chat_list")) {
|
||||||
|
JSONArray activeJson = data.getJSONArray("active_chat_list");
|
||||||
|
for (Object obj : activeJson) {
|
||||||
JSONObject chat = (JSONObject) obj;
|
JSONObject chat = (JSONObject) obj;
|
||||||
|
ChatEntry entry = parseChatEntry(chat);
|
||||||
ChatEntry entry = new ChatEntry(
|
activeChats.add(entry);
|
||||||
UUID.fromString(chat.getString("internal_id")),
|
}
|
||||||
chat.getString("id"),
|
|
||||||
chat.getString("name"),
|
|
||||||
chat.optString("image_url", ""),
|
|
||||||
chat.getString("type"),
|
|
||||||
chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
|
|
||||||
chat.optBoolean("is_owner", false),
|
|
||||||
chat.optBoolean("is_admin", false)
|
|
||||||
);
|
|
||||||
|
|
||||||
chatList.add(entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Session.chatList = chatList;
|
// 📁 Parse archived chat list
|
||||||
System.out.println("✅ Chat list updated. Total: " + chatList.size());
|
if (data.has("archived_chat_list") && !data.isNull("archived_chat_list")) {
|
||||||
|
JSONArray archivedJson = data.getJSONArray("archived_chat_list");
|
||||||
|
for (Object obj : archivedJson) {
|
||||||
|
JSONObject chat = (JSONObject) obj;
|
||||||
|
ChatEntry entry = parseChatEntry(chat);
|
||||||
|
archivedChats.add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Session.chatList = new ArrayList<>();
|
||||||
|
Session.chatList.addAll(activeChats);
|
||||||
|
Session.chatList.addAll(archivedChats);
|
||||||
|
|
||||||
|
Session.activeChats = activeChats;
|
||||||
|
Session.archivedChats = archivedChats;
|
||||||
|
|
||||||
|
System.out.println("✅ Chat list updated.");
|
||||||
|
System.out.println("📂 Active Chats: " + activeChats.size());
|
||||||
|
System.out.println("📁 Archived Chats: " + archivedChats.size());
|
||||||
} else {
|
} else {
|
||||||
System.out.println("⚠️ Response has no data object.");
|
System.out.println("⚠️ Response has no data object.");
|
||||||
}
|
}
|
||||||
@@ -312,6 +323,26 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ChatEntry parseChatEntry(JSONObject chat) {
|
||||||
|
ChatEntry entry = new ChatEntry(
|
||||||
|
UUID.fromString(chat.getString("internal_id")),
|
||||||
|
chat.getString("id"),
|
||||||
|
chat.getString("name"),
|
||||||
|
chat.optString("image_url", ""),
|
||||||
|
chat.getString("type"),
|
||||||
|
chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
|
||||||
|
chat.optBoolean("is_owner", false),
|
||||||
|
chat.optBoolean("is_admin", false)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (chat.has("other_user_id") && !chat.isNull("other_user_id")) {
|
||||||
|
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void createGroup() {
|
public void createGroup() {
|
||||||
String groupId = null;
|
String groupId = null;
|
||||||
@@ -489,7 +520,7 @@ public class ActionHandler {
|
|||||||
chat.optBoolean("is_admin", false)
|
chat.optBoolean("is_admin", false)
|
||||||
);
|
);
|
||||||
if (chat.has("other_user_id")) {
|
if (chat.has("other_user_id")) {
|
||||||
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id"))); // 👈 اضافه کردن برای private chat
|
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
|
||||||
}
|
}
|
||||||
archivedChats.add(entry);
|
archivedChats.add(entry);
|
||||||
|
|
||||||
@@ -510,7 +541,7 @@ public class ActionHandler {
|
|||||||
chat.optBoolean("is_admin", false)
|
chat.optBoolean("is_admin", false)
|
||||||
);
|
);
|
||||||
if (chat.has("other_user_id")) {
|
if (chat.has("other_user_id")) {
|
||||||
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id"))); // 👈 اضافه کردن برای private chat
|
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
|
||||||
}
|
}
|
||||||
|
|
||||||
activeChats.add(entry);
|
activeChats.add(entry);
|
||||||
@@ -576,12 +607,24 @@ public class ActionHandler {
|
|||||||
String type = item.getString("type");
|
String type = item.getString("type");
|
||||||
|
|
||||||
if (type.equals("message")) {
|
if (type.equals("message")) {
|
||||||
System.out.println((i + 1) + ". [message] \"" + item.getString("content") + "\""
|
String senderName = item.optString("sender_name", item.optString("sender", "Unknown"));
|
||||||
+ " (from: " + item.optString("sender", "N/A") + ", at: " + item.getString("time") + ")");
|
String time = item.optString("time", "Unknown time");
|
||||||
|
String content = item.optString("content", "[No content]");
|
||||||
|
String context = "";
|
||||||
|
|
||||||
|
if (item.has("group_name")) {
|
||||||
|
context = " in group: " + item.getString("group_name");
|
||||||
|
} else if (item.has("channel_name")) {
|
||||||
|
context = " in channel: " + item.getString("channel_name");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println((i + 1) + ". [message] \"" + content + "\""
|
||||||
|
+ " (from: " + senderName + context + ", at: " + time + ")");
|
||||||
} else {
|
} else {
|
||||||
System.out.println((i + 1) + ". [" + 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 (or 0 to exit): ");
|
System.out.print("Select a result number to interact (or 0 to exit): ");
|
||||||
@@ -683,6 +726,22 @@ public class ActionHandler {
|
|||||||
case "message" -> {
|
case "message" -> {
|
||||||
String receiverId = selected.getString("receiver_id");
|
String receiverId = selected.getString("receiver_id");
|
||||||
String receiverType = selected.getString("receiver_type");
|
String receiverType = selected.getString("receiver_type");
|
||||||
|
String senderName = selected.optString("sender_name", "Unknown");
|
||||||
|
String content = selected.optString("content", "[No content]");
|
||||||
|
String time = selected.optString("time", "Unknown time");
|
||||||
|
|
||||||
|
String context = "";
|
||||||
|
if (receiverType.equals("group")) {
|
||||||
|
context = "in group: " + selected.optString("group_name", "Unknown group");
|
||||||
|
} else if (receiverType.equals("channel")) {
|
||||||
|
context = "in channel: " + selected.optString("channel_name", "Unknown channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("📩 Message preview:");
|
||||||
|
System.out.println("From: " + senderName);
|
||||||
|
if (!context.isEmpty()) System.out.println(context);
|
||||||
|
System.out.println("Time: " + time);
|
||||||
|
System.out.println("Content: \"" + content + "\"");
|
||||||
|
|
||||||
System.out.println("🔍 Looking for chat with displayId='" + receiverId + "' type='" + receiverType + "'");
|
System.out.println("🔍 Looking for chat with displayId='" + receiverId + "' type='" + receiverType + "'");
|
||||||
|
|
||||||
@@ -694,6 +753,7 @@ public class ActionHandler {
|
|||||||
openChat(chat);
|
openChat(chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default -> System.out.println("No interaction available for type: " + type);
|
default -> System.out.println("No interaction available for type: " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1182,7 +1242,8 @@ public class ActionHandler {
|
|||||||
System.out.println("4. Delete chat (both sides)");
|
System.out.println("4. Delete chat (both sides)");
|
||||||
System.out.println("5. View profile");
|
System.out.println("5. View profile");
|
||||||
System.out.println("6. Archive/Unarchived");
|
System.out.println("6. Archive/Unarchived");
|
||||||
System.out.println("7. Back");
|
System.out.println("7. View messages");
|
||||||
|
System.out.println("8. Back");
|
||||||
|
|
||||||
|
|
||||||
String input = scanner.nextLine();
|
String input = scanner.nextLine();
|
||||||
@@ -1228,8 +1289,11 @@ public class ActionHandler {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case "7" ->{
|
||||||
|
viewMessagesInChat(chat);
|
||||||
|
}
|
||||||
|
|
||||||
case "7" -> {
|
case "8" -> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
default -> System.out.println("Invalid choice.");
|
default -> System.out.println("Invalid choice.");
|
||||||
@@ -1295,6 +1359,7 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("12. Archive/Unarchived");
|
System.out.println("12. Archive/Unarchived");
|
||||||
|
System.out.println("13. View messages");
|
||||||
|
|
||||||
System.out.println("0. Back to Chat List");
|
System.out.println("0. Back to Chat List");
|
||||||
|
|
||||||
@@ -1349,6 +1414,10 @@ public class ActionHandler {
|
|||||||
toggleArchive(chat.getId() , "group");
|
toggleArchive(chat.getId() , "group");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "13" ->{
|
||||||
|
viewMessagesInChat(chat);
|
||||||
|
}
|
||||||
|
|
||||||
case "0" -> {
|
case "0" -> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1416,6 +1485,7 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("12. Archive/Unarchived");
|
System.out.println("12. Archive/Unarchived");
|
||||||
|
System.out.println("13. View messages");
|
||||||
System.out.println("0. Back to Chat List");
|
System.out.println("0. Back to Chat List");
|
||||||
|
|
||||||
String input = scanner.nextLine();
|
String input = scanner.nextLine();
|
||||||
@@ -1498,7 +1568,9 @@ public class ActionHandler {
|
|||||||
case "12"->{
|
case "12"->{
|
||||||
toggleArchive(chat.getId() , "channel");
|
toggleArchive(chat.getId() , "channel");
|
||||||
}
|
}
|
||||||
|
case "13" ->{
|
||||||
|
viewMessagesInChat(chat);
|
||||||
|
}
|
||||||
|
|
||||||
case "0" -> {
|
case "0" -> {
|
||||||
return false;
|
return false;
|
||||||
@@ -2959,7 +3031,7 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
case "2" -> {
|
case "2" -> {
|
||||||
addContact(chat.getId());
|
addContact(chat.getId());
|
||||||
refreshChatList();
|
refreshContactList();
|
||||||
}
|
}
|
||||||
default -> System.out.println("Back...");
|
default -> System.out.println("Back...");
|
||||||
}
|
}
|
||||||
@@ -3213,9 +3285,396 @@ public class ActionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshContactList() {
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "get_contact_list");
|
||||||
|
req.put("user_id", Session.currentUser.getString("user_id"));
|
||||||
|
out.println(req.toString());
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject response = TelegramClient.responseQueue.take();
|
||||||
|
|
||||||
|
if (response != null && response.getString("status").equals("success")) {
|
||||||
|
if (response.has("data") && !response.isNull("data")) {
|
||||||
|
JSONObject data = response.getJSONObject("data");
|
||||||
|
|
||||||
|
if (!data.has("contact_list") || data.isNull("contact_list")) {
|
||||||
|
System.out.println("❌ contact_list not found in response data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray contactListJson = data.getJSONArray("contact_list");
|
||||||
|
List<ContactEntry> contactList = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Object obj : contactListJson) {
|
||||||
|
JSONObject c = (JSONObject) obj;
|
||||||
|
|
||||||
|
ContactEntry entry = new ContactEntry(
|
||||||
|
UUID.fromString(c.getString("contact_id")),
|
||||||
|
c.getString("user_id"),
|
||||||
|
c.getString("profile_name"),
|
||||||
|
c.optString("image_url", ""),
|
||||||
|
c.optBoolean("is_blocked", false)
|
||||||
|
);
|
||||||
|
|
||||||
|
contactList.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Session.contactEntries = contactList;
|
||||||
|
System.out.println("✅ Contact list updated. Total: " + contactList.size());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
System.out.println("⚠️ Response has no data object.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (response.has("message") && !response.isNull("message")) {
|
||||||
|
System.out.println("❌ Failed to refresh contact list: " + response.getString("message"));
|
||||||
|
} else {
|
||||||
|
System.out.println("❌ Failed to refresh contact list.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("❌ Error during refreshContactList: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void viewMessagesInChat(ChatEntry chat) {
|
||||||
|
int offset = 0;
|
||||||
|
int limit = 10;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "get_chat_messages");
|
||||||
|
req.put("chat_id", chat.getId());
|
||||||
|
req.put("chat_type", chat.getType());
|
||||||
|
req.put("offset", offset);
|
||||||
|
req.put("limit", limit);
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
if (res == null || !res.getString("status").equals("success")) {
|
||||||
|
System.out.println("❌ Failed to fetch messages.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray messages = res.getJSONObject("data").getJSONArray("get_chat_messages");
|
||||||
|
|
||||||
|
if (messages.isEmpty()) {
|
||||||
|
if (offset == 0)
|
||||||
|
System.out.println("📭 No messages in this chat.");
|
||||||
|
else
|
||||||
|
System.out.println("📭 No more messages.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("\n📥 Messages:");
|
||||||
|
System.out.println("─────────────────────────────────────────────");
|
||||||
|
for (int i = 0; i < messages.length(); i++) {
|
||||||
|
JSONObject msg = messages.getJSONObject(i);
|
||||||
|
String senderName = msg.optString("sender_name", "Unknown");
|
||||||
|
String content = msg.optString("content", "(no content)");
|
||||||
|
String time = msg.optString("time", "");
|
||||||
|
boolean isEdited = msg.optBoolean("is_edited", false);
|
||||||
|
String editedAt = msg.optString("edited_at", "");
|
||||||
|
String label = isEdited ? "🖊️ (edited at: " + editedAt + ")" : "";
|
||||||
|
|
||||||
|
// ✅ Forwarded info
|
||||||
|
String forwardLabel = "";
|
||||||
|
if (msg.optBoolean("is_forwarded", false)) {
|
||||||
|
String forwardFrom = msg.optString("forwarded_from_name", "Unknown");
|
||||||
|
forwardLabel = "🔁 Forwarded from " + forwardFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
//for reply
|
||||||
|
String replyLabel = "";
|
||||||
|
if (msg.has("reply_to_id")) {
|
||||||
|
String repliedSender = msg.optString("reply_to_sender", "Unknown");
|
||||||
|
String repliedContent = msg.optString("reply_to_content", "...");
|
||||||
|
replyLabel = "↪️ Reply to " + repliedSender + ": \"" + repliedContent + "\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray reactions = msg.optJSONArray("reactions");
|
||||||
|
if (reactions != null && !reactions.isEmpty()) {
|
||||||
|
System.out.print(" 💬 Reactions: ");
|
||||||
|
for (int j = 0; j < reactions.length(); j++) {
|
||||||
|
System.out.print(reactions.getString(j) + " ");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
System.out.printf("[%d] [%s] %s: \n", i + 1, time, senderName);
|
||||||
|
if (!forwardLabel.isEmpty()) System.out.println(forwardLabel);
|
||||||
|
if (!replyLabel.isEmpty()) System.out.println(replyLabel);
|
||||||
|
System.out.printf("%s %s\n", content, label);
|
||||||
|
System.out.println("─────────────────────────────────────────────");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.print("""
|
||||||
|
💬 Options:
|
||||||
|
[number] - Interact with message
|
||||||
|
N - Next page (older messages)
|
||||||
|
0 - Back to chat menu
|
||||||
|
S - Send message
|
||||||
|
➤ Choice: """);
|
||||||
|
|
||||||
|
String input = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
if (input.equals("0")) return;
|
||||||
|
if (input.equalsIgnoreCase("N")) {
|
||||||
|
offset += limit;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(input.equalsIgnoreCase("S")){
|
||||||
|
sendMessage(chat.getId(), chat.getType());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int index = Integer.parseInt(input);
|
||||||
|
if (index < 1 || index > messages.length()) {
|
||||||
|
System.out.println("❌ Invalid message number.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject selected = messages.getJSONObject(index - 1);
|
||||||
|
UUID messageId = UUID.fromString(selected.getString("message_id"));
|
||||||
|
UUID senderId = UUID.fromString(selected.getString("sender_id"));
|
||||||
|
boolean isSender = senderId.toString().equals(Session.currentUser.getString("internal_uuid"));
|
||||||
|
boolean isChannel = chat.getType().equals("channel");
|
||||||
|
boolean isOwnerOrAdmin = chat.isOwner() || chat.isAdmin();
|
||||||
|
|
||||||
|
System.out.println("\n🎯 Selected message by " + selected.getString("sender_name"));
|
||||||
|
|
||||||
|
// ✔ Show interaction menu based on access
|
||||||
|
System.out.println("2. Forward");
|
||||||
|
|
||||||
|
if (isChannel) {
|
||||||
|
if (isOwnerOrAdmin) {
|
||||||
|
System.out.println("1. Reply");
|
||||||
|
System.out.println("3. React");
|
||||||
|
if (isSender) {
|
||||||
|
System.out.println("4. Edit");
|
||||||
|
}
|
||||||
|
System.out.println("5. Delete");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("1. Reply");
|
||||||
|
System.out.println("3. React");
|
||||||
|
if (isSender) {
|
||||||
|
System.out.println("4. Edit");
|
||||||
|
}
|
||||||
|
if (isSender || isOwnerOrAdmin) {
|
||||||
|
System.out.println("5. Delete");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("0. Back to message list");
|
||||||
|
System.out.print("➤ Select an action: ");
|
||||||
|
String choice = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
switch (choice) {
|
||||||
|
case "1" -> {
|
||||||
|
if (!isChannel || isOwnerOrAdmin)
|
||||||
|
replyToMessage(messageId);
|
||||||
|
else
|
||||||
|
System.out.println("❌ You are not allowed to reply.");
|
||||||
|
}
|
||||||
|
case "2" -> forwardMessage(messageId);
|
||||||
|
case "3" -> {
|
||||||
|
if (!isChannel || isOwnerOrAdmin)
|
||||||
|
reactToMessage(messageId);
|
||||||
|
else
|
||||||
|
System.out.println("❌ You are not allowed to react.");
|
||||||
|
}
|
||||||
|
case "4" -> {
|
||||||
|
if (isSender)
|
||||||
|
editMessage(messageId);
|
||||||
|
else
|
||||||
|
System.out.println("❌ You can only edit your own messages.");
|
||||||
|
}
|
||||||
|
case "5" -> {
|
||||||
|
if (!isChannel || isOwnerOrAdmin || isSender)
|
||||||
|
deleteMessage(messageId);
|
||||||
|
else
|
||||||
|
System.out.println("❌ You are not allowed to delete this message.");
|
||||||
|
}
|
||||||
|
case "0" -> {}
|
||||||
|
default -> System.out.println("❌ Invalid option.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.out.println("❌ Please enter a valid number or command.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void editMessage(UUID messageId) {
|
||||||
|
System.out.print("📝 Enter new content: ");
|
||||||
|
String newContent = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
if (newContent.isEmpty()) {
|
||||||
|
System.out.println("❌ Content cannot be empty.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "edit_message");
|
||||||
|
req.put("message_id", messageId.toString());
|
||||||
|
req.put("new_content", newContent);
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
if (res == null || !res.getString("status").equals("success")) {
|
||||||
|
System.out.println("❌ Failed to edit message.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("✅ Message updated successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void reactToMessage(UUID messageId) {
|
||||||
|
System.out.print("😊 Enter your reaction (e.g., ❤️, 👍, 😂): ");
|
||||||
|
String reaction = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
if (reaction.isEmpty()) {
|
||||||
|
System.out.println("⚠️ Empty reaction discarded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "react_to_message");
|
||||||
|
req.put("message_id", messageId.toString());
|
||||||
|
req.put("reaction", reaction);
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
if (res != null && res.getString("status").equals("success")) {
|
||||||
|
System.out.println("✅ Reaction sent.");
|
||||||
|
} else {
|
||||||
|
System.out.println("❌ Failed to react to message.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void forwardMessage(UUID originalMessageId) {
|
||||||
|
System.out.println("\n📤 Select a chat to forward this message to:");
|
||||||
|
|
||||||
|
List<ChatEntry> chatList = Session.chatList;
|
||||||
|
for (int i = 0; i < chatList.size(); i++) {
|
||||||
|
ChatEntry c = chatList.get(i);
|
||||||
|
System.out.printf("%d. [%s] %s\n", i + 1, c.getType(), c.getName());
|
||||||
|
}
|
||||||
|
System.out.println("0. Cancel");
|
||||||
|
|
||||||
|
System.out.print("➤ Enter chat number: ");
|
||||||
|
String input = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
if (input.equals("0")) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
int choice = Integer.parseInt(input);
|
||||||
|
if (choice < 1 || choice > chatList.size()) {
|
||||||
|
System.out.println("❌ Invalid choice.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatEntry target = chatList.get(choice - 1);
|
||||||
|
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "forward_message");
|
||||||
|
req.put("target_chat_id", target.getId());
|
||||||
|
req.put("target_chat_type", target.getType());
|
||||||
|
req.put("original_message_id", originalMessageId.toString());
|
||||||
|
req.put("forwarded_by", Session.currentUser.getString("internal_uuid"));
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
if (res != null && res.getString("status").equals("success")) {
|
||||||
|
System.out.println("✅ Message forwarded successfully.");
|
||||||
|
} else {
|
||||||
|
System.out.println("❌ Failed to forward the message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.out.println("❌ Invalid input.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void replyToMessage(UUID messageId) {
|
||||||
|
System.out.print("💬 Enter your reply: ");
|
||||||
|
String content = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
if (content.isEmpty()) {
|
||||||
|
System.out.println("⚠️ Empty reply discarded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID chatId = UUID.fromString(Session.currentChatId);
|
||||||
|
String chatType = Session.currentChatType;
|
||||||
|
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "send_reply_message");
|
||||||
|
req.put("receiver_type", chatType);
|
||||||
|
req.put("receiver_id", chatId.toString());
|
||||||
|
req.put("content", content);
|
||||||
|
req.put("reply_to_id", messageId.toString());
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
|
||||||
|
if (res != null && res.getString("status").equals("success")) {
|
||||||
|
System.out.println("✅ Reply sent.");
|
||||||
|
} else {
|
||||||
|
System.out.println("❌ Failed to send reply.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void deleteMessage(UUID messageId) {
|
||||||
|
System.out.println("\n🗑️ Delete Message Options:");
|
||||||
|
System.out.println("1. Delete for yourself (one-sided)");
|
||||||
|
System.out.println("2. Delete for everyone (global) [only if allowed]");
|
||||||
|
System.out.println("0. Cancel");
|
||||||
|
|
||||||
|
String choice = scanner.nextLine().trim();
|
||||||
|
|
||||||
|
switch (choice) {
|
||||||
|
case "1" -> {
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "delete_message");
|
||||||
|
req.put("message_id", messageId.toString());
|
||||||
|
req.put("delete_type", "one-sided");
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
if (res != null && res.getString("status").equals("success")) {
|
||||||
|
System.out.println("✅ Message deleted for you.");
|
||||||
|
} else {
|
||||||
|
System.out.println("❌ Failed to delete message.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "2" -> {
|
||||||
|
JSONObject req = new JSONObject();
|
||||||
|
req.put("action", "delete_message");
|
||||||
|
req.put("message_id", messageId.toString());
|
||||||
|
req.put("delete_type", "global");
|
||||||
|
|
||||||
|
JSONObject res = sendWithResponse(req);
|
||||||
|
if (res != null && res.getString("status").equals("success")) {
|
||||||
|
System.out.println("✅ Message deleted for everyone.");
|
||||||
|
} else {
|
||||||
|
System.out.println("❌ Failed to delete message globally.");
|
||||||
|
if (res != null) System.out.println("⚠️ " + res.optString("message"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "0" -> {
|
||||||
|
System.out.println("❎ Delete canceled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
default -> {
|
||||||
|
System.out.println("❌ Invalid option.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,24 +74,24 @@ public class IncomingMessageListener implements Runnable {
|
|||||||
|
|
||||||
private boolean isRealTimeEvent(String action) {
|
private boolean isRealTimeEvent(String action) {
|
||||||
return switch (action) {
|
return switch (action) {
|
||||||
case "new_message", "message_edited", "message_deleted",
|
case "new_message", "message_edited", "message_deleted_global",
|
||||||
"user_status_changed", "added_to_group", "added_to_channel",
|
"user_status_changed", "added_to_group", "added_to_channel",
|
||||||
"update_group_or_channel", "chat_deleted",
|
"update_group_or_channel", "chat_deleted",
|
||||||
"blocked_by_user", "unblocked_by_user", "message_seen",
|
"blocked_by_user", "unblocked_by_user", "message_seen",
|
||||||
"removed_from_group", "removed_from_channel",
|
"removed_from_group", "removed_from_channel",
|
||||||
"became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated" -> true;
|
"became_admin", "removed_admin", "ownership_transferred","admin_permissions_updated","created_private_chat" , "message_reacted" , "message_unreacted" -> true;
|
||||||
default -> false;
|
default -> false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleRealTimeEvent(JSONObject response) throws IOException {
|
void handleRealTimeEvent(JSONObject response) throws IOException {
|
||||||
String action = response.getString("action");
|
String action = response.getString("action");
|
||||||
JSONObject msg = response.getJSONObject("data");
|
JSONObject msg = response.has("data") ? response.getJSONObject("data") : new JSONObject();
|
||||||
|
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "added_to_group", "added_to_channel",
|
case "added_to_group", "added_to_channel",
|
||||||
"removed_from_group", "removed_from_channel", "chat_deleted" -> {
|
"removed_from_group", "removed_from_channel", "chat_deleted","created_private_chat" -> {
|
||||||
System.out.println("🔄 Chat list changed. Updating...");
|
System.out.println("🔄 Chat list changed. Updating...");
|
||||||
Session.forceRefreshChatList = true;
|
Session.forceRefreshChatList = true;
|
||||||
System.out.println("🧪 Calling requestChatList() after being added");
|
System.out.println("🧪 Calling requestChatList() after being added");
|
||||||
@@ -135,10 +135,6 @@ public class IncomingMessageListener implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
default -> displayRealTimeMessage(action, msg);
|
default -> displayRealTimeMessage(action, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,36 +294,52 @@ public class IncomingMessageListener implements Runnable {
|
|||||||
private void displayRealTimeMessage(String action, JSONObject msg) {
|
private void displayRealTimeMessage(String action, JSONObject msg) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "new_message" -> {
|
case "new_message" -> {
|
||||||
System.out.println("\n🔔 New Message Received:");
|
String senderName = msg.optString("sender_name","Unknown");
|
||||||
String senderName = msg.optString("sender_name", "Unknown");
|
String content = msg.optString("content","(empty)");
|
||||||
String content = msg.optString("content", "(empty)");
|
String sendAt = msg.optString("send_at","-");
|
||||||
String sendAt = msg.optString("send_at", "-");
|
String chatId = msg.optString("receiver_id", msg.optString("chat_id",""));
|
||||||
|
String kind = msg.optString("kind","plain");
|
||||||
|
JSONObject meta = msg.optJSONObject("meta");
|
||||||
|
|
||||||
String receiverId = msg.optString("receiver_id", "");
|
String prefix = "";
|
||||||
String receiverType = msg.optString("receiver_type", "");
|
if ("reply".equals(kind) && meta != null) {
|
||||||
|
var rt = meta.optJSONObject("reply_to");
|
||||||
|
prefix = "[reply → " + (rt!=null?rt.optString("excerpt",""):"") + "] ";
|
||||||
|
} else if ("forward".equals(kind) && meta != null) {
|
||||||
|
var ff = meta.optJSONObject("forwarded_from");
|
||||||
|
prefix = "[forwarded from " + (ff!=null?ff.optString("sender_name","unknown"):"unknown") + "] ";
|
||||||
|
}
|
||||||
|
|
||||||
boolean isInCurrentChat = Session.inChatMenu &&
|
boolean isInCurrentChat = Session.inChatMenu &&
|
||||||
Session.currentChatId != null &&
|
Session.currentChatId != null && Session.currentChatId.equals(chatId);
|
||||||
Session.currentChatId.equals(receiverId);
|
|
||||||
|
|
||||||
if (isInCurrentChat) {
|
if (isInCurrentChat) {
|
||||||
System.out.println(senderName + ": " + content + " (" + sendAt + ")");
|
System.out.println(senderName + ": " + prefix + content + " (" + sendAt + ")");
|
||||||
} else {
|
} else {
|
||||||
System.out.println("💬 Message from " + senderName + " in " + receiverType + " chat: " + content);
|
System.out.println("💬 Message from " + senderName + ": " + prefix + content);
|
||||||
Session.forceRefreshChatList = true;
|
Session.forceRefreshChatList = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case "message_edited" -> {
|
case "message_edited" -> {
|
||||||
System.out.println("\n✏️ Message Edited:");
|
System.out.println("\n✏️ Message Edited:");
|
||||||
System.out.println("ID: " + msg.getString("message_id"));
|
System.out.println("ID: " + msg.getString("message_id"));
|
||||||
System.out.println("New Content: " + msg.getString("new_content"));
|
System.out.println("New Content: " + msg.getString("new_content"));
|
||||||
System.out.println("Edit Time: " + msg.getString("edited_at"));
|
System.out.println("Edit Time: " + msg.getString("edited_at"));
|
||||||
}
|
}
|
||||||
case "message_deleted" -> {
|
case "message_deleted_global" -> {
|
||||||
System.out.println("\n🗑️ Message Deleted:");
|
System.out.println("\n🗑️ Message Deleted:");
|
||||||
System.out.println("Message ID: " + msg.getString("message_id"));
|
System.out.println("Message ID: " + msg.getString("message_id"));
|
||||||
}
|
}
|
||||||
|
case "message_reacted", "message_unreacted" -> {
|
||||||
|
String mid = msg.getString("message_id");
|
||||||
|
String emoji = msg.getString("emoji");
|
||||||
|
JSONObject counts = msg.optJSONObject("counts");
|
||||||
|
int n = msg.optInt("count_for_emoji", 0);
|
||||||
|
System.out.println("\n⭐ Reaction update on " + mid + " : " + emoji + " → " + n);
|
||||||
|
}
|
||||||
|
|
||||||
case "user_status_changed" -> {
|
case "user_status_changed" -> {
|
||||||
System.out.println("\n🔄 User Status Changed:");
|
System.out.println("\n🔄 User Status Changed:");
|
||||||
System.out.println("User: " + msg.getString("user_id"));
|
System.out.println("User: " + msg.getString("user_id"));
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ public class Session {
|
|||||||
public static String currentChatId = null;
|
public static String currentChatId = null;
|
||||||
public static ChatEntry currentChatEntry = null;
|
public static ChatEntry currentChatEntry = null;
|
||||||
public static List<ContactEntry> contactEntries = new ArrayList<>();
|
public static List<ContactEntry> contactEntries = new ArrayList<>();
|
||||||
|
public static boolean inContactListMenu = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static String getUserUUID() {
|
public static String getUserUUID() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.to.telegramfinalproject.Database;
|
package org.to.telegramfinalproject.Database;
|
||||||
|
|
||||||
import org.to.telegramfinalproject.Models.Contact;
|
import org.to.telegramfinalproject.Models.Contact;
|
||||||
|
import org.to.telegramfinalproject.Models.ContactEntry;
|
||||||
import org.to.telegramfinalproject.Models.User;
|
import org.to.telegramfinalproject.Models.User;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
@@ -80,7 +81,6 @@ public class ContactDatabase {
|
|||||||
}
|
}
|
||||||
return !currentlyBlocked;
|
return !currentlyBlocked;
|
||||||
} else {
|
} else {
|
||||||
// اگر رابطه وجود نداره، اول باید کاربر رو به contact ها اضافه کنیم
|
|
||||||
String insertSql = "INSERT INTO contacts (user_id, contact_id, is_blocked) VALUES (?, ?, ?)";
|
String insertSql = "INSERT INTO contacts (user_id, contact_id, is_blocked) VALUES (?, ?, ?)";
|
||||||
try (PreparedStatement insertStmt = conn.prepareStatement(insertSql)) {
|
try (PreparedStatement insertStmt = conn.prepareStatement(insertSql)) {
|
||||||
insertStmt.setObject(1, userId);
|
insertStmt.setObject(1, userId);
|
||||||
@@ -150,8 +150,13 @@ public class ContactDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean eitherBlocks(UUID userA, UUID userB) {
|
||||||
|
return isBlocked(userA, userB) || isBlocked(userB, userA);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isBlocked(UUID user_id, UUID contact_id) {
|
|
||||||
|
|
||||||
|
public static boolean isBlocked(UUID user_id, UUID contact_id) {
|
||||||
String sql = "SELECT is_blocked FROM contacts WHERE user_id = ? AND contact_id = ?";
|
String sql = "SELECT is_blocked FROM contacts WHERE user_id = ? AND contact_id = ?";
|
||||||
try (Connection connection = getConnection()) {
|
try (Connection connection = getConnection()) {
|
||||||
PreparedStatement stmt = connection.prepareStatement(sql);
|
PreparedStatement stmt = connection.prepareStatement(sql);
|
||||||
@@ -268,5 +273,39 @@ public class ContactDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<ContactEntry> getContactEntries(UUID userId) {
|
||||||
|
List<ContactEntry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
String sql = """
|
||||||
|
SELECT c.contact_id, c.is_blocked, u.user_id, u.profile_name, u.image_url
|
||||||
|
FROM contacts c
|
||||||
|
JOIN users u ON c.contact_id = u.internal_uuid
|
||||||
|
WHERE c.user_id = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, userId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
|
||||||
|
while (rs.next()) {
|
||||||
|
UUID contactId = UUID.fromString(rs.getString("contact_id"));
|
||||||
|
boolean isBlocked = rs.getBoolean("is_blocked");
|
||||||
|
String userIdStr = rs.getString("user_id");
|
||||||
|
String profileName = rs.getString("profile_name");
|
||||||
|
String imageUrl = rs.getString("image_url");
|
||||||
|
|
||||||
|
ContactEntry entry = new ContactEntry(contactId, userIdStr, profileName, imageUrl, isBlocked);
|
||||||
|
entries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.to.telegramfinalproject.Models.Message;
|
|||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -76,7 +77,7 @@ public class MessageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void logDeletedMessagesFor(UUID chatId, UUID userId) {
|
public static void logDeletedMessagesFor(UUID chatId, UUID userId) {
|
||||||
List<Message> messages = MessageDatabase.privateChatHistory(chatId);
|
List<Message> messages = MessageDatabase.privateChatHistory(chatId, userId);
|
||||||
for (Message message : messages) {
|
for (Message message : messages) {
|
||||||
if (!isMessageDeleted(message.getMessage_id(), userId)) {
|
if (!isMessageDeleted(message.getMessage_id(), userId)) {
|
||||||
String sql = """
|
String sql = """
|
||||||
@@ -110,6 +111,258 @@ public class MessageDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Message> getMessagesForChat(UUID chatId, String chatType, UUID currentUserId, int offset, int limit) {
|
||||||
|
List<Message> messages = new ArrayList<>();
|
||||||
|
|
||||||
|
String sql = """
|
||||||
|
SELECT * FROM messages m
|
||||||
|
WHERE m.receiver_type = ?
|
||||||
|
AND m.receiver_id = ?
|
||||||
|
AND m.is_deleted_globally = FALSE
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM deleted_messages d
|
||||||
|
WHERE d.message_id = m.message_id
|
||||||
|
AND d.user_id = ?
|
||||||
|
)
|
||||||
|
ORDER BY m.send_at DESC
|
||||||
|
LIMIT ? OFFSET ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
ps.setString(1, chatType);
|
||||||
|
ps.setObject(2, chatId);
|
||||||
|
ps.setObject(3, currentUserId);
|
||||||
|
ps.setInt(4, limit);
|
||||||
|
ps.setInt(5, offset);
|
||||||
|
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
Message msg = new Message(
|
||||||
|
UUID.fromString(rs.getString("message_id")),
|
||||||
|
UUID.fromString(rs.getString("sender_id")),
|
||||||
|
rs.getString("receiver_type"),
|
||||||
|
UUID.fromString(rs.getString("receiver_id")),
|
||||||
|
rs.getString("content"),
|
||||||
|
rs.getString("message_type"),
|
||||||
|
rs.getTimestamp("send_at").toLocalDateTime(),
|
||||||
|
rs.getString("status"),
|
||||||
|
rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null,
|
||||||
|
rs.getBoolean("is_edited"),
|
||||||
|
rs.getBoolean("is_deleted_globally"),
|
||||||
|
rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null,
|
||||||
|
rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null,
|
||||||
|
rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null,
|
||||||
|
rs.getTimestamp("edited_at") != null
|
||||||
|
? rs.getTimestamp("edited_at").toLocalDateTime()
|
||||||
|
: null
|
||||||
|
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
messages.add(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Message findById(UUID messageId) {
|
||||||
|
String sql = "SELECT * FROM messages WHERE message_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return new Message(
|
||||||
|
UUID.fromString(rs.getString("message_id")),
|
||||||
|
UUID.fromString(rs.getString("sender_id")),
|
||||||
|
rs.getString("receiver_type"),
|
||||||
|
UUID.fromString(rs.getString("receiver_id")),
|
||||||
|
rs.getString("content"),
|
||||||
|
rs.getString("message_type"),
|
||||||
|
rs.getTimestamp("send_at").toLocalDateTime(),
|
||||||
|
rs.getString("status"),
|
||||||
|
rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null,
|
||||||
|
rs.getBoolean("is_edited"),
|
||||||
|
rs.getBoolean("is_deleted_globally"),
|
||||||
|
rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null,
|
||||||
|
rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null,
|
||||||
|
rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean markAsDeletedForUser(UUID messageId, UUID userId) {
|
||||||
|
String sql = "INSERT INTO deleted_messages (message_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
ps.setObject(2, userId);
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean markAsGloballyDeleted(UUID messageId) {
|
||||||
|
String sql = "UPDATE messages SET is_deleted_globally = true WHERE message_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean updateContentAndMarkEdited(UUID messageId, String newContent) {
|
||||||
|
String sql = """
|
||||||
|
UPDATE messages
|
||||||
|
SET content = ?, is_edited = TRUE, edited_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE message_id = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setString(1, newContent);
|
||||||
|
ps.setObject(2, messageId);
|
||||||
|
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean saveReplyMessage(Message message) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO messages (
|
||||||
|
message_id, sender_id, receiver_type, receiver_id, content, message_type,
|
||||||
|
send_at, status, reply_to_id, is_edited, is_deleted_globally,
|
||||||
|
original_message_id, forwarded_by, forwarded_from, edited_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
ps.setObject(1, message.getMessage_id());
|
||||||
|
ps.setObject(2, message.getSender_id());
|
||||||
|
ps.setString(3, message.getReceiver_type());
|
||||||
|
ps.setObject(4, message.getReceiver_id());
|
||||||
|
ps.setString(5, message.getContent());
|
||||||
|
ps.setString(6, message.getMessage_type());
|
||||||
|
ps.setTimestamp(7, Timestamp.valueOf(message.getSend_at()));
|
||||||
|
ps.setString(8, message.getStatus());
|
||||||
|
ps.setObject(9, message.getReply_to_id()); //Important part because of reply message id
|
||||||
|
ps.setBoolean(10, message.isIs_edited());
|
||||||
|
ps.setBoolean(11, message.isIs_deleted_globally());
|
||||||
|
ps.setObject(12, message.getOriginal_message_id());
|
||||||
|
ps.setObject(13, message.getForwarded_by());
|
||||||
|
ps.setObject(14, message.getForwarded_from());
|
||||||
|
ps.setTimestamp(15, message.getEdited_at() != null ? Timestamp.valueOf(message.getEdited_at()) : null);
|
||||||
|
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean saveForwardedMessage(Message message) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO messages (
|
||||||
|
message_id, sender_id, receiver_type, receiver_id,
|
||||||
|
content, message_type, send_at, status,
|
||||||
|
reply_to_id, is_edited, is_deleted_globally,
|
||||||
|
original_message_id, forwarded_by, forwarded_from
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
ps.setObject(1, message.getMessage_id());
|
||||||
|
ps.setObject(2, message.getSender_id());
|
||||||
|
ps.setString(3, message.getReceiver_type());
|
||||||
|
ps.setObject(4, message.getReceiver_id());
|
||||||
|
ps.setString(5, message.getContent());
|
||||||
|
ps.setString(6, message.getMessage_type());
|
||||||
|
ps.setTimestamp(7, Timestamp.valueOf(message.getSend_at()));
|
||||||
|
ps.setString(8, message.getStatus());
|
||||||
|
ps.setObject(9, message.getReply_to_id());
|
||||||
|
ps.setBoolean(10, message.isIs_edited());
|
||||||
|
ps.setBoolean(11, message.isIs_deleted_globally());
|
||||||
|
ps.setObject(12, message.getOriginal_message_id()); //original id
|
||||||
|
ps.setObject(13, message.getForwarded_by()); //forwarded by
|
||||||
|
ps.setObject(14, message.getForwarded_from()); //forwarded from
|
||||||
|
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getExcerpt(UUID messageId) {
|
||||||
|
return getExcerpt(messageId, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getExcerpt(UUID messageId, int maxLen) {
|
||||||
|
String sql = "SELECT content, message_type, is_deleted_globally FROM messages WHERE message_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
if (!rs.next()) return "Message isn't available";
|
||||||
|
|
||||||
|
String content = rs.getString("content");
|
||||||
|
String type = rs.getString("message_type"); // e.g. TEXT, IMAGE, VIDEO, AUDIO, FILE, STICKER...
|
||||||
|
boolean deleted = rs.getBoolean("is_deleted_globally");
|
||||||
|
|
||||||
|
if (deleted) return "This message was deleted";
|
||||||
|
|
||||||
|
if (type == null || type.equalsIgnoreCase("TEXT")) {
|
||||||
|
if (content == null || content.isBlank()) return "(empty)";
|
||||||
|
return shorten(content, maxLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
//for media
|
||||||
|
switch (type.toUpperCase()) {
|
||||||
|
case "IMAGE": return "[Photo]";
|
||||||
|
case "VIDEO": return "[Video]";
|
||||||
|
case "AUDIO": return "[Audio]";
|
||||||
|
case "VOICE": return "[Voice]";
|
||||||
|
case "FILE": return "[File]";
|
||||||
|
case "STICKER":return "[Sticker]";
|
||||||
|
default: return "[" + type + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String shorten(String s, int maxLen) {
|
||||||
|
if (s.length() <= maxLen) return s;
|
||||||
|
return s.substring(0, Math.max(0, maxLen - 1)).trim() + "…";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -128,14 +381,17 @@ public class MessageDatabase {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static List<Message> getUnreadMessages(UUID userId) {
|
public static List<Message> getUnreadMessages(UUID userId) {
|
||||||
List<Message> messages = new ArrayList<>();
|
List<Message> messages = new ArrayList<>();
|
||||||
|
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT m.* FROM messages m
|
SELECT m.*
|
||||||
|
FROM messages m
|
||||||
LEFT JOIN message_receipts r ON m.message_id = r.message_id AND r.user_id = ?
|
LEFT JOIN message_receipts r ON m.message_id = r.message_id AND r.user_id = ?
|
||||||
|
LEFT JOIN deleted_messages d ON m.message_id = d.message_id AND d.user_id = ?
|
||||||
WHERE r.user_id IS NULL
|
WHERE r.user_id IS NULL
|
||||||
|
AND d.message_id IS NULL
|
||||||
|
AND m.is_deleted_globally = FALSE
|
||||||
AND (
|
AND (
|
||||||
(m.receiver_type = 'private' AND m.receiver_id = ?)
|
(m.receiver_type = 'private' AND m.receiver_id = ?)
|
||||||
OR
|
OR
|
||||||
@@ -147,19 +403,21 @@ public class MessageDatabase {
|
|||||||
SELECT 1 FROM channel_subscribers cs WHERE cs.channel_id = m.receiver_id AND cs.user_id = ?
|
SELECT 1 FROM channel_subscribers cs WHERE cs.channel_id = m.receiver_id AND cs.user_id = ?
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
|
ORDER BY m.send_at DESC
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
stmt.setObject(1, userId);
|
stmt.setObject(1, userId); // for message_receipts
|
||||||
stmt.setObject(2, userId);
|
stmt.setObject(2, userId); // for deleted_messages
|
||||||
stmt.setObject(3, userId);
|
stmt.setObject(3, userId); // for private messages
|
||||||
stmt.setObject(4, userId);
|
stmt.setObject(4, userId); // for group members
|
||||||
|
stmt.setObject(5, userId); // for channel subscribers
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
messages.add(new Message(
|
Message message = new Message(
|
||||||
UUID.fromString(rs.getString("message_id")),
|
UUID.fromString(rs.getString("message_id")),
|
||||||
rs.getObject("sender_id") != null ? UUID.fromString(rs.getString("sender_id")) : null,
|
rs.getObject("sender_id") != null ? UUID.fromString(rs.getString("sender_id")) : null,
|
||||||
rs.getString("receiver_type"),
|
rs.getString("receiver_type"),
|
||||||
@@ -170,10 +428,13 @@ public class MessageDatabase {
|
|||||||
rs.getString("status"),
|
rs.getString("status"),
|
||||||
rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null,
|
rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null,
|
||||||
rs.getBoolean("is_edited"),
|
rs.getBoolean("is_edited"),
|
||||||
|
rs.getBoolean("is_deleted_globally"),
|
||||||
rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null,
|
rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null,
|
||||||
rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null,
|
rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null,
|
||||||
rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null
|
rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null
|
||||||
));
|
);
|
||||||
|
|
||||||
|
messages.add(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -183,6 +444,8 @@ public class MessageDatabase {
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static List<FileAttachment> getAttachments(UUID messageId) {
|
public static List<FileAttachment> getAttachments(UUID messageId) {
|
||||||
List<FileAttachment> attachments = new ArrayList<>();
|
List<FileAttachment> attachments = new ArrayList<>();
|
||||||
String sql = "SELECT file_url, file_type FROM message_attachments WHERE message_id = ?";
|
String sql = "SELECT file_url, file_type FROM message_attachments WHERE message_id = ?";
|
||||||
@@ -271,6 +534,7 @@ public class MessageDatabase {
|
|||||||
rs.getString("status"),
|
rs.getString("status"),
|
||||||
(UUID) rs.getObject("reply_to_id"),
|
(UUID) rs.getObject("reply_to_id"),
|
||||||
rs.getBoolean("is_edited"),
|
rs.getBoolean("is_edited"),
|
||||||
|
rs.getBoolean("is_deleted_globally"),
|
||||||
(UUID) rs.getObject("original_message_id"),
|
(UUID) rs.getObject("original_message_id"),
|
||||||
(UUID) rs.getObject("forwarded_by"),
|
(UUID) rs.getObject("forwarded_by"),
|
||||||
(UUID) rs.getObject("forwarded_from")
|
(UUID) rs.getObject("forwarded_from")
|
||||||
@@ -279,45 +543,129 @@ public class MessageDatabase {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// public static List<Message> searchMessagesForUser(UUID userId, String keyword) {
|
||||||
|
// List<Message> result = new ArrayList<>();
|
||||||
|
// String sql = """
|
||||||
|
// SELECT * FROM messages
|
||||||
|
// WHERE receiver_type = 'private'
|
||||||
|
// AND (sender_id = ? OR receiver_id = ?)
|
||||||
|
// AND content ILIKE ?
|
||||||
|
// ORDER BY send_at DESC
|
||||||
|
// """;
|
||||||
|
//
|
||||||
|
// try (Connection conn = ConnectionDb.connect();
|
||||||
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
// stmt.setObject(1, userId);
|
||||||
|
// stmt.setObject(2, userId);
|
||||||
|
// stmt.setString(3, "%" + keyword + "%");
|
||||||
|
//
|
||||||
|
// ResultSet rs = stmt.executeQuery();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// result.add(extractMessage(rs));
|
||||||
|
// }
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
public static List<Message> searchMessagesForUser(UUID userId, String keyword) {
|
public static List<Message> searchMessagesForUser(UUID userId, String keyword) {
|
||||||
List<Message> result = new ArrayList<>();
|
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT * FROM messages
|
SELECT * FROM messages m
|
||||||
WHERE receiver_type = 'private'
|
WHERE m.receiver_type = 'private'
|
||||||
AND (sender_id = ? OR receiver_id = ?)
|
AND m.content ILIKE ?
|
||||||
AND content ILIKE ?
|
AND m.is_deleted_globally = FALSE
|
||||||
ORDER BY send_at DESC
|
AND (m.sender_id = ? OR m.receiver_id = ?)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1 FROM deleted_messages d
|
||||||
|
WHERE d.message_id = m.message_id AND d.user_id = ?
|
||||||
|
)
|
||||||
|
ORDER BY m.send_at DESC
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
stmt.setObject(1, userId);
|
|
||||||
stmt.setObject(2, userId);
|
|
||||||
stmt.setString(3, "%" + keyword + "%");
|
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ps.setString(1, "%" + keyword + "%");
|
||||||
|
ps.setObject(2, userId); // m.sender_id
|
||||||
|
ps.setObject(3, userId); // m.receiver_id
|
||||||
|
ps.setObject(4, userId); // deleted_messages
|
||||||
|
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
List<Message> messages = new ArrayList<>();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(extractMessage(rs));
|
messages.add(mapResultSetToMessage(rs));
|
||||||
}
|
}
|
||||||
|
return messages;
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
private static Message mapResultSetToMessage(ResultSet rs) throws SQLException {
|
||||||
|
return new Message(
|
||||||
|
UUID.fromString(rs.getString("message_id")),
|
||||||
|
rs.getObject("sender_id") != null ? UUID.fromString(rs.getString("sender_id")) : null,
|
||||||
|
rs.getString("receiver_type"),
|
||||||
|
UUID.fromString(rs.getString("receiver_id")),
|
||||||
|
rs.getString("content"),
|
||||||
|
rs.getString("message_type"),
|
||||||
|
rs.getTimestamp("send_at").toLocalDateTime(),
|
||||||
|
rs.getString("status"),
|
||||||
|
rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null,
|
||||||
|
rs.getBoolean("is_edited"),
|
||||||
|
rs.getBoolean("is_deleted_globally"),
|
||||||
|
rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null,
|
||||||
|
rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null,
|
||||||
|
rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Message> privateChatHistory(UUID chatId) {
|
|
||||||
|
|
||||||
|
// public static List<Message> privateChatHistory(UUID chatId) {
|
||||||
|
// List<Message> result = new ArrayList<>();
|
||||||
|
// String sql = """
|
||||||
|
// SELECT * FROM messages
|
||||||
|
// WHERE receiver_type = 'private'
|
||||||
|
// AND receiver_id = ?
|
||||||
|
// ORDER BY send_at
|
||||||
|
// """;
|
||||||
|
//
|
||||||
|
// try (Connection conn = ConnectionDb.connect();
|
||||||
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
// stmt.setObject(1, chatId);
|
||||||
|
// ResultSet rs = stmt.executeQuery();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// result.add(extractMessage(rs));
|
||||||
|
// }
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static List<Message> privateChatHistory(UUID chatId, UUID userId) {
|
||||||
List<Message> result = new ArrayList<>();
|
List<Message> result = new ArrayList<>();
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT * FROM messages
|
SELECT * FROM messages
|
||||||
WHERE receiver_type = 'private'
|
WHERE receiver_type = 'private'
|
||||||
AND receiver_id = ?
|
AND receiver_id = ?
|
||||||
|
AND is_deleted_globally = FALSE
|
||||||
|
AND message_id NOT IN (
|
||||||
|
SELECT message_id FROM deleted_messages WHERE user_id = ?
|
||||||
|
)
|
||||||
ORDER BY send_at
|
ORDER BY send_at
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
stmt.setObject(1, chatId);
|
stmt.setObject(1, chatId);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(extractMessage(rs));
|
result.add(extractMessage(rs));
|
||||||
@@ -331,17 +679,47 @@ public class MessageDatabase {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static List<Message> groupChatHistory(UUID groupId) {
|
|
||||||
|
// public static List<Message> groupChatHistory(UUID groupId) {
|
||||||
|
// List<Message> result = new ArrayList<>();
|
||||||
|
// String sql = """
|
||||||
|
// SELECT * FROM messages
|
||||||
|
// WHERE receiver_type = 'group' AND receiver_id = ?
|
||||||
|
// ORDER BY send_at
|
||||||
|
// """;
|
||||||
|
//
|
||||||
|
// try (Connection conn = ConnectionDb.connect();
|
||||||
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
// stmt.setObject(1, groupId);
|
||||||
|
// ResultSet rs = stmt.executeQuery();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// result.add(extractMessage(rs));
|
||||||
|
// }
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public static List<Message> groupChatHistory(UUID groupId,UUID userId) {
|
||||||
List<Message> result = new ArrayList<>();
|
List<Message> result = new ArrayList<>();
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT * FROM messages
|
SELECT * FROM messages
|
||||||
WHERE receiver_type = 'group' AND receiver_id = ?
|
WHERE receiver_type = 'group'
|
||||||
|
AND receiver_id = ?
|
||||||
|
AND is_deleted_globally = FALSE
|
||||||
|
AND message_id NOT IN (
|
||||||
|
SELECT message_id FROM deleted_messages WHERE user_id = ?
|
||||||
|
)
|
||||||
ORDER BY send_at
|
ORDER BY send_at
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
stmt.setObject(1, groupId);
|
stmt.setObject(1, groupId);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(extractMessage(rs));
|
result.add(extractMessage(rs));
|
||||||
@@ -353,17 +731,48 @@ public class MessageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Message> channelChatHistory(UUID channelId) {
|
|
||||||
|
// public static List<Message> channelChatHistory(UUID channelId) {
|
||||||
|
// List<Message> result = new ArrayList<>();
|
||||||
|
// String sql = """
|
||||||
|
// SELECT * FROM messages
|
||||||
|
// WHERE receiver_type = 'channel' AND receiver_id = ?
|
||||||
|
// ORDER BY send_at
|
||||||
|
// """;
|
||||||
|
//
|
||||||
|
// try (Connection conn = ConnectionDb.connect();
|
||||||
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
// stmt.setObject(1, channelId);
|
||||||
|
// ResultSet rs = stmt.executeQuery();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// result.add(extractMessage(rs));
|
||||||
|
// }
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public static List<Message> channelChatHistory(UUID channelId, UUID userId) {
|
||||||
List<Message> result = new ArrayList<>();
|
List<Message> result = new ArrayList<>();
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT * FROM messages
|
SELECT * FROM messages
|
||||||
WHERE receiver_type = 'channel' AND receiver_id = ?
|
WHERE receiver_type = 'channel'
|
||||||
|
AND receiver_id = ?
|
||||||
|
AND is_deleted_globally = FALSE
|
||||||
|
AND message_id NOT IN (
|
||||||
|
SELECT message_id FROM deleted_messages WHERE user_id = ?
|
||||||
|
)
|
||||||
ORDER BY send_at
|
ORDER BY send_at
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
stmt.setObject(1, channelId);
|
stmt.setObject(1, channelId);
|
||||||
|
stmt.setObject(2, userId);
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(extractMessage(rs));
|
result.add(extractMessage(rs));
|
||||||
@@ -375,32 +784,75 @@ public class MessageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Message> searchMessagesInGroups(List<UUID> groupIds, String keyword) {
|
|
||||||
|
// public static List<Message> searchMessagesInGroups(List<UUID> groupIds, String keyword) {
|
||||||
|
// List<Message> result = new ArrayList<>();
|
||||||
|
// if (groupIds.isEmpty()) return result;
|
||||||
|
//
|
||||||
|
// String placeholders = groupIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
||||||
|
// String sql = """
|
||||||
|
// SELECT * FROM messages
|
||||||
|
// WHERE receiver_type = 'group'
|
||||||
|
// AND receiver_id IN (%s)
|
||||||
|
// AND content ILIKE ?
|
||||||
|
// ORDER BY send_at DESC
|
||||||
|
// """.formatted(placeholders);
|
||||||
|
//
|
||||||
|
// try (Connection conn = ConnectionDb.connect();
|
||||||
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
//
|
||||||
|
// int i = 1;
|
||||||
|
// for (UUID id : groupIds) {
|
||||||
|
// stmt.setObject(i++, id);
|
||||||
|
// }
|
||||||
|
// stmt.setString(i, "%" + keyword + "%");
|
||||||
|
//
|
||||||
|
// ResultSet rs = stmt.executeQuery();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// result.add(extractMessage(rs));
|
||||||
|
// }
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static List<Message> searchMessagesInGroups(List<UUID> groupIds, String keyword, UUID userId) {
|
||||||
List<Message> result = new ArrayList<>();
|
List<Message> result = new ArrayList<>();
|
||||||
if (groupIds.isEmpty()) return result;
|
if (groupIds.isEmpty()) return result;
|
||||||
|
|
||||||
String placeholders = groupIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
String placeholders = groupIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT * FROM messages
|
SELECT m.*
|
||||||
WHERE receiver_type = 'group'
|
FROM messages m
|
||||||
AND receiver_id IN (%s)
|
LEFT JOIN deleted_messages d ON m.message_id = d.message_id AND d.user_id = ?
|
||||||
AND content ILIKE ?
|
WHERE m.receiver_type = 'group'
|
||||||
ORDER BY send_at DESC
|
AND m.receiver_id IN (%s)
|
||||||
|
AND d.message_id IS NULL
|
||||||
|
AND m.is_deleted_globally = FALSE
|
||||||
|
AND m.content ILIKE ?
|
||||||
|
ORDER BY m.send_at DESC
|
||||||
""".formatted(placeholders);
|
""".formatted(placeholders);
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (UUID id : groupIds) {
|
stmt.setObject(i++, userId);
|
||||||
stmt.setObject(i++, id);
|
for (UUID groupId : groupIds) {
|
||||||
|
stmt.setObject(i++, groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.setString(i, "%" + keyword + "%");
|
stmt.setString(i, "%" + keyword + "%");
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(extractMessage(rs));
|
result.add(mapResultSetToMessage(rs));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -409,32 +861,71 @@ public class MessageDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// public static List<Message> searchMessagesInChannels(List<UUID> channelIds, String keyword) {
|
||||||
|
// List<Message> result = new ArrayList<>();
|
||||||
|
// if (channelIds.isEmpty()) return result;
|
||||||
|
//
|
||||||
|
// String placeholders = channelIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
||||||
|
// String sql = """
|
||||||
|
// SELECT * FROM messages
|
||||||
|
// WHERE receiver_type = 'channel'
|
||||||
|
// AND receiver_id IN (%s)
|
||||||
|
// AND content ILIKE ?
|
||||||
|
// ORDER BY send_at DESC
|
||||||
|
// """.formatted(placeholders);
|
||||||
|
//
|
||||||
|
// try (Connection conn = ConnectionDb.connect();
|
||||||
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
//
|
||||||
|
// int i = 1;
|
||||||
|
// for (UUID id : channelIds) {
|
||||||
|
// stmt.setObject(i++, id);
|
||||||
|
// }
|
||||||
|
// stmt.setString(i, "%" + keyword + "%");
|
||||||
|
//
|
||||||
|
// ResultSet rs = stmt.executeQuery();
|
||||||
|
// while (rs.next()) {
|
||||||
|
// result.add(extractMessage(rs));
|
||||||
|
// }
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static List<Message> searchMessagesInChannels(List<UUID> channelIds, String keyword) {
|
public static List<Message> searchMessagesInChannels(List<UUID> channelIds, String keyword) {
|
||||||
List<Message> result = new ArrayList<>();
|
List<Message> result = new ArrayList<>();
|
||||||
if (channelIds.isEmpty()) return result;
|
if (channelIds.isEmpty()) return result;
|
||||||
|
|
||||||
String placeholders = channelIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
String placeholders = channelIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT * FROM messages
|
SELECT m.*
|
||||||
WHERE receiver_type = 'channel'
|
FROM messages m
|
||||||
AND receiver_id IN (%s)
|
WHERE m.receiver_type = 'channel'
|
||||||
AND content ILIKE ?
|
AND m.receiver_id IN (%s)
|
||||||
ORDER BY send_at DESC
|
AND m.is_deleted_globally = FALSE
|
||||||
|
AND m.content ILIKE ?
|
||||||
|
ORDER BY m.send_at DESC
|
||||||
""".formatted(placeholders);
|
""".formatted(placeholders);
|
||||||
|
|
||||||
try (Connection conn = ConnectionDb.connect();
|
try (Connection conn = ConnectionDb.connect();
|
||||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (UUID id : channelIds) {
|
for (UUID channelId : channelIds) {
|
||||||
stmt.setObject(i++, id);
|
stmt.setObject(i++, channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.setString(i, "%" + keyword + "%");
|
stmt.setString(i, "%" + keyword + "%");
|
||||||
|
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
result.add(extractMessage(rs));
|
result.add(mapResultSetToMessage(rs));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -480,5 +971,39 @@ public class MessageDatabase {
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Message> getMessages(UUID chatId, UUID userId) {
|
||||||
|
String sql = """
|
||||||
|
SELECT * FROM messages
|
||||||
|
WHERE receiver_id = ?
|
||||||
|
AND is_deleted_globally = false
|
||||||
|
AND message_id NOT IN (
|
||||||
|
SELECT message_id FROM deleted_messages
|
||||||
|
WHERE user_id = ?
|
||||||
|
)
|
||||||
|
ORDER BY send_at
|
||||||
|
""";
|
||||||
|
|
||||||
|
List<Message> messages = new ArrayList<>();
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
ps.setObject(1, chatId);
|
||||||
|
ps.setObject(2, userId);
|
||||||
|
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
Message m = extractMessage(rs);
|
||||||
|
messages.add(m);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package org.to.telegramfinalproject.Database;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class MessageReactionDatabase {
|
||||||
|
|
||||||
|
public static boolean saveOrUpdateReaction(UUID messageId, UUID userId, String reaction) {
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO message_reactions (message_id, user_id, emoji)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT (message_id, user_id)
|
||||||
|
DO UPDATE SET emoji = EXCLUDED.emoji, reacted_at = CURRENT_TIMESTAMP
|
||||||
|
""";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
ps.setObject(2, userId);
|
||||||
|
ps.setString(3, reaction);
|
||||||
|
return ps.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getReactions(UUID messageId) {
|
||||||
|
String sql = "SELECT emoji FROM message_reactions WHERE message_id = ?";
|
||||||
|
List<String> reactions = new ArrayList<>();
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
while (rs.next()) {
|
||||||
|
reactions.add(rs.getString("emoji"));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return reactions;
|
||||||
|
}
|
||||||
|
public static JSONObject getCountsAsJson(UUID messageId) {
|
||||||
|
String sql = """
|
||||||
|
SELECT emoji, COUNT(*) AS c
|
||||||
|
FROM message_reactions
|
||||||
|
WHERE message_id = ?
|
||||||
|
GROUP BY emoji
|
||||||
|
""";
|
||||||
|
|
||||||
|
JSONObject counts = new JSONObject();
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
|
||||||
|
ps.setObject(1, messageId);
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
String emoji = rs.getString("emoji");
|
||||||
|
int c = rs.getInt("c");
|
||||||
|
counts.put(emoji, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return counts; // مثال: {"❤️":2,"👍":1}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -251,7 +251,36 @@ public class PrivateChatDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static UUID findChatBetween(UUID user1, UUID user2) {
|
||||||
|
String sql = """
|
||||||
|
SELECT chat_id FROM private_chat
|
||||||
|
WHERE (user1_id = ? AND user2_id = ?) OR (user1_id = ? AND user2_id = ?)
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
|
ps.setObject(1, user1);
|
||||||
|
ps.setObject(2, user2);
|
||||||
|
ps.setObject(3, user2);
|
||||||
|
ps.setObject(4, user1);
|
||||||
|
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
return (UUID) rs.getObject("chat_id");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID getOtherParticipant(UUID chatId, UUID me) {
|
||||||
|
List<UUID> members = getMembers(chatId);
|
||||||
|
for (UUID u : members) {
|
||||||
|
if (!u.equals(me)) return u;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,4 @@ public class Contact {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.to.telegramfinalproject.Models;
|
package org.to.telegramfinalproject.Models;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ContactEntry {
|
public class ContactEntry {
|
||||||
@@ -41,4 +43,16 @@ public class ContactEntry {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return profileName + " (" + userId + ")" + (isBlocked ? " [Blocked]" : "");
|
return profileName + " (" + userId + ")" + (isBlocked ? " [Blocked]" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public JSONObject toJson() {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("contact_id", contactId.toString());
|
||||||
|
obj.put("user_id", userId);
|
||||||
|
obj.put("profile_name", profileName);
|
||||||
|
obj.put("image_url", imageUrl);
|
||||||
|
obj.put("is_blocked", isBlocked);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.to.telegramfinalproject.Models;
|
|||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
import org.to.telegramfinalproject.Database.userDatabase;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -9,20 +10,43 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class JsonUtil {
|
public class JsonUtil {
|
||||||
public static JSONArray contactListToJson(List<Contact> contacts) {
|
public static JSONArray contactListToJson(List<Contact> contacts) {
|
||||||
|
|
||||||
|
|
||||||
JSONArray array = new JSONArray();
|
JSONArray array = new JSONArray();
|
||||||
|
|
||||||
for (Contact contact : contacts) {
|
for (Contact contact : contacts) {
|
||||||
|
UUID contactId = contact.getContact_id();
|
||||||
|
User contactUser = userDatabase.findByInternalUUID(contactId);
|
||||||
|
if (contactUser == null) continue;
|
||||||
|
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("user_id", contact.getUser_id().toString());
|
obj.put("contact_id", contactId.toString()); // UUID
|
||||||
obj.put("contact_id", contact.getContact_id().toString());
|
obj.put("user_id", contactUser.getUser_id()); // public ID
|
||||||
|
obj.put("profile_name", contactUser.getProfile_name());
|
||||||
|
obj.put("image_url", contactUser.getImage_url());
|
||||||
obj.put("is_blocked", contact.getIs_blocked());
|
obj.put("is_blocked", contact.getIs_blocked());
|
||||||
obj.put("added_at", contact.getAdd_at().toString());
|
|
||||||
array.put(obj);
|
array.put(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JSONArray contactEntryListToJson(List<ContactEntry> contactEntries) {
|
||||||
|
JSONArray array = new JSONArray();
|
||||||
|
|
||||||
|
for (ContactEntry entry : contactEntries) {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("contact_id", entry.getContactId().toString());
|
||||||
|
obj.put("user_id", entry.getUserId());
|
||||||
|
obj.put("profile_name", entry.getProfileName());
|
||||||
|
obj.put("image_url", entry.getImageUrl());
|
||||||
|
obj.put("is_blocked", entry.isBlocked());
|
||||||
|
array.put(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,37 @@
|
|||||||
package org.to.telegramfinalproject.Models;
|
package org.to.telegramfinalproject.Models;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class Message {
|
public class Message {
|
||||||
private UUID message_id;
|
private UUID message_id;
|
||||||
private UUID sender_id;
|
private UUID sender_id;
|
||||||
private String receiver_type; // private, group, channel
|
private String receiver_type;
|
||||||
private UUID receiver_id;
|
private UUID receiver_id;
|
||||||
private String content;
|
private String content;
|
||||||
private String message_type; // TEXT, IMAGE, FILE, ...
|
private String message_type;
|
||||||
private LocalDateTime send_at;
|
private LocalDateTime send_at;
|
||||||
private String status; // SENT, DELIVERED, READ
|
private String status;
|
||||||
private UUID reply_to_id;
|
private UUID reply_to_id;
|
||||||
private boolean is_edited;
|
private boolean is_edited;
|
||||||
|
private boolean is_deleted_globally;
|
||||||
private UUID original_message_id;
|
private UUID original_message_id;
|
||||||
private UUID forwarded_by;
|
private UUID forwarded_by;
|
||||||
private UUID forwarded_from;
|
private UUID forwarded_from;
|
||||||
private List<FileAttachment> attachments;
|
private List<FileAttachment> attachments;
|
||||||
|
private transient String sender_name;
|
||||||
|
private transient String receiver_name;
|
||||||
|
private LocalDateTime edited_at;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ✅ Full Constructor
|
||||||
public Message(UUID message_id, UUID sender_id, String receiver_type, UUID receiver_id, String content,
|
public Message(UUID message_id, UUID sender_id, String receiver_type, UUID receiver_id, String content,
|
||||||
String message_type, LocalDateTime send_at, String status,
|
String message_type, LocalDateTime send_at, String status,
|
||||||
UUID reply_to_id, boolean is_edited, UUID original_message_id,
|
UUID reply_to_id, boolean is_edited, boolean is_deleted_globally,
|
||||||
UUID forwarded_by, UUID forwarded_from) {
|
UUID original_message_id, UUID forwarded_by, UUID forwarded_from) {
|
||||||
this.message_id = message_id;
|
this.message_id = message_id;
|
||||||
this.sender_id = sender_id;
|
this.sender_id = sender_id;
|
||||||
this.receiver_type = receiver_type;
|
this.receiver_type = receiver_type;
|
||||||
@@ -34,127 +42,141 @@ public class Message {
|
|||||||
this.status = status;
|
this.status = status;
|
||||||
this.reply_to_id = reply_to_id;
|
this.reply_to_id = reply_to_id;
|
||||||
this.is_edited = is_edited;
|
this.is_edited = is_edited;
|
||||||
|
this.is_deleted_globally = is_deleted_globally;
|
||||||
this.original_message_id = original_message_id;
|
this.original_message_id = original_message_id;
|
||||||
this.forwarded_by = forwarded_by;
|
this.forwarded_by = forwarded_by;
|
||||||
this.forwarded_from = forwarded_from;
|
this.forwarded_from = forwarded_from;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message(UUID messageId, UUID senderId, UUID receiverId, String receiverType, String content, String messageType, LocalDateTime now) {
|
|
||||||
|
// ✅ Short Constructors
|
||||||
|
//for normal messages
|
||||||
|
public Message(UUID messageId, UUID senderId, UUID receiverId, String receiverType,
|
||||||
|
String content, String messageType, LocalDateTime sendAt) {
|
||||||
this.message_id = messageId;
|
this.message_id = messageId;
|
||||||
this.sender_id = senderId;
|
this.sender_id = senderId;
|
||||||
this.receiver_id = receiverId;
|
this.receiver_id = receiverId;
|
||||||
this.receiver_type = receiverType;
|
this.receiver_type = receiverType;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.message_type = messageType;
|
this.message_type = messageType;
|
||||||
this.send_at = now;
|
this.send_at = sendAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//for reply messages
|
||||||
public UUID getMessage_id() {
|
public Message(UUID messageId, UUID senderId, String receiverType, UUID receiverId,
|
||||||
return message_id;
|
String content, String messageType, LocalDateTime sendAt, String status,
|
||||||
}
|
UUID replyToId) {
|
||||||
|
this.message_id = messageId;
|
||||||
|
this.sender_id = senderId;
|
||||||
public UUID getSender_id() {
|
this.receiver_type = receiverType;
|
||||||
return sender_id;
|
this.receiver_id = receiverId;
|
||||||
}
|
|
||||||
|
|
||||||
public void setSender_id(UUID sender_id) {
|
|
||||||
this.sender_id = sender_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getReceiver_type() {
|
|
||||||
return receiver_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReceiver_type(String receiver_type) {
|
|
||||||
this.receiver_type = receiver_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getReceiver_id() {
|
|
||||||
return receiver_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReceiver_id(UUID receiver_id) {
|
|
||||||
this.receiver_id = receiver_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContent() {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setContent(String content) {
|
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
this.message_type = messageType;
|
||||||
|
this.send_at = sendAt;
|
||||||
public String getMessage_type() {
|
|
||||||
return message_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessage_type(String message_type) {
|
|
||||||
this.message_type = message_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LocalDateTime getSend_at() {
|
|
||||||
return send_at;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSend_at(LocalDateTime send_at) {
|
|
||||||
this.send_at = send_at;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(String status) {
|
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
this.reply_to_id = replyToId;
|
||||||
|
this.is_edited = false;
|
||||||
|
this.is_deleted_globally = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getReply_to_id() {
|
|
||||||
return reply_to_id;
|
|
||||||
|
|
||||||
|
public Message(UUID messageId, UUID senderId, String receiverType, UUID receiverId,
|
||||||
|
String content, String messageType, LocalDateTime sendAt, String status,
|
||||||
|
UUID replyToId, boolean isEdited, boolean isDeletedGlobally,
|
||||||
|
UUID originalMessageId, UUID forwardedBy, UUID forwardedFrom,
|
||||||
|
LocalDateTime editedAt) {
|
||||||
|
|
||||||
|
this.message_id = messageId;
|
||||||
|
this.sender_id = senderId;
|
||||||
|
this.receiver_type = receiverType;
|
||||||
|
this.receiver_id = receiverId;
|
||||||
|
this.content = content;
|
||||||
|
this.message_type = messageType;
|
||||||
|
this.send_at = sendAt;
|
||||||
|
this.status = status;
|
||||||
|
this.reply_to_id = replyToId;
|
||||||
|
this.is_edited = isEdited;
|
||||||
|
this.is_deleted_globally = isDeletedGlobally;
|
||||||
|
this.original_message_id = originalMessageId;
|
||||||
|
this.forwarded_by = forwardedBy;
|
||||||
|
this.forwarded_from = forwardedFrom;
|
||||||
|
this.edited_at = editedAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReply_to_id(UUID reply_to_id) {
|
|
||||||
this.reply_to_id = reply_to_id;
|
|
||||||
|
// ✅ Getters & Setters
|
||||||
|
public UUID getMessage_id() { return message_id; }
|
||||||
|
public void setMessage_id(UUID messageId) { this.message_id = messageId; }
|
||||||
|
|
||||||
|
public UUID getSender_id() { return sender_id; }
|
||||||
|
public void setSender_id(UUID sender_id) { this.sender_id = sender_id; }
|
||||||
|
|
||||||
|
public String getReceiver_type() { return receiver_type; }
|
||||||
|
public void setReceiver_type(String receiver_type) { this.receiver_type = receiver_type; }
|
||||||
|
|
||||||
|
public UUID getReceiver_id() { return receiver_id; }
|
||||||
|
public void setReceiver_id(UUID receiver_id) { this.receiver_id = receiver_id; }
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
|
||||||
|
public String getMessage_type() { return message_type; }
|
||||||
|
public void setMessage_type(String message_type) { this.message_type = message_type; }
|
||||||
|
|
||||||
|
public LocalDateTime getSend_at() { return send_at; }
|
||||||
|
public void setSend_at(LocalDateTime send_at) { this.send_at = send_at; }
|
||||||
|
|
||||||
|
public String getStatus() { return status; }
|
||||||
|
public void setStatus(String status) { this.status = status; }
|
||||||
|
|
||||||
|
public UUID getReply_to_id() { return reply_to_id; }
|
||||||
|
public void setReply_to_id(UUID reply_to_id) { this.reply_to_id = reply_to_id; }
|
||||||
|
|
||||||
|
public boolean isIs_edited() { return is_edited; }
|
||||||
|
public void setIs_edited(boolean is_edited) { this.is_edited = is_edited; }
|
||||||
|
|
||||||
|
public boolean isIs_deleted_globally() { return is_deleted_globally; }
|
||||||
|
public void setIs_deleted_globally(boolean is_deleted_globally) {
|
||||||
|
this.is_deleted_globally = is_deleted_globally;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isIs_edited() {
|
public UUID getOriginal_message_id() { return original_message_id; }
|
||||||
return is_edited;
|
public void setOriginal_message_id(UUID original_message_id) { this.original_message_id = original_message_id; }
|
||||||
|
|
||||||
|
public UUID getForwarded_by() { return forwarded_by; }
|
||||||
|
public void setForwarded_by(UUID forwarded_by) { this.forwarded_by = forwarded_by; }
|
||||||
|
|
||||||
|
public UUID getForwarded_from() { return forwarded_from; }
|
||||||
|
public void setForwarded_from(UUID forwarded_from) { this.forwarded_from = forwarded_from; }
|
||||||
|
|
||||||
|
public List<FileAttachment> getAttachments() { return attachments; }
|
||||||
|
public void setAttachments(List<FileAttachment> attachments) { this.attachments = attachments; }
|
||||||
|
public String getSender_name() {
|
||||||
|
return sender_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIs_edited(boolean is_edited) {
|
public void setSender_name(String sender_name) {
|
||||||
this.is_edited = is_edited;
|
this.sender_name = sender_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getOriginal_message_id() {
|
public String getReceiver_name() {
|
||||||
return original_message_id;
|
return receiver_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOriginal_message_id(UUID original_message_id) {
|
public void setReceiver_name(String receiver_name) {
|
||||||
this.original_message_id = original_message_id;
|
this.receiver_name = receiver_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getForwarded_by() {
|
public LocalDateTime getEdited_at() {
|
||||||
return forwarded_by;
|
return edited_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForwarded_by(UUID forwarded_by) {
|
public void setEdited_at(LocalDateTime edited_at) {
|
||||||
this.forwarded_by = forwarded_by;
|
this.edited_at = edited_at;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getForwarded_from() {
|
|
||||||
return forwarded_from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setForwarded_from(UUID forwarded_from) {
|
|
||||||
this.forwarded_from = forwarded_from;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAttachments(List<FileAttachment> attachments) {
|
|
||||||
this.attachments = attachments;
|
|
||||||
}
|
|
||||||
public List<FileAttachment> getAttachments() {
|
|
||||||
return attachments;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -419,10 +419,14 @@ public class ClientHandler implements Runnable {
|
|||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("type", "message");
|
obj.put("type", "message");
|
||||||
obj.put("content", m.getContent());
|
obj.put("content", m.getContent());
|
||||||
obj.put("sender", m.getSender_id().toString());
|
|
||||||
obj.put("time", m.getSend_at().toString());
|
obj.put("time", m.getSend_at().toString());
|
||||||
|
obj.put("sender", m.getSender_id() != null ? m.getSender_id().toString() : "N/A");
|
||||||
obj.put("receiver_id", m.getReceiver_id().toString());
|
obj.put("receiver_id", m.getReceiver_id().toString());
|
||||||
obj.put("receiver_type", m.getReceiver_type());
|
obj.put("receiver_type", m.getReceiver_type());
|
||||||
|
User senderUser = userDatabase.findByInternalUUID(m.getSender_id());
|
||||||
|
String senderName = senderUser != null ? senderUser.getProfile_name() : "Unknown";
|
||||||
|
obj.put("sender_name", senderName);
|
||||||
|
|
||||||
results.add(obj);
|
results.add(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,13 +443,23 @@ public class ClientHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<Message> groupMessages = MessageDatabase.searchMessagesInGroups(groupIds, keyword);
|
List<Message> groupMessages = MessageDatabase.searchMessagesInGroups(groupIds, keyword, currentUser.getInternal_uuid());
|
||||||
for (Message m : groupMessages) {
|
for (Message m : groupMessages) {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
obj.put("type", "message");
|
obj.put("type", "message");
|
||||||
obj.put("from", "group");
|
obj.put("from", "group");
|
||||||
obj.put("content", m.getContent());
|
obj.put("content", m.getContent());
|
||||||
obj.put("time", m.getSend_at().toString());
|
obj.put("time", m.getSend_at().toString());
|
||||||
|
obj.put("sender", m.getSender_id() != null ? m.getSender_id().toString() : "N/A");
|
||||||
|
obj.put("receiver_id", m.getReceiver_id().toString());
|
||||||
|
obj.put("receiver_type", "group");
|
||||||
|
Group senderGroup = GroupDatabase.findByInternalUUID(m.getReceiver_id());
|
||||||
|
String groupName = senderGroup != null ? senderGroup.getGroup_name() : "Unknown";
|
||||||
|
obj.put("group_name", groupName);
|
||||||
|
User senderUser = userDatabase.findByInternalUUID(m.getSender_id());
|
||||||
|
String senderName = senderUser != null ? senderUser.getProfile_name() : "Unknown";
|
||||||
|
obj.put("sender_name", senderName);
|
||||||
|
|
||||||
results.add(obj);
|
results.add(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,6 +470,15 @@ public class ClientHandler implements Runnable {
|
|||||||
obj.put("from", "channel");
|
obj.put("from", "channel");
|
||||||
obj.put("content", m.getContent());
|
obj.put("content", m.getContent());
|
||||||
obj.put("time", m.getSend_at().toString());
|
obj.put("time", m.getSend_at().toString());
|
||||||
|
obj.put("sender", m.getSender_id() != null ? m.getSender_id().toString() : "N/A");
|
||||||
|
obj.put("receiver_id", m.getReceiver_id().toString());
|
||||||
|
obj.put("receiver_type", "channel");
|
||||||
|
Channel SenderChannel = ChannelDatabase.findByInternalUUID(m.getReceiver_id());
|
||||||
|
String channelName = SenderChannel != null ? SenderChannel.getChannel_name() : "Unknown";
|
||||||
|
obj.put("channel_name", channelName);
|
||||||
|
User senderUser = userDatabase.findByInternalUUID(m.getSender_id());
|
||||||
|
String senderName = senderUser != null ? senderUser.getProfile_name() : "Unknown";
|
||||||
|
obj.put("sender_name", senderName);
|
||||||
results.add(obj);
|
results.add(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,6 +509,7 @@ public class ClientHandler implements Runnable {
|
|||||||
currentUser.getImage_url(),
|
currentUser.getImage_url(),
|
||||||
contactUUID
|
contactUUID
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response = success
|
response = success
|
||||||
@@ -1265,7 +1289,7 @@ public class ClientHandler implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
messages = MessageDatabase.privateChatHistory(chatId);
|
messages = MessageDatabase.privateChatHistory(chatId, currentUser.getInternal_uuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1275,7 +1299,7 @@ public class ClientHandler implements Runnable {
|
|||||||
response = new ResponseModel("error", "Group not found.");
|
response = new ResponseModel("error", "Group not found.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
messages = MessageDatabase.groupChatHistory(group.getInternal_uuid());
|
messages = MessageDatabase.groupChatHistory(group.getInternal_uuid(),currentUser.getInternal_uuid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1285,7 +1309,7 @@ public class ClientHandler implements Runnable {
|
|||||||
response = new ResponseModel("error", "Channel not found.");
|
response = new ResponseModel("error", "Channel not found.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
messages = MessageDatabase.channelChatHistory(channel.getInternal_uuid());
|
messages = MessageDatabase.channelChatHistory(channel.getInternal_uuid(),currentUser.getInternal_uuid());
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
response = new ResponseModel("error", "Invalid receiver type.");
|
response = new ResponseModel("error", "Invalid receiver type.");
|
||||||
@@ -1988,7 +2012,6 @@ public class ClientHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
case "get_or_create_private_chat": {
|
case "get_or_create_private_chat": {
|
||||||
if (currentUser == null) {
|
if (currentUser == null) {
|
||||||
response = new ResponseModel("error", "Unauthorized. Please login first.");
|
response = new ResponseModel("error", "Unauthorized. Please login first.");
|
||||||
@@ -1999,7 +2022,23 @@ public class ClientHandler implements Runnable {
|
|||||||
UUID user1 = UUID.fromString(requestJson.getString("user1"));
|
UUID user1 = UUID.fromString(requestJson.getString("user1"));
|
||||||
UUID user2 = UUID.fromString(requestJson.getString("user2"));
|
UUID user2 = UUID.fromString(requestJson.getString("user2"));
|
||||||
|
|
||||||
|
UUID oldChat = PrivateChatDatabase.findChatBetween(user1, user2);
|
||||||
UUID chatId = PrivateChatDatabase.getOrCreateChat(user1, user2);
|
UUID chatId = PrivateChatDatabase.getOrCreateChat(user1, user2);
|
||||||
|
boolean isNew = oldChat == null;
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
JSONObject chatPayload = new JSONObject();
|
||||||
|
chatPayload.put("action", "created_private_chat");
|
||||||
|
|
||||||
|
JSONObject chatData = new JSONObject();
|
||||||
|
chatData.put("chat_id", chatId.toString());
|
||||||
|
chatData.put("chat_type", "private");
|
||||||
|
chatData.put("last_message_time", LocalDateTime.now().toString());
|
||||||
|
chatPayload.put("data", chatData);
|
||||||
|
|
||||||
|
RealTimeEventDispatcher.sendToUser(user1, chatPayload);
|
||||||
|
RealTimeEventDispatcher.sendToUser(user2, chatPayload);
|
||||||
|
}
|
||||||
|
|
||||||
JSONObject data = new JSONObject();
|
JSONObject data = new JSONObject();
|
||||||
data.put("chat_id", chatId.toString());
|
data.put("chat_id", chatId.toString());
|
||||||
@@ -2014,6 +2053,7 @@ public class ClientHandler implements Runnable {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
case "get_private_chat_target": {
|
case "get_private_chat_target": {
|
||||||
UUID chatId = UUID.fromString(requestJson.getString("chat_id"));
|
UUID chatId = UUID.fromString(requestJson.getString("chat_id"));
|
||||||
userId = currentUser.getInternal_uuid();
|
userId = currentUser.getInternal_uuid();
|
||||||
@@ -2029,6 +2069,346 @@ public class ClientHandler implements Runnable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "get_contact_list": {
|
||||||
|
if (currentUser == null) {
|
||||||
|
response = new ResponseModel("error", "Unauthorized. Please login first.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Contact> contacts = ContactDatabase.getContacts(currentUser.getInternal_uuid());
|
||||||
|
List<ContactEntry> contactEntries = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Contact contact : contacts) {
|
||||||
|
UUID contactId = contact.getContact_id();
|
||||||
|
User contactUser = userDatabase.findByInternalUUID(contactId);
|
||||||
|
if (contactUser == null) continue;
|
||||||
|
|
||||||
|
ContactEntry entry = new ContactEntry(
|
||||||
|
contactId,
|
||||||
|
contactUser.getUser_id(),
|
||||||
|
contactUser.getProfile_name(),
|
||||||
|
contactUser.getImage_url(),
|
||||||
|
contact.getIs_blocked()
|
||||||
|
);
|
||||||
|
|
||||||
|
contactEntries.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: sort alphabetically
|
||||||
|
contactEntries.sort(Comparator.comparing(ContactEntry::getProfileName, String.CASE_INSENSITIVE_ORDER));
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("contact_list", JsonUtil.contactEntryListToJson(contactEntries));
|
||||||
|
|
||||||
|
response = new ResponseModel("success", "Contact list refreshed", data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "get_chat_messages" : {
|
||||||
|
UUID chatId = UUID.fromString(requestJson.getString("chat_id"));
|
||||||
|
String chatType = requestJson.getString("chat_type");
|
||||||
|
int offset = requestJson.optInt("offset", 0);
|
||||||
|
int limit = requestJson.optInt("limit", 10);
|
||||||
|
|
||||||
|
List<Message> messages = MessageDatabase.getMessagesForChat(chatId, chatType, currentUser.getInternal_uuid(), offset, limit);
|
||||||
|
|
||||||
|
JSONArray result = new JSONArray();
|
||||||
|
for (Message m : messages) {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("message_id", m.getMessage_id().toString());
|
||||||
|
obj.put("sender_id", m.getSender_id().toString());
|
||||||
|
|
||||||
|
User sender = userDatabase.findByInternalUUID(m.getSender_id());
|
||||||
|
obj.put("sender_name", sender != null ? sender.getProfile_name() : "Unknown");
|
||||||
|
|
||||||
|
obj.put("receiver_id", m.getReceiver_id().toString());
|
||||||
|
obj.put("receiver_type", m.getReceiver_type());
|
||||||
|
|
||||||
|
String receiverName = switch (m.getReceiver_type()) {
|
||||||
|
case "group" -> {
|
||||||
|
Group g = GroupDatabase.findByInternalUUID(m.getReceiver_id());
|
||||||
|
yield g != null ? g.getGroup_name() : "Unknown group";
|
||||||
|
}
|
||||||
|
case "channel" -> {
|
||||||
|
Channel c = ChannelDatabase.findByInternalUUID(m.getReceiver_id());
|
||||||
|
yield c != null ? c.getChannel_name() : "Unknown channel";
|
||||||
|
}
|
||||||
|
case "private" -> {
|
||||||
|
UUID otherId = PrivateChatDatabase.getOtherParticipant(
|
||||||
|
m.getReceiver_id(), // chat_id
|
||||||
|
currentUser.getInternal_uuid() // my user uuid
|
||||||
|
);
|
||||||
|
User other = userDatabase.findByInternalUUID(otherId);
|
||||||
|
yield other != null ? other.getProfile_name() : "Unknown user";
|
||||||
|
}
|
||||||
|
|
||||||
|
default -> "Unknown";
|
||||||
|
};
|
||||||
|
obj.put("receiver_name", receiverName);
|
||||||
|
|
||||||
|
obj.put("content", m.getContent());
|
||||||
|
obj.put("message_type", m.getMessage_type());
|
||||||
|
obj.put("time", m.getSend_at().toString());
|
||||||
|
obj.put("is_edited", m.isIs_edited());
|
||||||
|
obj.put("is_deleted_globally", m.isIs_deleted_globally());
|
||||||
|
obj.put("edited_at", m.getEdited_at() != null ? m.getEdited_at().toString() : JSONObject.NULL);
|
||||||
|
|
||||||
|
//For reply messages
|
||||||
|
if (m.getReply_to_id() != null) {
|
||||||
|
Message replied = MessageDatabase.findById(m.getReply_to_id());
|
||||||
|
if (replied != null) {
|
||||||
|
User repliedSender = userDatabase.findByInternalUUID(replied.getSender_id());
|
||||||
|
obj.put("reply_to_id", replied.getMessage_id().toString());
|
||||||
|
obj.put("reply_to_sender", repliedSender != null ? repliedSender.getProfile_name() : "Unknown");
|
||||||
|
obj.put("reply_to_content", replied.getContent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For forwarded messages
|
||||||
|
if (m.getOriginal_message_id() != null && m.getForwarded_from() != null) {
|
||||||
|
User originalSender = userDatabase.findByInternalUUID(m.getForwarded_from());
|
||||||
|
obj.put("is_forwarded", true);
|
||||||
|
obj.put("forwarded_from_id", m.getForwarded_from().toString());
|
||||||
|
obj.put("forwarded_from_name", originalSender != null ? originalSender.getProfile_name() : "Unknown");
|
||||||
|
} else {
|
||||||
|
obj.put("is_forwarded", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//For reactions
|
||||||
|
List<String> reactions = MessageReactionDatabase.getReactions(m.getMessage_id());
|
||||||
|
obj.put("reactions", new JSONArray(reactions));
|
||||||
|
|
||||||
|
|
||||||
|
result.put(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("get_chat_messages", result);
|
||||||
|
|
||||||
|
response = new ResponseModel("success", "get messages ", data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "delete_message" : {
|
||||||
|
UUID messageId = UUID.fromString(requestJson.getString("message_id"));
|
||||||
|
String deleteType = requestJson.getString("delete_type");
|
||||||
|
|
||||||
|
Message msg = MessageDatabase.findById(messageId);
|
||||||
|
if (msg == null) {
|
||||||
|
response = new ResponseModel("error", "Message not found.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID currentUserId = currentUser.getInternal_uuid();
|
||||||
|
|
||||||
|
if (deleteType.equals("one-sided")) {
|
||||||
|
boolean success = MessageDatabase.markAsDeletedForUser(messageId, currentUserId);
|
||||||
|
if (success)
|
||||||
|
response = new ResponseModel("success", "Message deleted for current user.");
|
||||||
|
else
|
||||||
|
response = new ResponseModel("error", "Failed to delete message for user.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If allowed to delete the message
|
||||||
|
if (deleteType.equals("global")) {
|
||||||
|
boolean allowed = false;
|
||||||
|
|
||||||
|
String type = msg.getReceiver_type();
|
||||||
|
UUID chatId = msg.getReceiver_id();
|
||||||
|
|
||||||
|
if (type.equals("private") || type.equals("group")) {
|
||||||
|
allowed = msg.getSender_id().equals(currentUserId);
|
||||||
|
} else if (type.equals("channel")) {
|
||||||
|
//Only owner and admins can delete messages
|
||||||
|
allowed = ChannelPermissionUtil.canDeleteMessage(chatId, currentUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowed) {
|
||||||
|
response = new ResponseModel("error", "You are not allowed to delete this message globally.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = MessageDatabase.markAsGloballyDeleted(messageId);
|
||||||
|
if (success) {
|
||||||
|
response = new ResponseModel("success", "Message deleted globally.");
|
||||||
|
Message updated = MessageDatabase.findById(messageId);
|
||||||
|
List<UUID> receivers = Receivers.resolveFor(updated.getReceiver_type(), updated.getReceiver_id(), null);
|
||||||
|
RealTimeEventDispatcher.notifyMessageDeletedGlobal(updated.getReceiver_id(), messageId, receivers);
|
||||||
|
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
response = new ResponseModel("error", "Failed to delete message globally.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
response = new ResponseModel("error", "Invalid delete_type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "edit_message" : {
|
||||||
|
UUID msgId = UUID.fromString(requestJson.getString("message_id"));
|
||||||
|
String newContent = requestJson.getString("new_content");
|
||||||
|
|
||||||
|
Message msg = MessageDatabase.findById(msgId);
|
||||||
|
if (msg == null) {
|
||||||
|
response = new ResponseModel("error", "Message not found.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only sender can edit
|
||||||
|
if (!msg.getSender_id().equals(currentUser.getInternal_uuid())) {
|
||||||
|
response = new ResponseModel("error", "You can only edit your own messages.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = MessageDatabase.updateContentAndMarkEdited(msgId, newContent);
|
||||||
|
if (!success) {
|
||||||
|
response = new ResponseModel("error", "Failed to update message.");
|
||||||
|
} else {
|
||||||
|
response = new ResponseModel("success", "Message edited.");
|
||||||
|
Message updated = MessageDatabase.findById(msgId);
|
||||||
|
List<UUID> receivers = Receivers.resolveFor(updated.getReceiver_type(), updated.getReceiver_id(), null);
|
||||||
|
RealTimeEventDispatcher.notifyMessageEdited(updated.getReceiver_id(), updated.getMessage_id(), newContent, LocalDateTime.now(), receivers);
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "send_reply_message" : {
|
||||||
|
UUID senderId = currentUser.getInternal_uuid();
|
||||||
|
String content = requestJson.getString("content");
|
||||||
|
String receiverType = requestJson.getString("receiver_type");
|
||||||
|
UUID receiverId = UUID.fromString(requestJson.getString("receiver_id"));
|
||||||
|
//For block
|
||||||
|
if ("private".equals(receiverType) && !canSendToPrivate(receiverId, senderId)) {
|
||||||
|
response = new ResponseModel("error", "You can't message this user (blocked).");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UUID replyToId = UUID.fromString(requestJson.getString("reply_to_id"));
|
||||||
|
|
||||||
|
Message message = new Message(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
senderId,
|
||||||
|
receiverType,
|
||||||
|
receiverId,
|
||||||
|
content,
|
||||||
|
"TEXT",
|
||||||
|
LocalDateTime.now(),
|
||||||
|
"SEND",
|
||||||
|
replyToId
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean saved = MessageDatabase.saveReplyMessage(message);
|
||||||
|
|
||||||
|
String excerpt = MessageDatabase.getExcerpt(replyToId);
|
||||||
|
JSONObject meta = new JSONObject().put("reply_to", new JSONObject()
|
||||||
|
.put("id", replyToId.toString())
|
||||||
|
.put("excerpt", excerpt));
|
||||||
|
|
||||||
|
List<UUID> receivers = Receivers.resolveFor(receiverType, receiverId, senderId);
|
||||||
|
//RealTimeEventDispatcher.sendNewMessage(message, receivers, "reply", meta);
|
||||||
|
RealTimeEventDispatcher.sendNewMessageFiltered(message, receivers, senderId, "reply", meta);
|
||||||
|
|
||||||
|
|
||||||
|
response = saved ?
|
||||||
|
new ResponseModel("success", "Reply sent") :
|
||||||
|
new ResponseModel("error", "Failed to send reply");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "forward_message" : {
|
||||||
|
UUID senderId = currentUser.getInternal_uuid();
|
||||||
|
UUID originalMessageId = UUID.fromString(requestJson.getString("original_message_id"));
|
||||||
|
UUID targetChatId = UUID.fromString(requestJson.getString("target_chat_id"));
|
||||||
|
String targetChatType = requestJson.getString("target_chat_type");
|
||||||
|
|
||||||
|
//For block
|
||||||
|
if ("private".equals(targetChatType) && !canSendToPrivate(targetChatId, senderId)) {
|
||||||
|
response = new ResponseModel("error", "You can't message this user (blocked).");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// original message
|
||||||
|
Message original = MessageDatabase.findById(originalMessageId);
|
||||||
|
if (original == null) {
|
||||||
|
response = new ResponseModel("error", "Original message not found.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//make forwarded message
|
||||||
|
Message forwarded = new Message(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
currentUser.getInternal_uuid(),
|
||||||
|
targetChatType,
|
||||||
|
targetChatId,
|
||||||
|
original.getContent(),
|
||||||
|
original.getMessage_type(),
|
||||||
|
LocalDateTime.now(),
|
||||||
|
"SEND",
|
||||||
|
null, // reply_to_id
|
||||||
|
false, // is_edited
|
||||||
|
false, // is_deleted_globally
|
||||||
|
original.getMessage_id(), //original message id
|
||||||
|
currentUser.getInternal_uuid(), // forwarded_by
|
||||||
|
original.getSender_id(), // forwarded_from
|
||||||
|
null // edited_at
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean success = MessageDatabase.saveForwardedMessage(forwarded);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
response = new ResponseModel("success", "Message forwarded.");
|
||||||
|
JSONObject meta = new JSONObject().put("forwarded_from", new JSONObject()
|
||||||
|
.put("chat_id", original.getReceiver_id().toString())
|
||||||
|
.put("message_id", original.getMessage_id().toString())
|
||||||
|
.put("sender_id", original.getSender_id().toString())
|
||||||
|
.put("sender_name", userDatabase.findByInternalUUID(original.getSender_id()).getProfile_name()));
|
||||||
|
|
||||||
|
List<UUID> receivers = Receivers.resolveFor(targetChatType, targetChatId, currentUser.getInternal_uuid());
|
||||||
|
//RealTimeEventDispatcher.sendNewMessage(forwarded, receivers, "forward", meta);
|
||||||
|
RealTimeEventDispatcher.sendNewMessageFiltered(forwarded, receivers, senderId, "forward", meta);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
response = new ResponseModel("error", "Failed to forward message.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "react_to_message": {
|
||||||
|
UUID messageId = UUID.fromString(requestJson.getString("message_id"));
|
||||||
|
String reaction = requestJson.getString("reaction");
|
||||||
|
boolean success = MessageReactionDatabase.saveOrUpdateReaction(messageId, currentUser.getInternal_uuid(), reaction);
|
||||||
|
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
Message msg = MessageDatabase.findById(messageId);
|
||||||
|
|
||||||
|
JSONObject counts = MessageReactionDatabase.getCountsAsJson(messageId);
|
||||||
|
|
||||||
|
List<UUID> receivers = Receivers.resolveFor(msg.getReceiver_type(), msg.getReceiver_id(), null);
|
||||||
|
|
||||||
|
RealTimeEventDispatcher.notifyReactionAdded(
|
||||||
|
msg.getReceiver_id(), // chatId
|
||||||
|
messageId, // messageId
|
||||||
|
reaction, // emoji
|
||||||
|
counts.optInt(reaction, 0),
|
||||||
|
counts,
|
||||||
|
receivers
|
||||||
|
);
|
||||||
|
|
||||||
|
response = new ResponseModel("success", "Reaction saved.");
|
||||||
|
} else {
|
||||||
|
response = new ResponseModel("error", "Failed to save reaction.");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2103,6 +2483,13 @@ public class ClientHandler implements Runnable {
|
|||||||
|
|
||||||
if(Objects.equals(receiverType, "private")){
|
if(Objects.equals(receiverType, "private")){
|
||||||
PrivateChatDatabase.clearDeletedFlag(senderId, receiverId);
|
PrivateChatDatabase.clearDeletedFlag(senderId, receiverId);
|
||||||
|
UUID other = PrivateChatDatabase.getOtherParticipant(receiverId, senderId);
|
||||||
|
if (other == null) {
|
||||||
|
return new ResponseModel("error", "Invalid private chat.");
|
||||||
|
}
|
||||||
|
if (ContactDatabase.isBlocked(senderId, other) || ContactDatabase.isBlocked(other, senderId)) {
|
||||||
|
return new ResponseModel("error", "You can't message this user (blocked).");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2210,6 +2597,14 @@ public class ClientHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper
|
||||||
|
private boolean canSendToPrivate(UUID chatId, UUID senderId) {
|
||||||
|
UUID other = PrivateChatDatabase.getOtherParticipant(chatId, senderId);
|
||||||
|
if (other == null) return false;
|
||||||
|
return !(ContactDatabase.isBlocked(senderId, other) || ContactDatabase.isBlocked(other, senderId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.to.telegramfinalproject.Server;
|
|||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.to.telegramfinalproject.Database.ChannelDatabase;
|
import org.to.telegramfinalproject.Database.ChannelDatabase;
|
||||||
|
import org.to.telegramfinalproject.Database.ContactDatabase;
|
||||||
import org.to.telegramfinalproject.Database.GroupDatabase;
|
import org.to.telegramfinalproject.Database.GroupDatabase;
|
||||||
import org.to.telegramfinalproject.Database.userDatabase;
|
import org.to.telegramfinalproject.Database.userDatabase;
|
||||||
import org.to.telegramfinalproject.Models.Message;
|
import org.to.telegramfinalproject.Models.Message;
|
||||||
@@ -81,33 +82,119 @@ public class RealTimeEventDispatcher {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void notifyMessageEdited(UUID messageId, String newContent, List<UUID> receivers) {
|
public static void notifyMessageEdited(UUID chatId, UUID messageId, String newContent, LocalDateTime editedAt, List<UUID> receivers) {
|
||||||
|
|
||||||
String editTime = LocalDateTime.now().toString();
|
|
||||||
JSONObject data = new JSONObject();
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_id", chatId.toString());
|
||||||
data.put("message_id", messageId.toString());
|
data.put("message_id", messageId.toString());
|
||||||
data.put("new_content", newContent);
|
data.put("new_content", newContent);
|
||||||
data.put("edited_at", editTime);
|
data.put("edited_at", editedAt.toString());
|
||||||
|
|
||||||
JSONObject event = new JSONObject();
|
JSONObject event = new JSONObject();
|
||||||
event.put("action", "edit_message");
|
event.put("action", "message_edited");
|
||||||
event.put("data", data);
|
event.put("data", data);
|
||||||
|
|
||||||
broadcastToUsers(receivers, event);
|
broadcastToUsers(receivers, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void notifyMessageDeleted(UUID messageId, List<UUID> receivers) {
|
|
||||||
|
public static void sendNewMessage(Message message, List<UUID> receivers, String kind, JSONObject meta) {
|
||||||
|
JSONObject payload = new JSONObject();
|
||||||
|
payload.put("action", "new_message");
|
||||||
|
|
||||||
JSONObject data = new JSONObject();
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("id", message.getMessage_id().toString());
|
||||||
|
data.put("chat_id", message.getReceiver_id().toString());
|
||||||
|
data.put("sender_id", message.getSender_id().toString());
|
||||||
|
data.put("receiver_id", message.getReceiver_id().toString());
|
||||||
|
data.put("receiver_type", message.getReceiver_type());
|
||||||
|
data.put("content", message.getContent());
|
||||||
|
data.put("message_type", message.getMessage_type());
|
||||||
|
data.put("send_at", message.getSend_at().toString());
|
||||||
|
|
||||||
|
User sender = userDatabase.findByInternalUUID(message.getSender_id());
|
||||||
|
if (sender != null) data.put("sender_name", sender.getProfile_name());
|
||||||
|
|
||||||
|
data.put("kind", kind == null ? "plain" : kind); // plain|reply|forward
|
||||||
|
if (meta != null) data.put("meta", meta); // reply_to {...} | forwarded_from {...}
|
||||||
|
|
||||||
|
payload.put("data", data);
|
||||||
|
for (UUID userId : receivers) sendToUser(userId, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendNewMessageFiltered(Message m, List<UUID> receivers, UUID senderId, String kind, JSONObject meta) {
|
||||||
|
JSONObject payload = new JSONObject()
|
||||||
|
.put("action", "new_message")
|
||||||
|
.put("data", new JSONObject()
|
||||||
|
.put("id", m.getMessage_id().toString())
|
||||||
|
.put("message_id", m.getMessage_id().toString())
|
||||||
|
.put("sender_id", m.getSender_id().toString())
|
||||||
|
.put("sender_name", userDatabase.findByInternalUUID(m.getSender_id()).getProfile_name())
|
||||||
|
.put("receiver_id", m.getReceiver_id().toString())
|
||||||
|
.put("receiver_type", m.getReceiver_type())
|
||||||
|
.put("content", m.getContent())
|
||||||
|
.put("message_type", m.getMessage_type())
|
||||||
|
.put("send_at", m.getSend_at().toString())
|
||||||
|
.put("kind", kind)
|
||||||
|
.put("meta", meta != null ? meta : JSONObject.NULL)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (UUID uid : receivers) {
|
||||||
|
//If receiver blocked
|
||||||
|
if ("private".equalsIgnoreCase(m.getReceiver_type()) && ContactDatabase.isBlocked(uid, senderId)) continue;
|
||||||
|
sendToUser(uid, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyMessageDeletedGlobal(UUID chatId, UUID messageId, List<UUID> receivers) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_id", chatId.toString());
|
||||||
data.put("message_id", messageId.toString());
|
data.put("message_id", messageId.toString());
|
||||||
|
|
||||||
JSONObject event = new JSONObject();
|
JSONObject event = new JSONObject();
|
||||||
event.put("action", "delete_message");
|
event.put("action", "message_deleted_global");
|
||||||
event.put("data", data);
|
event.put("data", data);
|
||||||
|
|
||||||
broadcastToUsers(receivers, event);
|
broadcastToUsers(receivers, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void notifyReactionAdded(UUID chatId, UUID messageId, String emoji,
|
||||||
|
int totalForEmoji, JSONObject countsAll, List<UUID> receivers) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_id", chatId.toString());
|
||||||
|
data.put("message_id", messageId.toString());
|
||||||
|
data.put("emoji", emoji);
|
||||||
|
data.put("counts", countsAll); // {"❤️":3,"👍":1,...}
|
||||||
|
data.put("count_for_emoji", totalForEmoji);
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "message_reacted");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(receivers, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void notifyReactionRemoved(UUID chatId, UUID messageId, String emoji,
|
||||||
|
int totalForEmoji, JSONObject countsAll, List<UUID> receivers) {
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("chat_id", chatId.toString());
|
||||||
|
data.put("message_id", messageId.toString());
|
||||||
|
data.put("emoji", emoji);
|
||||||
|
data.put("counts", countsAll);
|
||||||
|
data.put("count_for_emoji", totalForEmoji);
|
||||||
|
|
||||||
|
JSONObject event = new JSONObject();
|
||||||
|
event.put("action", "message_unreacted");
|
||||||
|
event.put("data", data);
|
||||||
|
|
||||||
|
broadcastToUsers(receivers, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void notifyUserUpdated(UUID userId, String newProfileName, String newImageUrl, List<UUID> contactIds) {
|
public static void notifyUserUpdated(UUID userId, String newProfileName, String newImageUrl, List<UUID> contactIds) {
|
||||||
JSONObject data = new JSONObject();
|
JSONObject data = new JSONObject();
|
||||||
data.put("user_id", userId.toString());
|
data.put("user_id", userId.toString());
|
||||||
@@ -328,6 +415,13 @@ public class RealTimeEventDispatcher {
|
|||||||
for (UUID userId : receivers) {
|
for (UUID userId : receivers) {
|
||||||
sendToUser(userId, payload);
|
sendToUser(userId, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (UUID uid : receivers) {
|
||||||
|
//If receiver blocked
|
||||||
|
if ("private".equalsIgnoreCase(message.getReceiver_type()) && ContactDatabase.isBlocked(uid, message.getSender_id())) continue;
|
||||||
|
sendToUser(uid, payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.to.telegramfinalproject.Server;
|
||||||
|
|
||||||
|
import org.to.telegramfinalproject.Database.ChannelDatabase;
|
||||||
|
import org.to.telegramfinalproject.Database.GroupDatabase;
|
||||||
|
import org.to.telegramfinalproject.Database.PrivateChatDatabase;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class Receivers {
|
||||||
|
public static List<UUID> resolveFor(String type, UUID chatId, UUID exclude) {
|
||||||
|
List<UUID> ids = switch (type) {
|
||||||
|
case "private" -> PrivateChatDatabase.getMembers(chatId);
|
||||||
|
case "group" -> GroupDatabase.getMemberUUIDs(chatId);
|
||||||
|
case "channel" -> ChannelDatabase.getSubscriberUUIDs(chatId);
|
||||||
|
default -> List.of();
|
||||||
|
};
|
||||||
|
if (exclude != null) ids.remove(exclude);
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,12 @@ package org.to.telegramfinalproject.Utils;
|
|||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.to.telegramfinalproject.Database.ChannelDatabase;
|
import org.to.telegramfinalproject.Database.ChannelDatabase;
|
||||||
|
import org.to.telegramfinalproject.Database.ConnectionDb;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ChannelPermissionUtil {
|
public class ChannelPermissionUtil {
|
||||||
@@ -51,4 +56,21 @@ public class ChannelPermissionUtil {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean canDeleteMessage(UUID channelId, UUID userId) {
|
||||||
|
String roleSql = "SELECT role FROM channel_subscribers WHERE channel_id = ? AND user_id = ?";
|
||||||
|
try (Connection conn = ConnectionDb.connect();
|
||||||
|
PreparedStatement ps = conn.prepareStatement(roleSql)) {
|
||||||
|
ps.setObject(1, channelId);
|
||||||
|
ps.setObject(2, userId);
|
||||||
|
ResultSet rs = ps.executeQuery();
|
||||||
|
if (rs.next()) {
|
||||||
|
String role = rs.getString("role");
|
||||||
|
return role.equalsIgnoreCase("owner") || role.equalsIgnoreCase("admin");
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user