Compare commits

..

No commits in common. "654aca86cc6c1be827d5a321b20ea182832cd7b1" and "2ab3c5e01dc160d3c0a075d39421cb99ba00d87c" have entirely different histories.

11 changed files with 23 additions and 39 deletions

13
.env.example Normal file
View file

@ -0,0 +1,13 @@
# No need to change
POSTGRES_DB=dchat
# "db" being the name of the postgresql service inside the docker-compose.yml, "5432" is the port on which postgres
# listens for new connections and "dchat" is the database name.
POSTGRES_URL=jdbc:postgresql://db:5432/dchat
# Please change
DISCORD_API_KEY=<discord-api-key>
OPENAI_API_KEY=<openai-api-key>
# Those values can not change after the first start
POSTGRES_USER=<postgres-user>
POSTGRES_PASSWORD=<postgres-password>

1
.gitignore vendored
View file

@ -126,6 +126,5 @@ fabric.properties
.idea/caches/build_file_checksums.ser .idea/caches/build_file_checksums.ser
.env .env
dchat.properties
data/ data/

View file

@ -46,6 +46,7 @@
<!--Remove unneeded files and symbols to reduce the image size and performance--> <!--Remove unneeded files and symbols to reduce the image size and performance-->
<noHeaderFiles>true</noHeaderFiles> <noHeaderFiles>true</noHeaderFiles>
<noManPages>true</noManPages> <noManPages>true</noManPages>
<stripDebug>true</stripDebug>
<!--Compression is not needed since we unzip it in our OCI build--> <!--Compression is not needed since we unzip it in our OCI build-->
<compress>zip-0</compress> <compress>zip-0</compress>
</configuration> </configuration>

View file

@ -10,7 +10,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
public final class MessageService { public final class MessageService {
@ -28,12 +27,12 @@ public final class MessageService {
} }
public void process(final String messageContent, final String referencedMessageContent, final String guildId, final String channelId, final String originalMessageId) { public void process(final String messageContent, final String referencedMessageContent, final String guildId, final String channelId, final String originalMessageId) {
final var chatGPTRequest = new ChatGPTRequestBuilder().contextRequest(referencedMessageContent, messageContent, systemMessage); final var chatGPTRequest = new ChatGPTRequestBuilder().contextRequest(referencedMessageContent, messageContent, "system msg");
process(chatGPTRequest, channelId, originalMessageId, guildId); process(chatGPTRequest, channelId, originalMessageId, guildId);
} }
public void process(final String messageContent, final String guildId, final String channelId, final String originalMessageId) { public void process(final String messageContent, final String guildId, final String channelId, final String originalMessageId) {
final var chatGPTRequest = new ChatGPTRequestBuilder().contextRequest(messageContent, systemMessage); final var chatGPTRequest = new ChatGPTRequestBuilder().contextRequest(messageContent, "system msg");
process(chatGPTRequest, channelId, originalMessageId, guildId); process(chatGPTRequest, channelId, originalMessageId, guildId);
} }
@ -47,11 +46,6 @@ public final class MessageService {
} }
}; };
// FIXME: We should find a solution to not block the main thread without loosing the started thread. executorService.submit(callable);
try {
executorService.submit(callable).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
} }
} }

View file

@ -22,15 +22,12 @@ public final class DiscordRest {
} }
public void postMessage(final String channelId, final Message message) throws IOException, InterruptedException { public void postMessage(final String channelId, final Message message) throws IOException, InterruptedException {
final String data = message.toJson();
logger.trace("Submitting new discord message: {}", data);
final var uri = URI.create(DISCORD_URL + "/channels/%s/messages".formatted(channelId)); final var uri = URI.create(DISCORD_URL + "/channels/%s/messages".formatted(channelId));
final HttpRequest request = HttpRequest.newBuilder() final HttpRequest request = HttpRequest.newBuilder()
.uri(uri) .uri(uri)
.header("Authorization", "Bot " + discordApiKey) .header("Authorization", "Bot " + discordApiKey)
.header("User-Agent", USER_AGENT) .header("User-Agent", USER_AGENT)
.header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(message.toJson()))
.POST(HttpRequest.BodyPublishers.ofString(data))
.build(); .build();
final HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding()); final HttpResponse<Void> response = httpClient.send(request, HttpResponse.BodyHandlers.discarding());

View file

