Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ jobs:
uses: gradle/actions/wrapper-validation@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
- name: Setup Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
gradle-version: "8.10.2"
- name: Run tests and checks
run: ./gradlew check jacocoAllReport --stacktrace
- name: Publish coverage summary
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.krotname.networkchat.client;

import dev.krotname.networkchat.protocol.ChatMessage;
import dev.krotname.networkchat.protocol.MessageType;
import java.io.IOException;
import java.time.Clock;
import java.time.LocalDateTime;
Expand Down Expand Up @@ -85,6 +86,23 @@ String answerForCommand(ChatMessage message) {
return String.format("Информация для %s: %s", message.sender(), answer);
}

/**
* Answers where the command was asked: privately to the sender of a private command, otherwise in
* the room the command came from. Replying with a plain text message would publish the answer to
* a private question in the general room.
*/
ChatMessage replyTo(ChatMessage message, String answer) {
String sender = getResolvedUserName() == null ? botUserName : getResolvedUserName();
if (message.type() == MessageType.PRIVATE_TEXT) {
return ChatMessage.privateText(answer, sender, message.sender());
}
String room = message.room();
if (room == null || room.isBlank()) {
return ChatMessage.text(answer, sender);
}
return ChatMessage.roomText(answer, sender, room);
}

private final class BotSocketThread extends SocketThread {
private static final String GREETING =
"Привет чатику. Я бот. Понимаю команды: дата, день, месяц, год, время, час, минуты, секунды.";
Expand All @@ -98,9 +116,10 @@ protected void clientHandshake() throws IOException {
@Override
protected void processIncomingMessage(ChatMessage message) {
String answer = answerForCommand(message);
if (answer != null) {
sendTextMessage(answer);
if (answer == null) {
return;
}
sendMessage(replyTo(message, answer));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ private void init(boolean visible) {
new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
// Otherwise a thread waiting in requestConnectionSettings never wakes up.
cancelConnectionSettings();
controller.disconnect();
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,9 @@ private void serverMainLoop(ChatConnection connection, String userName, ClientLi
case PRIVATE_TEXT -> handlePrivateText(message, userName, connection);
case ROOM_JOIN -> handleRoomJoin(message, userName, connection, limits);
case ROOM_LEAVE -> handleRoomLeave(message, userName, connection);
case NAME_ACCEPTED,
case NAME_REQUEST,
USER_NAME,
NAME_ACCEPTED,
USER_ADDED,
USER_REMOVED,
ROOM_ADDED,
Expand All @@ -367,7 +369,6 @@ private void serverMainLoop(ChatConnection connection, String userName, ClientLi
ERROR ->
connection.send(
ChatMessage.withData(MessageType.ERROR, "Unsupported client frame", null));
default -> throw new IOException("Unsupported message type: " + message.type());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;

import dev.krotname.networkchat.protocol.ChatMessage;
import dev.krotname.networkchat.protocol.MessageType;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
Expand All @@ -21,6 +22,29 @@ void answersCommandUsingStructuredSender() {
assertEquals("Информация для alice: 2026", answer);
}

@Test
void answersPrivateCommandsPrivately() {
BotChatClient bot =
new BotChatClient(Clock.fixed(Instant.parse("2026-06-10T12:34:56Z"), ZoneId.of("UTC")));

ChatMessage reply = bot.replyTo(ChatMessage.privateText("год", "alice", "bot"), "answer");

assertEquals(MessageType.PRIVATE_TEXT, reply.type());
assertEquals("alice", reply.recipient());
assertEquals("answer", reply.data());
}

@Test
void answersRoomCommandsInTheSameRoom() {
BotChatClient bot =
new BotChatClient(Clock.fixed(Instant.parse("2026-06-10T12:34:56Z"), ZoneId.of("UTC")));

ChatMessage reply = bot.replyTo(ChatMessage.roomText("год", "alice", "team"), "answer");

assertEquals(MessageType.ROOM_TEXT, reply.type());
assertEquals("team", reply.room());
}

@Test
void ignoresUnknownOrSenderlessCommands() {
BotChatClient bot =
Expand Down
Loading