Work on sending files

This commit is contained in:
2025-08-13 17:43:43 +03:30
parent f49dc171f5
commit eb633b5e13
6 changed files with 123 additions and 66 deletions
@@ -2,20 +2,15 @@ package org.to.telegramfinalproject.Client;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.to.telegramfinalproject.Database.PrivateChatDatabase;
import org.to.telegramfinalproject.Database.ContactDatabase;
import org.to.telegramfinalproject.Models.ChatEntry; import org.to.telegramfinalproject.Models.ChatEntry;
import org.to.telegramfinalproject.Models.ContactEntry; import org.to.telegramfinalproject.Models.ContactEntry;
import org.to.telegramfinalproject.Models.SearchRequestModel; import org.to.telegramfinalproject.Models.SearchRequestModel;
import org.to.telegramfinalproject.Models.SearchResultModel;
import java.io.*; import java.io.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class ActionHandler { public class ActionHandler {
@@ -1434,7 +1429,7 @@ public class ActionHandler {
String input = scanner.nextLine(); String input = scanner.nextLine();
switch (input) { switch (input) {
case "1" -> sendMessage(chat.getId(), "private"); case "1" -> sendMessageInteractive(chat.getId(), "private");
case "2" -> toggleBlock(chat.getOtherUserId()); case "2" -> toggleBlock(chat.getOtherUserId());
case "3" -> { case "3" -> {
deleteChat(chat.getId(), false); deleteChat(chat.getId(), false);
@@ -1551,7 +1546,7 @@ public class ActionHandler {
String input = scanner.nextLine(); String input = scanner.nextLine();
switch (input) { switch (input) {
case "1" -> sendMessage(chat.getId(), "group"); case "1" -> sendMessageInteractive(chat.getId(), "group");
case "2" -> viewGroupMembers(chat.getId()); case "2" -> viewGroupMembers(chat.getId());
case "3" -> { case "3" -> {
if (isOwner || (isAdmin && perms.optBoolean("can_add_members", false))) if (isOwner || (isAdmin && perms.optBoolean("can_add_members", false)))
@@ -1678,7 +1673,7 @@ public class ActionHandler {
switch (input) { switch (input) {
case "1" -> { case "1" -> {
if (isOwner || (isAdmin && perms.optBoolean("can_post", false))) { if (isOwner || (isAdmin && perms.optBoolean("can_post", false))) {
sendMessage(chat.getId(), "channel"); sendMessageInteractive(chat.getId(), "channel");
} else { } else {
System.out.println("❌ You don't have permission to post."); System.out.println("❌ You don't have permission to post.");
} }
@@ -4018,36 +4013,43 @@ public class ActionHandler {
} }
private void sendTextMessage(UUID receiverId, String receiverType, String content) { private void sendTextMessage(UUID receiverId, String receiverType, String content) {
org.json.JSONObject msg = new org.json.JSONObject() JSONObject req = new JSONObject()
.put("action", "send_message") .put("action", "send_message")
.put("receiver_type", receiverType) .put("receiver_type", receiverType)
.put("receiver_id", receiverId.toString()) .put("receiver_id", receiverId.toString())
.put("message_type", "TEXT") .put("message_type", "TEXT")
.put("content", content == null ? "" : content); .put("content", content == null ? "" : content);
sendWithResponse(msg); JSONObject resp = sendWithResponse(req);
try { if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
String line = in.readLine(); // Ack
if (line == null) { System.out.println("❌ No response"); return; }
org.json.JSONObject resp = new org.json.JSONObject(line);
if ("success".equalsIgnoreCase(resp.optString("status"))) {
System.out.println("✅ Sent. id=" + resp.optJSONObject("data").optString("message_id","")); System.out.println("✅ Sent. id=" + resp.optJSONObject("data").optString("message_id",""));
} else { } else {
System.out.println("❌ Failed: " + resp.optString("message")); System.out.println("❌ Failed: " + (resp != null ? resp.optString("message") : "no response"));
}
} catch (Exception e) {
e.printStackTrace();
} }
} }
public void sendMediaMessage(UUID receiverId, String receiverType, String type /*IMAGE/AUDIO*/, File file, String caption) { public void sendMediaMessage(UUID receiverId, String receiverType, String type /* IMAGE/AUDIO */, File file, String caption) {
// اعتبارسنجی ورودی فایل (اگر ورودی از یوزر میاد، قبل از ساخت File کوتیشن‌ها رو حذف کن)
if (file == null) {
System.out.println("❌ File is null");
return;
}
if (!file.exists()) {
System.out.println("❌ File not found: " + file.getAbsolutePath());
return;
}
if (file.isDirectory()) {
System.out.println("❌ Path is a directory, expected a file: " + file.getAbsolutePath());
return;
}
final UUID messageId = UUID.randomUUID();
try { try {
String mime = java.nio.file.Files.probeContentType(file.toPath()); String mime = detectMime(file, type.toUpperCase());
if (mime == null) mime = type.equalsIgnoreCase("IMAGE") ? "image/*" : "audio/*"; if (mime == null) mime = type.equalsIgnoreCase("IMAGE") ? "image/*" : "audio/*";
UUID messageId = UUID.randomUUID();
JSONObject header = new JSONObject() JSONObject header = new JSONObject()
.put("message_id", messageId.toString()) .put("message_id", messageId.toString())
.put("sender_id", TelegramClient.loggedInUserId.toString()) .put("sender_id", TelegramClient.loggedInUserId.toString())
@@ -4061,19 +4063,21 @@ public class ActionHandler {
byte[] headerBytes = header.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8); byte[] headerBytes = header.toString().getBytes(java.nio.charset.StandardCharsets.UTF_8);
long contentLen = file.length(); long contentLen = file.length();
// قبل از ارسال، صف برای ACK ثبت کن // صف ACK
BlockingQueue<JSONObject> q = new LinkedBlockingQueue<>(1); BlockingQueue<JSONObject> q = new LinkedBlockingQueue<>(1);
TelegramClient.pendingResponses.put(messageId.toString(), q); TelegramClient.pendingResponses.put(messageId.toString(), q);
try {
// ⛔ مهم: فقط از outBin استفاده کن تا بافرها قاطی نشن
// 1) خط سوئیچ به MEDIA // 1) خط سوئیچ به MEDIA
out.print("MEDIA\n"); outBin.write("MEDIA\n".getBytes(java.nio.charset.StandardCharsets.US_ASCII));
out.flush(); outBin.flush();
// 2) فریم باینری: magic + headerLen + header + contentLen + content // 2) فریم باینری: magic + headerLen + header + contentLen + content
outBin.writeInt(0x4D444D31); // "MDM1" outBin.writeInt(0x4D444D31); // "MDM1"
outBin.writeInt(headerBytes.length); // headerLen outBin.writeInt(headerBytes.length); // headerLen (int)
outBin.write(headerBytes); // header outBin.write(headerBytes); // header
outBin.writeLong(contentLen); // contentLen outBin.writeLong(contentLen); // contentLen (long) ← مطمئن شو سرور هم Long می‌خونه
try (InputStream fis = new BufferedInputStream(new FileInputStream(file))) { try (InputStream fis = new BufferedInputStream(new FileInputStream(file))) {
byte[] buf = new byte[8192]; byte[] buf = new byte[8192];
@@ -4084,16 +4088,25 @@ public class ActionHandler {
} }
outBin.flush(); outBin.flush();
// 3) منتظر ACK از Listener (با message_id) // 3) منتظر ACK با تایم‌اوت (فقط یکی!)
JSONObject ack = q.take(); // بلاک تا بیاد JSONObject ack = q.poll(20, java.util.concurrent.TimeUnit.SECONDS);
TelegramClient.pendingResponses.remove(messageId.toString()); if (ack == null) {
System.out.println("❌ Media ACK timeout for " + messageId);
return;
}
if ("success".equalsIgnoreCase(ack.optString("status"))) { String status = ack.optString("status", "error");
if ("success".equalsIgnoreCase(status)) {
System.out.println("✅ Media sent. id=" + ack.optString("message_id") + System.out.println("✅ Media sent. id=" + ack.optString("message_id") +
" url=" + ack.optString("file_url")); " url=" + ack.optString("file_url"));
} else { } else {
System.out.println("❌ Media failed: " + ack.optString("message")); System.out.println("❌ Media failed: " + ack.optString("message"));
} }
} finally {
TelegramClient.pendingResponses.remove(messageId.toString());
}
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
System.out.println("❌ sendMediaMessage error: " + e.getMessage()); System.out.println("❌ sendMediaMessage error: " + e.getMessage());
@@ -4101,6 +4114,21 @@ public class ActionHandler {
} }
private static String detectMime(File f, String typeUpper /* IMAGE or AUDIO */) {
try {
String m = java.nio.file.Files.probeContentType(f.toPath());
if (m != null) return m;
} catch (Exception ignored) {}
String name = f.getName().toLowerCase();
if (name.endsWith(".png")) return "image/png";
if (name.endsWith(".jpg") || name.endsWith(".jpeg")) return "image/jpeg";
if (name.endsWith(".gif")) return "image/gif";
if (name.endsWith(".mp3")) return "audio/mpeg";
if (name.endsWith(".wav")) return "audio/wav";
if (name.endsWith(".ogg")) return "audio/ogg";
return typeUpper.equals("IMAGE") ? "image/*" : "audio/*";
}
} }
@@ -28,6 +28,17 @@ public class IncomingMessageListener implements Runnable {
JSONObject response = new JSONObject(line); JSONObject response = new JSONObject(line);
System.out.println("📥 Received raw line: " + line); System.out.println("📥 Received raw line: " + line);
//for media
String mid = response.optString("message_id", "");
if (!mid.isEmpty()) {
BlockingQueue<JSONObject> q = TelegramClient.pendingResponses.get(mid);
if (q != null) {
q.put(response);
continue;
}
}
//if it has reqID answer //if it has reqID answer
if (response.has("request_id")) { if (response.has("request_id")) {
String requestId = response.getString("request_id"); String requestId = response.getString("request_id");
@@ -10,7 +10,7 @@ import java.awt.image.BufferedImage;
public class MediaSender { class MediaSender {
public static void sendImageOrAudio(Socket socket, public static void sendImageOrAudio(Socket socket,
UUID senderId, UUID senderId,
@@ -13,7 +13,7 @@ import java.util.concurrent.LinkedBlockingQueue;
public class TelegramClient { public class TelegramClient {
private static final String SERVER_HOST = "localhost"; private static final String SERVER_HOST = "localhost";
private static final int SERVER_PORT = 8000; private static final int SERVER_PORT = 8080;
private static Socket socket; private static Socket socket;
private BufferedReader in; private BufferedReader in;
private PrintWriter out; private PrintWriter out;
@@ -32,7 +32,7 @@ public class ClientHandler implements Runnable {
// BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// PrintWriter out = new PrintWriter(socket.getOutputStream(), true) // PrintWriter out = new PrintWriter(socket.getOutputStream(), true)
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
DataInputStream dis = new DataInputStream(bis); // برای MEDIA و هدرهای باینری DataInputStream dis = new DataInputStream(bis); //for binary headers
PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), java.nio.charset.StandardCharsets.UTF_8), true); PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), java.nio.charset.StandardCharsets.UTF_8), true);
@@ -2871,8 +2871,7 @@ public class ClientHandler implements Runnable {
String receiverType = json.getString("receiver_type"); String receiverType = json.getString("receiver_type");
UUID receiverId = UUID.fromString(json.getString("receiver_id")); UUID receiverId = UUID.fromString(json.getString("receiver_id"));
// private validations...
// ...
String content = json.optString("content", ""); String content = json.optString("content", "");
String messageType = json.optString("message_type", "TEXT"); String messageType = json.optString("message_type", "TEXT");
@@ -2935,7 +2934,6 @@ public class ClientHandler implements Runnable {
// Real-Time // Real-Time
Message msg = new Message(messageId, senderId, receiverId, receiverType, content, messageType, LocalDateTime.now()); Message msg = new Message(messageId, senderId, receiverId, receiverType, content, messageType, LocalDateTime.now());
// رویداد با پیوست‌ها
JSONObject payload = new JSONObject(); JSONObject payload = new JSONObject();
payload.put("action", "new_message"); payload.put("action", "new_message");
JSONObject data = new JSONObject(); JSONObject data = new JSONObject();
@@ -2969,21 +2967,41 @@ public class ClientHandler implements Runnable {
if (sender != null) data.put("sender_name", sender.getProfile_name()); if (sender != null) data.put("sender_name", sender.getProfile_name());
payload.put("data", data); payload.put("data", data);
List<UUID> receivers = getReceiversForChat(receiverId, receiverType); // List<UUID> receivers = getReceiversForChat(receiverId, receiverType);
receivers.remove(senderId); // receivers.remove(senderId);
RealTimeEventDispatcher.broadcastToUsers(receivers, payload); // RealTimeEventDispatcher.broadcastToUsers(receivers, payload);
//
//
//
// // chat_updated
// JSONObject chatUpdate = new JSONObject()
// .put("chat_id", receiverId.toString())
// .put("chat_type", receiverType)
// .put("last_message_time", LocalDateTime.now().toString());
//
// JSONObject chatPayload = new JSONObject()
// .put("action", "chat_updated")
// .put("data", chatUpdate);
//
// for (UUID r : receivers) RealTimeEventDispatcher.sendToUser(r, chatPayload);
// chat_updated
List<UUID> allMembers = getReceiversForChat(receiverId, receiverType); // شامل sender
// به همه chat_updated بده
JSONObject chatUpdate = new JSONObject() JSONObject chatUpdate = new JSONObject()
.put("chat_id", receiverId.toString()) .put("chat_id", receiverId.toString())
.put("chat_type", receiverType) .put("chat_type", receiverType)
.put("last_message_time", LocalDateTime.now().toString()); .put("last_message_time", LocalDateTime.now().toString());
JSONObject chatPayload = new JSONObject() JSONObject chatPayload = new JSONObject()
.put("action", "chat_updated") .put("action", "chat_updated")
.put("data", chatUpdate); .put("data", chatUpdate);
for (UUID u : allMembers) RealTimeEventDispatcher.sendToUser(u, chatPayload);
// حالا new_message را فقط به غیر از sender
List<UUID> others = new ArrayList<>(allMembers);
others.remove(senderId);
RealTimeEventDispatcher.broadcastToUsers(others, payload);
for (UUID r : receivers) RealTimeEventDispatcher.sendToUser(r, chatPayload);
JSONObject respData = new JSONObject().put("message_id", messageId.toString()); JSONObject respData = new JSONObject().put("message_id", messageId.toString());
return new ResponseModel("success", "Message sent successfully.", respData); return new ResponseModel("success", "Message sent successfully.", respData);
@@ -7,7 +7,7 @@ import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
public class MainServer { public class MainServer {
private static final int PORT = 8000; private static final int PORT = 8080;
public static void main(String[] args) { public static void main(String[] args) {