mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-05-10 19:00:57 +08:00
feat: add :search, :last commands and connection duration in :list
New interactive commands: - :search <keyword> — search messages by content or username (max 20 results) - :last [N] — show last N messages (default 10, max 50) with timestamps - :list now shows connection duration per user (e.g., "5m32s", "2h15m") Adds connected_at timestamp to client_t for session duration tracking. Updated :help to document all new commands.
This commit is contained in:
parent
0de13a6314
commit
524fe10d60
2 changed files with 80 additions and 6 deletions
|
|
@ -27,6 +27,7 @@ typedef struct client {
|
||||||
atomic_bool redraw_pending;
|
atomic_bool redraw_pending;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
atomic_bool connected;
|
atomic_bool connected;
|
||||||
|
time_t connected_at;
|
||||||
int ref_count; /* Reference count for safe cleanup */
|
int ref_count; /* Reference count for safe cleanup */
|
||||||
pthread_mutex_t ref_lock; /* Lock for ref_count */
|
pthread_mutex_t ref_lock; /* Lock for ref_count */
|
||||||
pthread_mutex_t io_lock; /* Serialize SSH channel writes */
|
pthread_mutex_t io_lock; /* Serialize SSH channel writes */
|
||||||
|
|
|
||||||
|
|
@ -1097,11 +1097,21 @@ static void execute_command(client_t *client) {
|
||||||
"----------------------------------------\n",
|
"----------------------------------------\n",
|
||||||
g_room->client_count);
|
g_room->client_count);
|
||||||
|
|
||||||
|
time_t now = time(NULL);
|
||||||
for (int i = 0; i < g_room->client_count; i++) {
|
for (int i = 0; i < g_room->client_count; i++) {
|
||||||
char marker = (g_room->clients[i] == client) ? '*' : ' ';
|
char marker = (g_room->clients[i] == client) ? '*' : ' ';
|
||||||
buffer_appendf(output, sizeof(output), &pos,
|
time_t dur = now - g_room->clients[i]->connected_at;
|
||||||
"%c %d. %s\n", marker, i + 1,
|
int dm = (int)(dur / 60);
|
||||||
g_room->clients[i]->username);
|
int ds = (int)(dur % 60);
|
||||||
|
if (dm >= 60) {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"%c %d. %-20s %dh%dm\n", marker, i + 1,
|
||||||
|
g_room->clients[i]->username, dm / 60, dm % 60);
|
||||||
|
} else {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"%c %d. %-20s %dm%ds\n", marker, i + 1,
|
||||||
|
g_room->clients[i]->username, dm, ds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_rwlock_unlock(&g_room->lock);
|
pthread_rwlock_unlock(&g_room->lock);
|
||||||
|
|
@ -1110,14 +1120,76 @@ static void execute_command(client_t *client) {
|
||||||
"========================================\n"
|
"========================================\n"
|
||||||
"* = you / 你\n");
|
"* = you / 你\n");
|
||||||
|
|
||||||
|
} else if (strncmp(cmd, "search ", 7) == 0) {
|
||||||
|
char *term = cmd + 7;
|
||||||
|
while (*term == ' ') term++;
|
||||||
|
if (term[0] == '\0') {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Usage: search <keyword>\n");
|
||||||
|
} else {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Search: \"%s\"\n"
|
||||||
|
"----------------------------------------\n", term);
|
||||||
|
int found = 0;
|
||||||
|
int total = room_get_message_count(g_room);
|
||||||
|
for (int i = 0; i < total && found < 20; i++) {
|
||||||
|
message_t m;
|
||||||
|
if (room_get_message(g_room, i, &m)) {
|
||||||
|
if (strstr(m.content, term) || strstr(m.username, term)) {
|
||||||
|
char ts[32];
|
||||||
|
struct tm ti;
|
||||||
|
localtime_r(&m.timestamp, &ti);
|
||||||
|
strftime(ts, sizeof(ts), "%H:%M", &ti);
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"[%s] %s: %s\n", ts, m.username, m.content);
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found == 0) {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos, "(no matches)\n");
|
||||||
|
} else {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"----------------------------------------\n"
|
||||||
|
"%d result(s)\n", found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (strncmp(cmd, "last", 4) == 0 &&
|
||||||
|
(cmd[4] == '\0' || cmd[4] == ' ')) {
|
||||||
|
int n = 10;
|
||||||
|
if (cmd[4] == ' ') {
|
||||||
|
int parsed = atoi(cmd + 5);
|
||||||
|
if (parsed > 0 && parsed <= 50) n = parsed;
|
||||||
|
}
|
||||||
|
int total = room_get_message_count(g_room);
|
||||||
|
int start = total - n;
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Last %d message(s):\n"
|
||||||
|
"----------------------------------------\n", total - start);
|
||||||
|
for (int i = start; i < total; i++) {
|
||||||
|
message_t m;
|
||||||
|
if (room_get_message(g_room, i, &m)) {
|
||||||
|
char ts[32];
|
||||||
|
struct tm ti;
|
||||||
|
localtime_r(&m.timestamp, &ti);
|
||||||
|
strftime(ts, sizeof(ts), "%H:%M", &ti);
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"[%s] %s: %s\n", ts, m.username, m.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "commands") == 0) {
|
} else if (strcmp(cmd, "help") == 0 || strcmp(cmd, "commands") == 0) {
|
||||||
buffer_appendf(output, sizeof(output), &pos,
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
"========================================\n"
|
"========================================\n"
|
||||||
" Available Commands\n"
|
" Available Commands\n"
|
||||||
"========================================\n"
|
"========================================\n"
|
||||||
"list, users, who - Show online users\n"
|
"list, users, who - Online users + duration\n"
|
||||||
"help, commands - Show this help\n"
|
"search <keyword> - Search messages\n"
|
||||||
"clear, cls - Clear command output\n"
|
"last [N] - Show last N messages\n"
|
||||||
|
"help, commands - Show this help\n"
|
||||||
|
"clear, cls - Clear command output\n"
|
||||||
"========================================\n");
|
"========================================\n");
|
||||||
|
|
||||||
} else if (strcmp(cmd, "clear") == 0 || strcmp(cmd, "cls") == 0) {
|
} else if (strcmp(cmd, "clear") == 0 || strcmp(cmd, "cls") == 0) {
|
||||||
|
|
@ -1339,6 +1411,7 @@ 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->connected_at = time(NULL);
|
||||||
|
|
||||||
/* Check for exec command */
|
/* Check for exec command */
|
||||||
if (client->exec_command[0] != '\0') {
|
if (client->exec_command[0] != '\0') {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue