Delete messages in chats
This commit is contained in:
@@ -1242,7 +1242,8 @@ public class ActionHandler {
|
||||
System.out.println("4. Delete chat (both sides)");
|
||||
System.out.println("5. View profile");
|
||||
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();
|
||||
@@ -1288,8 +1289,11 @@ public class ActionHandler {
|
||||
|
||||
return true;
|
||||
}
|
||||
case "7" ->{
|
||||
viewMessagesInChat(chat);
|
||||
}
|
||||
|
||||
case "7" -> {
|
||||
case "8" -> {
|
||||
return false;
|
||||
}
|
||||
default -> System.out.println("Invalid choice.");
|
||||
@@ -1355,6 +1359,7 @@ public class ActionHandler {
|
||||
}
|
||||
|
||||
System.out.println("12. Archive/Unarchived");
|
||||
System.out.println("13. View messages");
|
||||
|
||||
System.out.println("0. Back to Chat List");
|
||||
|
||||
@@ -1409,6 +1414,10 @@ public class ActionHandler {
|
||||
toggleArchive(chat.getId() , "group");
|
||||
}
|
||||
|
||||
case "13" ->{
|
||||
viewMessagesInChat(chat);
|
||||
}
|
||||
|
||||
case "0" -> {
|
||||
return false;
|
||||
}
|
||||
@@ -1476,6 +1485,7 @@ public class ActionHandler {
|
||||
}
|
||||
|
||||
System.out.println("12. Archive/Unarchived");
|
||||
System.out.println("13. View messages");
|
||||
System.out.println("0. Back to Chat List");
|
||||
|
||||
String input = scanner.nextLine();
|
||||
@@ -1558,7 +1568,9 @@ public class ActionHandler {
|
||||
case "12"->{
|
||||
toggleArchive(chat.getId() , "channel");
|
||||
}
|
||||
|
||||
case "13" ->{
|
||||
viewMessagesInChat(chat);
|
||||
}
|
||||
|
||||
case "0" -> {
|
||||
return false;
|
||||
@@ -3327,8 +3339,173 @@ public class ActionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
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 label = isEdited ? "🖊️ (edited)" : "";
|
||||
System.out.printf("[%d] [%s] %s: %s %s\n", i + 1, time, senderName, content, label);
|
||||
}
|
||||
System.out.println("─────────────────────────────────────────────");
|
||||
|
||||
System.out.print("""
|
||||
💬 Options:
|
||||
[number] - Interact with message
|
||||
N - Next page (older messages)
|
||||
0 - Back to chat menu
|
||||
➤ Choice: """);
|
||||
|
||||
String input = scanner.nextLine().trim();
|
||||
|
||||
if (input.equals("0")) return;
|
||||
if (input.equalsIgnoreCase("N")) {
|
||||
offset += limit;
|
||||
continue;
|
||||
}
|
||||
|
||||
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"));
|
||||
|
||||
System.out.println("\n🎯 Selected message by " + selected.getString("sender_name"));
|
||||
System.out.println("1. Reply");
|
||||
System.out.println("2. Forward");
|
||||
System.out.println("3. React");
|
||||
|
||||
if (senderId.toString().equals(Session.currentUser.getString("internal_uuid"))) {
|
||||
System.out.println("4. Edit");
|
||||
System.out.println("5. Delete");
|
||||
}
|
||||
|
||||
System.out.println("0. Back to message list");
|
||||
|
||||
String choice = scanner.nextLine().trim();
|
||||
switch (choice) {
|
||||
case "1" -> replyToMessage(messageId);
|
||||
case "2" -> forwardMessage(messageId);
|
||||
case "3" -> reactToMessage(messageId);
|
||||
case "4" -> {
|
||||
if (senderId.toString().equals(Session.currentUser.getString("internal_uuid")))
|
||||
editMessage(messageId);
|
||||
}
|
||||
case "5" -> {
|
||||
if (senderId.toString().equals(Session.currentUser.getString("internal_uuid")))
|
||||
deleteMessage(messageId);
|
||||
}
|
||||
case "0" -> {}
|
||||
default -> System.out.println("❌ Invalid option.");
|
||||
}
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("❌ Please enter a valid number or command.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String editMessage(UUID messageId) {
|
||||
return "not ready";
|
||||
|
||||
}
|
||||
|
||||
private String reactToMessage(UUID messageId) {
|
||||
return "not ready";
|
||||
|
||||
}
|
||||
|
||||
private String forwardMessage(UUID messageId) {
|
||||
return "not ready";
|
||||
}
|
||||
|
||||
private String replyToMessage(UUID messageId) {
|
||||
return "not ready";
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -111,7 +111,116 @@ 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
|
||||
);
|
||||
|
||||
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 void markMessageAsRead(UUID messageId, UUID userId) {
|
||||
@@ -752,4 +861,6 @@ public class MessageDatabase {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ public class Message {
|
||||
private UUID forwarded_by;
|
||||
private UUID forwarded_from;
|
||||
private List<FileAttachment> attachments;
|
||||
private transient String sender_name;
|
||||
private transient String receiver_name;
|
||||
|
||||
|
||||
// ✅ Full Constructor
|
||||
public Message(UUID message_id, UUID sender_id, String receiver_type, UUID receiver_id, String content,
|
||||
@@ -101,4 +104,20 @@ public class Message {
|
||||
|
||||
public List<FileAttachment> getAttachments() { return attachments; }
|
||||
public void setAttachments(List<FileAttachment> attachments) { this.attachments = attachments; }
|
||||
public String getSender_name() {
|
||||
return sender_name;
|
||||
}
|
||||
|
||||
public void setSender_name(String sender_name) {
|
||||
this.sender_name = sender_name;
|
||||
}
|
||||
|
||||
public String getReceiver_name() {
|
||||
return receiver_name;
|
||||
}
|
||||
|
||||
public void setReceiver_name(String receiver_name) {
|
||||
this.receiver_name = receiver_name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2104,7 +2104,111 @@ public class ClientHandler implements Runnable {
|
||||
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); // پیشفرض 0
|
||||
int limit = requestJson.optInt("limit", 10); // پیشفرض 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 = m.getSender_id().equals(currentUser.getInternal_uuid()) ? m.getReceiver_id() : m.getSender_id();
|
||||
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());
|
||||
|
||||
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.");
|
||||
else
|
||||
response = new ResponseModel("error", "Failed to delete message globally.");
|
||||
break;
|
||||
}
|
||||
|
||||
response = new ResponseModel("error", "Invalid delete_type.");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2289,4 +2393,6 @@ public class ClientHandler implements Runnable {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -2,7 +2,12 @@ package org.to.telegramfinalproject.Utils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
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;
|
||||
|
||||
public class ChannelPermissionUtil {
|
||||
@@ -51,4 +56,21 @@ public class ChannelPermissionUtil {
|
||||
}
|
||||
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