From 4faba0d2eb215530dcf1d1a6ba3a564ec74009a0 Mon Sep 17 00:00:00 2001
From: Andrew Lalis
Date: Sat, 21 Aug 2021 23:57:33 +0200
Subject: [PATCH] Added chat history stuff.
---
.../nl/andrewl/concord_core/msg/Message.java | 9 +++
.../msg/types/ChatHistoryRequest.java | 71 +++++++++++++++++++
.../msg/types/ChatHistoryResponse.java | 54 ++++++++++++++
.../andrewl/concord_server/ClientThread.java | 3 +
.../andrewl/concord_server/ConcordServer.java | 20 ++++++
5 files changed, 157 insertions(+)
create mode 100644 core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryRequest.java
create mode 100644 core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryResponse.java
diff --git a/core/src/main/java/nl/andrewl/concord_core/msg/Message.java b/core/src/main/java/nl/andrewl/concord_core/msg/Message.java
index 9bd98b8..1d56223 100644
--- a/core/src/main/java/nl/andrewl/concord_core/msg/Message.java
+++ b/core/src/main/java/nl/andrewl/concord_core/msg/Message.java
@@ -82,4 +82,13 @@ public interface Message {
if (read != length) throw new IOException("Not all bytes of a string of length " + length + " could be read.");
return new String(data, StandardCharsets.UTF_8);
}
+
+ default void writeEnum(Enum> value, DataOutputStream o) throws IOException {
+ o.writeInt(value.ordinal());
+ }
+
+ default > T readEnum(Class e, DataInputStream i) throws IOException {
+ int ordinal = i.readInt();
+ return e.getEnumConstants()[ordinal];
+ }
}
diff --git a/core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryRequest.java b/core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryRequest.java
new file mode 100644
index 0000000..04e1a09
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryRequest.java
@@ -0,0 +1,71 @@
+package nl.andrewl.concord_core.msg.types;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import nl.andrewl.concord_core.msg.Message;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * A message which clients can send to the server to request some messages from
+ * the server's history of all sent messages from a particular source. Every
+ * request must provide the id of the source that messages should be fetched
+ * from, in addition to the type of source (channel, thread, dm).
+ *
+ * The query string is a specially-formatted string that allows you to
+ * filter results to only certain messages, using different parameters that
+ * are separated by the ;
character.
+ *
+ *
+ * All query parameters are of the form param=value
, where
+ * param
is the case-sensitive name of the parameter, and
+ * value
is the value of the parameter.
+ *
+ *
+ * The following query parameters are supported:
+ *
+ * count
- Fetch up to N messages. Minimum of 1, and
+ * a server-specific maximum count, usually no higher than 1000.
+ * from
- ISO-8601 timestamp indicating the timestamp
+ * after which messages should be fetched. Only messages after this
+ * point in time are returned.
+ * to
- ISO-8601 timestamp indicating the timestamp
+ * before which messages should be fetched. Only messages before this
+ * point in time are returned.
+ *
+ *
+ *
+ * Responses to this request are sent via {@link ChatHistoryResponse}, where
+ * the list of messages is always sorted by the timestamp.
+ *
+ */
+@Data
+@NoArgsConstructor
+public class ChatHistoryRequest implements Message {
+ public enum Source {CHANNEL, THREAD, DIRECT_MESSAGE}
+
+ private long sourceId;
+ private Source sourceType;
+ private String query;
+
+ @Override
+ public int getByteCount() {
+ return Long.BYTES + Integer.BYTES + getByteSize(this.query);
+ }
+
+ @Override
+ public void write(DataOutputStream o) throws IOException {
+ o.writeLong(sourceId);
+ writeEnum(this.sourceType, o);
+ writeString(this.query, o);
+ }
+
+ @Override
+ public void read(DataInputStream i) throws IOException {
+ this.sourceId = i.readLong();
+ this.sourceType = readEnum(Source.class, i);
+ this.query = readString(i);
+ }
+}
diff --git a/core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryResponse.java b/core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryResponse.java
new file mode 100644
index 0000000..84aa044
--- /dev/null
+++ b/core/src/main/java/nl/andrewl/concord_core/msg/types/ChatHistoryResponse.java
@@ -0,0 +1,54 @@
+package nl.andrewl.concord_core.msg.types;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import nl.andrewl.concord_core.msg.Message;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * The response that a server sends to a {@link ChatHistoryRequest}.
+ */
+@Data
+@NoArgsConstructor
+public class ChatHistoryResponse implements Message {
+ private long sourceId;
+ private ChatHistoryRequest.Source sourceType;
+ List messages;
+
+ @Override
+ public int getByteCount() {
+ int count = Long.BYTES + Integer.BYTES + Integer.BYTES;
+ for (var message : this.messages) {
+ count += message.getByteCount();
+ }
+ return count;
+ }
+
+ @Override
+ public void write(DataOutputStream o) throws IOException {
+ o.writeLong(this.sourceId);
+ writeEnum(this.sourceType, o);
+ o.writeInt(messages.size());
+ for (var message : this.messages) {
+ message.write(o);
+ }
+ }
+
+ @Override
+ public void read(DataInputStream i) throws IOException {
+ this.sourceId = i.readInt();
+ this.sourceType = readEnum(ChatHistoryRequest.Source.class, i);
+ int messageCount = i.readInt();
+ Chat[] messages = new Chat[messageCount];
+ for (int k = 0; k < messageCount; k++) {
+ Chat c = new Chat();
+ c.read(i);
+ messages[k] = c;
+ }
+ this.messages = List.of(messages);
+ }
+}
diff --git a/server/src/main/java/nl/andrewl/concord_server/ClientThread.java b/server/src/main/java/nl/andrewl/concord_server/ClientThread.java
index 6fccd3b..d4d99f5 100644
--- a/server/src/main/java/nl/andrewl/concord_server/ClientThread.java
+++ b/server/src/main/java/nl/andrewl/concord_server/ClientThread.java
@@ -5,6 +5,7 @@ import lombok.extern.java.Log;
import nl.andrewl.concord_core.msg.Message;
import nl.andrewl.concord_core.msg.Serializer;
import nl.andrewl.concord_core.msg.types.Chat;
+import nl.andrewl.concord_core.msg.types.ChatHistoryRequest;
import nl.andrewl.concord_core.msg.types.Identification;
import nl.andrewl.concord_core.msg.types.ServerWelcome;
@@ -65,6 +66,8 @@ public class ClientThread extends Thread {
var msg = Serializer.readMessage(this.in);
if (msg instanceof Chat chat) {
this.server.handleChat(chat);
+ } else if (msg instanceof ChatHistoryRequest historyRequest) {
+ this.server.handleHistoryRequest(historyRequest, this);
}
} catch (IOException e) {
log.info("Client disconnected: " + e.getMessage());
diff --git a/server/src/main/java/nl/andrewl/concord_server/ConcordServer.java b/server/src/main/java/nl/andrewl/concord_server/ConcordServer.java
index b60b329..e10936a 100644
--- a/server/src/main/java/nl/andrewl/concord_server/ConcordServer.java
+++ b/server/src/main/java/nl/andrewl/concord_server/ConcordServer.java
@@ -3,6 +3,9 @@ package nl.andrewl.concord_server;
import lombok.extern.java.Log;
import nl.andrewl.concord_core.msg.Serializer;
import nl.andrewl.concord_core.msg.types.Chat;
+import nl.andrewl.concord_core.msg.types.ChatHistoryRequest;
+import org.dizitart.no2.Document;
+import org.dizitart.no2.Nitrite;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
@@ -19,10 +22,14 @@ public class ConcordServer implements Runnable {
private final Map clients = new ConcurrentHashMap<>(32);
private final int port;
private final Random random;
+ private final Nitrite db;
public ConcordServer(int port) {
this.port = port;
this.random = new SecureRandom();
+ this.db = Nitrite.builder()
+ .filePath("concord-server.db")
+ .openOrCreate();
}
public long registerClient(ClientThread clientThread) {
@@ -37,6 +44,15 @@ public class ConcordServer implements Runnable {
}
public void handleChat(Chat chat) {
+ var collection = db.getCollection("channel-TEST");
+ long messageId = this.random.nextLong();
+ Document doc = Document.createDocument(Long.toHexString(messageId), "message")
+ .put("senderId", Long.toHexString(chat.getSenderId()))
+ .put("senderNickname", chat.getSenderNickname())
+ .put("timestamp", chat.getTimestamp())
+ .put("message", chat.getMessage());
+ collection.insert(doc);
+ db.commit();
System.out.println(chat.getSenderNickname() + ": " + chat.getMessage());
ByteArrayOutputStream baos = new ByteArrayOutputStream(chat.getByteCount());
try {
@@ -51,6 +67,10 @@ public class ConcordServer implements Runnable {
}
}
+ public void handleHistoryRequest(ChatHistoryRequest request, ClientThread clientThread) {
+
+ }
+
@Override
public void run() {
ServerSocket serverSocket;