mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-05-10 19:00:57 +08:00
feat: add whisper messaging and command history
- Add :msg/:w command for private whisper messages between users - Add command history with UP/DOWN arrow navigation in command mode - Store up to 16 commands in ring buffer per client session - Update help text to document new features - Fix unit test Makefile to link common.c
This commit is contained in:
parent
0de13a6314
commit
a832fe703f
3 changed files with 97 additions and 2 deletions
|
|
@ -21,6 +21,9 @@ typedef struct client {
|
||||||
int help_scroll_pos;
|
int help_scroll_pos;
|
||||||
bool show_help;
|
bool show_help;
|
||||||
char command_input[256];
|
char command_input[256];
|
||||||
|
char command_history[16][256];
|
||||||
|
int command_history_count;
|
||||||
|
int command_history_pos;
|
||||||
char command_output[2048];
|
char command_output[2048];
|
||||||
char exec_command[MAX_EXEC_COMMAND_LEN];
|
char exec_command[MAX_EXEC_COMMAND_LEN];
|
||||||
char ssh_login[MAX_USERNAME_LEN];
|
char ssh_login[MAX_USERNAME_LEN];
|
||||||
|
|
|
||||||
|
|
@ -1084,6 +1084,21 @@ static void execute_command(client_t *client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Save to command history */
|
||||||
|
if (cmd[0] != '\0') {
|
||||||
|
int max_hist = 16;
|
||||||
|
if (client->command_history_count >= max_hist) {
|
||||||
|
memmove(&client->command_history[0], &client->command_history[1],
|
||||||
|
(max_hist - 1) * sizeof(client->command_history[0]));
|
||||||
|
client->command_history_count = max_hist - 1;
|
||||||
|
}
|
||||||
|
strncpy(client->command_history[client->command_history_count],
|
||||||
|
cmd, sizeof(client->command_history[0]) - 1);
|
||||||
|
client->command_history[client->command_history_count][sizeof(client->command_history[0]) - 1] = '\0';
|
||||||
|
client->command_history_count++;
|
||||||
|
client->command_history_pos = client->command_history_count;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(cmd, "list") == 0 || strcmp(cmd, "users") == 0 ||
|
if (strcmp(cmd, "list") == 0 || strcmp(cmd, "users") == 0 ||
|
||||||
strcmp(cmd, "who") == 0) {
|
strcmp(cmd, "who") == 0) {
|
||||||
buffer_appendf(output, sizeof(output), &pos,
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
|
@ -1116,10 +1131,52 @@ static void execute_command(client_t *client) {
|
||||||
" Available Commands\n"
|
" Available Commands\n"
|
||||||
"========================================\n"
|
"========================================\n"
|
||||||
"list, users, who - Show online users\n"
|
"list, users, who - Show online users\n"
|
||||||
|
"msg/w <user> <text> - Whisper to user\n"
|
||||||
"help, commands - Show this help\n"
|
"help, commands - Show this help\n"
|
||||||
"clear, cls - Clear command output\n"
|
"clear, cls - Clear command output\n"
|
||||||
|
"Up/Down arrows - Command history\n"
|
||||||
"========================================\n");
|
"========================================\n");
|
||||||
|
|
||||||
|
} else if (strncmp(cmd, "msg ", 4) == 0 || strncmp(cmd, "w ", 2) == 0) {
|
||||||
|
char *rest = (cmd[0] == 'w') ? cmd + 2 : cmd + 4;
|
||||||
|
while (*rest == ' ') rest++;
|
||||||
|
char target_name[MAX_USERNAME_LEN] = {0};
|
||||||
|
int ti = 0;
|
||||||
|
while (*rest && *rest != ' ' && ti < MAX_USERNAME_LEN - 1) {
|
||||||
|
target_name[ti++] = *rest++;
|
||||||
|
}
|
||||||
|
while (*rest == ' ') rest++;
|
||||||
|
|
||||||
|
if (target_name[0] == '\0' || rest[0] == '\0') {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Usage: msg <username> <message>\n"
|
||||||
|
" w <username> <message>\n");
|
||||||
|
} else {
|
||||||
|
bool found = false;
|
||||||
|
pthread_rwlock_rdlock(&g_room->lock);
|
||||||
|
for (int i = 0; i < g_room->client_count; i++) {
|
||||||
|
if (strcmp(g_room->clients[i]->username, target_name) == 0) {
|
||||||
|
char whisper[MAX_MESSAGE_LEN];
|
||||||
|
snprintf(whisper, sizeof(whisper),
|
||||||
|
"\r\n\033[35m[whisper from %s]: %s\033[0m\r\n",
|
||||||
|
client->username, rest);
|
||||||
|
client_send(g_room->clients[i], whisper, strlen(whisper));
|
||||||
|
g_room->clients[i]->redraw_pending = true;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&g_room->lock);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Whisper sent to %s\n", target_name);
|
||||||
|
} else {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"User '%s' not found\n", target_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strcmp(cmd, "clear") == 0 || strcmp(cmd, "cls") == 0) {
|
} else if (strcmp(cmd, "clear") == 0 || strcmp(cmd, "cls") == 0) {
|
||||||
buffer_appendf(output, sizeof(output), &pos, "Command output cleared\n");
|
buffer_appendf(output, sizeof(output), &pos, "Command output cleared\n");
|
||||||
|
|
||||||
|
|
@ -1290,7 +1347,39 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MODE_COMMAND:
|
case MODE_COMMAND:
|
||||||
if (key == 27) { /* ESC */
|
if (key == 27) { /* ESC - check for arrow key sequences */
|
||||||
|
char seq[2];
|
||||||
|
int n = ssh_channel_read_timeout(client->channel, seq, 1, 0, 50);
|
||||||
|
if (n == 1 && seq[0] == '[') {
|
||||||
|
n = ssh_channel_read_timeout(client->channel, &seq[1], 1, 0, 50);
|
||||||
|
if (n == 1) {
|
||||||
|
if (seq[1] == 'A') { /* Up arrow */
|
||||||
|
if (client->command_history_count > 0 &&
|
||||||
|
client->command_history_pos > 0) {
|
||||||
|
client->command_history_pos--;
|
||||||
|
strncpy(client->command_input,
|
||||||
|
client->command_history[client->command_history_pos],
|
||||||
|
sizeof(client->command_input) - 1);
|
||||||
|
client->command_input[sizeof(client->command_input) - 1] = '\0';
|
||||||
|
tui_render_screen(client);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (seq[1] == 'B') { /* Down arrow */
|
||||||
|
if (client->command_history_pos < client->command_history_count - 1) {
|
||||||
|
client->command_history_pos++;
|
||||||
|
strncpy(client->command_input,
|
||||||
|
client->command_history[client->command_history_pos],
|
||||||
|
sizeof(client->command_input) - 1);
|
||||||
|
client->command_input[sizeof(client->command_input) - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
client->command_history_pos = client->command_history_count;
|
||||||
|
client->command_input[0] = '\0';
|
||||||
|
}
|
||||||
|
tui_render_screen(client);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
client->mode = MODE_NORMAL;
|
client->mode = MODE_NORMAL;
|
||||||
client->command_input[0] = '\0';
|
client->command_input[0] = '\0';
|
||||||
tui_render_screen(client);
|
tui_render_screen(client);
|
||||||
|
|
@ -1339,6 +1428,8 @@ void* client_handle_session(void *arg) {
|
||||||
client->mode = MODE_INSERT;
|
client->mode = MODE_INSERT;
|
||||||
client->help_lang = LANG_ZH;
|
client->help_lang = LANG_ZH;
|
||||||
client->connected = true;
|
client->connected = true;
|
||||||
|
client->command_history_count = 0;
|
||||||
|
client->command_history_pos = 0;
|
||||||
|
|
||||||
/* Check for exec command */
|
/* Check for exec command */
|
||||||
if (client->exec_command[0] != '\0') {
|
if (client->exec_command[0] != '\0') {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ LDFLAGS = -pthread
|
||||||
# Source files
|
# Source files
|
||||||
UTF8_SRC = ../../src/utf8.c
|
UTF8_SRC = ../../src/utf8.c
|
||||||
MESSAGE_SRC = ../../src/message.c
|
MESSAGE_SRC = ../../src/message.c
|
||||||
|
COMMON_SRC = ../../src/common.c
|
||||||
|
|
||||||
TESTS = test_utf8 test_message
|
TESTS = test_utf8 test_message
|
||||||
|
|
||||||
|
|
@ -16,7 +17,7 @@ all: $(TESTS)
|
||||||
test_utf8: test_utf8.c $(UTF8_SRC)
|
test_utf8: test_utf8.c $(UTF8_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
test_message: test_message.c $(MESSAGE_SRC) $(UTF8_SRC)
|
test_message: test_message.c $(MESSAGE_SRC) $(UTF8_SRC) $(COMMON_SRC)
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||||
|
|
||||||
run: all
|
run: all
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue