package tldrrepo import ( "context" "database/sql" ) type Repo struct { db *sql.DB } func NewRepo(db *sql.DB) *Repo { return &Repo{db: db} } // GetCursors returns channel_id -> last_message_id for one user in one guild. func (r *Repo) GetCursors(ctx context.Context, userID, guildID int64) (map[int64]int64, error) { const q = ` SELECT channel_id, last_message_id FROM tldr_cursors WHERE user_id = $1 AND guild_id = $2 ` rows, err := r.db.QueryContext(ctx, q, userID, guildID) if err != nil { return nil, err } defer rows.Close() out := make(map[int64]int64) for rows.Next() { var channelID, messageID int64 if err := rows.Scan(&channelID, &messageID); err != nil { return nil, err } out[channelID] = messageID } return out, rows.Err() } // SetCursors upserts the given channel cursors in one transaction. GREATEST // means a cursor only ever moves forward, so two overlapping runs can't drag // it back and re-summarize messages. func (r *Repo) SetCursors(ctx context.Context, userID, guildID int64, cursors map[int64]int64) error { if len(cursors) == 0 { return nil } tx, err := r.db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() const q = ` INSERT INTO tldr_cursors (user_id, channel_id, guild_id, last_message_id) VALUES ($1, $2, $3, $4) ON CONFLICT (user_id, channel_id) DO UPDATE SET last_message_id = GREATEST(tldr_cursors.last_message_id, EXCLUDED.last_message_id), updated_at = NOW() ` for channelID, messageID := range cursors { if _, err := tx.ExecContext(ctx, q, userID, channelID, guildID, messageID); err != nil { return err } } return tx.Commit() }