Manage data after login

This commit is contained in:
2025-06-08 13:58:43 +03:30
parent e9a6789dfa
commit 7a7141866e
20 changed files with 1175 additions and 33 deletions
@@ -3,8 +3,14 @@ package org.to.telegramfinalproject.Client;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner; import java.util.Scanner;
import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.to.telegramfinalproject.Models.ChatEntry;
public class ActionHandler { public class ActionHandler {
private final PrintWriter out; private final PrintWriter out;
@@ -51,20 +57,146 @@ public class ActionHandler {
this.send(request); this.send(request);
} }
public void search(){
System.out.print("Enter keyword to search: ");
String keyword = scanner.nextLine();
JSONObject request = new JSONObject();
request.put("action", "search");
request.put("keyword", keyword);
send(request);
}
private void send(JSONObject request) { private void send(JSONObject request) {
try { try {
this.out.println(request.toString()); this.out.println(request.toString());
String responseText = this.in.readLine(); String responseText = this.in.readLine();
if (responseText != null) { if (responseText != null) {
JSONObject response = new JSONObject(responseText); JSONObject response = new JSONObject(responseText);
System.out.println("Server response: " + response.getString("message")); System.out.println("Server response: " + response.getString("message"));
String status = response.getString("status");
String action = request.getString("action");
if (status.equals("success") && response.has("data") && !response.isNull("data")) {
if (action.equals("login") || action.equals("register")) {
Session.currentUser = response.getJSONObject("data");
JSONArray chatListJson = Session.currentUser.getJSONArray("chat_list");
List<ChatEntry> chatList = new ArrayList<>();
for (Object obj : chatListJson) {
JSONObject chat = (JSONObject) obj;
ChatEntry entry = new ChatEntry(
chat.getString("id"),
chat.getString("name"),
chat.getString("image_url"),
chat.getString("type"),
chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time"))
);
chatList.add(entry);
}
Session.chatList = chatList;
}
else if (action.equals("search")) {
JSONArray results = response.getJSONObject("data").getJSONArray("results");
System.out.println("\nSearch Results:");
for (Object obj : results) {
JSONObject item = (JSONObject) obj;
System.out.println("- [" + item.getString("type") + "] " + item.getString("name") + " (ID: " + item.getString("id") + ")");
}
}
}
} else { } else {
System.out.println("No response from server."); System.out.println("No response from server.");
} }
} catch (IOException e) { } catch (IOException e) {
System.err.println("Error: " + e.getMessage()); System.err.println("Error: " + e.getMessage());
} }
} }
public void userMenu() {
while (true) {
System.out.println("\nUser Menu:");
System.out.println("1. Show chat list");
System.out.println("2. Search");
System.out.println("3. Logout");
System.out.print("Choose an option: ");
String choice = scanner.nextLine();
switch (choice) {
case "1":
showChatListAndSelect();
break;
case "2" :
search();
break;
case "3":
Session.currentUser = null;
Session.chatList = null;
System.out.println("Logged out.");
return;
default:
System.out.println("Invalid choice.");
}
}
}
public void showChatListAndSelect() {
if (Session.chatList == null || Session.chatList.isEmpty()) {
System.out.println("No chats available.");
return;
}
System.out.println("\nYour Chats:");
for (int i = 0; i < Session.chatList.size(); i++) {
ChatEntry entry = Session.chatList.get(i);
String time = entry.getLastMessageTime() == null ? "No messages yet" : entry.getLastMessageTime().toString();
System.out.println((i + 1) + ". [" + entry.getType() + "] " + entry.getName() + " - Last: " + time);
}
System.out.print("Select a chat by number: ");
int choice = Integer.parseInt(scanner.nextLine()) - 1;
if (choice < 0 || choice >= Session.chatList.size()) {
System.out.println("Invalid selection.");
return;
}
ChatEntry selected = Session.chatList.get(choice);
openChat(selected);
}
private void openChat(ChatEntry chat) {
JSONObject request = new JSONObject();
request.put("action", "get_messages");
request.put("receiver_id", chat.getId());
request.put("receiver_type", chat.getType());
send(request);
}
} }
@@ -0,0 +1,13 @@
package org.to.telegramfinalproject.Client;
import org.json.JSONObject;
import org.to.telegramfinalproject.Models.ChatEntry;
import java.util.List;
// method for save data from server response
public class Session {
public static JSONObject currentUser;
public static List<ChatEntry> chatList;
}
@@ -47,6 +47,13 @@ public class TelegramClient {
break; break;
case "2": case "2":
this.handler.loginHandler(); this.handler.loginHandler();
if (Session.currentUser != null) {
System.out.println("Login successful.");
this.handler.userMenu();
} else {
System.out.println("Login failed.");
}
break; break;
case "3": case "3":
System.out.println(" Disconnecting..."); System.out.println(" Disconnecting...");
@@ -0,0 +1,76 @@
package org.to.telegramfinalproject.Database;
import org.to.telegramfinalproject.Models.Channel;
import org.to.telegramfinalproject.Models.Group;
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 ChannelDatabase {
public static List<Channel> getChannelsByUser(UUID internalUuid) {
List<Channel> channels = new ArrayList<>();
String sql = """
SELECT c.* FROM channels c
JOIN channel_subscribers cs ON c.internal_uuid = cs.channel_id
WHERE cs.user_id = ?
""";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, internalUuid);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Channel channel = new Channel();
channel.setInternal_uuid(UUID.fromString(rs.getString("internal_uuid")));
channel.setChannel_id(rs.getString("channel_id"));
channel.setChannel_name(rs.getString("channel_name"));
channel.setCreator_id(UUID.fromString(rs.getString("creator_id")));
channel.setImage_url(rs.getString("image_url"));
channel.setDescription(rs.getString("description"));
channel.setCreated_at(rs.getTimestamp("created_at").toLocalDateTime());
channels.add(channel);
}
} catch (SQLException e) {
e.printStackTrace();
}
return channels;
}
public static List<Channel> searchChannels(String keyword) {
List<Channel> result = new ArrayList<>();
String sql = "SELECT * FROM channels WHERE channel_name ILIKE ? OR channel_id ILIKE ?";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "%" + keyword + "%");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Channel channel = new Channel(
UUID.fromString(rs.getString("internal_uuid")),
rs.getString("channel_name"),
UUID.fromString(rs.getString("creator_id")),
rs.getTimestamp("created_at").toLocalDateTime()
);
channel.setChannel_id(rs.getString("channel_id"));
channel.setImage_url(rs.getString("image_url"));
channel.setDescription(rs.getString("description"));
result.add(channel);
}
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
}
@@ -0,0 +1,158 @@
package org.to.telegramfinalproject.Database;
import org.to.telegramfinalproject.Models.Contact;
import org.to.telegramfinalproject.Models.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ContactDatabase {
public ContactDatabase(){}
private static Connection getConnection() throws SQLException {
return ConnectionDb.connect();
}
public boolean addContact(UUID user_id, UUID contact_id) {
String sql = "INSERT INTO contacts (user_id, contact_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setObject(2,contact_id);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public boolean removeContact(UUID user_id, UUID contact_id) {
String sql = "DELETE FROM contacts WHERE user_id = ? AND contact_id = ?";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setObject(2, contact_id);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public boolean blockContact(UUID user_id, UUID contact_id) {
String sql = "UPDATE contacts SET is_blocked = TRUE WHERE user_id = ? AND contact_id = ?";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setObject(2, contact_id);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public boolean unblockContact(UUID user_id, UUID contact_id) {
String sql = "UPDATE contacts SET is_blocked = FALSE WHERE user_id = ? AND contact_id = ?";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setObject(2, contact_id);
return stmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static List<Contact> getContacts(UUID user_id) {
List<Contact> contacts = new ArrayList<>();
String sql = "SELECT * FROM contacts WHERE user_id = ?";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
UUID contactId = (UUID) rs.getObject("contact_id");
boolean isBlocked = rs.getBoolean("is_blocked");
Timestamp addedAt = rs.getTimestamp("added_at");
Contact contact = new Contact(user_id, contactId);
contact.setIs_blocked(isBlocked);
contact.setAdd_at(addedAt.toLocalDateTime());
contacts.add(contact);
}
} catch (SQLException e) {
e.printStackTrace();
}
return contacts;
}
public boolean existsContact(UUID user_id, UUID contact_id) {
String sql = "SELECT 1 FROM contacts WHERE user_id = ? AND contact_id = ? LIMIT 1"; // stop searching when find the first item in DB(LIMIT 1)
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setObject(2, contact_id);
ResultSet rs = stmt.executeQuery();
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public boolean isBlocked(UUID user_id, UUID contact_id) {
String sql = "SELECT is_blocked FROM contacts WHERE user_id = ? AND contact_id = ?";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setObject(2, contact_id);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getBoolean("is_blocked");
}
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
public List<Contact> searchContacts(UUID user_id, String searchTerm) {
List<Contact> results = new ArrayList<>(); //ILIKE case-insensitive
String sql = """
SELECT c.contact_id, c.added_at, c.is_blocked
FROM contacts c
JOIN users u ON c.contact_id = u.internal_uuid
WHERE c.user_id = ? AND (u.user_id ILIKE ? OR u.profile_name ILIKE ?)
""";
try (Connection connection = getConnection()) {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, user_id);
stmt.setString(2, "%" + searchTerm + "%");
stmt.setString(3, "%" + searchTerm + "%");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
UUID contactId = (UUID) rs.getObject("contact_id");
boolean isBlocked = rs.getBoolean("is_blocked");
Timestamp addedAt = rs.getTimestamp("added_at");
Contact contact = new Contact(user_id, contactId);
contact.setIs_blocked(isBlocked);
contact.setAdd_at(addedAt.toLocalDateTime());
results.add(contact);
}
} catch (SQLException e) {
e.printStackTrace();
}
return results;
}
}
@@ -0,0 +1,78 @@
package org.to.telegramfinalproject.Database;
import org.to.telegramfinalproject.Models.Group;
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 GroupDatabase {
public static List<Group> getGroupsByUser(UUID internalUuid) {
List<Group> groups = new ArrayList<>();
String sql = """
SELECT g.* FROM groups g
JOIN group_members gm ON g.internal_uuid = gm.group_id
WHERE gm.user_id = ?
""";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, internalUuid);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Group group = new Group();
group.setInternal_uuid(UUID.fromString(rs.getString("internal_uuid")));
group.setGroup_id(rs.getString("group_id"));
group.setGroup_name(rs.getString("group_name"));
group.setCreator_id(UUID.fromString(rs.getString("creator_id")));
group.setImage_url(rs.getString("image_url"));
group.setDescription(rs.getString("description"));
group.setCreated_at(rs.getTimestamp("created_at").toLocalDateTime());
groups.add(group);
}
} catch (SQLException e) {
e.printStackTrace();
}
return groups;
}
public static List<Group> searchGroups(String keyword) {
List<Group> result = new ArrayList<>();
String sql = "SELECT * FROM groups WHERE group_name ILIKE ? OR group_id ILIKE ?";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "%" + keyword + "%");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Group group = new Group(
UUID.fromString(rs.getString("internal_uuid")),
rs.getString("group_name"),
UUID.fromString(rs.getString("creator_id")),
rs.getTimestamp("created_at").toLocalDateTime()
);
group.setGroup_id(rs.getString("group_id"));
group.setImage_url(rs.getString("image_url"));
group.setDescription(rs.getString("description"));
result.add(group);
}
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
}
@@ -0,0 +1,135 @@
package org.to.telegramfinalproject.Database;
import org.to.telegramfinalproject.Models.Message;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class MessageDatabase {
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 = ?
WHERE r.user_id IS NULL
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 = ?
))
)
""";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, userId);
stmt.setObject(2, userId);
stmt.setObject(3, userId);
stmt.setObject(4, userId);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
messages.add(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.getString("file_url"),
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
));
}
} catch (SQLException e) {
e.printStackTrace();
}
return messages;
}
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;
}
}
@@ -3,10 +3,7 @@ package org.to.telegramfinalproject.Database;
import org.to.telegramfinalproject.Models.User; import org.to.telegramfinalproject.Models.User;
import java.sql.Connection; import java.sql.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -203,5 +200,87 @@ public class userDatabase {
return false; return false;
} }
} }
public static void updateUserStatus(UUID uuid, String status) {
String sql = "UPDATE users SET status = ? WHERE internal_uuid = ?";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, status);
stmt.setObject(2, uuid);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void updateLastSeen(UUID uuid) {
String sql = "UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE internal_uuid = ?";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, uuid);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static User findByInternalUUID(UUID internalUuid) {
String sql = "SELECT * FROM users WHERE internal_uuid = ?";
try (Connection conn = ConnectionDb.connect();
PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, internalUuid);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
User user = new User(
rs.getString("user_id"),
UUID.fromString(rs.getString("internal_uuid")),
rs.getString("username"),
rs.getString("password"),
rs.getString("profile_name")
);
user.setBio(rs.getString("bio"));
user.setImage_url(rs.getString("image_url"));
user.setStatus(rs.getString("status"));
Timestamp lastSeenTs = rs.getTimestamp("last_seen");
if (lastSeenTs != null) {
user.setLast_seen(lastSeenTs.toLocalDateTime());
}
return user;
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public List<User> searchUsers(String keyword) {
String query = "SELECT * FROM users WHERE user_id ILIKE ? OR profile_name ILIKE ?"; //(ILIKE) case_insensitive
List<User> result = new ArrayList<>();
try (Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)) {
stmt.setString(1, "%" + keyword + "%");
stmt.setString(2, "%" + keyword + "%");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
result.add(extractUser(rs));
}
} catch (SQLException e) {
e.printStackTrace();
}
return result;
}
} }
@@ -21,6 +21,10 @@ public class Channel {
this.created_at = created_at; this.created_at = created_at;
} }
public Channel() {
}
public void setChannel_id(String Channel_id){this.channel_id = Channel_id;} public void setChannel_id(String Channel_id){this.channel_id = Channel_id;}
public void setCreator_id(UUID creator_id){this.creator_id = creator_id;} public void setCreator_id(UUID creator_id){this.creator_id = creator_id;}
public void setChannel_name(String channel_name){this.channel_name = channel_name;} public void setChannel_name(String channel_name){this.channel_name = channel_name;}
@@ -40,4 +44,6 @@ public class Channel {
return this.internal_uuid; return this.internal_uuid;
} }
public void setInternal_uuid(UUID internalUuid) { this.internal_uuid = internalUuid;
}
} }
@@ -3,27 +3,27 @@ package org.to.telegramfinalproject.Models;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.UUID; import java.util.UUID;
public class ChannelSubscribe { public class ChannelSubscribe{
private UUID channel_id; private UUID channel_id;
private UUID user_id; private UUID user_id;
private LocalDateTime join_at; private LocalDateTime Subscribed_at;
public ChannelSubscribe(UUID channel_id, UUID user_id, String role){ public ChannelSubscribe(UUID channel_id, UUID user_id, String role){
this.channel_id = channel_id; this.channel_id = channel_id;
this.user_id = user_id; this.user_id = user_id;
this.join_at = LocalDateTime.now(); this.Subscribed_at = LocalDateTime.now();
} }
public void setChannel_id(UUID group_id){this.channel_id = group_id;} public void setChannel_id(UUID group_id){this.channel_id = group_id;}
public void setUser_id(UUID user_id){this.user_id = user_id;} public void setUser_id(UUID user_id){this.user_id = user_id;}
public void setJoin_at(LocalDateTime join_at){this.join_at = join_at;} public void setJoin_at(LocalDateTime join_at){this.Subscribed_at = join_at;}
public UUID getChannel_id(){return this.channel_id;} public UUID getChannel_id(){return this.channel_id;}
public UUID getUser_id(){return this.user_id;} public UUID getUser_id(){return this.user_id;}
public LocalDateTime getJoin_at(){return this.join_at;} public LocalDateTime getJoin_at(){return this.Subscribed_at;}
} }
@@ -0,0 +1,39 @@
package org.to.telegramfinalproject.Models;
import java.time.LocalDateTime;
public class ChatEntry {
private final String name;
private final String id;
private final String imageUrl;
private final String type; // "private", "group", "channel"
private final LocalDateTime lastMessageTime;
public ChatEntry(String name, String id, String imageUrl, String type, LocalDateTime lastMessageTime) {
this.name = name;
this.id = id;
this.imageUrl = imageUrl;
this.type = type;
this.lastMessageTime = lastMessageTime;
}
public String getName() {
return name;
}
public String getId() {
return id;
}
public String getImageUrl() {
return imageUrl;
}
public String getType() {
return type;
}
public LocalDateTime getLastMessageTime() {
return lastMessageTime;
}
}
@@ -1,23 +1,27 @@
package org.to.telegramfinalproject.Models; package org.to.telegramfinalproject.Models;
import org.json.JSONArray;
import org.json.JSONObject;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public class Contact { public class Contact {
private UUID user_id; private UUID user_id;
private UUID contact_id; private UUID contact_id;
private LocalDateTime add_at; private LocalDateTime added_at;
private Boolean is_blocked; private Boolean is_blocked;
public Contact(UUID user_id, UUID contact_id){ public Contact(UUID user_id, UUID contact_id){
this.user_id = user_id; this.user_id = user_id;
this.contact_id = contact_id; this.contact_id = contact_id;
this.add_at = LocalDateTime.now(); this.added_at = LocalDateTime.now();
} }
public void setUser_id(UUID user_id){this.user_id = user_id;} public void setUser_id(UUID user_id){this.user_id = user_id;}
public void setContact_id(UUID contact_id){this.contact_id = contact_id;} public void setContact_id(UUID contact_id){this.contact_id = contact_id;}
public void setAdd_at(LocalDateTime add_at){this.add_at =add_at;} public void setAdd_at(LocalDateTime add_at){this.added_at =add_at;}
public void setIs_blocked(Boolean is_blocked){this.is_blocked =is_blocked;} public void setIs_blocked(Boolean is_blocked){this.is_blocked =is_blocked;}
public UUID getUser_id(){ public UUID getUser_id(){
@@ -27,9 +31,14 @@ public class Contact {
public UUID getContact_id(){return this.contact_id;} public UUID getContact_id(){return this.contact_id;}
public LocalDateTime getAdd_at() { public LocalDateTime getAdd_at() {
return add_at; return added_at;
} }
public Boolean getIs_blocked(){ public Boolean getIs_blocked(){
return is_blocked; return is_blocked;
} }
} }
@@ -21,6 +21,10 @@ public class Group {
this.created_at = created_at; this.created_at = created_at;
} }
public Group() {
}
public void setGroup_id(String group_id){this.group_id = group_id;} public void setGroup_id(String group_id){this.group_id = group_id;}
public void setCreator_id(UUID creator_id){this.creator_id = creator_id;} public void setCreator_id(UUID creator_id){this.creator_id = creator_id;}
public void setGroup_name(String group_name){this.group_name = group_name;} public void setGroup_name(String group_name){this.group_name = group_name;}
@@ -40,4 +44,6 @@ public class Group {
return this.internal_uuid; return this.internal_uuid;
} }
public void setInternal_uuid(UUID internalUuid) { this.internal_uuid = internalUuid;
}
} }
@@ -0,0 +1,157 @@
package org.to.telegramfinalproject.Models;
import org.json.JSONArray;
import org.json.JSONObject;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
public class JsonUtil {
public static JSONArray contactListToJson(List<Contact> contacts) {
JSONArray array = new JSONArray();
for (Contact contact : contacts) {
JSONObject obj = new JSONObject();
obj.put("user_id", contact.getUser_id().toString());
obj.put("contact_id", contact.getContact_id().toString());
obj.put("is_blocked", contact.getIs_blocked());
obj.put("added_at", contact.getAdd_at().toString());
array.put(obj);
}
return array;
}
public static JSONObject userToJson(User user) {
JSONObject obj = new JSONObject();
obj.put("internalUUID", user.getInternal_uuid().toString());
obj.put("user_id", user.getUser_id() != null ?user.getUser_id().toString() :JSONObject.NULL);
obj.put("username", user.getUsername());
obj.put("profile_name", user.getProfile_name());
obj.put("bio", user.getBio()!= null ?user.getBio().toString() :JSONObject.NULL);
obj.put("image_url", user.getImage_url() != null ?user.getImage_url().toString() :JSONObject.NULL);
obj.put("status", user.getStatus());
obj.put("last_seen", user.getLast_seen() != null ? user.getLast_seen().toString() : JSONObject.NULL);
obj.put("contactList", JsonUtil.contactListToJson(user.getContactList()));
obj.put("channelList", JsonUtil.channelListToJson(user.getChannelList()));
obj.put("groupList", JsonUtil.groupListToJson(user.getGroupList()));
obj.put("unreadMessages", JsonUtil.messageListToJson(user.getUnreadMessages()));
return obj;
}
public static JSONArray messageListToJson(List<Message> messages) {
JSONArray array = new JSONArray();
for (Message message : messages) {
JSONObject obj = new JSONObject();
obj.put("message_id", message.getMessage_id().toString());
obj.put("sender_id", message.getSender_id() != null ? message.getSender_id().toString() : JSONObject.NULL);
obj.put("receiver_type", message.getReceiver_type());
obj.put("receiver_id", message.getReceiver_id().toString());
obj.put("content", message.getContent());
obj.put("message_type", message.getMessage_type());
obj.put("file_url", message.getFile_url());
obj.put("send_at", message.getSend_at().toString());
obj.put("status", message.getStatus());
obj.put("reply_to_id", message.getReply_to_id() != null ? message.getReply_to_id().toString() : JSONObject.NULL);
obj.put("is_edited", message.isIs_edited());
obj.put("original_message_id", message.getOriginal_message_id() != null ? message.getOriginal_message_id().toString() : JSONObject.NULL);
obj.put("forwarded_by", message.getForwarded_by() != null ? message.getForwarded_by().toString() : JSONObject.NULL);
obj.put("forwarded_from", message.getForwarded_from() != null ? message.getForwarded_from().toString() : JSONObject.NULL);
array.put(obj);
}
return array;
}
public static JSONArray groupListToJson(List<Group> groups) {
JSONArray array = new JSONArray();
for (Group group : groups) {
JSONObject obj = new JSONObject();
obj.put("internal_uuid", group.getInternal_uuid().toString());
obj.put("group_id", group.getGroup_id() != null ?group.getGroup_id().toString() :JSONObject.NULL);
obj.put("group_name", group.getGroup_name());
obj.put("creator_id", group.getCreator_id().toString());
obj.put("image_url",group.getImage_url()!= null ?group.getImage_url().toString() : JSONObject.NULL );
obj.put("description", group.getDescription() != null ?group.getDescription().toString() : JSONObject.NULL );
obj.put("created_at", group.getCreated_at().toString());
obj.put("members", JsonUtil.groupMemberListToJson(group.getMembers()));
array.put(obj);
}
return array;
}
public static JSONArray channelListToJson(List<Channel> channels) {
JSONArray array = new JSONArray();
for (Channel channel : channels) {
JSONObject obj = new JSONObject();
obj.put("internal_uuid",channel.getInternal_uuid().toString());
obj.put("channel_id", channel.getChannel_id() != null ?channel.getChannel_id().toString() :JSONObject.NULL);
obj.put("channel_name", channel.getChannel_name());
obj.put("creator_id", channel.getCreator_id().toString());
obj.put("image_url",channel.getImage_url()!= null ?channel.getImage_url().toString() : JSONObject.NULL );
obj.put("description",channel.getDescription() != null ?channel.getDescription().toString() : JSONObject.NULL );
obj.put("created_at",channel.getCreated_at().toString());
obj.put("members", JsonUtil.channelSubscribeToJson(channel.getMembers()));
array.put(obj);
}
return array;
}
public static JSONArray groupMemberListToJson(List<GroupMember> members) {
JSONArray array = new JSONArray();
for (GroupMember m : members) {
JSONObject obj = new JSONObject();
obj.put("group_id", m.getGroup_id().toString());
obj.put("user_id", m.getUser_id().toString());
obj.put("joined_at", m.getJoin_at().toString());
obj.put("role", m.getRole());
array.put(obj);
}
return array;
}
public static JSONArray channelSubscribeToJson(List<ChannelSubscribe> subscribes){
JSONArray array = new JSONArray();
for(ChannelSubscribe s :subscribes ){
JSONObject obj = new JSONObject();
obj.put("channel_id",s.getChannel_id().toString());
obj.put("user_id", s.getUser_id().toString());
obj.put("Subscribed_at", s.getJoin_at().toString());
}
return array;
}
public static JSONArray chatListToJson(List<ChatEntry> chatList) {
JSONArray jsonArray = new JSONArray();
for (ChatEntry entry : chatList) {
JSONObject obj = new JSONObject();
obj.put("id", entry.getId() != null ?entry.getId() :JSONObject.NULL);
obj.put("name", entry.getName());
obj.put("image_url", entry.getImageUrl() != null ?entry.getImageUrl() :JSONObject.NULL);
obj.put("type", entry.getType());
obj.put("last_message_time", entry.getLastMessageTime() != null ? entry.getLastMessageTime().toString() : JSONObject.NULL);
jsonArray.put(obj);
}
return jsonArray;
}
}
@@ -0,0 +1,32 @@
package org.to.telegramfinalproject.Models;
import java.util.List;
public class LoginBootstrapData {
private User user;
private List<Contact> contacts;
private List<Group> groups;
private List<Channel> channels;
private List<Message> unreadMessages;
public LoginBootstrapData(User user, List<Contact> contacts, List<Group> groups,
List<Channel> channels, List<Message> unreadMessages) {
this.user = user;
this.contacts = contacts;
this.groups = groups;
this.channels = channels;
this.unreadMessages = unreadMessages;
}
public void setUnreadMessages(List<Message> unreadMessages){this.unreadMessages = unreadMessages;}
public void setContacts(List<Contact> contacts){this.contacts =contacts;}
public void setGroups(List<Group> groups){this.groups = groups;}
public void setChannels(List<Channel> channels){this.channels = channels;}
public void setUser(User user){this.user = user;}
public List<Message> getUnreadMessages(){return unreadMessages;}
public List<Group> getGroups(){return groups;}
public List<Contact> getContacts(){return contacts;}
public List<Channel> getChannels(){return channels;}
public User getUser(User user){return user;}
}
@@ -1,12 +1,23 @@
package org.to.telegramfinalproject.Models; package org.to.telegramfinalproject.Models;
import org.json.JSONObject;
public class ResponseModel { public class ResponseModel {
private String status; private String status;
private String message; private String message;
private JSONObject data;
public ResponseModel(String status, String message) { public ResponseModel(String status, String message) {
this.status = status; this.status = status;
this.message = message; this.message = message;
this.data = null;
}
public ResponseModel(String status, String message, JSONObject data) {
this.status = status;
this.message = message;
this.data = data;
} }
public String getStatus() { public String getStatus() {
@@ -16,4 +27,6 @@ public class ResponseModel {
public String getMessage() { public String getMessage() {
return this.message; return this.message;
} }
public JSONObject getData() {return this.data;}
} }
@@ -1,5 +1,7 @@
package org.to.telegramfinalproject.Models; package org.to.telegramfinalproject.Models;
import org.json.JSONObject;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -17,6 +19,8 @@ public class User {
private List<Contact> contactList; private List<Contact> contactList;
private List<Group> groupList; private List<Group> groupList;
private List<Channel> channelList; private List<Channel> channelList;
private List<Message> unreadMessages;
private List<ChatEntry> chatList;
public User(String user_id, UUID internal_uuid, String username, String password, String profile_name) { public User(String user_id, UUID internal_uuid, String username, String password, String profile_name) {
this.user_id = user_id; this.user_id = user_id;
@@ -64,6 +68,10 @@ public class User {
public void setGroupList(List<Group> groupList){this.groupList = groupList;} public void setGroupList(List<Group> groupList){this.groupList = groupList;}
public void setUnreadMessages(List<Message> unreadMessages){this.unreadMessages = unreadMessages;}
public void setChatList(List<ChatEntry> chatList){this.chatList = chatList;}
public UUID getInternal_uuid() { public UUID getInternal_uuid() {
return this.internal_uuid; return this.internal_uuid;
} }
@@ -105,5 +113,13 @@ public class User {
public List<Channel> getChannelList(){return this.channelList;} public List<Channel> getChannelList(){return this.channelList;}
public List<Group> getGroupList(){return this.groupList;} public List<Group> getGroupList(){return this.groupList;}
public List<Message> getUnreadMessages(){return this.unreadMessages;}
public List<ChatEntry> getChatList(){return this.chatList;}
} }
@@ -41,6 +41,19 @@ public class AuthService {
return user; return user;
} }
} }
public boolean loginCheck(String username , String password){
User user = userDb.findByUsername(username);
String pass = user.getPassword();
password = PasswordHashing.hash(password);
String Username = user.getUsername();
boolean login = false;
if(user!= null && Username.equals(username)&& pass.equals(password)){
login = true;
}
return login;
}
} }
@@ -1,12 +1,16 @@
package org.to.telegramfinalproject.Server; package org.to.telegramfinalproject.Server;
import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.to.telegramfinalproject.Models.RequestModel; import org.to.telegramfinalproject.Database.*;
import org.to.telegramfinalproject.Models.ResponseModel; import org.to.telegramfinalproject.Models.*;
import org.to.telegramfinalproject.Models.User;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ClientHandler implements Runnable { public class ClientHandler implements Runnable {
private final Socket socket; private final Socket socket;
@@ -34,9 +38,11 @@ public class ClientHandler implements Runnable {
requestJson.optString("profile_name") requestJson.optString("profile_name")
); );
ResponseModel response; ResponseModel response = null;
switch (request.getAction()) { String action = requestJson.optString("action");
switch (action) {
case "register": case "register":
boolean registered = authService.register( boolean registered = authService.register(
request.getUser_id(), request.getUser_id(),
@@ -51,27 +57,159 @@ public class ClientHandler implements Runnable {
case "login": case "login":
User user = authService.login(request.getUsername(), request.getPassword()); User user = authService.login(request.getUsername(), request.getPassword());
response = (user != null) if (user == null) {
? new ResponseModel("success", "Welcome " + user.getProfile_name()) response = new ResponseModel("error", "Login failed.");
: new ResponseModel("error", "Login failed."); }
else {
SessionManager.addUser(user.getInternal_uuid(), this.socket);
userDatabase.updateUserStatus(user.getInternal_uuid(), "online");
List<Contact> contacts = ContactDatabase.getContacts(user.getInternal_uuid());
List<Group> groups = GroupDatabase.getGroupsByUser(user.getInternal_uuid());
List<Channel> channels = ChannelDatabase.getChannelsByUser(user.getInternal_uuid());
List<Message> unreadMessages = MessageDatabase.getUnreadMessages(user.getInternal_uuid());
user.setContactList(contacts);
user.setGroupList(groups);
user.setChannelList(channels);
user.setUnreadMessages(unreadMessages);
List<ChatEntry> chatList = new ArrayList<>();
for (Contact contact : contacts) {
User target = userDatabase.findByInternalUUID(contact.getContact_id());
if (target == null) continue;
LocalDateTime last = MessageDatabase.getLastMessageTimeBetween(user.getInternal_uuid(), target.getInternal_uuid(), "private");
chatList.add(new ChatEntry(
target.getUser_id(),
target.getProfile_name(),
target.getImage_url(),
"private",
last
));
}
for (Group group : groups) {
LocalDateTime last = MessageDatabase.getLastMessageTime(group.getInternal_uuid(), "group");
chatList.add(new ChatEntry(
group.getGroup_id(),
group.getGroup_name(),
group.getImage_url(),
"group",
last
));
}
for (Channel channel : channels) {
LocalDateTime last = MessageDatabase.getLastMessageTime(channel.getInternal_uuid(), "channel");
chatList.add(new ChatEntry(
channel.getChannel_id(),
channel.getChannel_name(),
channel.getImage_url(),
"channel",
last
));
}
chatList.sort((a, b) -> {
if (a.getLastMessageTime() == null) return 1;
if (b.getLastMessageTime() == null) return -1;
return b.getLastMessageTime().compareTo(a.getLastMessageTime());
});
JSONObject userData = JsonUtil.userToJson(user);
JSONArray chatListJson = JsonUtil.chatListToJson(chatList);
userData.put("chat_list", chatListJson);
response = new ResponseModel("success", "Welcome " + user.getProfile_name(), userData);
}
break; break;
case "logout": case "logout":
UUID uuid = UUID.fromString(request.getUser_id());
userDatabase.updateUserStatus(uuid, "offline");
userDatabase.updateLastSeen(uuid);
SessionManager.removeUser(uuid);
response = new ResponseModel("success", "Logged out."); response = new ResponseModel("success", "Logged out.");
break; break;
case "search":
String keyword = requestJson.optString("keyword");
List<JSONObject> results = new ArrayList<>();
// User search
List<User> users = new userDatabase().searchUsers(keyword);
for (User u : users) {
JSONObject obj = new JSONObject();
obj.put("type", "user");
obj.put("id", u.getUser_id());
obj.put("name", u.getProfile_name());
results.add(obj);
}
// Group search
List<Group> groups = GroupDatabase.searchGroups(keyword);
for (Group g : groups) {
JSONObject obj = new JSONObject();
obj.put("type", "group");
obj.put("id", g.getGroup_id());
obj.put("name", g.getGroup_name());
results.add(obj);
}
// Channel search
List<Channel> channels = ChannelDatabase.searchChannels(keyword);
for (Channel c : channels) {
JSONObject obj = new JSONObject();
obj.put("type", "channel");
obj.put("id", c.getChannel_id());
obj.put("name", c.getChannel_name());
results.add(obj);
}
JSONArray dataArray = new JSONArray(results);
JSONObject data = new JSONObject();
data.put("results", dataArray);
response = new ResponseModel("success", "Search results found", data);
break;
default: default:
response = new ResponseModel("error", "Unknown action: " + request.getAction()); response = new ResponseModel("error", "Unknown action: " + action);
} }
JSONObject responseJson = new JSONObject(); JSONObject responseJson = new JSONObject();
responseJson.put("status", response.getStatus()); responseJson.put("status", response.getStatus());
responseJson.put("message", response.getMessage()); responseJson.put("message", response.getMessage());
responseJson.put("data",response.getData() != null ?response.getData() :JSONObject.NULL);
out.println(responseJson.toString()); out.println(responseJson.toString());
} }
} catch (IOException e) { } catch(IOException e){
System.out.println("Connection with client lost."); System.out.println("Connection with client lost.");
UUID userId = SessionManager.getUserIdBySocket(this.socket);
if (userId != null) {
userDatabase.updateUserStatus(userId, "offline");
userDatabase.updateLastSeen(userId);
SessionManager.removeUser(userId);
} }
} }
}
} }
@@ -0,0 +1,35 @@
package org.to.telegramfinalproject.Server;
import java.net.Socket;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class SessionManager {
private static final Map<UUID, Socket> onlineUsers = new ConcurrentHashMap<>(); //catch UUID for find the user socket
public static void addUser(UUID userId, Socket socket) {
onlineUsers.put(userId, socket);
}
public static void removeUser(UUID userId) {
onlineUsers.remove(userId);
}
public static boolean isOnline(UUID userId) {
return onlineUsers.containsKey(userId);
}
public static Socket getUserSocket(UUID userId) {
return onlineUsers.get(userId);
}
public static UUID getUserIdBySocket(Socket socket) {
for (Map.Entry<UUID, Socket> entry : onlineUsers.entrySet()) {
if (entry.getValue().equals(socket)) {
return entry.getKey();
}
}
return null;
}
}