Compare commits
5 commits
5662d2faf1
...
f5e40e324d
Author | SHA1 | Date | |
---|---|---|---|
f5e40e324d | |||
5459cf1dd1 | |||
3641b01162 | |||
f457c23301 | |||
92473716fb |
9 changed files with 119 additions and 59 deletions
13
.env.example
Normal file
13
.env.example
Normal 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
1
.gitignore
vendored
|
@ -125,3 +125,4 @@ fabric.properties
|
|||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
.env
|
||||
|
|
51
README.md
51
README.md
|
@ -7,28 +7,67 @@ A ChatGPT Bot for Discord.
|
|||
Requirements:
|
||||
|
||||
- [git](https://git-scm.com/)
|
||||
- [Podman](https://podman.io/) or [Docker](https://www.docker.com/)
|
||||
- [Docker](https://www.docker.com/)
|
||||
- [OpenAI API Key](https://platform.openai.com/account/api-keys)
|
||||
- [Discord Bot Token](https://javacord.org/wiki/getting-started/creating-a-bot-account.html)
|
||||
|
||||
Clone the project:
|
||||
### Clone the project
|
||||
|
||||
```shell
|
||||
git clone https://git.hhhammer.de/hamburghammer/dchat.git
|
||||
cd dchat
|
||||
```
|
||||
|
||||
and navigate into it.
|
||||
### Obtain the images
|
||||
|
||||
Change the environment variable inside the `docker-compose.yml`.
|
||||
#### Build the images
|
||||
|
||||
```shell
|
||||
podman-compose up -d
|
||||
docker compose build
|
||||
```
|
||||
|
||||
#### Pull the prebuilt images
|
||||
|
||||
```shell
|
||||
docker compose pull
|
||||
```
|
||||
|
||||
### Configure environment
|
||||
|
||||
```shell
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Fill the required variables.
|
||||
|
||||
### Start
|
||||
|
||||
For the fist time we want to start the containers in the following order:
|
||||
|
||||
First crate the DB.
|
||||
|
||||
```shell
|
||||
docker compose up -d db
|
||||
```
|
||||
|
||||
Crate the required tables and migrate already existing data.
|
||||
|
||||
```shell
|
||||
docker compose up -d migration
|
||||
```
|
||||
|
||||
Start the final apps.
|
||||
|
||||
```shell
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Invite
|
||||
|
||||
Invite the bot through the link provided in the container logs.
|
||||
|
||||
```shell
|
||||
podman container logs dchat_db_1
|
||||
docker compose logs bot
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
package de.hhhammer.dchat.bot.discord;
|
||||
|
||||
import de.hhhammer.dchat.bot.openai.ResponseException;
|
||||
import org.javacord.api.entity.message.MessageType;
|
||||
import org.javacord.api.event.message.MessageCreateEvent;
|
||||
import org.javacord.api.listener.message.MessageCreateListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MessageCreateHandler implements MessageCreateListener {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageCreateHandler.class);
|
||||
|
||||
private final MessageHandler messageHandler;
|
||||
|
||||
public MessageCreateHandler(MessageHandler messageHandler) {
|
||||
|
@ -27,7 +34,12 @@ public class MessageCreateHandler implements MessageCreateListener {
|
|||
event.getChannel().sendMessage("Rate limit hit - cooling down...");
|
||||
return;
|
||||
}
|
||||
this.messageHandler.handle(event);
|
||||
try {
|
||||
this.messageHandler.handle(event);
|
||||
} catch (ResponseException | IOException | InterruptedException e) {
|
||||
logger.error("Reading a message from the listener", e);
|
||||
event.getMessage().reply("Sorry but something went wrong :(");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package de.hhhammer.dchat.bot.discord;
|
||||
|
||||
import de.hhhammer.dchat.bot.openai.ResponseException;
|
||||
import org.javacord.api.event.message.MessageCreateEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface MessageHandler {
|
||||
void handle(MessageCreateEvent event);
|
||||
void handle(MessageCreateEvent event) throws ResponseException, IOException, InterruptedException;
|
||||
|
||||
boolean isAllowed(MessageCreateEvent event);
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public class ServerMessageHandler implements MessageHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handle(MessageCreateEvent event) {
|
||||
public void handle(MessageCreateEvent event) throws ResponseException, IOException, InterruptedException {
|
||||
String content = extractContent(event);
|
||||
var serverId = event.getServer().get().getId();
|
||||
var systemMessage = this.serverDBService.getConfig(String.valueOf(serverId)).get().systemMessage();
|
||||
|
@ -41,19 +41,14 @@ public class ServerMessageHandler implements MessageHandler {
|
|||
.stream().toList(),
|
||||
content, systemMessage) :
|
||||
new ChatGPTRequestBuilder().simpleRequest(content, systemMessage);
|
||||
try {
|
||||
var response = this.chatGPTService.submit(request);
|
||||
if (response.choices().size() < 1) {
|
||||
event.getChannel().sendMessage("No response available");
|
||||
return;
|
||||
}
|
||||
var answer = response.choices().get(0).message().content();
|
||||
logServerMessage(event, response.usage().totalTokens());
|
||||
event.getMessage().reply(answer);
|
||||
} catch (IOException | InterruptedException | ResponseException e) {
|
||||
logger.error("Reading a message from the listener", e);
|
||||
event.getChannel().sendMessage("Sorry but something went wrong :(");
|
||||
var response = this.chatGPTService.submit(request);
|
||||
if (response.choices().size() < 1) {
|
||||
event.getMessage().reply("No response available");
|
||||
return;
|
||||
}
|
||||
var answer = response.choices().get(0).message().content();
|
||||
logServerMessage(event, response.usage().totalTokens());
|
||||
event.getMessage().reply(answer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package de.hhhammer.dchat.bot.discord;
|
||||
|
||||
import de.hhhammer.dchat.db.UserDBService;
|
||||
import de.hhhammer.dchat.db.models.user.UserMessage;
|
||||
import de.hhhammer.dchat.bot.openai.ChatGPTRequestBuilder;
|
||||
import de.hhhammer.dchat.bot.openai.ChatGPTService;
|
||||
import de.hhhammer.dchat.bot.openai.ResponseException;
|
||||
import de.hhhammer.dchat.db.UserDBService;
|
||||
import de.hhhammer.dchat.db.models.user.UserMessage;
|
||||
import org.javacord.api.event.message.MessageCreateEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -22,24 +22,19 @@ public class UserMessageHandler implements MessageHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void handle(MessageCreateEvent event) {
|
||||
public void handle(MessageCreateEvent event) throws ResponseException, IOException, InterruptedException {
|
||||
String content = event.getReadableMessageContent();
|
||||
var userId = event.getMessageAuthor().getId();
|
||||
var systemMessage = this.userDBService.getConfig(String.valueOf(userId)).get().systemMessage();
|
||||
var request = new ChatGPTRequestBuilder().simpleRequest(content, systemMessage);
|
||||
try {
|
||||
var response = this.chatGPTService.submit(request);
|
||||
if (response.choices().size() < 1) {
|
||||
event.getChannel().sendMessage("No response available");
|
||||
return;
|
||||
}
|
||||
var answer = response.choices().get(0).message().content();
|
||||
logUserMessage(event, content, answer, response.usage().totalTokens());
|
||||
event.getMessage().reply(answer);
|
||||
} catch (IOException | InterruptedException | ResponseException e) {
|
||||
logger.error("Reading a message from the listener", e);
|
||||
event.getChannel().sendMessage("Sorry but something went wrong :(");
|
||||
var response = this.chatGPTService.submit(request);
|
||||
if (response.choices().size() < 1) {
|
||||
event.getMessage().reply("No response available");
|
||||
return;
|
||||
}
|
||||
var answer = response.choices().get(0).message().content();
|
||||
logUserMessage(event, content, answer, response.usage().totalTokens());
|
||||
event.getMessage().reply(answer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.net.http.HttpResponse;
|
|||
import java.time.Duration;
|
||||
|
||||
public class ChatGPTService {
|
||||
private final String url = "https://api.openai.com/v1/chat/completions";
|
||||
private static final String url = "https://api.openai.com/v1/chat/completions";
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private final String apiKey;
|
||||
private final HttpClient httpClient;
|
||||
|
@ -29,15 +29,14 @@ public class ChatGPTService {
|
|||
.POST(HttpRequest.BodyPublishers.ofByteArray(data))
|
||||
.setHeader("Content-Type", "application/json")
|
||||
.setHeader("Authorization", "Bearer " + this.apiKey)
|
||||
.timeout(Duration.ofSeconds(30))
|
||||
.timeout(Duration.ofSeconds(90))
|
||||
.build();
|
||||
|
||||
var responseStream = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
if (responseStream.statusCode() != 200) {
|
||||
throw new ResponseException("Response status code was not 200: " + responseStream.statusCode());
|
||||
}
|
||||
ChatGPTResponse response = mapper.readValue(responseStream.body(), ChatGPTResponse.class);
|
||||
|
||||
return response;
|
||||
return mapper.readValue(responseStream.body(), ChatGPTResponse.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,46 +3,49 @@ version: "3"
|
|||
services:
|
||||
bot:
|
||||
image: git.hhhammer.de/hamburghammer/dchat/bot:latest
|
||||
build: ./bot/
|
||||
build:
|
||||
dockerfile: Containerfile
|
||||
context: .
|
||||
target: bot
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
- migration
|
||||
environment:
|
||||
- DISCORD_API_KEY=<discord-api-key>
|
||||
- OPENAI_API_KEY=<openai-api-key>
|
||||
- POSTGRES_USER=<postgres-user>
|
||||
- POSTGRES_PASSWORD=<postgres-password>
|
||||
- POSTGRES_URL=jdbc:postgresql://db:5432/dchat
|
||||
- JDK_JAVA_OPTIONS="--enable-preview"
|
||||
|
||||
web:
|
||||
image: git.hhhammer.de/hamburghammer/dchat/web:latest
|
||||
build: ./web/
|
||||
build:
|
||||
dockerfile: Containerfile
|
||||
context: .
|
||||
target: web
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
- migration
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
- POSTGRES_USER=<postgres-user>
|
||||
- POSTGRES_PASSWORD=<postgres-password>
|
||||
- POSTGRES_URL=jdbc:postgresql://db:5432/dchat
|
||||
- JDK_JAVA_OPTIONS="--enable-preview"
|
||||
|
||||
migration:
|
||||
image: git.hhhammer.de/hamburghammer/dchat/migration:latest
|
||||
build: ./migration/
|
||||
build:
|
||||
dockerfile: Containerfile
|
||||
context: .
|
||||
target: migration
|
||||
depends_on:
|
||||
- db
|
||||
environment:
|
||||
- POSTGRES_USER=<postgres-user>
|
||||
- POSTGRES_PASSWORD=<postgres-password>
|
||||
- POSTGRES_URL=jdbc:postgresql://db:5432/dchat
|
||||
|
||||
db:
|
||||
image: docker.io/postgres:15-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=<postgres-user>
|
||||
- POSTGRES_PASSWORD=<postgres-password>
|
||||
- POSTGRES_DB=dchat
|
||||
volumes:
|
||||
- ./data/postgres:/var/lib/postgresql/data:rw
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready", "-d", $POSTGRES_DB ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
|
Loading…
Reference in a new issue