1573 lines
59 KiB
Java
1573 lines
59 KiB
Java
package org.to.telegramfinalproject.Database;
|
|
|
|
import org.to.telegramfinalproject.Models.FileAttachment;
|
|
import org.to.telegramfinalproject.Models.MediaRow;
|
|
import org.to.telegramfinalproject.Models.Message;
|
|
import org.to.telegramfinalproject.Utils.ChannelPermissionUtil;
|
|
|
|
import java.sql.*;
|
|
import java.time.LocalDateTime;
|
|
import java.util.*;
|
|
import java.util.stream.Collectors;
|
|
|
|
public class MessageDatabase {
|
|
|
|
|
|
|
|
|
|
|
|
public static boolean insertMessage(UUID messageId, UUID senderId, UUID receiverId,
|
|
String receiverType, String content, String messageType) {
|
|
String sql = "INSERT INTO messages (message_id, sender_id, receiver_type, receiver_id, content, message_type) " +
|
|
"VALUES (?, ?, ?, ?, ?, ?)";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
ps.setObject(1, messageId);
|
|
ps.setObject(2, senderId);
|
|
ps.setString(3, receiverType);
|
|
ps.setObject(4, receiverId);
|
|
ps.setString(5, content);
|
|
ps.setString(6, messageType);
|
|
|
|
return ps.executeUpdate() > 0;
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static boolean insertAttachments(UUID messageId, List<FileAttachment> attachments) {
|
|
String sql = "INSERT INTO message_attachments (attachment_id, message_id, file_url, file_type) " +
|
|
"VALUES (?, ?, ?, ?)";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
for (FileAttachment att : attachments) {
|
|
ps.setObject(1, UUID.randomUUID());
|
|
ps.setObject(2, messageId);
|
|
ps.setString(3, att.getFileUrl());
|
|
ps.setString(4, att.getFileType());
|
|
ps.addBatch();
|
|
}
|
|
|
|
ps.executeBatch();
|
|
return true;
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static void markGloballyDeleted(UUID chatId) {
|
|
String sql = "UPDATE messages SET is_deleted_globally = true WHERE receiver_id = ? AND receiver_type = 'private'";
|
|
try (Connection conn = ConnectionDb.connect(); PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, chatId);
|
|
ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
public static void logDeletedMessagesFor(UUID chatId, UUID userId) {
|
|
List<Message> messages = MessageDatabase.privateChatHistory(chatId, userId);
|
|
for (Message message : messages) {
|
|
if (!isMessageDeleted(message.getMessage_id(), userId)) {
|
|
String sql = """
|
|
INSERT INTO deleted_messages (message_id, user_id)
|
|
VALUES (?, ?)
|
|
ON CONFLICT (message_id, user_id) DO NOTHING
|
|
""";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, message.getMessage_id());
|
|
ps.setObject(2, userId);
|
|
ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean isMessageDeleted(UUID messageId, UUID userId) {
|
|
String sql = "SELECT 1 FROM deleted_messages WHERE message_id = ? AND user_id = ?";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, messageId);
|
|
ps.setObject(2, userId);
|
|
ResultSet rs = ps.executeQuery();
|
|
return rs.next();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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() + "…";
|
|
}
|
|
|
|
public static boolean saveMessageWithOptionalAttachments(
|
|
UUID messageId, UUID senderId, UUID receiverId,
|
|
String receiverType, String content, String messageType,
|
|
List<FileAttachment> attachments
|
|
) {
|
|
Connection conn = null;
|
|
try {
|
|
conn = ConnectionDb.connect();
|
|
conn.setAutoCommit(false);
|
|
|
|
boolean isText = "TEXT".equalsIgnoreCase(messageType);
|
|
boolean isImage = "IMAGE".equalsIgnoreCase(messageType);
|
|
boolean isAudio = "AUDIO".equalsIgnoreCase(messageType);
|
|
if (!isText && !isImage && !isAudio) {
|
|
throw new IllegalArgumentException("messageType must be TEXT, IMAGE, or AUDIO");
|
|
}
|
|
|
|
if (isText) {
|
|
if (attachments != null && !attachments.isEmpty())
|
|
throw new IllegalArgumentException("TEXT must not have attachments");
|
|
if (content == null || content.isBlank())
|
|
throw new IllegalArgumentException("TEXT must have non-empty content");
|
|
} else {
|
|
if (attachments == null || attachments.isEmpty())
|
|
throw new IllegalArgumentException("Non-TEXT must have at least one attachment");
|
|
|
|
for (FileAttachment a : attachments) {
|
|
if (a == null) throw new IllegalArgumentException("Attachment is null");
|
|
String ft = a.getFileType();
|
|
if (isImage && !"IMAGE".equalsIgnoreCase(ft))
|
|
throw new IllegalArgumentException("All attachments must be IMAGE for messageType=IMAGE");
|
|
if (isAudio && !"AUDIO".equalsIgnoreCase(ft))
|
|
throw new IllegalArgumentException("All attachments must be AUDIO for messageType=AUDIO");
|
|
}
|
|
}
|
|
|
|
insertMessageTx(conn, messageId, senderId, receiverId, receiverType, content, messageType.toUpperCase());
|
|
if (!isText) insertAttachmentsTx(conn, messageId, attachments);
|
|
|
|
conn.commit();
|
|
return true;
|
|
} catch (Exception e) {
|
|
if (conn != null) try { conn.rollback(); } catch (SQLException ignored) {}
|
|
e.printStackTrace();
|
|
return false;
|
|
} finally {
|
|
if (conn != null) {
|
|
try { conn.setAutoCommit(true); } catch (SQLException ignored) {}
|
|
try { conn.close(); } catch (SQLException ignored) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static boolean insertMessageTx(Connection conn, UUID messageId, UUID senderId, UUID receiverId,
|
|
String receiverType, String content, String messageType) throws SQLException {
|
|
String sql = "INSERT INTO messages (message_id, sender_id, receiver_type, receiver_id, content, message_type) " +
|
|
"VALUES (?, ?, ?, ?, ?, ?)";
|
|
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, messageId);
|
|
ps.setObject(2, senderId);
|
|
ps.setString(3, receiverType);
|
|
ps.setObject(4, receiverId);
|
|
if (content == null || content.isBlank()) ps.setNull(5, java.sql.Types.VARCHAR); else ps.setString(5, content);
|
|
ps.setString(6, messageType);
|
|
return ps.executeUpdate() > 0;
|
|
}
|
|
}
|
|
|
|
public static boolean insertAttachmentsTx(Connection conn, UUID messageId, List<FileAttachment> attachments) throws SQLException {
|
|
if (attachments == null || attachments.isEmpty()) return true;
|
|
|
|
final String sql = """
|
|
INSERT INTO message_attachments(
|
|
attachment_id, message_id,
|
|
file_url, file_type, file_name, file_size, mime_type,
|
|
width, height, duration_seconds, thumbnail_url,
|
|
media_key, storage_path
|
|
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
""";
|
|
|
|
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
for (FileAttachment att : attachments) {
|
|
if (att == null) throw new IllegalArgumentException("Attachment is null");
|
|
UUID attachmentId = att.getAttachmentId() != null ? att.getAttachmentId() : UUID.randomUUID();
|
|
UUID mediaKey = att.getMediaKey() != null ? att.getMediaKey() : attachmentId; // سادهترین حالت
|
|
|
|
String ft = att.getFileType();
|
|
if (!"IMAGE".equalsIgnoreCase(ft) && !"AUDIO".equalsIgnoreCase(ft)) {
|
|
throw new IllegalArgumentException("file_type must be IMAGE or AUDIO");
|
|
}
|
|
if (att.getStoragePath() == null || att.getStoragePath().isBlank()) {
|
|
throw new IllegalArgumentException("storage_path is required for socket downloads");
|
|
}
|
|
|
|
int i = 1;
|
|
ps.setObject(i++, attachmentId);
|
|
ps.setObject(i++, messageId);
|
|
//file url (display link)
|
|
if (att.getFileUrl() == null || att.getFileUrl().isBlank()) ps.setNull(i++, java.sql.Types.VARCHAR);
|
|
else ps.setString(i++, att.getFileUrl());
|
|
|
|
ps.setString(i++, ft.toUpperCase());
|
|
ps.setString(i++, att.getFileName());
|
|
if (att.getFileSize() == null) ps.setNull(i++, java.sql.Types.BIGINT); else ps.setLong(i++, att.getFileSize());
|
|
if (att.getMimeType() == null) ps.setNull(i++, java.sql.Types.VARCHAR); else ps.setString(i++, att.getMimeType());
|
|
if (att.getWidth() == null) ps.setNull(i++, java.sql.Types.INTEGER); else ps.setInt(i++, att.getWidth());
|
|
if (att.getHeight() == null) ps.setNull(i++, java.sql.Types.INTEGER); else ps.setInt(i++, att.getHeight());
|
|
if (att.getDurationSeconds() == null) ps.setNull(i++, java.sql.Types.INTEGER); else ps.setInt(i++, att.getDurationSeconds());
|
|
if (att.getThumbnailUrl() == null || att.getThumbnailUrl().isBlank()) ps.setNull(i++, java.sql.Types.VARCHAR);
|
|
else ps.setString(i++, att.getThumbnailUrl());
|
|
|
|
ps.setObject(i++, mediaKey);
|
|
ps.setString(i++, att.getStoragePath());
|
|
|
|
ps.addBatch();
|
|
|
|
att.setAttachmentId(attachmentId);
|
|
att.setMediaKey(mediaKey);
|
|
}
|
|
ps.executeBatch();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public static MediaRow findMediaByKey(UUID mediaKey) throws SQLException {
|
|
String sql = """
|
|
SELECT
|
|
ma.attachment_id,
|
|
ma.message_id,
|
|
ma.media_key,
|
|
ma.file_name,
|
|
ma.file_size,
|
|
ma.mime_type,
|
|
ma.file_type,
|
|
ma.width,
|
|
ma.height,
|
|
ma.duration_seconds,
|
|
ma.thumbnail_url,
|
|
ma.file_url,
|
|
ma.storage_path,
|
|
m.receiver_type,
|
|
m.receiver_id,
|
|
m.sender_id
|
|
FROM message_attachments ma
|
|
JOIN messages m ON m.message_id = ma.message_id
|
|
WHERE ma.media_key = ?
|
|
LIMIT 1
|
|
""";
|
|
|
|
try (Connection c = ConnectionDb.connect();
|
|
PreparedStatement ps = c.prepareStatement(sql)) {
|
|
ps.setObject(1, mediaKey);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
if (!rs.next()) return null;
|
|
|
|
MediaRow a = new MediaRow();
|
|
a.attachmentId = (UUID) rs.getObject("attachment_id");
|
|
a.messageId = (UUID) rs.getObject("message_id");
|
|
a.mediaKey = (UUID) rs.getObject("media_key");
|
|
a.fileName = rs.getString("file_name");
|
|
|
|
long sz = rs.getLong("file_size");
|
|
a.fileSize = rs.wasNull() ? null : sz; // MediaRow.fileSize = Long
|
|
|
|
a.mimeType = rs.getString("mime_type");
|
|
a.fileType = rs.getString("file_type");
|
|
int w = rs.getInt("width"); a.width = rs.wasNull() ? null : w;
|
|
int h = rs.getInt("height"); a.height = rs.wasNull() ? null : h;
|
|
int d = rs.getInt("duration_seconds"); a.durationSeconds = rs.wasNull() ? null : d;
|
|
a.thumbnailUrl = rs.getString("thumbnail_url");
|
|
a.fileUrl = rs.getString("file_url");
|
|
a.storagePath = rs.getString("storage_path");
|
|
a.receiverType = rs.getString("receiver_type");
|
|
a.receiverId = (UUID) rs.getObject("receiver_id");
|
|
a.senderId = (UUID) rs.getObject("sender_id");
|
|
return a;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean canAccess(UUID requester, MediaRow mr) {
|
|
if (requester == null || mr == null || mr.receiverType == null) return false;
|
|
|
|
// اختیاری: فرستنده همیشه مجاز
|
|
if (requester.equals(mr.senderId)) return true;
|
|
|
|
switch (mr.receiverType.toLowerCase(Locale.ROOT)) {
|
|
case "private":
|
|
// receiver_id در پیامهای private = UUID چت خصوصی
|
|
return PrivateChatDatabase.isParticipant(mr.receiverId, requester);
|
|
|
|
case "group":
|
|
return GroupDatabase.isMember(mr.receiverId, requester);
|
|
|
|
case "channel":
|
|
return ChannelPermissionUtil.isUserInChannel(requester, mr.receiverId);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public void markMessageAsRead(UUID messageId, UUID userId) {
|
|
String sql = "INSERT INTO message_receipts (message_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
stmt.setObject(1, messageId);
|
|
stmt.setObject(2, userId);
|
|
stmt.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// public static List<Message> getUnreadMessages(UUID userId) {
|
|
// List<Message> messages = new ArrayList<>();
|
|
//
|
|
// String sql = """
|
|
// SELECT m.*
|
|
// FROM messages m
|
|
// 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
|
|
// AND d.message_id IS NULL
|
|
// AND m.is_deleted_globally = FALSE
|
|
// AND (
|
|
// (m.receiver_type = 'private' AND m.receiver_id = ?)
|
|
// OR
|
|
// (m.receiver_type = 'group' AND EXISTS (
|
|
// SELECT 1 FROM group_members gm WHERE gm.group_id = m.receiver_id AND gm.user_id = ?
|
|
// ))
|
|
// OR
|
|
// (m.receiver_type = 'channel' AND EXISTS (
|
|
// 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();
|
|
// PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
//
|
|
// stmt.setObject(1, userId); // for message_receipts
|
|
// stmt.setObject(2, userId); // for deleted_messages
|
|
// stmt.setObject(3, userId); // for private messages
|
|
// stmt.setObject(4, userId); // for group members
|
|
// stmt.setObject(5, userId); // for channel subscribers
|
|
//
|
|
// ResultSet rs = stmt.executeQuery();
|
|
// while (rs.next()) {
|
|
// Message message = 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.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.getBoolean("is_deleted_globally"),
|
|
// rs.getTimestamp("edited_at") != null ? rs.getTimestamp("edited_at").toLocalDateTime() : null
|
|
// );
|
|
//
|
|
// messages.add(message);
|
|
// }
|
|
//
|
|
// } catch (SQLException e) {
|
|
// e.printStackTrace();
|
|
// }
|
|
//
|
|
// return messages;
|
|
// }
|
|
|
|
|
|
|
|
public static List<Message> getUnreadMessages(UUID userId) {
|
|
return getUnreadMessages(userId, 200);
|
|
}
|
|
|
|
public static List<Message> getUnreadMessages(UUID userId, int limit) {
|
|
List<Message> messages = new ArrayList<>();
|
|
|
|
String sql = """
|
|
SELECT m.*
|
|
FROM messages m
|
|
LEFT JOIN message_receipts r
|
|
ON r.message_id = m.message_id AND r.user_id = ?
|
|
LEFT JOIN deleted_messages d
|
|
ON d.message_id = m.message_id AND d.user_id = ?
|
|
WHERE r.message_id IS NULL
|
|
AND d.message_id IS NULL
|
|
AND m.is_deleted_globally = FALSE
|
|
AND m.sender_id <> ?
|
|
AND (
|
|
(m.receiver_type = 'private' AND EXISTS (
|
|
SELECT 1
|
|
FROM private_chat pc
|
|
WHERE pc.chat_id = m.receiver_id
|
|
AND (pc.user1_id = ? OR pc.user2_id = ?)
|
|
))
|
|
OR (m.receiver_type = 'group' AND EXISTS (
|
|
SELECT 1
|
|
FROM group_members gm
|
|
WHERE gm.group_id = m.receiver_id
|
|
AND gm.user_id = ?
|
|
))
|
|
OR (m.receiver_type = 'channel' AND EXISTS (
|
|
SELECT 1
|
|
FROM channel_subscribers cs
|
|
WHERE cs.channel_id = m.receiver_id
|
|
AND cs.user_id = ?
|
|
))
|
|
)
|
|
ORDER BY m.send_at ASC
|
|
LIMIT ?
|
|
""";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
int i = 1;
|
|
ps.setObject(i++, userId); // r.user_id
|
|
ps.setObject(i++, userId); // d.user_id
|
|
ps.setObject(i++, userId); // m.sender_id <> ?
|
|
ps.setObject(i++, userId); // pc.user1_id
|
|
ps.setObject(i++, userId); // pc.user2_id
|
|
ps.setObject(i++, userId); // gm.user_id
|
|
ps.setObject(i++, userId); // cs.user_id
|
|
ps.setInt(i, Math.max(1, limit));
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
while (rs.next()) messages.add(mapMessage(rs));
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return messages;
|
|
}
|
|
|
|
|
|
|
|
private static java.util.UUID readUUID(ResultSet rs, String col) throws SQLException {
|
|
Object o = rs.getObject(col);
|
|
if (o == null) return null;
|
|
if (o instanceof java.util.UUID) return (java.util.UUID) o;
|
|
return java.util.UUID.fromString(o.toString());
|
|
}
|
|
|
|
private static java.time.LocalDateTime readLdt(ResultSet rs, String col) throws SQLException {
|
|
java.sql.Timestamp ts = rs.getTimestamp(col);
|
|
return (ts == null) ? null : ts.toLocalDateTime();
|
|
}
|
|
|
|
private static boolean readBool(ResultSet rs, String col) throws SQLException {
|
|
boolean v = rs.getBoolean(col);
|
|
return v;
|
|
}
|
|
|
|
private static Message mapMessage(ResultSet rs) throws SQLException {
|
|
Message m = new Message();
|
|
|
|
m.setMessage_id( readUUID(rs, "message_id") );
|
|
m.setSender_id( readUUID(rs, "sender_id") );
|
|
m.setReceiver_type( rs.getString("receiver_type") );
|
|
m.setReceiver_id( readUUID(rs, "receiver_id") );
|
|
m.setContent( rs.getString("content") );
|
|
m.setMessage_type( rs.getString("message_type") );
|
|
m.setSend_at( readLdt(rs, "send_at") );
|
|
m.setStatus( rs.getString("status") );
|
|
m.setReply_to_id( readUUID(rs, "reply_to_id") );
|
|
m.setIs_edited( readBool(rs, "is_edited") );
|
|
m.setEdited_at( readLdt(rs, "edited_at") );
|
|
m.setOriginal_message_id( readUUID(rs, "original_message_id") );
|
|
m.setForwarded_by( readUUID(rs, "forwarded_by") );
|
|
m.setForwarded_from( readUUID(rs, "forwarded_from") );
|
|
m.setIs_deleted_globally( readBool(rs, "is_deleted_globally") );
|
|
|
|
return m;
|
|
}
|
|
|
|
public static List<FileAttachment> getAttachments(UUID messageId) {
|
|
List<FileAttachment> attachments = new ArrayList<>();
|
|
String sql = "SELECT file_url, file_type FROM message_attachments WHERE message_id = ?";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
|
stmt.setObject(1, messageId);
|
|
ResultSet rs = stmt.executeQuery();
|
|
|
|
while (rs.next()) {
|
|
attachments.add(new FileAttachment(
|
|
rs.getString("file_url"),
|
|
rs.getString("file_type")
|
|
));
|
|
}
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return attachments;
|
|
}
|
|
|
|
|
|
|
|
public static LocalDateTime getLastMessageTimeBetween(UUID user1, UUID user2, String type) {
|
|
String sql = """
|
|
SELECT MAX(send_at) FROM messages
|
|
WHERE receiver_type = ?
|
|
AND (
|
|
(sender_id = ? AND receiver_id = ?)
|
|
OR (sender_id = ? AND receiver_id = ?)
|
|
)
|
|
""";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
stmt.setString(1, type);
|
|
stmt.setObject(2, user1);
|
|
stmt.setObject(3, user2);
|
|
stmt.setObject(4, user2);
|
|
stmt.setObject(5, user1);
|
|
ResultSet rs = stmt.executeQuery();
|
|
if (rs.next()) {
|
|
Timestamp ts = rs.getTimestamp(1);
|
|
return ts != null ? ts.toLocalDateTime() : null;
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
public static LocalDateTime getLastMessageTime(UUID receiverId, String type) {
|
|
String sql = "SELECT MAX(send_at) FROM messages WHERE receiver_id = ? AND receiver_type = ?";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
stmt.setObject(1, receiverId);
|
|
stmt.setString(2, type);
|
|
ResultSet rs = stmt.executeQuery();
|
|
if (rs.next()) {
|
|
Timestamp ts = rs.getTimestamp(1);
|
|
return ts != null ? ts.toLocalDateTime() : null;
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
public static Message extractMessage(ResultSet rs) throws SQLException {
|
|
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"),
|
|
(UUID) rs.getObject("reply_to_id"),
|
|
rs.getBoolean("is_edited"),
|
|
(UUID) rs.getObject("original_message_id"),
|
|
(UUID) rs.getObject("forwarded_by"),
|
|
(UUID) rs.getObject("forwarded_from"),
|
|
rs.getBoolean("is_deleted_globally"),
|
|
(rs.getTimestamp("edited_at") != null) ? rs.getTimestamp("edited_at").toLocalDateTime() : null
|
|
);
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
String sql = """
|
|
SELECT * FROM messages m
|
|
WHERE m.receiver_type = 'private'
|
|
AND m.content ILIKE ?
|
|
AND m.is_deleted_globally = FALSE
|
|
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();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
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()) {
|
|
messages.add(mapResultSetToMessage(rs));
|
|
}
|
|
return messages;
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return Collections.emptyList();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// 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<>();
|
|
String sql = """
|
|
SELECT * FROM messages
|
|
WHERE receiver_type = 'private'
|
|
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
|
|
""";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
stmt.setObject(1, chatId);
|
|
stmt.setObject(2, userId);
|
|
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) {
|
|
// 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<>();
|
|
String sql = """
|
|
SELECT * FROM messages
|
|
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
|
|
""";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
stmt.setObject(1, groupId);
|
|
stmt.setObject(2, userId);
|
|
|
|
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) {
|
|
// 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<>();
|
|
String sql = """
|
|
SELECT * FROM messages
|
|
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
|
|
""";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
|
stmt.setObject(1, channelId);
|
|
stmt.setObject(2, userId);
|
|
|
|
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) {
|
|
// 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<>();
|
|
if (groupIds.isEmpty()) return result;
|
|
|
|
String placeholders = groupIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
|
String sql = """
|
|
SELECT m.*
|
|
FROM messages m
|
|
LEFT JOIN deleted_messages d ON m.message_id = d.message_id AND d.user_id = ?
|
|
WHERE m.receiver_type = 'group'
|
|
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);
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
|
int i = 1;
|
|
stmt.setObject(i++, userId);
|
|
for (UUID groupId : groupIds) {
|
|
stmt.setObject(i++, groupId);
|
|
}
|
|
|
|
stmt.setString(i, "%" + keyword + "%");
|
|
|
|
ResultSet rs = stmt.executeQuery();
|
|
while (rs.next()) {
|
|
result.add(mapResultSetToMessage(rs));
|
|
}
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// 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) {
|
|
List<Message> result = new ArrayList<>();
|
|
if (channelIds.isEmpty()) return result;
|
|
|
|
String placeholders = channelIds.stream().map(id -> "?").collect(Collectors.joining(", "));
|
|
String sql = """
|
|
SELECT m.*
|
|
FROM messages m
|
|
WHERE m.receiver_type = 'channel'
|
|
AND m.receiver_id IN (%s)
|
|
AND m.is_deleted_globally = FALSE
|
|
AND m.content ILIKE ?
|
|
ORDER BY m.send_at DESC
|
|
""".formatted(placeholders);
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
|
|
|
int i = 1;
|
|
for (UUID channelId : channelIds) {
|
|
stmt.setObject(i++, channelId);
|
|
}
|
|
|
|
stmt.setString(i, "%" + keyword + "%");
|
|
|
|
ResultSet rs = stmt.executeQuery();
|
|
while (rs.next()) {
|
|
result.add(mapResultSetToMessage(rs));
|
|
}
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public List<Message> getMessagesForPrivateChat(UUID user1, UUID user2) {
|
|
UUID chatId = PrivateChatDatabase.findChatIdByUsers(user1, user2);
|
|
if (chatId == null) return new ArrayList<>();
|
|
return findByReceiver("private", chatId);
|
|
}
|
|
|
|
public static List<Message> findByReceiver(String receiverType, UUID receiverId) {
|
|
List<Message> messages = new ArrayList<>();
|
|
String sql = "SELECT * FROM messages WHERE receiver_type = ? AND receiver_id = ? ORDER BY send_at";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
ps.setString(1, receiverType);
|
|
ps.setObject(2, receiverId);
|
|
|
|
ResultSet rs = ps.executeQuery();
|
|
while (rs.next()) {
|
|
UUID messageId = (UUID) rs.getObject("message_id");
|
|
UUID senderId = (UUID) rs.getObject("sender_id");
|
|
UUID recId = (UUID) rs.getObject("receiver_id");
|
|
String type = rs.getString("receiver_type");
|
|
String content = rs.getString("content");
|
|
String messageType = rs.getString("message_type");
|
|
LocalDateTime sendAt = rs.getTimestamp("send_at").toLocalDateTime();
|
|
|
|
Message message = new Message(messageId, senderId, recId, type, content, messageType, sendAt);
|
|
messages.add(message);
|
|
}
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public static boolean insertSavedMessage(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, edited_at," +
|
|
" original_message_id, forwarded_by, forwarded_from, is_deleted_globally) " +
|
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
ps.setObject(1, message.getMessage_id()); // message_id
|
|
ps.setObject(2, message.getSender_id()); // sender_id
|
|
ps.setString(3, message.getReceiver_type()); // receiver_type (private)
|
|
ps.setObject(4, message.getReceiver_id()); // receiver_id (same as sender_id for saved messages)
|
|
ps.setString(5, message.getContent()); // content
|
|
ps.setString(6, message.getMessage_type()); // message_type
|
|
ps.setTimestamp(7, Timestamp.valueOf(message.getSend_at())); // send_at
|
|
ps.setString(8, message.getStatus()); // status
|
|
if (message.getReply_to_id() != null)
|
|
ps.setObject(9, message.getReply_to_id()); // reply_to_id
|
|
else
|
|
ps.setNull(9, Types.OTHER);
|
|
|
|
ps.setBoolean(10, message.isIs_edited()); // is_edited
|
|
|
|
if (message.getEdited_at() != null)
|
|
ps.setTimestamp(11, Timestamp.valueOf(message.getEdited_at())); // edited_at
|
|
else
|
|
ps.setNull(11, Types.TIMESTAMP);
|
|
|
|
if (message.getOriginal_message_id() != null)
|
|
ps.setObject(12, message.getOriginal_message_id()); // original_message_id
|
|
else
|
|
ps.setNull(12, Types.OTHER);
|
|
|
|
if (message.getForwarded_by() != null)
|
|
ps.setObject(13, message.getForwarded_by()); // forwarded_by
|
|
else
|
|
ps.setNull(13, Types.OTHER);
|
|
|
|
if (message.getForwarded_from() != null)
|
|
ps.setObject(14, message.getForwarded_from()); // forwarded_from
|
|
else
|
|
ps.setNull(14, Types.OTHER);
|
|
|
|
ps.setBoolean(15, message.getIs_deleted_globally()); // is_deleted_globally
|
|
|
|
return ps.executeUpdate() > 0;
|
|
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
public static Message getLastMessage(UUID targetId, String type) {
|
|
final String sql =
|
|
"SELECT * FROM messages " +
|
|
"WHERE receiver_type = ? AND receiver_id = ? " +
|
|
"ORDER BY send_at DESC LIMIT 1";
|
|
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
|
|
ps.setString(1, type.toLowerCase()); // "private" | "group" | "channel"
|
|
ps.setObject(2, targetId);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
return rs.next() ? mapRow(rs) : null;
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// public static int getUnreadCount(UUID me, UUID targetId, String type) {
|
|
// final String sql =
|
|
// "SELECT COUNT(*) FROM messages " +
|
|
// "WHERE receiver_type = ? AND receiver_id = ? " +
|
|
// "AND sender_id <> ? " +
|
|
// "AND (status IS NULL OR status <> 'SEEN')";
|
|
//
|
|
// try (Connection conn = ConnectionDb.connect();
|
|
// PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
//
|
|
// ps.setString(1, type.toLowerCase());
|
|
// ps.setObject(2, targetId);
|
|
// ps.setObject(3, me);
|
|
//
|
|
// try (ResultSet rs = ps.executeQuery()) {
|
|
// return rs.next() ? rs.getInt(1) : 0;
|
|
// }
|
|
// } catch (SQLException e) {
|
|
// e.printStackTrace();
|
|
// return 0;
|
|
// }
|
|
//
|
|
//
|
|
// }
|
|
|
|
private static Message mapRow(ResultSet rs) throws SQLException {
|
|
Message m = new Message();
|
|
m.setMessage_id((UUID) rs.getObject("message_id"));
|
|
m.setSender_id((UUID) rs.getObject("sender_id"));
|
|
m.setReceiver_id((UUID) rs.getObject("receiver_id"));
|
|
m.setReceiver_type(rs.getString("receiver_type"));
|
|
if ("private".equalsIgnoreCase(m.getReceiver_type())) {
|
|
m.setReceiver_id(m.getReceiver_id());
|
|
} else {
|
|
m.setReceiver_id(null);
|
|
}
|
|
m.setMessage_type(rs.getString("message_type"));
|
|
m.setContent(rs.getString("content"));
|
|
Timestamp ts = rs.getTimestamp("send_at");
|
|
if (ts != null) m.setSend_at(ts.toLocalDateTime());
|
|
m.setIs_edited(rs.getBoolean("is_edited"));
|
|
m.setStatus(rs.getString("status"));
|
|
Object rtid = rs.getObject("reply_to_id");
|
|
m.setReply_to_id(rtid == null ? null : (UUID) rtid);
|
|
return m;
|
|
}
|
|
|
|
|
|
|
|
public static List<UUID> getUnreadMessageIds(UUID me, UUID chatId, String chatType, int limit) {
|
|
String sql = """
|
|
SELECT m.message_id
|
|
FROM messages m
|
|
WHERE m.receiver_id = ? AND m.receiver_type = ?
|
|
AND m.sender_id <> ?
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM message_receipts r
|
|
WHERE r.message_id = m.message_id
|
|
AND r.user_id = ?
|
|
)
|
|
ORDER BY m.send_at ASC
|
|
LIMIT ?
|
|
""";
|
|
List<UUID> ids = new ArrayList<>();
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, chatId);
|
|
ps.setString(2, chatType);
|
|
ps.setObject(3, me);
|
|
ps.setObject(4, me);
|
|
ps.setInt(5, limit);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
while (rs.next()) {
|
|
ids.add((UUID) rs.getObject(1));
|
|
}
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
}
|
|
return ids;
|
|
}
|
|
|
|
// تعداد ناخواندهها (برای چتلیست)
|
|
public static int getUnreadCount(UUID me, UUID chatId, String chatType) {
|
|
String sql = """
|
|
SELECT COUNT(1)
|
|
FROM messages m
|
|
WHERE m.receiver_id = ?
|
|
AND m.receiver_type = ?
|
|
AND m.sender_id <> ?
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM message_receipts r
|
|
WHERE r.message_id = m.message_id
|
|
AND r.user_id = ?
|
|
)
|
|
""";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, chatId);
|
|
ps.setString(2, chatType);
|
|
ps.setObject(3, me);
|
|
ps.setObject(4, me);
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
return rs.next() ? rs.getInt(1) : 0;
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
public static int insertReceiptIfAbsent(UUID messageId, UUID userId) {
|
|
String sql = """
|
|
INSERT INTO message_receipts(message_id, user_id, read_at)
|
|
VALUES (?, ?, now())
|
|
ON CONFLICT (message_id, user_id) DO NOTHING
|
|
""";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, messageId);
|
|
ps.setObject(2, userId);
|
|
return ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//Read status if it's first reader
|
|
public static int setMessageReadIfNeeded(UUID messageId, UUID viewerId) {
|
|
String sql = """
|
|
UPDATE messages m
|
|
SET status = 'READ'
|
|
WHERE m.message_id = ?
|
|
AND m.sender_id <> ?
|
|
AND m.status <> 'READ'
|
|
""";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, messageId);
|
|
ps.setObject(2, viewerId);
|
|
return ps.executeUpdate();
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public static String getFirstAttachmentUrlByType(UUID messageId, String messageType) {
|
|
final String sql = """
|
|
SELECT file_url
|
|
FROM message_attachments
|
|
WHERE message_id = ?
|
|
AND (file_type = ? OR ? IS NULL)
|
|
ORDER BY attachment_id
|
|
LIMIT 1
|
|
""";
|
|
try (Connection conn = ConnectionDb.connect();
|
|
PreparedStatement ps = conn.prepareStatement(sql)) {
|
|
ps.setObject(1, messageId);
|
|
// اگر messageType خالی بود، با NULL بفرست که شرط OR ? IS NULL برقرار شه
|
|
String mt = (messageType == null || messageType.isBlank()) ? null : messageType.toUpperCase();
|
|
ps.setString(2, mt);
|
|
if (mt == null) ps.setNull(3, Types.VARCHAR); else ps.setString(3, mt);
|
|
|
|
try (ResultSet rs = ps.executeQuery()) {
|
|
return rs.next() ? rs.getString("file_url") : null;
|
|
}
|
|
} catch (SQLException e) {
|
|
e.printStackTrace();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |