Changeset 665 in code for trunk


Ignore:
Timestamp:
Nov 3, 2021, 2:17:16 PM (4 years ago)
Author:
delthas
Message:

Add support for draft/event-playback

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/downstream.go

    r664 r665  
    519519        dc.SendMessage(msg)
    520520
    521         if id == "" || !dc.messageSupportsHistory(msg) {
     521        if id == "" || !dc.messageSupportsBacklog(msg) {
    522522                return
    523523        }
     
    530530// isn't enabled.
    531531func (dc *downstreamConn) advanceMessageWithID(msg *irc.Message, id string) {
    532         if id == "" || !dc.messageSupportsHistory(msg) {
     532        if id == "" || !dc.messageSupportsBacklog(msg) {
    533533                return
    534534        }
     
    572572// marshalMessage re-formats a message coming from an upstream connection so
    573573// that it's suitable for being sent on this downstream connection. Only
    574 // messages that may appear in logs are supported, except MODE.
     574// messages that may appear in logs are supported, except MODE messages which
     575// may only appear in single-upstream mode.
    575576func (dc *downstreamConn) marshalMessage(msg *irc.Message, net *network) *irc.Message {
     577        if dc.network != nil {
     578                return msg
     579        }
     580
    576581        msg = msg.Copy()
    577582        msg.Prefix = dc.marshalUserPrefix(net, msg.Prefix)
     
    984989                }
    985990        }
     991
     992        if dc.srv.LogPath != "" && dc.network != nil {
     993                dc.setSupportedCap("draft/event-playback", "")
     994        } else {
     995                dc.unsetSupportedCap("draft/event-playback")
     996        }
    986997}
    987998
     
    12981309}
    12991310
    1300 // messageSupportsHistory checks whether the provided message can be sent as
     1311// messageSupportsBacklog checks whether the provided message can be sent as
    13011312// part of an history batch.
    1302 func (dc *downstreamConn) messageSupportsHistory(msg *irc.Message) bool {
     1313func (dc *downstreamConn) messageSupportsBacklog(msg *irc.Message) bool {
    13031314        // Don't replay all messages, because that would mess up client
    13041315        // state. For instance we just sent the list of users, sending
    13051316        // PART messages for one of these users would be incorrect.
    1306         // TODO: add support for draft/event-playback
    13071317        switch msg.Command {
    13081318        case "PRIVMSG", "NOTICE":
     
    13291339        dc.SendBatch("chathistory", []string{dc.marshalEntity(net, target)}, nil, func(batchRef irc.TagValue) {
    13301340                for _, msg := range history {
    1331                         if !dc.messageSupportsHistory(msg) {
    1332                                 continue
    1333                         }
    1334 
    13351341                        if ch != nil && ch.Detached {
    13361342                                if net.detachedMessageNeedsRelay(ch, msg) {
     
    23272333                }
    23282334
     2335                eventPlayback := dc.caps["draft/event-playback"]
     2336
    23292337                var history []*irc.Message
    23302338                switch subcommand {
    23312339                case "BEFORE":
    2332                         history, err = store.LoadBeforeTime(network, entity, bounds[0], time.Time{}, limit)
     2340                        history, err = store.LoadBeforeTime(network, entity, bounds[0], time.Time{}, limit, eventPlayback)
    23332341                case "AFTER":
    2334                         history, err = store.LoadAfterTime(network, entity, bounds[0], time.Now(), limit)
     2342                        history, err = store.LoadAfterTime(network, entity, bounds[0], time.Now(), limit, eventPlayback)
    23352343                case "BETWEEN":
    23362344                        if bounds[0].Before(bounds[1]) {
    2337                                 history, err = store.LoadAfterTime(network, entity, bounds[0], bounds[1], limit)
     2345                                history, err = store.LoadAfterTime(network, entity, bounds[0], bounds[1], limit, eventPlayback)
    23382346                        } else {
    2339                                 history, err = store.LoadBeforeTime(network, entity, bounds[0], bounds[1], limit)
     2347                                history, err = store.LoadBeforeTime(network, entity, bounds[0], bounds[1], limit, eventPlayback)
    23402348                        }
    23412349                case "TARGETS":
    23422350                        // TODO: support TARGETS in multi-upstream mode
    2343                         targets, err := store.ListTargets(network, bounds[0], bounds[1], limit)
     2351                        targets, err := store.ListTargets(network, bounds[0], bounds[1], limit, eventPlayback)
    23442352                        if err != nil {
    23452353                                dc.logger.Printf("failed fetching targets for chathistory: %v", err)
  • trunk/msgstore.go

    r549 r665  
    1818        // used in history queries.
    1919        LastMsgID(network *network, entity string, t time.Time) (string, error)
     20        // LoadLatestID queries the latest non-event messages for the given network,
     21        // entity and date, up to a count of limit messages, sorted from oldest to newest.
    2022        LoadLatestID(network *network, entity, id string, limit int) ([]*irc.Message, error)
    2123        Append(network *network, entity string, msg *irc.Message) (id string, err error)
     
    3537        // It returns up to limit targets, starting from start and ending on end,
    3638        // both excluded. end may be before or after start.
    37         ListTargets(network *network, start, end time.Time, limit int) ([]chatHistoryTarget, error)
     39        // If events is false, only PRIVMSG/NOTICE messages are considered.
     40        ListTargets(network *network, start, end time.Time, limit int, events bool) ([]chatHistoryTarget, error)
    3841        // LoadBeforeTime loads up to limit messages before start down to end. The
    3942        // returned messages must be between and excluding the provided bounds.
    4043        // end is before start.
    41         LoadBeforeTime(network *network, entity string, start, end time.Time, limit int) ([]*irc.Message, error)
     44        // If events is false, only PRIVMSG/NOTICE messages are considered.
     45        LoadBeforeTime(network *network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
    4246        // LoadBeforeTime loads up to limit messages after start up to end. The
    4347        // returned messages must be between and excluding the provided bounds.
    4448        // end is after start.
    45         LoadAfterTime(network *network, entity string, start, end time.Time, limit int) ([]*irc.Message, error)
     49        // If events is false, only PRIVMSG/NOTICE messages are considered.
     50        LoadAfterTime(network *network, entity string, start, end time.Time, limit int, events bool) ([]*irc.Message, error)
    4651}
    4752
  • trunk/msgstore_fs.go

    r644 r665  
    250250}
    251251
    252 func parseMessage(line, entity string, ref time.Time) (*irc.Message, time.Time, error) {
     252func parseMessage(line, entity string, ref time.Time, events bool) (*irc.Message, time.Time, error) {
    253253        var hour, minute, second int
    254254        _, err := fmt.Sscanf(line, "[%02d:%02d:%02d] ", &hour, &minute, &second)
     
    258258        line = line[11:]
    259259
    260         var cmd, sender, text string
    261         if strings.HasPrefix(line, "<") {
    262                 cmd = "PRIVMSG"
    263                 parts := strings.SplitN(line[1:], "> ", 2)
     260        var cmd string
     261        var prefix *irc.Prefix
     262        var params []string
     263        if events && strings.HasPrefix(line, "*** ") {
     264                parts := strings.SplitN(line[4:], " ", 2)
    264265                if len(parts) != 2 {
    265266                        return nil, time.Time{}, nil
    266267                }
    267                 sender, text = parts[0], parts[1]
    268         } else if strings.HasPrefix(line, "-") {
    269                 cmd = "NOTICE"
    270                 parts := strings.SplitN(line[1:], "- ", 2)
    271                 if len(parts) != 2 {
     268                switch parts[0] {
     269                case "Joins:", "Parts:", "Quits:":
     270                        args := strings.SplitN(parts[1], " ", 3)
     271                        if len(args) < 2 {
     272                                return nil, time.Time{}, nil
     273                        }
     274                        nick := args[0]
     275                        mask := strings.TrimSuffix(strings.TrimPrefix(args[1], "("), ")")
     276                        maskParts := strings.SplitN(mask, "@", 2)
     277                        if len(maskParts) != 2 {
     278                                return nil, time.Time{}, nil
     279                        }
     280                        prefix = &irc.Prefix{
     281                                Name: nick,
     282                                User: maskParts[0],
     283                                Host: maskParts[1],
     284                        }
     285                        var reason string
     286                        if len(args) > 2 {
     287                                reason = strings.TrimSuffix(strings.TrimPrefix(args[2], "("), ")")
     288                        }
     289                        switch parts[0] {
     290                        case "Joins:":
     291                                cmd = "JOIN"
     292                                params = []string{entity}
     293                        case "Parts:":
     294                                cmd = "PART"
     295                                if reason != "" {
     296                                        params = []string{entity, reason}
     297                                } else {
     298                                        params = []string{entity}
     299                                }
     300                        case "Quits:":
     301                                cmd = "QUIT"
     302                                if reason != "" {
     303                                        params = []string{reason}
     304                                }
     305                        }
     306                default:
     307                        nick := parts[0]
     308                        rem := parts[1]
     309                        if r := strings.TrimPrefix(rem, "is now known as "); r != rem {
     310                                cmd = "NICK"
     311                                prefix = &irc.Prefix{
     312                                        Name: nick,
     313                                }
     314                                params = []string{r}
     315                        } else if r := strings.TrimPrefix(rem, "was kicked by "); r != rem {
     316                                args := strings.SplitN(r, " ", 2)
     317                                if len(args) != 2 {
     318                                        return nil, time.Time{}, nil
     319                                }
     320                                cmd = "KICK"
     321                                prefix = &irc.Prefix{
     322                                        Name: args[0],
     323                                }
     324                                reason := strings.TrimSuffix(strings.TrimPrefix(args[1], "("), ")")
     325                                params = []string{entity, nick}
     326                                if reason != "" {
     327                                        params = append(params, reason)
     328                                }
     329                        } else if r := strings.TrimPrefix(rem, "changes topic to "); r != rem {
     330                                cmd = "TOPIC"
     331                                prefix = &irc.Prefix{
     332                                        Name: nick,
     333                                }
     334                                topic := strings.TrimSuffix(strings.TrimPrefix(r, "'"), "'")
     335                                params = []string{entity, topic}
     336                        } else if r := strings.TrimPrefix(rem, "sets mode: "); r != rem {
     337                                cmd = "MODE"
     338                                prefix = &irc.Prefix{
     339                                        Name: nick,
     340                                }
     341                                params = append([]string{entity}, strings.Split(r, " ")...)
     342                        } else {
     343                                return nil, time.Time{}, nil
     344                        }
     345                }
     346        } else {
     347                var sender, text string
     348                if strings.HasPrefix(line, "<") {
     349                        cmd = "PRIVMSG"
     350                        parts := strings.SplitN(line[1:], "> ", 2)
     351                        if len(parts) != 2 {
     352                                return nil, time.Time{}, nil
     353                        }
     354                        sender, text = parts[0], parts[1]
     355                } else if strings.HasPrefix(line, "-") {
     356                        cmd = "NOTICE"
     357                        parts := strings.SplitN(line[1:], "- ", 2)
     358                        if len(parts) != 2 {
     359                                return nil, time.Time{}, nil
     360                        }
     361                        sender, text = parts[0], parts[1]
     362                } else if strings.HasPrefix(line, "* ") {
     363                        cmd = "PRIVMSG"
     364                        parts := strings.SplitN(line[2:], " ", 2)
     365                        if len(parts) != 2 {
     366                                return nil, time.Time{}, nil
     367                        }
     368                        sender, text = parts[0], "\x01ACTION "+parts[1]+"\x01"
     369                } else {
    272370                        return nil, time.Time{}, nil
    273371                }
    274                 sender, text = parts[0], parts[1]
    275         } else if strings.HasPrefix(line, "* ") {
    276                 cmd = "PRIVMSG"
    277                 parts := strings.SplitN(line[2:], " ", 2)
    278                 if len(parts) != 2 {
    279                         return nil, time.Time{}, nil
    280                 }
    281                 sender, text = parts[0], "\x01ACTION "+parts[1]+"\x01"
    282         } else {
    283                 return nil, time.Time{}, nil
     372
     373                prefix = &irc.Prefix{Name: sender}
     374                params = []string{entity, text}
    284375        }
    285376
     
    291382                        "time": irc.TagValue(t.UTC().Format(serverTimeLayout)),
    292383                },
    293                 Prefix:  &irc.Prefix{Name: sender},
     384                Prefix:  prefix,
    294385                Command: cmd,
    295                 Params:  []string{entity, text},
     386                Params:  params,
    296387        }
    297388        return msg, t, nil
    298389}
    299390
    300 func (ms *fsMessageStore) parseMessagesBefore(network *network, entity string, ref time.Time, end time.Time, limit int, afterOffset int64) ([]*irc.Message, error) {
     391func (ms *fsMessageStore) parseMessagesBefore(network *network, entity string, ref time.Time, end time.Time, events bool, limit int, afterOffset int64) ([]*irc.Message, error) {
    301392        path := ms.logPath(network, entity, ref)
    302393        f, err := os.Open(path)
     
    322413
    323414        for sc.Scan() {
    324                 msg, t, err := parseMessage(sc.Text(), entity, ref)
     415                msg, t, err := parseMessage(sc.Text(), entity, ref, events)
    325416                if err != nil {
    326417                        return nil, err
     
    354445}
    355446
    356 func (ms *fsMessageStore) parseMessagesAfter(network *network, entity string, ref time.Time, end time.Time, limit int) ([]*irc.Message, error) {
     447func (ms *fsMessageStore) parseMessagesAfter(network *network, entity string, ref time.Time, end time.Time, events bool, limit int) ([]*irc.Message, error) {
    357448        path := ms.logPath(network, entity, ref)
    358449        f, err := os.Open(path)
     
    368459        sc := bufio.NewScanner(f)
    369460        for sc.Scan() && len(history) < limit {
    370                 msg, t, err := parseMessage(sc.Text(), entity, ref)
     461                msg, t, err := parseMessage(sc.Text(), entity, ref, events)
    371462                if err != nil {
    372463                        return nil, err
     
    386477}
    387478
    388 func (ms *fsMessageStore) LoadBeforeTime(network *network, entity string, start time.Time, end time.Time, limit int) ([]*irc.Message, error) {
     479func (ms *fsMessageStore) LoadBeforeTime(network *network, entity string, start time.Time, end time.Time, limit int, events bool) ([]*irc.Message, error) {
    389480        start = start.In(time.Local)
    390481        end = end.In(time.Local)
     
    393484        tries := 0
    394485        for remaining > 0 && tries < fsMessageStoreMaxTries && end.Before(start) {
    395                 buf, err := ms.parseMessagesBefore(network, entity, start, end, remaining, -1)
     486                buf, err := ms.parseMessagesBefore(network, entity, start, end, events, remaining, -1)
    396487                if err != nil {
    397488                        return nil, err
     
    411502}
    412503
    413 func (ms *fsMessageStore) LoadAfterTime(network *network, entity string, start time.Time, end time.Time, limit int) ([]*irc.Message, error) {
     504func (ms *fsMessageStore) LoadAfterTime(network *network, entity string, start time.Time, end time.Time, limit int, events bool) ([]*irc.Message, error) {
    414505        start = start.In(time.Local)
    415506        end = end.In(time.Local)
     
    418509        tries := 0
    419510        for remaining > 0 && tries < fsMessageStoreMaxTries && start.Before(end) {
    420                 buf, err := ms.parseMessagesAfter(network, entity, start, end, remaining)
     511                buf, err := ms.parseMessagesAfter(network, entity, start, end, events, remaining)
    421512                if err != nil {
    422513                        return nil, err
     
    461552                }
    462553
    463                 buf, err := ms.parseMessagesBefore(network, entity, t, time.Time{}, remaining, offset)
     554                buf, err := ms.parseMessagesBefore(network, entity, t, time.Time{}, false, remaining, offset)
    464555                if err != nil {
    465556                        return nil, err
     
    479570}
    480571
    481 func (ms *fsMessageStore) ListTargets(network *network, start, end time.Time, limit int) ([]chatHistoryTarget, error) {
     572func (ms *fsMessageStore) ListTargets(network *network, start, end time.Time, limit int, events bool) ([]chatHistoryTarget, error) {
    482573        start = start.In(time.Local)
    483574        end = end.In(time.Local)
  • trunk/msgstore_memory.go

    r517 r665  
    7575
    7676func (ms *memoryMessageStore) Append(network *network, entity string, msg *irc.Message) (string, error) {
     77        switch msg.Command {
     78        case "PRIVMSG", "NOTICE":
     79        default:
     80                return "", nil
     81        }
     82
    7783        k := ringBufferKey{networkID: network.ID, entity: entity}
    7884        rb, ok := ms.buffers[k]
Note: See TracChangeset for help on using the changeset viewer.