3
0
mirror of https://github.com/ergochat/ergo.git synced 2026-04-26 10:38:23 +02:00

add support for draft/chathistory-end tag (#2379)

This commit is contained in:
Shivaram Lingamneni 2026-04-13 19:50:24 -07:00 committed by GitHub
parent 10da32292f
commit 9556e5c025
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 51 additions and 24 deletions

View File

@ -63,8 +63,9 @@ const (
// BOT mode: https://ircv3.net/specs/extensions/bot-mode
BotTagName = "bot"
// https://ircv3.net/specs/extensions/chathistory
ChathistoryTargetsBatchType = "draft/chathistory-targets"
ExtendedISupportBatchType = "draft/isupport"
ChathistoryTargetsBatchType = "draft/chathistory-targets"
ExtendedISupportBatchType = "draft/isupport"
ChathistoryEndOfPaginationTag = "draft/chathistory-end"
)
func init() {

View File

@ -979,7 +979,7 @@ func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, sk
}
}
if 0 < numItems {
channel.replayHistoryItems(rb, items, false)
channel.replayHistoryItems(rb, items, false, false)
rb.Flush(true)
}
}
@ -1069,7 +1069,7 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
client.server.logger.Debug("channels", fmt.Sprintf("%s left channel %s", details.nick, chname))
}
func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.Item, chathistoryCommand bool) {
func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.Item, chathistoryCommand, endOfPagination bool) {
// send an empty batch if necessary, as per the CHATHISTORY spec
chname := channel.Name()
client := rb.target
@ -1089,7 +1089,11 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
}
}
batchID := rb.StartNestedBatch("chathistory", chname)
var batchTags map[string]string
if chathistoryCommand && endOfPagination {
batchTags = endOfPaginationTag
}
batchID := rb.StartNestedBatch(batchTags, "chathistory", chname)
defer rb.EndNestedBatch(batchID)
for _, item := range items {

View File

@ -939,14 +939,18 @@ func (session *Session) Ping() {
session.Send(nil, "", "PING", session.client.Nick())
}
func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.Item, target string, chathistoryCommand bool) {
func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.Item, target string, chathistoryCommand, endOfPagination bool) {
var batchID string
details := client.Details()
nick := details.nick
if target == "" {
target = nick
}
batchID = rb.StartNestedBatch("chathistory", target)
var batchTags map[string]string
if chathistoryCommand && endOfPagination {
batchTags = endOfPaginationTag
}
batchID = rb.StartNestedBatch(batchTags, "chathistory", target)
isSelfMessage := func(item *history.Item) bool {
// XXX: Params[0] is the message target. if the source of this message is an in-memory

View File

@ -38,6 +38,12 @@ import (
"github.com/ergochat/ergo/irc/webpush"
)
var (
endOfPaginationTag = map[string]string{
caps.ChathistoryEndOfPaginationTag: "",
}
)
// helper function to parse ACC callbacks, e.g., mailto:person@example.com, tel:16505551234
func parseCallback(spec string, config *Config) (callbackNamespace string, callbackValue string, err error) {
// XXX if we don't require verification, ignore any callback that was passed here
@ -702,6 +708,7 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
var err error
var disabled, listTargets bool
var targets []history.TargetListing
var endOfPagination bool
defer func() {
// errors are sent either without a batch, or in a draft/labeled-response batch as usual
if disabled {
@ -715,7 +722,11 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
} else {
// successful responses are sent as a chathistory or history batch
if listTargets {
batchID := rb.StartNestedBatch(caps.ChathistoryTargetsBatchType)
var batchTags map[string]string
if endOfPagination {
batchTags = endOfPaginationTag
}
batchID := rb.StartNestedBatch(batchTags, caps.ChathistoryTargetsBatchType)
defer rb.EndNestedBatch(batchID)
for _, target := range targets {
name := server.UnfoldName(target.CfName)
@ -723,9 +734,9 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
target.Time.Format(utils.IRCv3TimestampFormat))
}
} else if channel != nil {
channel.replayHistoryItems(rb, items, true)
channel.replayHistoryItems(rb, items, true, endOfPagination)
} else {
client.replayPrivmsgHistory(rb, items, target, true)
client.replayPrivmsgHistory(rb, items, target, true, endOfPagination)
}
}
}()
@ -850,6 +861,12 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
return
}
targets, err = client.listTargets(start.Time, end.Time, limit)
// adding the end-of-pagination tag is best effort; it's OK if we omit it
// in the edge case where we're at the end of the window but we coincidentally
// filled the whole limit (the client will just incur an additional roundtrip
// to page one more time). in contrast, a false positive would be problematic
// because the client would stop paging.
endOfPagination = (err == nil) && len(targets) < limit
} else {
channel, sequence, err = server.GetHistorySequence(nil, client, target)
if err != nil || sequence == nil {
@ -860,6 +877,7 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
} else {
items, err = sequence.Between(start, end, limit)
}
endOfPagination = (err == nil) && len(items) < limit
}
return
}
@ -1246,9 +1264,9 @@ func historyHandler(server *Server, client *Client, msg ircmsg.Message, rb *Resp
if len(items) != 0 {
if channel != nil {
channel.replayHistoryItems(rb, items, true)
channel.replayHistoryItems(rb, items, true, false)
} else {
client.replayPrivmsgHistory(rb, items, "", true)
client.replayPrivmsgHistory(rb, items, "", true, false)
}
}
return false
@ -3285,7 +3303,7 @@ func metadataRegisteredHandler(client *Client, config *Config, subcommand string
return
}
batchId := rb.StartNestedBatch("metadata", target)
batchId := rb.StartNestedBatch(nil, "metadata", target)
defer rb.EndNestedBatch(batchId)
for _, key := range params[2:] {
@ -3429,7 +3447,7 @@ func metadataSubsHandler(client *Client, subcommand string, params []string, rb
subs := rb.session.MetadataSubscriptions()
batchID := rb.StartNestedBatch("metadata-subs")
batchID := rb.StartNestedBatch(nil, "metadata-subs")
defer rb.EndNestedBatch(batchID)
chunked := utils.ChunkifyParams(maps.Keys(subs), lineLength)

View File

@ -67,7 +67,7 @@ func broadcastMetadataUpdate(server *Server, sessions iter.Seq[*Session], origin
}
func syncClientMetadata(server *Server, rb *ResponseBuffer, target *Client) {
batchId := rb.StartNestedBatch("metadata", target.Nick())
batchId := rb.StartNestedBatch(nil, "metadata", target.Nick())
defer rb.EndNestedBatch(batchId)
subs := rb.session.MetadataSubscriptions()
@ -81,7 +81,7 @@ func syncClientMetadata(server *Server, rb *ResponseBuffer, target *Client) {
}
func syncChannelMetadata(server *Server, rb *ResponseBuffer, channel *Channel) {
batchId := rb.StartNestedBatch("metadata", channel.Name())
batchId := rb.StartNestedBatch(nil, "metadata", channel.Name())
defer rb.EndNestedBatch(batchId)
subs := rb.session.MetadataSubscriptions()
@ -107,7 +107,7 @@ func syncChannelMetadata(server *Server, rb *ResponseBuffer, channel *Channel) {
}
func playMetadataList(rb *ResponseBuffer, nick, target string, values map[string]string) {
batchId := rb.StartNestedBatch("metadata", target)
batchId := rb.StartNestedBatch(nil, "metadata", target)
defer rb.EndNestedBatch(batchId)
for key, val := range values {
@ -117,7 +117,7 @@ func playMetadataList(rb *ResponseBuffer, nick, target string, values map[string
}
func playMetadataVerbBatch(rb *ResponseBuffer, target string, values map[string]string) {
batchId := rb.StartNestedBatch("metadata", target)
batchId := rb.StartNestedBatch(nil, "metadata", target)
defer rb.EndNestedBatch(batchId)
for key, val := range values {

View File

@ -192,7 +192,7 @@ func (rb *ResponseBuffer) sendBatchEnd(blocking bool) {
// Starts a nested batch (see the ResponseBuffer struct definition for a description of
// how this works)
func (rb *ResponseBuffer) StartNestedBatch(batchType string, params ...string) (batchID string) {
func (rb *ResponseBuffer) StartNestedBatch(tags map[string]string, batchType string, params ...string) (batchID string) {
if !rb.session.capabilities.Has(caps.Batch) {
return
}
@ -201,7 +201,7 @@ func (rb *ResponseBuffer) StartNestedBatch(batchType string, params ...string) (
msgParams[0] = "+" + batchID
msgParams[1] = batchType
copy(msgParams[2:], params)
rb.AddMessage(ircmsg.MakeMessage(nil, rb.target.server.name, "BATCH", msgParams...))
rb.AddMessage(ircmsg.MakeMessage(tags, rb.target.server.name, "BATCH", msgParams...))
rb.nestedBatches = append(rb.nestedBatches, batchID)
return
}

View File

@ -535,7 +535,7 @@ func (server *Server) RplISupport(client *Client, rb *ResponseBuffer) {
func (server *Server) sendRplISupportLines(client *Client, rb *ResponseBuffer, lines [][]string) {
if rb.session.capabilities.Has(caps.ExtendedISupport) {
batchID := rb.StartNestedBatch(caps.ExtendedISupportBatchType)
batchID := rb.StartNestedBatch(nil, caps.ExtendedISupportBatchType)
defer rb.EndNestedBatch(batchID)
}
finalText := "are supported by this server"

View File

@ -203,7 +203,7 @@ func zncPlayPrivmsgsFrom(client *Client, rb *ResponseBuffer, target string, star
zncMax := client.server.Config().History.ZNCMax
items, err := sequence.Between(history.Selector{Time: start}, history.Selector{Time: end}, zncMax)
if err == nil && len(items) != 0 {
client.replayPrivmsgHistory(rb, items, target, false)
client.replayPrivmsgHistory(rb, items, target, false, false)
}
}
@ -211,7 +211,7 @@ func zncPlayPrivmsgsFromAll(client *Client, rb *ResponseBuffer, start, end time.
zncMax := client.server.Config().History.ZNCMax
items, err := client.privmsgsBetween(start, end, maxDMTargetsForAutoplay, zncMax)
if err == nil && len(items) != 0 {
client.replayPrivmsgHistory(rb, items, "", false)
client.replayPrivmsgHistory(rb, items, "", false, false)
}
}

@ -1 +1 @@
Subproject commit 17fac53c5cdfe78caecb601399512574f242cc85
Subproject commit c85b574765d67a0270d9c7b35f37ab57e7f95774