1
0
Fork 0
forked from External/ergo

channel history modifications

This commit is contained in:
CEF Server 2024-11-18 19:38:49 +00:00
parent f4c03b6765
commit 711af30aa8
5 changed files with 106 additions and 41 deletions

View file

@ -971,7 +971,7 @@ func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, sk
} }
} }
if 0 < numItems { if 0 < numItems {
channel.replayHistoryItems(rb, items, false) channel.replayHistoryItems(rb, items, false, "", "", numItems)
rb.Flush(true) rb.Flush(true)
} }
} }
@ -1062,7 +1062,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)) 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 bool, identifier string, preposition string, limit int) {
// send an empty batch if necessary, as per the CHATHISTORY spec // send an empty batch if necessary, as per the CHATHISTORY spec
chname := channel.Name() chname := channel.Name()
client := rb.target client := rb.target
@ -1082,7 +1082,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
} }
} }
batchID := rb.StartNestedBatch("chathistory", chname) batchID := rb.StartNestedBatch("chathistory", chname, identifier, preposition, strconv.Itoa(limit))
defer rb.EndNestedBatch(batchID) defer rb.EndNestedBatch(batchID)
for _, item := range items { for _, item := range items {

View file

@ -35,8 +35,8 @@ import (
) )
const ( const (
// maximum IRC line length, not including tags // Set to 4096 because CEF doesn't care about compatibility
DefaultMaxLineLen = 512 DefaultMaxLineLen = 4096
// IdentTimeout is how long before our ident (username) check times out. // IdentTimeout is how long before our ident (username) check times out.
IdentTimeout = time.Second + 500*time.Millisecond IdentTimeout = time.Second + 500*time.Millisecond
@ -649,6 +649,17 @@ func (client *Client) run(session *Session) {
firstLine := !isReattach firstLine := !isReattach
correspondents, _ := client.server.historyDB.GetPMs(client.NickCasefolded())
// For safety, let's keep this within the 4096 character barrier
var lineBuilder utils.TokenLineBuilder
lineBuilder.Initialize(MaxLineLen, ",")
for username, timestamp := range correspondents {
lineBuilder.Add(fmt.Sprintf("%s %d", client.server.getCurrentNick(username), timestamp))
}
for _, message := range lineBuilder.Lines() {
session.Send(nil, client.server.name, "PMS", message)
}
for { for {
var invalidUtf8 bool var invalidUtf8 bool
line, err := session.socket.Read() line, err := session.socket.Read()
@ -862,14 +873,14 @@ func (session *Session) Ping() {
session.Send(nil, "", "PING", session.client.Nick()) 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 bool, identifier string, preposition string, limit int) {
var batchID string var batchID string
details := client.Details() details := client.Details()
nick := details.nick nick := details.nick
if target == "" { if target == "" {
target = nick target = nick
} }
batchID = rb.StartNestedBatch("chathistory", target) batchID = rb.StartNestedBatch("chathistory", target, identifier, preposition, strconv.Itoa(limit))
isSelfMessage := func(item *history.Item) bool { isSelfMessage := func(item *history.Item) bool {
// XXX: Params[0] is the message target. if the source of this message is an in-memory // XXX: Params[0] is the message target. if the source of this message is an in-memory

View file

@ -712,6 +712,14 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
var err error var err error
var listTargets bool var listTargets bool
var targets []history.TargetListing var targets []history.TargetListing
var _, batchIdentifier = msg.GetTag("identifier")
var assuredPreposition = "error"
var limit int
if len(batchIdentifier) == 0 {
batchIdentifier = "UNIDENTIFIED"
}
defer func() { defer func() {
// errors are sent either without a batch, or in a draft/labeled-response batch as usual // errors are sent either without a batch, or in a draft/labeled-response batch as usual
if err == utils.ErrInvalidParams { if err == utils.ErrInvalidParams {
@ -731,9 +739,9 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
target.Time.Format(IRCv3TimestampFormat)) target.Time.Format(IRCv3TimestampFormat))
} }
} else if channel != nil { } else if channel != nil {
channel.replayHistoryItems(rb, items, true) channel.replayHistoryItems(rb, items, true, batchIdentifier, assuredPreposition, limit)
} else { } else {
client.replayPrivmsgHistory(rb, items, target, true) client.replayPrivmsgHistory(rb, items, target, true, batchIdentifier, assuredPreposition, limit)
} }
} }
}() }()
@ -786,7 +794,6 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
paramPos := 2 paramPos := 2
var start, end history.Selector var start, end history.Selector
var limit int
switch preposition { switch preposition {
case "targets": case "targets":
// use the same selector parsing as BETWEEN, // use the same selector parsing as BETWEEN,
@ -841,6 +848,7 @@ func chathistoryHandler(server *Server, client *Client, msg ircmsg.Message, rb *
err = utils.ErrInvalidParams err = utils.ErrInvalidParams
return return
} }
assuredPreposition = preposition
if listTargets { if listTargets {
targets, err = client.listTargets(start, end, limit) targets, err = client.listTargets(start, end, limit)
@ -1227,6 +1235,8 @@ Get an explanation of <argument>, or "index" for a list of help topics.`), rb)
// HISTORY alice 15 // HISTORY alice 15
// HISTORY #darwin 1h // HISTORY #darwin 1h
func historyHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool { func historyHandler(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool {
rb.Notice(client.t("This command is currently disabled. Please use CHATHISTORY"))
/*
config := server.Config() config := server.Config()
if !config.History.Enabled { if !config.History.Enabled {
rb.Notice(client.t("This command has been disabled by the server administrators")) rb.Notice(client.t("This command has been disabled by the server administrators"))
@ -1243,13 +1253,16 @@ func historyHandler(server *Server, client *Client, msg ircmsg.Message, rb *Resp
return false return false
} }
var _, batchIdentifier = msg.GetTag("identifier")
if len(items) != 0 { if len(items) != 0 {
if channel != nil { if channel != nil {
channel.replayHistoryItems(rb, items, true) channel.replayHistoryItems(rb, items, true, batchIdentifier)
} else { } else {
client.replayPrivmsgHistory(rb, items, "", true) client.replayPrivmsgHistory(rb, items, "", true, batchIdentifier)
} }
} }
*/
return false return false
} }

View file

@ -623,6 +623,9 @@ func (mysql *MySQL) AddChannelItem(target string, item history.Item, account str
func (mysql *MySQL) insertSequenceEntry(ctx context.Context, target string, messageTime int64, id int64) (err error) { func (mysql *MySQL) insertSequenceEntry(ctx context.Context, target string, messageTime int64, id int64) (err error) {
_, err = mysql.insertSequence.ExecContext(ctx, target, messageTime, id) _, err = mysql.insertSequence.ExecContext(ctx, target, messageTime, id)
if err != nil {
println(target, messageTime, id, ctx)
}
mysql.logError("could not insert sequence entry", err) mysql.logError("could not insert sequence entry", err)
return return
} }
@ -640,17 +643,17 @@ func (mysql *MySQL) insertCorrespondentsEntry(ctx context.Context, target, corre
} }
func (mysql *MySQL) insertBase(ctx context.Context, item history.Item) (id int64, err error) { func (mysql *MySQL) insertBase(ctx context.Context, item history.Item) (id int64, err error) {
value, err := marshalItem(&item) _, err := marshalItem(&item)
if mysql.logError("could not marshal item", err) { if mysql.logError("could not marshal item", err) {
return return
} }
msgidBytes, err := decodeMsgid(item.Message.Msgid) //msgidBytes, err := decodeMsgid(item.Message.Msgid)
if mysql.logError("could not decode msgid", err) { /*if mysql.logError("could not decode msgid", err) {
return return
} }*/
result, err := mysql.insertHistory.ExecContext(ctx, value, msgidBytes) result, err := mysql.insertHistory.ExecContext(ctx, value, item.Message.Msgid)
if mysql.logError("could not insert item", err) { if mysql.logError("could not insert item", err) {
return return
} }
@ -812,7 +815,6 @@ func (mysql *MySQL) Export(account string, writer io.Writer) {
} }
func (mysql *MySQL) lookupMsgid(ctx context.Context, msgid string, includeData bool) (result time.Time, id uint64, data []byte, err error) { func (mysql *MySQL) lookupMsgid(ctx context.Context, msgid string, includeData bool) (result time.Time, id uint64, data []byte, err error) {
decoded, err := decodeMsgid(msgid)
if err != nil { if err != nil {
return return
} }
@ -820,11 +822,14 @@ func (mysql *MySQL) lookupMsgid(ctx context.Context, msgid string, includeData b
if includeData { if includeData {
cols = `sequence.nanotime, conversations.nanotime, history.id, history.data` cols = `sequence.nanotime, conversations.nanotime, history.id, history.data`
} }
// Since CEF uses snowflakes and vanilla ergo uses blobs, we cast as int to make it function.
// May have to adjust it some day
row := mysql.db.QueryRowContext(ctx, fmt.Sprintf(` row := mysql.db.QueryRowContext(ctx, fmt.Sprintf(`
SELECT %s FROM history SELECT %s FROM history
LEFT JOIN sequence ON history.id = sequence.history_id LEFT JOIN sequence ON history.id = sequence.history_id
LEFT JOIN conversations ON history.id = conversations.history_id LEFT JOIN conversations ON history.id = conversations.history_id
WHERE history.msgid = ? LIMIT 1;`, cols), decoded) WHERE history.msgid = CAST(? AS INT) LIMIT 1;`, cols), msgid)
var nanoSeq, nanoConv sql.NullInt64 var nanoSeq, nanoConv sql.NullInt64
if !includeData { if !includeData {
err = row.Scan(&nanoSeq, &nanoConv) err = row.Scan(&nanoSeq, &nanoConv)
@ -1042,6 +1047,7 @@ func (s *mySQLHistorySequence) Between(start, end history.Selector, limit int) (
defer cancel() defer cancel()
startTime := start.Time startTime := start.Time
if start.Msgid != "" { if start.Msgid != "" {
startTime, _, _, err = s.mysql.lookupMsgid(ctx, start.Msgid, false) startTime, _, _, err = s.mysql.lookupMsgid(ctx, start.Msgid, false)
if err != nil { if err != nil {
@ -1055,6 +1061,7 @@ func (s *mySQLHistorySequence) Between(start, end history.Selector, limit int) (
endTime := end.Time endTime := end.Time
if end.Msgid != "" { if end.Msgid != "" {
endTime, _, _, err = s.mysql.lookupMsgid(ctx, end.Msgid, false) endTime, _, _, err = s.mysql.lookupMsgid(ctx, end.Msgid, false)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, nil return nil, nil
@ -1101,3 +1108,37 @@ func (mysql *MySQL) MakeSequence(target, correspondent string, cutoff time.Time)
cutoff: cutoff, cutoff: cutoff,
} }
} }
func (mysql *MySQL) GetPMs(casefoldedUser string) (results map[string]int64, err error) {
if mysql.db == nil {
return
}
results = make(map[string]int64)
ctx, cancel := context.WithTimeout(context.Background(), mysql.getTimeout())
defer cancel()
var queryBuf strings.Builder
args := make([]interface{}, 0)
queryBuf.WriteString(`SELECT max(nanotime), correspondent FROM conversations WHERE target = ? GROUP BY correspondent;`)
args = append(args, casefoldedUser)
rows, err := mysql.db.QueryContext(ctx, queryBuf.String(), args...)
if mysql.logError("could not get pms", err) {
return
}
defer rows.Close()
var last int64
var correspondent string
for rows.Next() {
err = rows.Scan(&last, &correspondent)
if mysql.logError("could not get pms", err) {
return
}
// We really don't need nanosecond precision
results[correspondent] = last / 1000000
}
return
}

View file

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