Merge branch 'develop' into SendMessage

# Conflicts:
#	src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java
#	src/main/java/org/to/telegramfinalproject/Database/PrivateChatDatabase.java
This commit is contained in:
2025-08-15 00:53:46 +03:30
43 changed files with 1533 additions and 524 deletions
@@ -148,7 +148,7 @@ public class ActionHandler {
req.put("keyword", keyword);
req.put("user_id", Session.currentUser.getString("user_id"));
req.put("entity_id", entityId.toString());
req.put("entity_type", entityType); // group یا channel
req.put("entity_type", entityType); // group or channel
JSONObject res = sendWithResponse(req);
@@ -336,6 +336,7 @@ public class ActionHandler {
if (chat.has("other_user_id") && !chat.isNull("other_user_id")) {
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
}
entry.setSavedMessages(chat.optBoolean("is_saved_messages", false));
return entry;
}
@@ -482,80 +483,96 @@ public class ActionHandler {
JSONArray Archived = Session.currentUser.getJSONArray("archived_chat_list");
JSONArray Active = Session.currentUser.getJSONArray("active_chat_list");
JSONArray contactList = Session.currentUser.getJSONArray("contact_list");
// List<ChatEntry> chatList = new ArrayList<>();
//
//
// for (Object obj : chatListJson) {
// JSONObject chat = (JSONObject) obj;
//
// ChatEntry entry = new ChatEntry(
// UUID.fromString(chat.getString("internal_id")),
// chat.getString("id"),
// chat.getString("name"),
// chat.optString("image_url", ""),
// chat.getString("type"),
// chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
// chat.optBoolean("is_owner", false),
// chat.optBoolean("is_admin", false)
//
//
// );
//
// if (chat.has("other_user_id")) {
// entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
// }
//
// if (chat.has("is_saved_messages")) {
// entry.setSavedMessages(chat.getBoolean("is_saved_messages"));
// }
//
// chatList.add(entry);
//
// }
//
// List<ChatEntry> archivedChats = new ArrayList<>();
// for (Object obj : Archived){
// JSONObject chat = (JSONObject) obj;
//
// ChatEntry entry = new ChatEntry(
// UUID.fromString(chat.getString("internal_id")),
// chat.getString("id"),
// chat.getString("name"),
// chat.optString("image_url", ""),
// chat.getString("type"),
// chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
// chat.optBoolean("is_owner", false),
// chat.optBoolean("is_admin", false)
// );
// if (chat.has("other_user_id")) {
// entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
// }
// archivedChats.add(entry);
//
// }
//
// List<ChatEntry> activeChats = new ArrayList<>();
// for (Object obj : Active){
// JSONObject chat = (JSONObject) obj;
//
// ChatEntry entry = new ChatEntry(
// UUID.fromString(chat.getString("internal_id")),
// chat.getString("id"),
// chat.getString("name"),
// chat.optString("image_url", ""),
// chat.getString("type"),
// chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
// chat.optBoolean("is_owner", false),
// chat.optBoolean("is_admin", false)
// );
// if (chat.has("other_user_id")) {
// entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
// }
//
// activeChats.add(entry);
//
// }
List<ChatEntry> chatList = new ArrayList<>();
for (Object obj : chatListJson) {
JSONObject chat = (JSONObject) obj;
ChatEntry entry = new ChatEntry(
UUID.fromString(chat.getString("internal_id")),
chat.getString("id"),
chat.getString("name"),
chat.optString("image_url", ""),
chat.getString("type"),
chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
chat.optBoolean("is_owner", false),
chat.optBoolean("is_admin", false)
);
if (chat.has("other_user_id")) {
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
}
if (chat.has("is_saved_messages")) {
entry.setSavedMessages(chat.getBoolean("is_saved_messages"));
}
chatList.add(entry);
chatList.add(parseChatEntry((JSONObject) obj));
}
List<ChatEntry> archivedChats = new ArrayList<>();
for (Object obj : Archived){
JSONObject chat = (JSONObject) obj;
ChatEntry entry = new ChatEntry(
UUID.fromString(chat.getString("internal_id")),
chat.getString("id"),
chat.getString("name"),
chat.optString("image_url", ""),
chat.getString("type"),
chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
chat.optBoolean("is_owner", false),
chat.optBoolean("is_admin", false)
);
if (chat.has("other_user_id")) {
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
}
archivedChats.add(entry);
for (Object obj : Archived) {
archivedChats.add(parseChatEntry((JSONObject) obj));
}
List<ChatEntry> activeChats = new ArrayList<>();
for (Object obj : Active){
JSONObject chat = (JSONObject) obj;
ChatEntry entry = new ChatEntry(
UUID.fromString(chat.getString("internal_id")),
chat.getString("id"),
chat.getString("name"),
chat.optString("image_url", ""),
chat.getString("type"),
chat.isNull("last_message_time") ? null : LocalDateTime.parse(chat.getString("last_message_time")),
chat.optBoolean("is_owner", false),
chat.optBoolean("is_admin", false)
);
if (chat.has("other_user_id")) {
entry.setOtherUserId(UUID.fromString(chat.getString("other_user_id")));
}
activeChats.add(entry);
for (Object obj : Active) {
activeChats.add(parseChatEntry((JSONObject) obj));
}
Session.contactEntries.clear();
for (Object obj : contactList) {
JSONObject c = (JSONObject) obj;
@@ -832,6 +849,8 @@ public class ActionHandler {
}
public void userMenu(UUID internal_uuid) throws IOException {
while (true) {
@@ -1133,86 +1152,158 @@ public class ActionHandler {
// }
// public void showChatListAndSelect() {
// if (Session.activeChats == null || Session.activeChats.isEmpty()) {
// System.out.println("No active chats.");
// return;
// }
//
// System.out.println("\nYour Chats:");
// System.out.println("0. 📦 Archived Chats");
//
// // Track index dynamically
// int index = 1;
//
// // Check if Saved Messages exists in the list
// int savedMessagesIndex = -1;
// for (int i = 0; i < Session.activeChats.size(); i++) {
// ChatEntry entry = Session.activeChats.get(i);
//
// if (entry.isSavedMessages()) {
// savedMessagesIndex = index;
// System.out.println(index + ". 📦 Saved Messages Chat");
// index++;
// break;
// }
// }
//
// // Print the rest of the chats
// for (int i = 0; i < Session.activeChats.size(); i++) {
// ChatEntry entry = Session.activeChats.get(i);
// if (entry.isSavedMessages()) {
// continue; // Already printed above
// }
//
// String time = (entry.getLastMessageTime() == null)
// ? "No messages yet"
// : entry.getLastMessageTime().toString();
// System.out.println(index + ". [" + entry.getType() + "] " +
// entry.getName() + " - Last: " + time);
// index++;
// }
//
// System.out.print("Select a chat by number: ");
// int choice = Integer.parseInt(scanner.nextLine());
//
// if (choice == 0) {
// showArchivedChats();
// return;
// }
//
// if (choice == savedMessagesIndex) {
// new SidebarHandler(scanner, this).getSavedMessagesData(Session.getUserUUID());
// return;
// }
//
// // Adjust for Saved Messages if it was in the list
// int baseIndex = (savedMessagesIndex != -1 && choice > savedMessagesIndex) ? 1 : 0;
// int chatIndex = choice - 1 - baseIndex;
//
// if (chatIndex < 0 || chatIndex >= Session.activeChats.size()) {
// System.out.println("Invalid selection.");
// return;
// }
//
// // Find the actual index of the chat, skipping the saved_messages entry
// int actualIndex = 0;
// for (int i = 0; i < Session.activeChats.size(); i++) {
// if (Session.activeChats.get(i).getType().equalsIgnoreCase("saved_messages")) {
// continue; // Skip saved_messages
// }
// if (actualIndex == chatIndex) {
// break;
// }
// actualIndex++;
// }
//
// ChatEntry selected = Session.activeChats.get(actualIndex);
// openChat(selected);
// }
public void showChatListAndSelect() {
if (Session.activeChats == null || Session.activeChats.isEmpty()) {
System.out.println("No active chats.");
System.out.println("📭 You have no chats.");
return;
}
System.out.println("\nYour Chats:");
System.out.println("0. 📦 Archived Chats");
// Track index dynamically
int index = 1;
// Check if Saved Messages exists in the list
int savedMessagesIndex = -1;
// پیدا کردن Saved در لیست
Integer savedIdxInActive = null;
for (int i = 0; i < Session.activeChats.size(); i++) {
ChatEntry entry = Session.activeChats.get(i);
if (entry.isSavedMessages()) {
savedMessagesIndex = index;
System.out.println(index + ". 📦 Saved Messages Chat");
index++;
if (Session.activeChats.get(i).isSavedMessages()) {
savedIdxInActive = i;
break;
}
}
// Print the rest of the chats
for (int i = 0; i < Session.activeChats.size(); i++) {
ChatEntry entry = Session.activeChats.get(i);
if (entry.isSavedMessages()) {
continue; // Already printed above
}
// مپ شمارهٔ نمایش → ایندکس واقعی در activeChats
Map<Integer, Integer> displayToActive = new HashMap<>();
String time = (entry.getLastMessageTime() == null)
? "No messages yet"
: entry.getLastMessageTime().toString();
System.out.println(index + ". [" + entry.getType() + "] " +
entry.getName() + " - Last: " + time);
index++;
int displayIndex = 1;
// چاپ Saved (در صورت وجود) و ثبت در مپ
if (savedIdxInActive != null) {
ChatEntry saved = Session.activeChats.get(savedIdxInActive);
String last = (saved.getLastMessageTime() == null) ? "No messages yet" : saved.getLastMessageTime().toString();
System.out.println(displayIndex + ". 💾 Saved Messages - Last: " + last);
// نکتهٔ کلیدی: مپ کن تا مثل بقیه با openChat باز شود
displayToActive.put(displayIndex, savedIdxInActive);
displayIndex++;
}
// چاپ بقیهٔ چت‌ها + ثبت مپ
for (int i = 0; i < Session.activeChats.size(); i++) {
if (savedIdxInActive != null && i == savedIdxInActive) continue;
ChatEntry e = Session.activeChats.get(i);
String last = (e.getLastMessageTime() == null) ? "No messages yet" : e.getLastMessageTime().toString();
System.out.println(displayIndex + ". [" + e.getType() + "] " + e.getName() + " - Last: " + last);
displayToActive.put(displayIndex, i);
displayIndex++;
}
// انتخاب
System.out.print("Select a chat by number: ");
int choice = Integer.parseInt(scanner.nextLine());
int choice;
try {
choice = Integer.parseInt(scanner.nextLine().trim());
} catch (Exception e) {
System.out.println("❌ Invalid selection.");
return;
}
if (choice == 0) {
showArchivedChats();
return;
}
if (choice == savedMessagesIndex) {
new SidebarHandler(scanner, this).getSavedMessagesData(Session.getUserUUID());
Integer activeIdx = displayToActive.get(choice);
if (activeIdx == null) {
System.out.println("❌ Invalid selection.");
return;
}
// Adjust for Saved Messages if it was in the list
int baseIndex = (savedMessagesIndex != -1 && choice > savedMessagesIndex) ? 1 : 0;
int chatIndex = choice - 1 - baseIndex;
if (chatIndex < 0 || chatIndex >= Session.activeChats.size()) {
System.out.println("Invalid selection.");
return;
}
// Find the actual index of the chat, skipping the saved_messages entry
int actualIndex = 0;
for (int i = 0; i < Session.activeChats.size(); i++) {
if (Session.activeChats.get(i).getType().equalsIgnoreCase("saved_messages")) {
continue; // Skip saved_messages
}
if (actualIndex == chatIndex) {
break;
}
actualIndex++;
}
ChatEntry selected = Session.activeChats.get(actualIndex);
openChat(selected);
ChatEntry selected = Session.activeChats.get(activeIdx);
openChat(selected); // برای Saved هم همین مسیر اجرا می‌شود
}
private void showArchivedChats() {
if (Session.archivedChats == null || Session.archivedChats.isEmpty()) {
System.out.println("📭 No archived chats.");
@@ -1373,6 +1464,116 @@ public class ActionHandler {
//
// private boolean showPrivateChatMenu(ChatEntry chat) {
//
// if (forceExitChat) {
// forceExitChat = false;
// System.out.println("🚪 Exiting chat due to real-time update.");
// return false;
// }
//
//
// JSONObject reqTarget = new JSONObject();
// reqTarget.put("action", "get_private_chat_target");
// reqTarget.put("chat_id", chat.getId());
//
// JSONObject resTarget = sendWithResponse(reqTarget);
// if (resTarget == null || !resTarget.getString("status").equals("success")) {
// System.out.println("❌ Failed to fetch target user for private chat.");
// return false;
// }
//
// String otherUserId = resTarget.getJSONObject("data").getString("target_id");
// chat.setOtherUserId(UUID.fromString(otherUserId));
//
//
// JSONObject req = new JSONObject();
// req.put("action", "view_profile");
// req.put("target_id", chat.getOtherUserId()); // internal UUID
//
// JSONObject res = sendWithResponse(req);
//
// if (res.getString("status").equals("success")) {
// JSONObject data = res.getJSONObject("data");
//
// System.out.println("\n💬 Private Chat with: " + data.getString("profile_name"));
//
// if (data.getBoolean("is_online")) {
// System.out.println("✅ Status: Online");
// } else {
// System.out.println("📅 Last seen: " + data.getString("last_seen"));
// }
//
// System.out.println("────────────────────────────────────────────");
// }
//
//
// System.out.println("1. Send message");
// System.out.println("2. Block/Unblock");
// System.out.println("3. Delete chat (one-sided)");
// System.out.println("4. Delete chat (both sides)");
// System.out.println("5. View profile");
// System.out.println("6. Archive/Unarchived");
// System.out.println("7. View messages");
// System.out.println("8. Back");
//
//
// String input = scanner.nextLine();
// switch (input) {
// case "1" -> sendMessage(chat.getId(), "private");
// case "2" -> toggleBlock(chat.getOtherUserId());
// case "3" -> {
// deleteChat(chat.getId(), false);
// return true;
// }
// case "4" -> {
// deleteChat(chat.getId(), true);
// return true;
// }
// case "5" -> {
// JSONObject reqProfile = new JSONObject();
// reqProfile.put("action", "view_profile");
// reqProfile.put("target_id", chat.getOtherUserId());
//
// JSONObject resProfile = sendWithResponse(reqProfile);
//
// if (resProfile.getString("status").equals("success")) {
// JSONObject profile = resProfile.getJSONObject("data");
// System.out.println("\n👤 Profile Info:");
// System.out.println("🔷 Name: " + profile.optString("profile_name", "Unknown"));
// System.out.println("📄 Bio: " + profile.optString("bio", "No bio set"));
// System.out.println("🖼️ Image URL: " + profile.optString("image_url", "N/A"));
//
// if (profile.getBoolean("is_online")) {
// System.out.println("✅ Status: Online");
// } else {
// System.out.println("📅 Last seen: " + profile.optString("last_seen", "Unknown"));
// }
// System.out.println("────────────────────────────────────────────");
// } else {
// System.out.println("❌ Could not load profile.");
// }
// return true;
// }
//
// case "6" ->{
// toggleArchive(chat.getId() , "private");
//
// return true;
// }
// case "7" ->{
// viewMessagesInChat(chat);
// }
//
// case "8" -> {
// return false;
// }
// default -> System.out.println("Invalid choice.");
// }
// return true;
// }
private boolean showPrivateChatMenu(ChatEntry chat) {
@@ -1382,41 +1583,57 @@ public class ActionHandler {
return false;
}
UUID chatId = chat.getId();
JSONObject reqTarget = new JSONObject();
reqTarget.put("action", "get_private_chat_target");
reqTarget.put("chat_id", chat.getId());
if (chat.getOtherUserId() == null) {
JSONObject reqTarget = new JSONObject()
.put("action", "get_private_chat_target")
.put("chat_id", chatId.toString());
JSONObject resTarget = sendWithResponse(reqTarget);
if (resTarget == null || !resTarget.getString("status").equals("success")) {
if (resTarget == null || !"success".equals(resTarget.optString("status"))) {
System.out.println("❌ Failed to fetch target user for private chat.");
return false;
}
String otherUserId = resTarget.getJSONObject("data").getString("target_id");
chat.setOtherUserId(UUID.fromString(otherUserId));
JSONObject req = new JSONObject();
req.put("action", "view_profile");
req.put("target_id", chat.getOtherUserId()); // internal UUID
JSONObject res = sendWithResponse(req);
if (res.getString("status").equals("success")) {
JSONObject data = res.getJSONObject("data");
System.out.println("\n💬 Private Chat with: " + data.getString("profile_name"));
if (data.getBoolean("is_online")) {
System.out.println("✅ Status: Online");
} else {
System.out.println("📅 Last seen: " + data.getString("last_seen"));
}
System.out.println("────────────────────────────────────────────");
chat.setOtherUserId(UUID.fromString(resTarget.getJSONObject("data").getString("target_id")));
}
UUID me = UUID.fromString(Session.currentUser.getString("internal_uuid"));
boolean isSaved = chat.isSavedMessages() || me.equals(chat.getOtherUserId());
System.out.println();
if (isSaved) {
System.out.println("💬 Private Chat with: Saved Messages");
System.out.println("────────────────────────────────────────────");
} else {
JSONObject req = new JSONObject()
.put("action", "view_profile")
.put("target_id", chat.getOtherUserId());
JSONObject res = sendWithResponse(req);
if (res != null && "success".equals(res.optString("status"))) {
JSONObject data = res.getJSONObject("data");
System.out.println("\n💬 Private Chat with: " + data.optString("profile_name", chat.getName()));
if (data.optBoolean("is_online", false)) System.out.println("✅ Status: Online");
else System.out.println("📅 Last seen: " + data.optString("last_seen", "Unknown"));
System.out.println("────────────────────────────────────────────");
}
}
if (isSaved) {
System.out.println("1. Send message");
System.out.println("2. View messages");
System.out.println("3. Back");
String input = scanner.nextLine().trim();
switch (input) {
case "1" -> sendMessage(chatId, "private");
case "2" -> { viewMessagesInChat(chat); }
case "3" -> { return false; }
default -> System.out.println("Invalid choice.");
}
return true;
}
System.out.println("1. Send message");
System.out.println("2. Block/Unblock");
@@ -1427,57 +1644,35 @@ public class ActionHandler {
System.out.println("7. View messages");
System.out.println("8. Back");
String input = scanner.nextLine();
String input = scanner.nextLine().trim();
switch (input) {
case "1" -> sendMessageInteractive(chat.getId(), "private");
case "2" -> toggleBlock(chat.getOtherUserId());
case "3" -> {
deleteChat(chat.getId(), false);
return true;
}
case "4" -> {
deleteChat(chat.getId(), true);
return true;
}
case "3" -> { deleteChat(chatId, false); return true; }
case "4" -> { deleteChat(chatId, true); return true; }
case "5" -> {
JSONObject reqProfile = new JSONObject();
reqProfile.put("action", "view_profile");
reqProfile.put("target_id", chat.getOtherUserId());
JSONObject reqProfile = new JSONObject()
.put("action", "view_profile")
.put("target_id", chat.getOtherUserId());
JSONObject resProfile = sendWithResponse(reqProfile);
if (resProfile.getString("status").equals("success")) {
if (resProfile != null && "success".equals(resProfile.optString("status"))) {
JSONObject profile = resProfile.getJSONObject("data");
System.out.println("\n👤 Profile Info:");
System.out.println("🔷 Name: " + profile.optString("profile_name", "Unknown"));
System.out.println("📄 Bio: " + profile.optString("bio", "No bio set"));
System.out.println("🖼️ Image URL: " + profile.optString("image_url", "N/A"));
if (profile.getBoolean("is_online")) {
System.out.println("✅ Status: Online");
} else {
System.out.println("📅 Last seen: " + profile.optString("last_seen", "Unknown"));
}
if (profile.optBoolean("is_online", false)) System.out.println("✅ Status: Online");
else System.out.println("📅 Last seen: " + profile.optString("last_seen", "Unknown"));
System.out.println("────────────────────────────────────────────");
} else {
System.out.println("❌ Could not load profile.");
}
return true;
}
case "6" ->{
toggleArchive(chat.getId() , "private");
return true;
}
case "7" ->{
viewMessagesInChat(chat);
}
case "8" -> {
return false;
}
case "6" -> { toggleArchive(chatId, "private"); return true; }
case "7" -> { viewMessagesInChat(chat); break; }
case "8" -> { return false; }
default -> System.out.println("Invalid choice.");
}
return true;
@@ -3447,7 +3642,8 @@ public class ActionHandler {
// }
// }
//
// // 🔹 فقط ارسال پیام با chat_id و receiver_type
//
//
// JSONObject messageJson = new JSONObject();
// messageJson.put("action", "send_message");
// messageJson.put("receiver_type", receiverType);
@@ -3568,9 +3764,13 @@ public class ActionHandler {
ContactEntry entry = new ContactEntry(
UUID.fromString(c.getString("contact_id")),
c.getString("user_id"),
c.getString("contact_displayId"),
c.getString("profile_name"),
c.optString("image_url", ""),
c.optBoolean("is_blocked", false)
c.optBoolean("is_blocked", false),
c.isNull("last_seen")
? null
: LocalDateTime.parse(c.getString("last_seen"))
);
contactList.add(entry);
@@ -4,6 +4,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.to.telegramfinalproject.Models.ChatEntry;
import org.to.telegramfinalproject.Models.FileAttachment;
import org.to.telegramfinalproject.Models.Message;
import java.time.LocalDateTime;
@@ -13,9 +14,16 @@ import static org.to.telegramfinalproject.Client.ActionHandler.sendWithResponse;
public class SidebarHandler {
private final Scanner scanner;
private final ActionHandler actionHandler;
private ActionHandler actionHandler ;
private final String userUUID;
public SidebarHandler(Scanner scanner, ActionHandler actionHandler, ActionHandler action) {
this.scanner = scanner;
this.actionHandler = action;
this.userUUID = Session.getUserUUID();
}
public SidebarHandler(Scanner scanner, ActionHandler actionHandler) {
this.scanner = scanner;
this.actionHandler = actionHandler;
@@ -38,7 +46,7 @@ public class SidebarHandler {
showContacts();
break;
case SAVED_MESSAGES:
getSavedMessagesData(userUUID);
ensureSavedMessagesCreated();
break;
case SETTINGS:
openSettings();
@@ -247,212 +255,312 @@ public class SidebarHandler {
actionHandler.showContactList();
}
public void getSavedMessagesData(String userId) {
try {
// Step 1: Create the request
JSONObject request = new JSONObject();
request.put("action", "get_saved_messages");
request.put("user_id", userId);
// Step 2: Send request and wait for response
JSONObject response = ActionHandler.sendWithResponse(request);
// Step 3: Check the response
if (!response.optString("status", "fail").equals("success")) {
System.out.println("Failed to open Saved Messages chat: " + response.optString("message", "Unknown error"));
return;
public void ensureSavedMessagesCreated() {
// 1) Client-side quick check
boolean alreadyExists = false;
if (Session.activeChats != null) {
for (ChatEntry e : Session.activeChats) {
if (e != null && e.isSavedMessages()) { alreadyExists = true; break; }
}
// Step 4: Extract "data" object
JSONObject data = response.getJSONObject("data");
UUID chatId = UUID.fromString(data.getString("chat_id"));
JSONArray messagesArray = data.getJSONArray("messages");
// Step 5: Parse messages
List<Message> messages = new ArrayList<>();
if (!messagesArray.isEmpty()) {
for (int i = 0; i < messagesArray.length(); i++) {
JSONObject msgJson = messagesArray.getJSONObject(i);
// Safely extract optional UUIDs
UUID replyToId = null;
String replyToIdStr = msgJson.optString("reply_to_id", null);
if (replyToIdStr != null && !replyToIdStr.equals("null")) {
replyToId = UUID.fromString(replyToIdStr);
}
UUID originalMessageId = null;
String originalMessageIdStr = msgJson.optString("original_message_id", null);
if (originalMessageIdStr != null && !originalMessageIdStr.equals("null")) {
originalMessageId = UUID.fromString(originalMessageIdStr);
}
UUID forwardedBy = null;
String forwardedByStr = msgJson.optString("forwarded_by", null);
if (forwardedByStr != null && !forwardedByStr.equals("null")) {
forwardedBy = UUID.fromString(forwardedByStr);
}
UUID forwardedFrom = null;
String forwardedFromStr = msgJson.optString("forwarded_from", null);
if (forwardedFromStr != null && !forwardedFromStr.equals("null")) {
forwardedFrom = UUID.fromString(forwardedFromStr);
}
Message msg = new Message(
UUID.fromString(msgJson.getString("message_id")),
UUID.fromString(msgJson.getString("sender_id")),
msgJson.getString("receiver_type"),
UUID.fromString(msgJson.getString("receiver_id")),
msgJson.getString("content"),
msgJson.getString("message_type"),
LocalDateTime.parse(msgJson.getString("send_at").replace(" ", "T")),
msgJson.getString("status"),
replyToId,
msgJson.getBoolean("is_edited"),
originalMessageId,
forwardedBy,
forwardedFrom,
msgJson.getBoolean("is_deleted_globally"),
LocalDateTime.parse(msgJson.getString("edited_at").replace(" ", "T"))
);
messages.add(msg);
}
}
// Step 6: Add to active chats if not already present
boolean alreadyExists = Session.activeChats.stream()
.anyMatch(entry -> entry.getId().equals(chatId));
if (!alreadyExists) {
ChatEntry savedEntry = new ChatEntry(
chatId,
"Saved-Messages",
"Saved Messages",
"📌", // or use a URL string if you have an icon for saved messages
"private",
messages.isEmpty() ? null : messages.get(messages.size() - 1).getSend_at()
);
savedEntry.setSavedMessages(true);
Session.activeChats.add(savedEntry);
}
// Step 7: Show chat
showSavedMessages(chatId, messages);
} catch (Exception e) {
System.out.println("An error occurred while retrieving Saved Messages.");
e.printStackTrace();
}
}
if (alreadyExists) {
System.out.println("️ Saved Messages already exists.");
return;
}
private void showSavedMessages(UUID chatId, List<Message> messages) {
Scanner scanner = new Scanner(System.in);
// 2) Ask server to get or create the self-chat
JSONObject res = sendWithResponse(new JSONObject().put("action", "get_or_create_saved_messages"));
if (res == null || !"success".equals(res.optString("status"))) {
System.out.println("❌ Failed to create/open Saved Messages: " + (res != null ? res.optString("message","") : ""));
return;
}
System.out.println("==== Saved Messages ====");
JSONObject data = res.optJSONObject("data");
if (data == null) {
System.out.println("❌ Invalid server response for Saved Messages.");
return;
}
// Show previous messages
if (messages.isEmpty()) {
System.out.println("No messages yet.");
boolean created = data.optBoolean("created", true); // if server sends it
String chatId = data.optString("chat_id", null);
if (created) {
System.out.println("✅ Saved Messages created." + (chatId != null ? " chat_id=" + chatId : ""));
try {
UUID cid = UUID.fromString(chatId);
ChatEntry saved = new ChatEntry(cid, "Saved Messages", "Saved Messages", "", "private", null, true, false);
saved.setSavedMessages(true);
if (Session.activeChats == null) Session.activeChats = new ArrayList<>();
Session.activeChats.add(0, saved);
} catch (Exception ignore) {}
} else {
for (Message msg : messages) {
System.out.println("[" + msg.getSend_at() + "] " + msg.getContent());
}
}
System.out.println("\n(Type your message below, or type 0 to exit)");
while (true) {
System.out.print("You: ");
String content = scanner.nextLine().trim();
if (content.equals("0")) {
System.out.println("Exiting Saved Messages.");
break;
}
System.out.print("Enter message type (TEXT / IMAGE / VIDEO / FILE / AUDIO): ");
String messageType = scanner.nextLine().toUpperCase();
Set<String> allowedTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE");
while (!allowedTypes.contains(messageType)) {
System.out.print("Invalid type. Try again (TEXT / IMAGE / VIDEO / FILE / AUDIO): ");
messageType = scanner.nextLine().toUpperCase();
}
// Attaching Files
JSONArray attachmentsArray = new JSONArray();
System.out.print("Do you want to attach files? (yes/no): ");
if (scanner.nextLine().equalsIgnoreCase("yes")) {
while (true) {
System.out.print("File URL: ");
String fileUrl = scanner.nextLine();
// URL validation
if (fileUrl.isEmpty()) {
System.out.print("URL can not be empty. Try again.");
continue;
}
if (fileUrl.contains(" ")) {
System.out.println("URL cannot contain spaces. Try again.");
continue;
}
if (!fileUrl.isEmpty() && !fileUrl.matches("^(http|https)://.*$")) {
System.out.println("Invalid URL format. Please enter a valid HTTP/HTTPS link.");
continue;
}
System.out.print("File Type (IMAGE / VIDEO / FILE / AUDIO): ");
String fileType = scanner.nextLine().toUpperCase();
Set<String> allowedFileTypes = Set.of("TEXT", "IMAGE", "VIDEO", "FILE");
while (!allowedFileTypes.contains(fileType)) {
System.out.print("Invalid type. Try again (IMAGE / VIDEO / FILE / AUDIO): ");
fileType = scanner.nextLine().toUpperCase();
}
JSONObject fileJson = new JSONObject();
fileJson.put("file_url", fileUrl);
fileJson.put("file_type", fileType);
attachmentsArray.put(fileJson);
System.out.print("Add another file? (yes/no): ");
if (!scanner.nextLine().equalsIgnoreCase("yes")) break;
}
}
// Prepare the request JSON
JSONObject request = new JSONObject();
request.put("action", "send_saved_messages");
request.put("message_id", UUID.randomUUID().toString());
request.put("sender_id", userUUID);
request.put("receiver_type", "private");
request.put("receiver_id", userUUID); // saved messages = to yourself
request.put("content", content);
request.put("message_type", "TEXT");
request.put("status", "READ");
request.put("reply_to_id", JSONObject.NULL);
request.put("is_edited", false);
request.put("original_message_id", JSONObject.NULL);
request.put("forwarded_by", JSONObject.NULL);
request.put("forwarded_from", JSONObject.NULL);
request.put("is_deleted_globally", JSONObject.NULL);
request.put("edited_at", JSONObject.NULL);
// Send the message and wait for response
JSONObject response = ActionHandler.sendWithResponse(request);
if (!response.optString("status", "fail").equals("success")) {
System.out.println("Failed to send message: " + response.optString("message", "Unknown error"));
} else {
System.out.println("Message sent.");
}
System.out.println("️ Saved Messages already exists." + (chatId != null ? " chat_id=" + chatId : ""));
}
}
private void openSettings() {
System.out.println("⚙️ Opening settings...");
while (true) {
System.out.println("⚙️ Settings\n");
String imageUrl = Session.currentUser.optString("image_url", "");
String profileName = Session.currentUser.optString("profile_name", "");
String userId = Session.currentUser.optString("user_id", "");
renderUserCard(imageUrl, profileName, userId);
System.out.println();
renderMenu();
System.out.print("\nChoose an option (0-4): ");
String pick = scanner.nextLine().trim();
switch (pick) {
case "1": openUserProfile(); break;
case "2": showPrivacySettings(); break;
case "3": showTelegramQA(); break;
case "4": showTelegramFeatures(); break;
case "0": return;
default:
System.out.println("❌ Invalid choice. Press Enter to continue...");
scanner.nextLine();
}
}
}
private void showPrivacySettings() {
while (true) {
System.out.println("🔒 Privacy\n");
System.out.println("1) Blocked users");
System.out.println("2) Change username / password");
System.out.println("0) Back");
System.out.print("\nChoose: ");
String pick = scanner.nextLine().trim();
switch (pick) {
case "1": viewBlockedUsers(); break;
case "2": changeCredentialsFlow(); break;
case "0": return;
default:
System.out.println("❌ Invalid choice. Press Enter...");
scanner.nextLine();
}
}
}
private void viewBlockedUsers() {
System.out.println("🚫 Blocked Users\n");
org.json.JSONObject req = new org.json.JSONObject();
req.put("action", "get_blocked_users");
org.json.JSONObject res = sendWithResponse(req);
if (res == null || !res.optString("status","error").equals("success")) {
System.out.println("❌ Failed to fetch blocked users. Press Enter...");
scanner.nextLine();
return;
}
org.json.JSONArray arr = res.getJSONObject("data").optJSONArray("blocked_users");
if (arr == null || arr.isEmpty()) {
System.out.println("📭 No blocked users.");
System.out.println("\nPress Enter...");
scanner.nextLine();
return;
}
for (int i = 0; i < arr.length(); i++) {
org.json.JSONObject u = arr.getJSONObject(i);
String profileName = u.optString("profile_name", "");
String userId = u.optString("user_id", "");
System.out.printf("%d) %s (@%s)\n", i + 1, profileName, userId);
}
System.out.println("\n0) Back");
System.out.print("\nChoose a user to UNBLOCK (number): ");
String pick = scanner.nextLine().trim();
if (pick.equals("0")) return;
int idx;
try { idx = Integer.parseInt(pick) - 1; } catch (Exception e) { idx = -1; }
if (idx < 0 || idx >= arr.length()) {
System.out.println("❌ Invalid index. Press Enter...");
scanner.nextLine();
return;
}
org.json.JSONObject target = arr.getJSONObject(idx);
String targetDisplayId = target.optString("user_id", "");
java.util.UUID targetInternalId = java.util.UUID.fromString(target.getString("internal_uuid"));
System.out.printf("Unblock %s (@%s)? (yes/no): ", target.optString("profile_name",""), targetDisplayId);
if (!scanner.nextLine().trim().equalsIgnoreCase("yes")) return;
org.json.JSONObject unReq = new org.json.JSONObject();
unReq.put("action", "toggle_block");
unReq.put("user_id",Session.getUserUUID());
unReq.put("target_id", targetInternalId.toString());
org.json.JSONObject unRes = sendWithResponse(unReq);
if (unRes != null && unRes.optString("status","error").equals("success")) {
System.out.println("✅ User unblocked.");
} else {
System.out.println("❌ Failed to unblock.");
}
System.out.println("Press Enter...");
scanner.nextLine();
}
private void changeCredentialsFlow() {
System.out.println("🛡️ Change Username / Password\n");
System.out.print("Enter current password: ");
String currentPassword = scanner.nextLine();
org.json.JSONObject verReq = new org.json.JSONObject();
verReq.put("action", "verify_password");
verReq.put("current_password", currentPassword);
org.json.JSONObject verRes = sendWithResponse(verReq);
if (verRes == null || !verRes.optString("status","error").equals("success")) {
System.out.println("❌ Current password is incorrect.");
System.out.println("Press Enter...");
scanner.nextLine();
return;
}
String currentUsername = Session.currentUser.optString("username", "");
System.out.println("\n✅ Verified.");
System.out.println("Current username: " + currentUsername);
System.out.println("Current password: ******** (hidden)");
System.out.println("\nWhat do you want to change?");
System.out.println("1) Username");
System.out.println("2) Password");
System.out.println("3) Both");
System.out.println("0) Back");
System.out.print("\nChoose: ");
String pick = scanner.nextLine().trim();
switch (pick) {
case "1":
changeUsername(currentPassword);
break;
case "2":
changePassword(currentPassword);
break;
case "3":
boolean uOk = changeUsername(currentPassword);
boolean pOk = changePassword(currentPassword);
if (uOk && pOk) System.out.println("✅ Username and password updated.");
System.out.println("Press Enter...");
scanner.nextLine();
break;
case "0":
return;
default:
System.out.println("❌ Invalid choice. Press Enter...");
scanner.nextLine();
}
}
private boolean changeUsername(String currentPassword) {
System.out.print("\nNew username: ");
String newUsername = scanner.nextLine().trim();
if (!isValidUsername(newUsername)) {
System.out.println("❌ Invalid username. Use 432 chars: letters, digits, underscore.");
return false;
}
org.json.JSONObject req = new org.json.JSONObject();
req.put("action", "update_username");
req.put("current_password", currentPassword);
req.put("new_username", newUsername);
org.json.JSONObject res = sendWithResponse(req);
if (res != null && res.optString("status","error").equals("success")) {
Session.currentUser.put("username", newUsername);
System.out.println("✅ Username updated.");
return true;
} else {
String msg = (res == null) ? "No response." : res.optString("message","Update failed.");
System.out.println("" + msg);
return false;
}
}
private boolean isValidUsername(String s) {
//4-32 char
return s != null && s.matches("^[A-Za-z0-9_]{4,32}$");
}
private boolean changePassword(String currentPassword) {
System.out.print("\nNew password: ");
String newPassword = scanner.nextLine();
System.out.print("Repeat new password: ");
String repeat = scanner.nextLine();
if (!newPassword.equals(repeat)) {
System.out.println("❌ Passwords do not match.");
return false;
}
if (!isStrongPassword(newPassword)) {
System.out.println("❌ Weak password. Min 8 chars, include letters and digits.");
return false;
}
org.json.JSONObject req = new org.json.JSONObject();
req.put("action", "update_password");
req.put("current_password", currentPassword);
req.put("new_password", newPassword);
org.json.JSONObject res = sendWithResponse(req);
if (res != null && res.optString("status","error").equals("success")) {
System.out.println("✅ Password updated.");
return true;
} else {
String msg = (res == null) ? "No response." : res.optString("message","Update failed.");
System.out.println("" + msg);
return false;
}
}
private void renderUserCard(String imageUrl, String profileName, String userId) {
int w = 60;
String top = "" + "".repeat(w - 2) + "";
String bot = "" + "".repeat(w - 2) + "";
System.out.println(top);
System.out.println(padBoxLine("Profile", w));
System.out.println("" + "".repeat(w - 2) + "");
System.out.println(padBoxLine("Image URL: " + imageUrl, w));
System.out.println(padBoxLine("Profile Name: "+ profileName, w));
System.out.println(padBoxLine("User ID: " + userId, w));
System.out.println(bot);
}
private String padBoxLine(String text, int width) {
final int inner = width - 2;
if (text.length() > inner) {
text = text.substring(0, inner - 1) + "";
}
int spaces = inner - text.length();
return "" + text + " ".repeat(Math.max(0, spaces)) + "";
}
private void renderMenu() {
System.out.println("1) My Account");
System.out.println("2) Privacy");
System.out.println("3) Telegram Q&A");
System.out.println("4) Telegram Features");
System.out.println("0) Back");
}
private void showTelegramFeatures() {
@@ -462,4 +570,11 @@ public class SidebarHandler {
private void showTelegramQA() {
System.out.println("❓ Showing Q&A...");
}
private boolean isStrongPassword(String s) {
boolean approved = s.matches("\\b(?=[^\\s]*[A-Z])(?=[^\\s]*[a-z])(?=[^\\s]*\\d)(?=[^\\s]*[!@#$%^&*])[^\\s]{8,}\\b");
return approved ;
}
}