package msgstore import ( "context" "time" "git.sr.ht/~emersion/soju/database" "git.sr.ht/~sircmpwn/go-bare" "gopkg.in/irc.v4" ) type dbMsgID struct { ID bare.Uint } func (dbMsgID) msgIDType() msgIDType { return msgIDDB } func parseDBMsgID(s string) (msgID int64, err error) { var id dbMsgID _, _, err = ParseMsgID(s, &id) if err != nil { return 0, err } return int64(id.ID), nil } func formatDBMsgID(netID int64, target string, msgID int64) string { id := dbMsgID{bare.Uint(msgID)} return formatMsgID(netID, target, &id) } // dbMessageStore is a persistent store for IRC messages, that // stores messages in the soju database. type dbMessageStore struct { db database.Database } var ( _ Store = (*dbMessageStore)(nil) _ ChatHistoryStore = (*dbMessageStore)(nil) _ SearchStore = (*dbMessageStore)(nil) ) func NewDBStore(db database.Database) *dbMessageStore { return &dbMessageStore{ db: db, } } func (ms *dbMessageStore) Close() error { return nil } func (ms *dbMessageStore) LastMsgID(network *database.Network, entity string, t time.Time) (string, error) { // TODO: what should we do with t? id, err := ms.db.GetMessageLastID(context.TODO(), network.ID, entity) if err != nil { return "", err } return formatDBMsgID(network.ID, entity, id), nil } func (ms *dbMessageStore) LoadLatestID(ctx context.Context, id string, options *LoadMessageOptions) ([]*irc.Message, error) { msgID, err := parseDBMsgID(id) if err != nil { return nil, err } l, err := ms.db.ListMessages(ctx, options.Network.ID, options.Entity, &database.MessageOptions{ AfterID: msgID, Limit: options.Limit, TakeLast: true, }) if err != nil { return nil, err } return l, nil } func (ms *dbMessageStore) Append(network *database.Network, entity string, msg *irc.Message) (string, error) { ids, err := ms.db.StoreMessages(context.TODO(), network.ID, entity, []*irc.Message{msg}) if err != nil { return "", err } return formatDBMsgID(network.ID, entity, ids[0]), nil } func (ms *dbMessageStore) ListTargets(ctx context.Context, network *database.Network, start, end time.Time, limit int, events bool) ([]ChatHistoryTarget, error) { var opts *database.MessageOptions if start.Before(end) { opts = &database.MessageOptions{ AfterTime: start, BeforeTime: end, Limit: limit, Events: events, } } else { opts = &database.MessageOptions{ AfterTime: end, BeforeTime: start, Limit: limit, Events: events, TakeLast: true, } } l, err := ms.db.ListMessageLastPerTarget(ctx, network.ID, opts) if err != nil { return nil, err } targets := make([]ChatHistoryTarget, len(l)) for i, v := range l { targets[i] = ChatHistoryTarget{ Name: v.Name, LatestMessage: v.LatestMessage, } } return targets, nil } func (ms *dbMessageStore) LoadBeforeTime(ctx context.Context, start, end time.Time, options *LoadMessageOptions) ([]*irc.Message, error) { l, err := ms.db.ListMessages(ctx, options.Network.ID, options.Entity, &database.MessageOptions{ AfterTime: end, BeforeTime: start, Limit: options.Limit, Events: options.Events, TakeLast: true, }) if err != nil { return nil, err } return l, nil } func (ms *dbMessageStore) LoadAfterTime(ctx context.Context, start, end time.Time, options *LoadMessageOptions) ([]*irc.Message, error) { l, err := ms.db.ListMessages(ctx, options.Network.ID, options.Entity, &database.MessageOptions{ AfterTime: start, BeforeTime: end, Limit: options.Limit, Events: options.Events, }) if err != nil { return nil, err } return l, nil } func (ms *dbMessageStore) Search(ctx context.Context, network *database.Network, options *SearchMessageOptions) ([]*irc.Message, error) { l, err := ms.db.ListMessages(ctx, network.ID, options.In, &database.MessageOptions{ AfterTime: options.Start, BeforeTime: options.End, Limit: options.Limit, Sender: options.From, Text: options.Text, TakeLast: true, }) if err != nil { return nil, err } return l, nil }