Work on sending files
This commit is contained in:
@@ -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,42 +4013,49 @@ 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
|
System.out.println("✅ Sent. id=" + resp.optJSONObject("data").optString("message_id",""));
|
||||||
if (line == null) { System.out.println("❌ No response"); return; }
|
} else {
|
||||||
org.json.JSONObject resp = new org.json.JSONObject(line);
|
System.out.println("❌ Failed: " + (resp != null ? resp.optString("message") : "no response"));
|
||||||
if ("success".equalsIgnoreCase(resp.optString("status"))) {
|
|
||||||
System.out.println("✅ Sent. id=" + resp.optJSONObject("data").optString("message_id",""));
|
|
||||||
} else {
|
|
||||||
System.out.println("❌ Failed: " + resp.optString("message"));
|
|
||||||
}
|
|
||||||
} 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) {
|
||||||
try {
|
// اعتبارسنجی ورودی فایل (اگر ورودی از یوزر میاد، قبل از ساخت File کوتیشنها رو حذف کن)
|
||||||
String mime = java.nio.file.Files.probeContentType(file.toPath());
|
if (file == null) {
|
||||||
if (mime == null) mime = type.equalsIgnoreCase("IMAGE") ? "image/*" : "audio/*";
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
UUID messageId = UUID.randomUUID();
|
final UUID messageId = UUID.randomUUID();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String mime = detectMime(file, type.toUpperCase());
|
||||||
|
if (mime == null) mime = type.equalsIgnoreCase("IMAGE") ? "image/*" : "audio/*";
|
||||||
|
|
||||||
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())
|
||||||
.put("receiver_type", receiverType) // private|group|channel
|
.put("receiver_type", receiverType) // private|group|channel
|
||||||
.put("receiver_id", receiverId.toString())
|
.put("receiver_id", receiverId.toString())
|
||||||
.put("message_type", type.toUpperCase()) // IMAGE | AUDIO
|
.put("message_type", type.toUpperCase()) // IMAGE | AUDIO
|
||||||
.put("file_name", file.getName())
|
.put("file_name", file.getName())
|
||||||
.put("mime_type", mime)
|
.put("mime_type", mime)
|
||||||
.put("text", caption == null ? "" : caption);
|
.put("text", caption == null ? "" : caption);
|
||||||
@@ -4061,39 +4063,50 @@ 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);
|
||||||
|
|
||||||
// 1) خط سوئیچ به MEDIA
|
try {
|
||||||
out.print("MEDIA\n");
|
// ⛔ مهم: فقط از outBin استفاده کن تا بافرها قاطی نشن
|
||||||
out.flush();
|
// 1) خط سوئیچ به MEDIA
|
||||||
|
outBin.write("MEDIA\n".getBytes(java.nio.charset.StandardCharsets.US_ASCII));
|
||||||
|
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];
|
||||||
int n;
|
int n;
|
||||||
while ((n = fis.read(buf)) != -1) {
|
while ((n = fis.read(buf)) != -1) {
|
||||||
outBin.write(buf, 0, n);
|
outBin.write(buf, 0, n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
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");
|
||||||
System.out.println("✅ Media sent. id=" + ack.optString("message_id") +
|
if ("success".equalsIgnoreCase(status)) {
|
||||||
" url=" + ack.optString("file_url"));
|
System.out.println("✅ Media sent. id=" + ack.optString("message_id") +
|
||||||
} else {
|
" url=" + ack.optString("file_url"));
|
||||||
System.out.println("❌ Media failed: " + ack.optString("message"));
|
} else {
|
||||||
|
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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user