state

package
v0.0.0-...-72f1ef6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2026 License: GPL-3.0 Imports: 11 Imported by: 0

Documentation

Overview

Package state provides persistent storage for bot state across restarts.

Index

Constants

This section is empty.

Variables

View Source
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) Close

func (s *DatastoreStore) Close() error

Close releases resources.

func (*DatastoreStore) DMMessage

func (s *DatastoreStore) DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)

DMMessage retrieves DM message info with Datastore-first, memory fallback.

func (*DatastoreStore) LastDM

func (s *DatastoreStore) LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)

LastDM retrieves last DM time with Datastore-first, memory fallback.

func (*DatastoreStore) LastDigest

func (s *DatastoreStore) LastDigest(ctx context.Context, userID, date string) (time.Time, bool)

LastDigest retrieves last digest time.

func (*DatastoreStore) LastNotification

func (s *DatastoreStore) LastNotification(ctx context.Context, prURL string) time.Time

LastNotification retrieves when a PR was last notified about.

func (*DatastoreStore) LastReportSent

func (s *DatastoreStore) LastReportSent(ctx context.Context, userID string) (time.Time, bool)

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

func (s *DatastoreStore) PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)

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

func NewJSONStore() (*JSONStore, error)

NewJSONStore creates a new JSON-based state store. Uses os.UserCacheDir() + /slacker/state/ for storage.

func (*JSONStore) Cleanup

func (s *JSONStore) Cleanup(ctx context.Context) error

Cleanup removes old data from all maps.

func (*JSONStore) Close

func (s *JSONStore) Close() error

Close saves any pending changes and releases resources.

func (*JSONStore) DMMessage

func (s *JSONStore) DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)

DMMessage retrieves DM message information for a user and PR.

func (*JSONStore) LastDM

func (s *JSONStore) LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)

LastDM retrieves the last DM timestamp for a user and PR.

func (*JSONStore) LastDigest

func (s *JSONStore) LastDigest(ctx context.Context, userID, date string) (time.Time, bool)

LastDigest retrieves the last digest timestamp for a user and date.

func (*JSONStore) LastNotification

func (s *JSONStore) LastNotification(ctx context.Context, prURL string) time.Time

LastNotification retrieves the last notification timestamp for a PR.

func (*JSONStore) LastReportSent

func (s *JSONStore) LastReportSent(_ context.Context, userID string) (time.Time, bool)

LastReportSent returns when the last daily report was sent to a user.

func (*JSONStore) ListDMUsers

func (s *JSONStore) ListDMUsers(ctx context.Context, prURL string) []string

ListDMUsers returns all user IDs who have received DMs for a given PR.

func (*JSONStore) MarkProcessed

func (s *JSONStore) MarkProcessed(ctx context.Context, eventKey string, _ time.Duration) error

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

func (s *JSONStore) PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)

PendingDMs returns all pending DMs that should be sent (SendAfter <= before).

func (*JSONStore) QueuePendingDM

func (s *JSONStore) QueuePendingDM(ctx context.Context, dm *PendingDM) error

QueuePendingDM adds a DM to the pending queue.

func (*JSONStore) RecordDM

func (s *JSONStore) RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error

RecordDM records when a DM was sent to a user about a PR.

func (*JSONStore) RecordDigest

func (s *JSONStore) RecordDigest(ctx context.Context, userID, date string, sentAt time.Time) error

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

func (s *JSONStore) RecordReportSent(_ context.Context, userID string, sentAt time.Time) error

RecordReportSent records when a daily report was sent to a user.

func (*JSONStore) RemovePendingDM

func (s *JSONStore) RemovePendingDM(ctx context.Context, id string) error

RemovePendingDM removes a pending DM from the queue.

func (*JSONStore) SaveDMMessage

func (s *JSONStore) SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error

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.

func (*JSONStore) Thread

func (s *JSONStore) Thread(ctx context.Context, owner, repo string, number int, channelID string) (ThreadInfo, bool)

Thread retrieves thread information for a PR.

func (*JSONStore) WasProcessed

func (s *JSONStore) WasProcessed(ctx context.Context, eventKey string) bool

WasProcessed checks if an event was already processed.

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) Close

func (*MemoryStore) Close() error

Close is a no-op for in-memory store.

func (*MemoryStore) DMMessage

func (s *MemoryStore) DMMessage(ctx context.Context, userID, prURL string) (DMInfo, bool)

DMMessage retrieves DM message information for a user and PR.

func (*MemoryStore) LastDM

func (s *MemoryStore) LastDM(ctx context.Context, userID, prURL string) (time.Time, bool)

LastDM retrieves the last DM timestamp for a user and PR.

func (*MemoryStore) LastDigest

func (s *MemoryStore) LastDigest(ctx context.Context, userID, date string) (time.Time, bool)

LastDigest retrieves the last digest timestamp for a user and date.

func (*MemoryStore) LastNotification

func (s *MemoryStore) LastNotification(ctx context.Context, prURL string) time.Time

LastNotification retrieves the last notification timestamp for a PR.

func (*MemoryStore) LastReportSent

func (s *MemoryStore) LastReportSent(_ context.Context, userID string) (time.Time, bool)

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

func (s *MemoryStore) MarkProcessed(ctx context.Context, eventKey string, _ time.Duration) error

MarkProcessed marks an event as processed.

func (*MemoryStore) PendingDMs

func (s *MemoryStore) PendingDMs(ctx context.Context, before time.Time) ([]PendingDM, error)

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) RecordDM

func (s *MemoryStore) RecordDM(ctx context.Context, userID, prURL string, sentAt time.Time) error

RecordDM records when a DM was sent to a user about a PR.

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

func (s *MemoryStore) RecordReportSent(_ context.Context, userID string, sentAt time.Time) error

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

func (s *MemoryStore) SaveDMMessage(ctx context.Context, userID, prURL string, info DMInfo) error

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL