Create groups an channels

This commit is contained in:
2025-06-12 12:25:28 +03:30
parent fb19b55593
commit 2b972c4b7c
8 changed files with 547 additions and 23 deletions
@@ -94,6 +94,117 @@ public class ActionHandler {
}
private ChatEntry fetchChatInfo(String receiverId, String receiverType) {
JSONObject req = new JSONObject();
req.put("action", "get_chat_info");
req.put("receiver_id", receiverId);
req.put("receiver_type", receiverType);
out.println(req.toString());
try {
String responseText = in.readLine();
if (responseText != null) {
JSONObject response = new JSONObject(responseText);
if (response.getString("status").equals("success")) {
JSONObject data = response.getJSONObject("data");
return new ChatEntry(
receiverId,
data.getString("name"),
data.optString("image_url", ""),
receiverType,
null
);
}
}
} catch (Exception e) {
System.err.println("Error fetching chat info: " + e.getMessage());
}
return new ChatEntry(receiverId, "[Unknown " + receiverType + "]", "", receiverType, null);
}
private void refreshChatList() {
JSONObject req = new JSONObject();
req.put("action", "get_chat_list");
req.put("user_id", Session.currentUser.getString("user_id"));
out.println(req.toString());
try {
String responseText = in.readLine();
if (responseText != null) {
JSONObject response = new JSONObject(responseText);
if (response.getString("status").equals("success")) {
JSONArray chatListJson = response.getJSONObject("data").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.optString("image_url", ""),
chat.getString("type"),
chat.isNull("last_message_time")
? null
: LocalDateTime.parse(chat.getString("last_message_time"))
);
chatList.add(entry);
}
Session.chatList = chatList;
System.out.println("✅ Chat list updated.");
}
}
} catch (Exception e) {
System.err.println("❌ Failed to refresh chat list: " + e.getMessage());
}
}
public void createGroup() {
System.out.print("Enter group ID: ");
String groupId = scanner.nextLine();
System.out.print("Enter group name: ");
String groupName = scanner.nextLine();
System.out.print("Enter image URL (optional): ");
String imageUrl = scanner.nextLine();
JSONObject req = new JSONObject();
req.put("action", "create_group");
req.put("user_id", Session.getUserUUID());
req.put("group_id", groupId); // ID قابل نمایش
req.put("group_name", groupName);
req.put("image_url", imageUrl.isBlank() ? JSONObject.NULL : imageUrl);
send(req);
}
public void createChannel() {
System.out.print("Enter channel ID: ");
String channelId = scanner.nextLine();
System.out.print("Enter channel name: ");
String channelName = scanner.nextLine();
System.out.print("Enter image URL (optional): ");
String imageUrl = scanner.nextLine();
JSONObject req = new JSONObject();
req.put("action", "create_channel");
req.put("user_id", Session.getUserUUID());
req.put("channel_id", channelId);
req.put("channel_name", channelName);
req.put("image_url", imageUrl.isBlank() ? JSONObject.NULL : imageUrl);
send(req);
}
private void send(JSONObject request) {
try {
if (!request.has("action") || request.isNull("action")) {
@@ -129,7 +240,7 @@ public class ActionHandler {
ChatEntry entry = new ChatEntry(
chat.getString("id"),
chat.getString("name"),
chat.getString("image_url"),
chat.optString("image_url", ""),
chat.getString("type"),
chat.isNull("last_message_time") ? null :
LocalDateTime.parse(chat.getString("last_message_time"))
@@ -165,22 +276,86 @@ public class ActionHandler {
JSONObject selected = results.getJSONObject(index);
String type = selected.getString("type");
switch (type) {
case "user" -> {
UUID contactId = UUID.fromString(selected.getString("uuid")); // ✅ درست
addContact(contactId);
String userId = selected.getString("id");
String uuid = selected.getString("uuid");
ChatEntry existing = Session.chatList.stream()
.filter(c -> c.getId().equals(userId) && c.getType().equals("private"))
.findFirst()
.orElse(null);
if (existing != null) {
openChat(existing);
} else {
UUID contactId = UUID.fromString(uuid);
addContact(contactId);
ChatEntry newChat = fetchChatInfo(contactId.toString(), "private");
refreshChatList();
openChat(newChat);
}
}
case "group", "channel" -> {
joinGroupOrChannel(selected.getString("type"), selected.getString("uuid")); // ✅
String id = selected.getString("id");
String uuid = selected.getString("uuid");
ChatEntry existing = Session.chatList.stream()
.filter(c -> c.getId().equals(id) && c.getType().equals(type))
.findFirst()
.orElse(null);
if (existing != null) {
openChat(existing);
} else {
joinGroupOrChannel(type, uuid);
ChatEntry newChat = fetchChatInfo(uuid, type);
refreshChatList();
openChat(newChat);
}
}
case "message" -> {
String receiverId = selected.getString("receiver_id");
String receiverType = selected.getString("receiver_type");
ChatEntry chat = Session.chatList.stream()
.filter(c -> c.getId().equals(receiverId) && c.getType().equals(receiverType))
.findFirst()
.orElseGet(() -> fetchChatInfo(receiverId, receiverType));
openChat(chat);
}
default -> System.out.println("No interaction available for type: " + type);
}
}
break;
case "create_group":
case "create_channel":
if (status.equals("success") && response.has("data")) {
JSONObject chatJson = response.getJSONObject("data");
ChatEntry chat = new ChatEntry(
chatJson.getString("id"),
chatJson.getString("name"),
chatJson.optString("image_url", ""),
chatJson.getString("type"),
null
);
refreshChatList();
System.out.println("✅ Created and opening chat...");
openChat(chat);
}
break;
case "get_messages":
// Optional: handle later
@@ -200,16 +375,18 @@ public class ActionHandler {
System.out.println("\nUser Menu:");
System.out.println("1. Show chat list");
System.out.println("2. Search");
System.out.println("3. Add contact");
System.out.println("4. Logout");
System.out.println("3. Create Channel");
System.out.println("4. Create group");
System.out.println("5. Logout");
System.out.print("Choose an option: ");
String choice = scanner.nextLine();
switch (choice) {
case "1" -> showChatListAndSelect();
case "2" -> search();
case "3" -> addContact(internal_uuid);
case "4" -> {
case "3" -> createChannel();
case "4" -> createGroup();
case "5" -> {
logout();
return;
}
@@ -10,4 +10,12 @@ import java.util.List;
public class Session {
public static JSONObject currentUser;
public static List<ChatEntry> chatList;
public static String getUserUUID() {
if (currentUser.has("uuid")) return currentUser.getString("uuid");
if (currentUser.has("internal_uuid")) return currentUser.getString("internal_uuid");
if (currentUser.has("internalUUID")) return currentUser.getString("internalUUID");
throw new RuntimeException("❌ No UUID found in currentUser!");
}
}
@@ -7,6 +7,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -139,16 +140,7 @@ public class ChannelDatabase {
return false;
}
public static void addSubscriber(UUID channelInternalId, UUID userId) {
String sql = "INSERT INTO channel_subscribe (channel_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, channelInternalId);
stmt.setObject(2, userId);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static UUID findInternalUUIDByChannelId(String channelId) {
@@ -186,4 +178,78 @@ public class ChannelDatabase {
}
public static boolean createChannel(Channel channel, UUID creatorId) {
String sql = """
INSERT INTO channels (
internal_uuid, channel_id, channel_name,
creator_id, image_url, description, created_at
)
VALUES (gen_random_uuid(), ?, ?, ?, ?, ?, ?)
RETURNING internal_uuid
""";
String subscriberSql = """
INSERT INTO channel_subscribers (channel_id, user_id) VALUES (?, ?)
""";
try (Connection conn = ConnectionDb.connect()) {
// مرحله اول: ساخت کانال
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, channel.getChannel_id());
stmt.setString(2, channel.getChannel_name());
stmt.setObject(3, creatorId);
stmt.setString(4, channel.getImage_url());
stmt.setString(5, channel.getDescription());
stmt.setObject(6, channel.getCreated_at());
ResultSet rs = stmt.executeQuery();
if (!rs.next()) return false;
UUID internalUUID = (UUID) rs.getObject("internal_uuid");
channel.setInternal_uuid(internalUUID); // اختیاری برای پیگیری بعدی
// مرحله دوم: افزودن کاربر به لیست سابسکرایبرها
PreparedStatement subStmt = conn.prepareStatement(subscriberSql);
subStmt.setObject(1, internalUUID);
subStmt.setObject(2, creatorId);
subStmt.executeUpdate();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean insertChannel(UUID internalUUID, String channelId, String channelName, UUID creatorId, String imageUrl, LocalDateTime createdAt) {
String sql = "INSERT INTO channels (internal_uuid, channel_id, channel_name, creator_id, image_url, created_at) VALUES (?, ?, ?, ?, ?, ?)";
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, internalUUID);
stmt.setString(2, channelId);
stmt.setString(3, channelName);
stmt.setObject(4, creatorId);
stmt.setString(5, imageUrl);
stmt.setObject(6, createdAt);
stmt.executeUpdate();
return true;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static void addSubscriber(UUID channelId, UUID userId) {
String sql = "INSERT INTO channel_subscribers (channel_id, user_id) VALUES (?, ?) ON CONFLICT DO NOTHING";
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, channelId);
stmt.setObject(2, userId);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@@ -6,6 +6,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -182,5 +183,77 @@ public class GroupDatabase {
return false;
}
}
public static boolean createGroup(Group group, UUID creatorId) {
String sql = """
INSERT INTO groups (
internal_uuid, group_id, group_name,
creator_id, image_url, description, created_at
)
VALUES (gen_random_uuid(), ?, ?, ?, ?, ?, ?)
RETURNING internal_uuid
""";
String memberSql = """
INSERT INTO group_members (group_id, user_id) VALUES (?, ?)
""";
try (Connection conn = ConnectionDb.connect()) {
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, group.getGroup_id());
stmt.setString(2, group.getGroup_name());
stmt.setObject(3, creatorId);
stmt.setString(4, group.getImage_url());
stmt.setString(5, group.getDescription());
stmt.setObject(6, group.getCreated_at());
ResultSet rs = stmt.executeQuery();
if (!rs.next()) return false;
UUID internalUUID = (UUID) rs.getObject("internal_uuid");
group.setInternal_uuid(internalUUID);
PreparedStatement memberStmt = conn.prepareStatement(memberSql);
memberStmt.setObject(1, internalUUID);
memberStmt.setObject(2, creatorId);
memberStmt.executeUpdate();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static boolean insertGroup(UUID internalUUID, String groupId, String groupName, UUID creatorId, String imageUrl, LocalDateTime createdAt) {
String sql = "INSERT INTO groups (internal_uuid, group_id, group_name, creator_id, image_url, created_at) VALUES (?, ?, ?, ?, ?, ?)";
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, internalUUID);
stmt.setString(2, groupId);
stmt.setString(3, groupName);
stmt.setObject(4, creatorId);
stmt.setString(5, imageUrl);
stmt.setObject(6, createdAt);
stmt.executeUpdate();
return true;
} catch (SQLException e) {
e.printStackTrace();
return false;
}
}
public static void addMember(UUID groupId, UUID userId, String role) {
String sql = "INSERT INTO group_members (group_id, user_id, role) VALUES (?, ?, ?) ON CONFLICT DO NOTHING";
try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setObject(1, groupId);
stmt.setObject(2, userId);
stmt.setString(3, role);
stmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@@ -154,4 +154,16 @@ public class JsonUtil {
}
public static JSONObject chatToJson(ChatEntry chat) {
JSONObject obj = new JSONObject();
obj.put("id", chat.getId());
obj.put("name", chat.getName());
obj.put("image_url", chat.getImageUrl() != null ? chat.getImageUrl() : JSONObject.NULL);
obj.put("type", chat.getType());
obj.put("last_message_time", chat.getLastMessageTime() != null ? chat.getLastMessageTime().toString() : JSONObject.NULL);
return obj;
}
}
@@ -0,0 +1,23 @@
package org.to.telegramfinalproject.Server;
import org.to.telegramfinalproject.Models.Channel;
import org.to.telegramfinalproject.Database.ChannelDatabase;
import java.time.LocalDateTime;
import java.util.UUID;
public class ChannelService {
public static boolean createChannel(String channelId, String channelName, UUID creatorUUID, String imageUrl) {
UUID internalUUID = UUID.randomUUID();
LocalDateTime now = LocalDateTime.now();
boolean inserted = ChannelDatabase.insertChannel(internalUUID, channelId, channelName, creatorUUID, imageUrl, now);
if (inserted) {
ChannelDatabase.addSubscriber(internalUUID, creatorUUID); // اضافه کردن سازنده
return true;
}
return false;
}
}
@@ -141,7 +141,7 @@ public class ClientHandler implements Runnable {
JSONObject obj = new JSONObject();
obj.put("type", "user");
obj.put("id", u.getUser_id());
obj.put("uuid", u.getInternal_uuid().toString()); // ✅ اضافه شود
obj.put("uuid", u.getInternal_uuid().toString());
obj.put("name", u.getProfile_name());
results.add(obj);
}
@@ -149,8 +149,8 @@ public class ClientHandler implements Runnable {
for (Group g : GroupDatabase.searchGroups(keyword)) {
JSONObject obj = new JSONObject();
obj.put("type", "group");
obj.put("id", g.getGroup_id()); // قابل نمایش
obj.put("uuid", g.getInternal_uuid().toString()); // برای عملیات
obj.put("id", g.getGroup_id());
obj.put("uuid", g.getInternal_uuid().toString());
obj.put("name", g.getGroup_name());
results.add(obj);
}
@@ -158,8 +158,8 @@ public class ClientHandler implements Runnable {
for (Channel c : ChannelDatabase.searchChannels(keyword)) {
JSONObject obj = new JSONObject();
obj.put("type", "channel");
obj.put("id", c.getChannel_id()); // قابل نمایش
obj.put("uuid", c.getInternal_uuid().toString()); // برای عملیات
obj.put("id", c.getChannel_id());
obj.put("uuid", c.getInternal_uuid().toString());
obj.put("name", c.getChannel_name());
results.add(obj);
}
@@ -171,6 +171,8 @@ public class ClientHandler implements Runnable {
obj.put("content", m.getContent());
obj.put("sender", m.getSender_id().toString());
obj.put("time", m.getSend_at().toString());
obj.put("receiver_id", m.getReceiver_id().toString());
obj.put("receiver_type", m.getReceiver_type());
results.add(obj);
}
@@ -248,6 +250,146 @@ public class ClientHandler implements Runnable {
}
case "get_chat_info": {
String id = requestJson.getString("receiver_id");
String type = requestJson.getString("receiver_type");
JSONObject data = new JSONObject();
switch (type) {
case "private" -> {
User u = new userDatabase().findByUserId(id);
if (u != null) {
data.put("name", u.getProfile_name());
data.put("image_url", u.getImage_url());
} else {
response = new ResponseModel("error", "User not found.");
break;
}
}
case "group" -> {
Group g = GroupDatabase.findByGroupId(id);
if (g != null) {
data.put("name", g.getGroup_name());
data.put("image_url", g.getImage_url());
} else {
response = new ResponseModel("error", "Group not found.");
break;
}
}
case "channel" -> {
Channel c = ChannelDatabase.findByChannelId(id);
if (c != null) {
data.put("name", c.getChannel_name());
data.put("image_url", c.getImage_url());
} else {
response = new ResponseModel("error", "Channel not found.");
break;
}
}
default -> {
response = new ResponseModel("error", "Unknown type.");
break;
}
}
if (data.has("name")) {
response = new ResponseModel("success", "Chat info fetched", data);
}
break;
}
case "get_chat_list": {
String userIdStr = requestJson.getString("user_id");
User user = new userDatabase().findByUserId(userIdStr);
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<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 data = new JSONObject();
data.put("chat_list", JsonUtil.chatListToJson(chatList));
response = new ResponseModel("success", "Chat list updated.", data);
break;
}
case "create_group": {
try {
String groupId = requestJson.getString("group_id"); // ID نمایشی
String groupName = requestJson.getString("group_name");
String userIdStr = requestJson.getString("user_id");
String imageUrl = requestJson.optString("image_url", null);
UUID creatorUUID = UUID.fromString(userIdStr);
boolean created = GroupService.createGroup(groupId, groupName, creatorUUID, imageUrl);
response = created
? new ResponseModel("success", "Group created.")
: new ResponseModel("error", "Group creation failed.");
} catch (Exception e) {
response = new ResponseModel("error", "Error creating group: " + e.getMessage());
}
break;
}
case "create_channel": {
try {
String channelId = requestJson.getString("channel_id");
String channelName = requestJson.getString("channel_name");
String userIdStr = requestJson.getString("user_id");
String imageUrl = requestJson.optString("image_url", null);
UUID creatorUUID = UUID.fromString(userIdStr);
boolean created = ChannelService.createChannel(channelId, channelName, creatorUUID, imageUrl);
response = created
? new ResponseModel("success", "Channel created.")
: new ResponseModel("error", "Channel creation failed.");
} catch (Exception e) {
response = new ResponseModel("error", "Error creating channel: " + e.getMessage());
}
break;
}
default:
response = new ResponseModel("error", "Unknown action: " + action);
}
@@ -0,0 +1,23 @@
package org.to.telegramfinalproject.Server;
import org.to.telegramfinalproject.Models.Group;
import org.to.telegramfinalproject.Database.GroupDatabase;
import java.time.LocalDateTime;
import java.util.UUID;
public class GroupService {
public static boolean createGroup(String groupId, String groupName, UUID creatorUUID, String imageUrl) {
UUID internalUUID = UUID.randomUUID();
LocalDateTime now = LocalDateTime.now();
boolean inserted = GroupDatabase.insertGroup(internalUUID, groupId, groupName, creatorUUID, imageUrl, now);
if (inserted) {
GroupDatabase.addMember(internalUUID, creatorUUID, "owner"); // سازنده owner می‌شود
return true;
}
return false;
}
}