Skip to content

Commit 8cf29b2

Browse files
MrLeninclaude
andcommitted
Support draft/chathistory-end tag for explicit end-of-history signalling
Parse the draft/chathistory-end tag from BATCH START lines and use it to set history_exhausted immediately, avoiding an extra empty-batch round-trip. Existing heuristics (empty batch, stale count) remain as fallback for servers that don't send the tag. See ircv3/ircv3-specifications#598 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 486142e commit 8cf29b2

3 files changed

Lines changed: 23 additions & 7 deletions

File tree

src/common/chathistory.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,7 @@ typedef struct {
11881188
time_t newest_timestamp;
11891189
char *batch_oldest_msgid; /* owned copy */
11901190
gboolean is_catchup;
1191+
gboolean chathistory_end; /* server signalled no more history (draft/chathistory-end) */
11911192
guint idle_tag;
11921193
scrollback_db *db; /* for transaction begin/commit between chunks */
11931194
} chathistory_chunk_state;
@@ -1236,6 +1237,10 @@ finish_batch_processing (chathistory_chunk_state *chunk)
12361237
sess->oldest_msgid = g_strdup (chunk->batch_oldest_msgid);
12371238
}
12381239

1240+
/* Server explicitly signalled end of history via draft/chathistory-end tag */
1241+
if (chunk->chathistory_end)
1242+
sess->history_exhausted = TRUE;
1243+
12391244
/* Catch-up loop */
12401245
if (chunk->is_catchup)
12411246
{
@@ -1244,10 +1249,9 @@ finish_batch_processing (chathistory_chunk_state *chunk)
12441249
/* --- BEFORE pagination phase --- */
12451250
sess->history_catchup_retrieved += chunk->msg_count;
12461251

1247-
/* Empty batch → server has no more history */
1248-
if (chunk->raw_count == 0)
1252+
/* No more history (empty batch or chathistory-end tag) */
1253+
if (chunk->raw_count == 0 || sess->history_exhausted)
12491254
{
1250-
sess->history_exhausted = TRUE;
12511255
finish_catchup (sess);
12521256
chathistory_check_before_catchup (serv);
12531257
return;
@@ -1467,12 +1471,16 @@ chathistory_process_batch (server *serv, batch_info *batch)
14671471
if (!batch->messages)
14681472
{
14691473
gboolean used_msgid = sess->history_request_used_msgid;
1474+
if (batch->chathistory_end)
1475+
sess->history_exhausted = TRUE;
14701476
chathistory_request_complete (sess);
14711477
if (is_catchup)
14721478
{
14731479
/* Server may not recognize our msgid (e.g., server restart).
1474-
* Fall back to timestamp-based LATEST, then LATEST *. */
1475-
if (used_msgid && sess->scrollback_newest_time > 0)
1480+
* Fall back to timestamp-based LATEST, then LATEST *.
1481+
* But not if chathistory-end tells us there's nothing. */
1482+
if (!sess->history_exhausted &&
1483+
used_msgid && sess->scrollback_newest_time > 0)
14761484
{
14771485
char ref[64];
14781486
g_snprintf (ref, sizeof (ref), "timestamp=%" G_GINT64_FORMAT,
@@ -1481,8 +1489,8 @@ chathistory_process_batch (server *serv, batch_info *batch)
14811489
return;
14821490
}
14831491
/* Catch-up complete — no new messages since last disconnect.
1484-
* Don't set history_exhausted: older history may still exist
1485-
* for scroll-to-top requests. */
1492+
* Don't set history_exhausted unless chathistory-end was sent:
1493+
* older history may still exist for scroll-to-top requests. */
14861494
finish_catchup (sess);
14871495
if (serv->chathistory_latest_pending > 0)
14881496
serv->chathistory_latest_pending--;
@@ -1527,6 +1535,7 @@ chathistory_process_batch (server *serv, batch_info *batch)
15271535
sync_state.remaining = batch->messages;
15281536
sync_state.raw_count = raw_count;
15291537
sync_state.is_catchup = is_catchup;
1538+
sync_state.chathistory_end = batch->chathistory_end;
15301539
sync_state.batch_oldest_msgid = (char *)batch_oldest_msgid; /* borrowed, not freed */
15311540

15321541
if (db)
@@ -1552,6 +1561,7 @@ chathistory_process_batch (server *serv, batch_info *batch)
15521561
chunk->remaining = batch->messages;
15531562
chunk->raw_count = raw_count;
15541563
chunk->is_catchup = is_catchup;
1564+
chunk->chathistory_end = batch->chathistory_end;
15551565
chunk->db = db;
15561566
chunk->batch_oldest_msgid = g_strdup (batch_oldest_msgid);
15571567
batch->messages = NULL; /* prevent batch_info_free from freeing */

src/common/hexchat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ typedef struct batch_info
520520
char *outer_batch; /* For nested batches: reference to parent batch ID */
521521
char *label; /* labeled-response: label tag from BATCH START */
522522
char *msgid; /* msgid tag from BATCH START (for echo confirmation) */
523+
unsigned int chathistory_end:1; /* draft/chathistory-end tag on BATCH START */
523524
GSList *messages; /* Collected messages (each element is batch_message) */
524525
time_t started; /* When the batch was opened (wall clock, for stale detection) */
525526
time_t server_time; /* server-time from BATCH START tags (for nested batch timestamps) */

src/common/inbound.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,11 @@ batch_info_new (const char *id, const char *type, char *word[],
19051905
if (tags_data->msgid)
19061906
batch->msgid = g_strdup (tags_data->msgid);
19071907

1908+
/* IRCv3 draft/chathistory-end: server signals no more history available */
1909+
if (tags_data->all_tags &&
1910+
g_hash_table_contains (tags_data->all_tags, "draft/chathistory-end"))
1911+
batch->chathistory_end = TRUE;
1912+
19081913
/* Count and copy parameters (starting from word[5]) */
19091914
param_count = 0;
19101915
for (i = 5; word[i] && *word[i]; i++)

0 commit comments

Comments
 (0)