Documentation
¶
Overview ¶
Package state provides persistent storage for bot state across restarts.
Index ¶
- Variables
- type DMInfo
- type DatastoreStore
- func (s *DatastoreStore) Cleanup(ctx context.Context) error
- func (s *DatastoreStore) Close() error
- func (s *DatastoreStore) DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)
- func (s *DatastoreStore) LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)
- func (s *DatastoreStore) LastDigest(ctx context.Context, userID, date string) (time.Time, bool)
- func (s *DatastoreStore) LastNotification(ctx context.Context, prURL string) time.Time
- func (s *DatastoreStore) LastReportSent(ctx context.Context, userID string) (time.Time, bool)
- func (s *DatastoreStore) ListDMUsers(ctx context.Context, prURL string) []string
- func (s *DatastoreStore) MarkProcessed(ctx context.Context, eventKey string, ttl time.Duration) error
- func (s *DatastoreStore) PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)
- func (s *DatastoreStore) QueuePendingDM(ctx context.Context, dm *PendingDM) error
- func (s *DatastoreStore) RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error
- func (s *DatastoreStore) RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error
- func (s *DatastoreStore) RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
- func (s *DatastoreStore) RecordReportSent(ctx context.Context, userID string, sentAt time.Time) error
- func (s *DatastoreStore) RemovePendingDM(ctx context.Context, id string) error
- func (s *DatastoreStore) SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error
- func (s *DatastoreStore) SaveThread(ctx context.Context, owner, repo string, number int, channelID string, ...) error
- func (s *DatastoreStore) Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)
- func (s *DatastoreStore) WasProcessed(ctx context.Context, eventKey string) bool
- type JSONStore
- func (s *JSONStore) Cleanup(ctx context.Context) error
- func (s *JSONStore) Close() error
- func (s *JSONStore) DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)
- func (s *JSONStore) LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)
- func (s *JSONStore) LastDigest(ctx context.Context, userID, date string) (time.Time, bool)
- func (s *JSONStore) LastNotification(ctx context.Context, prURL string) time.Time
- func (s *JSONStore) LastReportSent(_ context.Context, userID string) (time.Time, bool)
- func (s *JSONStore) ListDMUsers(ctx context.Context, prURL string) []string
- func (s *JSONStore) MarkProcessed(ctx context.Context, eventKey string, _ time.Duration) error
- func (s *JSONStore) PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)
- func (s *JSONStore) QueuePendingDM(ctx context.Context, dm *PendingDM) error
- func (s *JSONStore) RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error
- func (s *JSONStore) RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error
- func (s *JSONStore) RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
- func (s *JSONStore) RecordReportSent(_ context.Context, userID string, sentAt time.Time) error
- func (s *JSONStore) RemovePendingDM(ctx context.Context, id string) error
- func (s *JSONStore) SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error
- func (s *JSONStore) SaveThread(ctx context.Context, owner, repo string, number int, channelID string, ...) error
- func (s *JSONStore) Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)
- func (s *JSONStore) WasProcessed(ctx context.Context, eventKey string) bool
- type MemoryStore
- func (s *MemoryStore) Cleanup(ctx context.Context) error
- func (*MemoryStore) Close() error
- func (s *MemoryStore) DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)
- func (s *MemoryStore) LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)
- func (s *MemoryStore) LastDigest(ctx context.Context, userID, date string) (time.Time, bool)
- func (s *MemoryStore) LastNotification(ctx context.Context, prURL string) time.Time
- func (s *MemoryStore) LastReportSent(_ context.Context, userID string) (time.Time, bool)
- func (s *MemoryStore) ListDMUsers(ctx context.Context, prURL string) []string
- func (s *MemoryStore) MarkProcessed(ctx context.Context, eventKey string, _ time.Duration) error
- func (s *MemoryStore) PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)
- func (s *MemoryStore) QueuePendingDM(ctx context.Context, dm *PendingDM) error
- func (s *MemoryStore) RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error
- func (s *MemoryStore) RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error
- func (s *MemoryStore) RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
- func (s *MemoryStore) RecordReportSent(_ context.Context, userID string, sentAt time.Time) error
- func (s *MemoryStore) RemovePendingDM(ctx context.Context, id string) error
- func (s *MemoryStore) SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error
- func (s *MemoryStore) SaveThread(ctx context.Context, owner, repo string, number int, channelID string, ...) error
- func (s *MemoryStore) Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)
- func (s *MemoryStore) WasProcessed(ctx context.Context, eventKey string) bool
- type PendingDM
- type Store
- type ThreadInfo
Constants ¶
This section is empty.
Variables ¶
var ErrAlreadyProcessed = errors.New("event already processed by another instance")
ErrAlreadyProcessed indicates an event was already processed by another instance. This is used for cross-instance deduplication during rolling deployments.
Functions ¶
This section is empty.
Types ¶
type DMInfo ¶
type DMInfo struct {
UpdatedAt time.Time `json:"updated_at"` // When we last updated this message
SentAt time.Time `json:"sent_at"` // When we first sent this message
ChannelID string `json:"channel_id"` // DM conversation channel ID
MessageTS string `json:"message_ts"` // Message timestamp for updating
MessageText string `json:"message_text"` // Current message text
LastState string `json:"last_state"` // Last PR state we notified about (for idempotency)
}
DMInfo stores information about a DM message for a PR.
type DatastoreStore ¶
type DatastoreStore struct {
// contains filtered or unexported fields
}
DatastoreStore implements Store using Google Cloud Datastore with in-memory cache. Uses hybrid approach: in-memory cache for speed, Datastore for persistence.
func NewDatastoreStore ¶
func NewDatastoreStore(ctx context.Context, projectID, databaseID string) (*DatastoreStore, error)
NewDatastoreStore creates a new Datastore-backed store with in-memory cache. The databaseID parameter specifies which Datastore database to use (e.g., "slacker", "(default)").
func (*DatastoreStore) Cleanup ¶
func (s *DatastoreStore) Cleanup(ctx context.Context) error
Cleanup removes old data from both stores.
func (*DatastoreStore) DMMessage ¶
DMMessage retrieves DM message info with Datastore-first, memory fallback.
func (*DatastoreStore) LastDM ¶
LastDM retrieves last DM time with Datastore-first, memory fallback.
func (*DatastoreStore) LastDigest ¶
LastDigest retrieves last digest time.
func (*DatastoreStore) LastNotification ¶
LastNotification retrieves when a PR was last notified about.
func (*DatastoreStore) LastReportSent ¶
LastReportSent retrieves last daily report time.
func (*DatastoreStore) ListDMUsers ¶
func (s *DatastoreStore) ListDMUsers(ctx context.Context, prURL string) []string
ListDMUsers returns all user IDs who have received DMs for a given PR. Queries both memory cache and Datastore to ensure data persists across restarts.
func (*DatastoreStore) MarkProcessed ¶
func (s *DatastoreStore) MarkProcessed(ctx context.Context, eventKey string, ttl time.Duration) error
MarkProcessed marks an event as processed (distributed coordination). Returns error if already processed by another instance (enables race detection).
func (*DatastoreStore) PendingDMs ¶
PendingDMs returns all pending DMs that should be sent. Reads from memory cache first, falls back to Datastore if empty.
func (*DatastoreStore) QueuePendingDM ¶
func (s *DatastoreStore) QueuePendingDM(ctx context.Context, dm *PendingDM) error
QueuePendingDM adds a pending DM to both memory and Datastore.
func (*DatastoreStore) RecordDM ¶
func (s *DatastoreStore) RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error
RecordDM saves DM timestamp to both stores.
func (*DatastoreStore) RecordDigest ¶
func (s *DatastoreStore) RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error
RecordDigest saves digest timestamp to memory and attempts persistence to Datastore. Memory is always updated (primary storage for runtime). Datastore is best-effort for restart recovery. Degrades gracefully: logs errors but continues operating if Datastore unavailable.
func (*DatastoreStore) RecordNotification ¶
func (s *DatastoreStore) RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
RecordNotification records when we notified about a PR.
func (*DatastoreStore) RecordReportSent ¶
func (s *DatastoreStore) RecordReportSent(ctx context.Context, userID string, sentAt time.Time) error
RecordReportSent saves daily report timestamp to memory and attempts persistence to Datastore. Memory is always updated (primary storage for runtime). Datastore is best-effort for restart recovery. Degrades gracefully: logs errors but continues operating if Datastore unavailable.
func (*DatastoreStore) RemovePendingDM ¶
func (s *DatastoreStore) RemovePendingDM(ctx context.Context, id string) error
RemovePendingDM removes a pending DM from both memory and Datastore.
func (*DatastoreStore) SaveDMMessage ¶
func (s *DatastoreStore) SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error
SaveDMMessage saves DM message info to both stores.
func (*DatastoreStore) SaveThread ¶
func (s *DatastoreStore) SaveThread(ctx context.Context, owner, repo string, number int, channelID string, info ThreadInfo) error
SaveThread saves thread info to memory and Datastore.
func (*DatastoreStore) Thread ¶
func (s *DatastoreStore) Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)
Thread retrieves thread info with memory-first, then Datastore fallback.
func (*DatastoreStore) WasProcessed ¶
func (s *DatastoreStore) WasProcessed(ctx context.Context, eventKey string) bool
WasProcessed checks if an event was already processed (distributed check).
type JSONStore ¶
type JSONStore struct {
// contains filtered or unexported fields
}
JSONStore implements Store using JSON files in the user cache directory. Simple, reliable, and easy to debug.
func NewJSONStore ¶
NewJSONStore creates a new JSON-based state store. Uses os.UserCacheDir() + /slacker/state/ for storage.
func (*JSONStore) LastDigest ¶
LastDigest retrieves the last digest timestamp for a user and date.
func (*JSONStore) LastNotification ¶
LastNotification retrieves the last notification timestamp for a PR.
func (*JSONStore) LastReportSent ¶
LastReportSent returns when the last daily report was sent to a user.
func (*JSONStore) ListDMUsers ¶
ListDMUsers returns all user IDs who have received DMs for a given PR.
func (*JSONStore) MarkProcessed ¶
MarkProcessed marks an event as processed with an optional TTL. Note: TTL is currently ignored - cleanup uses hardcoded 24-hour retention. This could be enhanced in the future to support per-event TTL.
func (*JSONStore) PendingDMs ¶
PendingDMs returns all pending DMs that should be sent (SendAfter <= before).
func (*JSONStore) QueuePendingDM ¶
QueuePendingDM adds a DM to the pending queue.
func (*JSONStore) RecordDigest ¶
RecordDigest records when a digest was sent to a user.
func (*JSONStore) RecordNotification ¶
func (s *JSONStore) RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
RecordNotification records when a notification was sent for a PR.
func (*JSONStore) RecordReportSent ¶
RecordReportSent records when a daily report was sent to a user.
func (*JSONStore) RemovePendingDM ¶
RemovePendingDM removes a pending DM from the queue.
func (*JSONStore) SaveDMMessage ¶
SaveDMMessage saves DM message information for a user and PR.
func (*JSONStore) SaveThread ¶
func (s *JSONStore) SaveThread(ctx context.Context, owner, repo string, number int, channelID string, info ThreadInfo) error
SaveThread saves thread information for a PR.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore implements Store using in-memory maps only (no persistence). This is used as a cache layer on top of Datastore or JSON backends.
func NewMemoryStore ¶
func NewMemoryStore() *MemoryStore
NewMemoryStore creates a new in-memory-only state store.
func (*MemoryStore) Cleanup ¶
func (s *MemoryStore) Cleanup(ctx context.Context) error
Cleanup removes old data from memory.
func (*MemoryStore) LastDigest ¶
LastDigest retrieves the last digest timestamp for a user and date.
func (*MemoryStore) LastNotification ¶
LastNotification retrieves the last notification timestamp for a PR.
func (*MemoryStore) LastReportSent ¶
LastReportSent returns when the last daily report was sent to a user.
func (*MemoryStore) ListDMUsers ¶
func (s *MemoryStore) ListDMUsers(ctx context.Context, prURL string) []string
ListDMUsers returns all user IDs who have received DMs for a given PR.
func (*MemoryStore) MarkProcessed ¶
MarkProcessed marks an event as processed.
func (*MemoryStore) PendingDMs ¶
PendingDMs returns all pending DMs that should be sent (SendAfter <= before).
func (*MemoryStore) QueuePendingDM ¶
func (s *MemoryStore) QueuePendingDM(ctx context.Context, dm *PendingDM) error
QueuePendingDM adds a DM to the pending queue.
func (*MemoryStore) RecordDigest ¶
func (s *MemoryStore) RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error
RecordDigest records when a digest was sent to a user.
func (*MemoryStore) RecordNotification ¶
func (s *MemoryStore) RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
RecordNotification records when a notification was sent for a PR.
func (*MemoryStore) RecordReportSent ¶
RecordReportSent records when a daily report was sent to a user.
func (*MemoryStore) RemovePendingDM ¶
func (s *MemoryStore) RemovePendingDM(ctx context.Context, id string) error
RemovePendingDM removes a pending DM from the queue.
func (*MemoryStore) SaveDMMessage ¶
SaveDMMessage saves DM message information for a user and PR.
func (*MemoryStore) SaveThread ¶
func (s *MemoryStore) SaveThread(ctx context.Context, owner, repo string, number int, channelID string, info ThreadInfo) error
SaveThread saves thread information for a PR.
func (*MemoryStore) Thread ¶
func (s *MemoryStore) Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)
Thread retrieves thread information for a PR.
func (*MemoryStore) WasProcessed ¶
func (s *MemoryStore) WasProcessed(ctx context.Context, eventKey string) bool
WasProcessed checks if an event was already processed.
type PendingDM ¶
type PendingDM struct {
QueuedAt time.Time `json:"queued_at"`
SendAfter time.Time `json:"send_after"`
PRAuthor string `json:"pr_author"`
PRState string `json:"pr_state"`
PRRepo string `json:"pr_repo"`
WorkspaceID string `json:"workspace_id"`
PRURL string `json:"pr_url"`
PRTitle string `json:"pr_title"`
ID string `json:"id"`
PROwner string `json:"pr_owner"`
WorkflowState string `json:"workflow_state"`
NextActions string `json:"next_actions"`
ChannelID string `json:"channel_id"`
ChannelName string `json:"channel_name"`
UserID string `json:"user_id"`
PRNumber int `json:"pr_number"`
}
PendingDM represents a DM scheduled to be sent later.
type Store ¶
type Store interface {
// Thread operations - map PR to Slack thread
Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)
SaveThread(ctx context.Context, owner, repo string, number int, channelID string, info ThreadInfo) error
// DM tracking - prevent duplicate notifications
LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)
RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error
// DM message tracking - store DM message info for updating
DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)
SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error
ListDMUsers(ctx context.Context, prURL string) []string
// Daily digest tracking - one per user per day (legacy)
LastDigest(ctx context.Context, userID, date string) (time.Time, bool)
RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error
// Daily report tracking - timestamp-based (23+ hour intervals)
LastReportSent(ctx context.Context, userID string) (time.Time, bool)
RecordReportSent(ctx context.Context, userID string, sentAt time.Time) error
// Event deduplication - prevent processing same event twice
WasProcessed(ctx context.Context, eventKey string) bool
MarkProcessed(ctx context.Context, eventKey string, ttl time.Duration) error
// Notification tracking - track when we last notified about a PR
LastNotification(ctx context.Context, prURL string) time.Time
RecordNotification(ctx context.Context, prURL string, notifiedAt time.Time) error
// Pending DM queue - schedule DMs to be sent later
QueuePendingDM(ctx context.Context, dm *PendingDM) error
PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)
RemovePendingDM(ctx context.Context, id string) error
// Cleanup old data
Cleanup(ctx context.Context) error
// Close releases resources
Close() error
}
Store provides persistent storage for bot state. Implementations must be safe for concurrent use.
type ThreadInfo ¶
type ThreadInfo struct {
UpdatedAt time.Time `json:"updated_at"`
LastEventTime time.Time `json:"last_event_time"` // Last sprinkler event timestamp for turnclient cache optimization
ThreadTS string `json:"thread_ts"`
ChannelID string `json:"channel_id"`
LastState string `json:"last_state"`
MessageText string `json:"message_text"`
}
ThreadInfo stores information about a Slack thread for a PR.