@ -1,12 +1,8 @@
package de.hhhammer.dchat.discord.ws.connection; package de.hhhammer.dchat.discord.ws.connection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.http.WebSocket; import java.net.http.WebSocket;
public final class ResumeConnectionInitiator implements ConnectionInitiator { public final class ResumeConnectionInitiator implements ConnectionInitiator {
private static final Logger logger = LoggerFactory.getLogger(ResumeConnectionInitiator.class);
private final String token; private final String token;
private final String sessionId; private final String sessionId;
private final int lastSequence; private final int lastSequence;
@ -20,7 +16,6 @@ public final class ResumeConnectionInitiator implements ConnectionInitiator {
@Override @Override
public void initiate(final WebSocket webSocket) { public void initiate(final WebSocket webSocket) {
final String identifyPayload = "{\"op\": 6, \"d\": {\"token\": \"" + token + "\", \"session_id\": \"" + sessionId + "\", \"seq\": " + lastSequence + "}}"; final String identifyPayload = "{\"op\": 6, \"d\": {\"token\": \"" + token + "\", \"session_id\": \"" + sessionId + "\", \"seq\": " + lastSequence + "}}";
logger.debug("Resuming connection");
webSocket.sendText(identifyPayload, true); webSocket.sendText(identifyPayload, true);
} }
} }

View file

@ -68,7 +68,7 @@ public final class DiscordListener implements WebSocket.Listener {
final String text = optText.get(); final String text = optText.get();
final Event event = eventDeserializer.deserialize(text); final Event event = eventDeserializer.deserialize(text);
final int currentSequence = event.sequence(); final int currentSequence = event.sequence();
if (currentSequence != 0) lastSeq.set(currentSequence); lastSeq.set(currentSequence);
if (!(event.type() instanceof EventType.Empty)) { if (!(event.type() instanceof EventType.Empty)) {
if (event.type() instanceof EventType.Ready) { if (event.type() instanceof EventType.Ready) {
final JSONObject payload = event.data(); final JSONObject payload = event.data();
@ -109,7 +109,6 @@ public final class DiscordListener implements WebSocket.Listener {
@Override @Override
public CompletionStage<?> onClose(final WebSocket webSocket, final int statusCode, final String reason) { public CompletionStage<?> onClose(final WebSocket webSocket, final int statusCode, final String reason) {
logger.info("Connection closed: {}: {}", statusCode, reason);
switch (statusCode) { switch (statusCode) {
case 0, 4000, 4001, 4002, 4005, 4008 -> case 0, 4000, 4001, 4002, 4005, 4008 ->
this.closeEventQueue.add(new CloseEvent.ResumableCloseEvent(this.resumeGatewayUrl.get(), this.sessionId.get(), lastSeq.get())); this.closeEventQueue.add(new CloseEvent.ResumableCloseEvent(this.resumeGatewayUrl.get(), this.sessionId.get(), lastSeq.get()));
@ -142,7 +141,6 @@ public final class DiscordListener implements WebSocket.Listener {
final int intSeq = lastSeq.get(); final int intSeq = lastSeq.get();
final String stringSeq = intSeq != 0 ? String.valueOf(intSeq) : "null"; final String stringSeq = intSeq != 0 ? String.valueOf(intSeq) : "null";
// Send heartbeat // Send heartbeat
logger.debug("Sending heartbeat: {}",stringSeq);
webSocket.sendText("{\"op\": 1, \"d\": %s}".formatted(stringSeq), true); webSocket.sendText("{\"op\": 1, \"d\": %s}".formatted(stringSeq), true);
receivedAck.set(false); receivedAck.set(false);
TimeUnit.MILLISECONDS.sleep(heartbeatInterval); TimeUnit.MILLISECONDS.sleep(heartbeatInterval);

View file

@ -1,11 +1,10 @@
services: services:
bot: bot:
image: git.hhhammer.de/hamburghammer/dchat/bot:latest image: git.hhhammer.de/hamburghammer/dchat/bot:latest
env_file:
- .env
build: build:
dockerfile: Dockerfile dockerfile: Dockerfile
context: . context: .
target: bot
restart: unless-stopped restart: unless-stopped
environment:
DCHAT_CONFIG_FILE: "/opt/dchat/dchat.properties"
volumes:
- ./dchat.properties:/opt/dchat/dchat.properties:ro

View file

@ -1,7 +0,0 @@
dchat.discord-api-key=xxx
dchat.openai-api-key=xxx
dchat.bot-id=xxx
# comma separated list
dchat.allowed-guild-ids=foo,bar
# not allowed to contain "!
dchat.system-message=

View file

@ -2,8 +2,6 @@ package de.hhhammer.dchat.openai.rest;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
@ -13,7 +11,6 @@ import java.net.http.HttpResponse;
import java.time.Duration; import java.time.Duration;
public final class ChatGPTService { public final class ChatGPTService {
private static final Logger logger = LoggerFactory.getLogger(ChatGPTService.class);
private final String apiKey; private final String apiKey;
private final HttpClient httpClient; private final HttpClient httpClient;
@ -24,7 +21,6 @@ public final class ChatGPTService {
public String submit(final ChatGPTRequest chatGPTRequest) throws IOException, InterruptedException, ResponseException { public String submit(final ChatGPTRequest chatGPTRequest) throws IOException, InterruptedException, ResponseException {
final String data = chatGPTRequest.toJson().toString(); final String data = chatGPTRequest.toJson().toString();
logger.trace("Submitting new ChatGPT request: {}", data);
final URI uri = URI.create("https://api.openai.com/v1/chat/completions"); final URI uri = URI.create("https://api.openai.com/v1/chat/completions");
final HttpRequest request = HttpRequest.newBuilder(uri) final HttpRequest request = HttpRequest.newBuilder(uri)
.POST(HttpRequest.BodyPublishers.ofString(data)) .POST(HttpRequest.BodyPublishers.ofString(data))
@ -38,7 +34,7 @@ public final class ChatGPTService {
throw new ResponseException("Response status code was not 200: " + response.statusCode()); throw new ResponseException("Response status code was not 200: " + response.statusCode());
} }
final var responseJson = new JSONObject(response.body()); final var responseJson = new JSONObject(response);
final JSONArray choices = responseJson.getJSONArray("choices"); final JSONArray choices = responseJson.getJSONArray("choices");
return getResponse(choices); return getResponse(choices);
} }

View file

@ -5,5 +5,4 @@ module de.hhhammer.dchat.openai.rest {
requires org.json; requires org.json;
requires static org.jetbrains.annotations; requires static org.jetbrains.annotations;
requires org.slf4j;
} }