dtrpg

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package dtrpg wraps github.com/pilgrimagesoftware/dtrpg-sdk.go for the CLI's `import dtrpg` commands: application-key storage in the OS keychain, session exchange, paged library retrieval, and product-to-volume mapping. It drives catalog-api only through the internal/client package, never directly.

Index

Constants

View Source
const (
	// KeychainService matches auth.ServiceName. Duplicated as an untyped
	// constant to avoid importing internal/auth from here.
	KeychainService = "sweetrpg-cli"
	// KeychainAccount is the slot holding the DriveThruRPG application key.
	KeychainAccount = "dtrpg-app-key"
)

Keychain identifiers. The service name is shared with the platform session store; the account keeps the DriveThruRPG key in its own slot so `auth logout` and `dtrpg logout` never touch each other's credentials. One DriveThruRPG login is shared by every consumer (`catalog import dtrpg library`, `game-room import dtrpg`) - it's one external account either way, so there's nothing to isolate between them.

View Source
const (
	PropProductID = "dtrpg_product_id"
	PropISBN      = "dtrpg_isbn"
)

Volume property names carrying DriveThruRPG provenance. dtrpg_product_id is also the idempotency key: a second import skips any product whose ID already appears here on an existing volume. Purchase date, order id, and cover URL are deliberately not recorded - they are personal-order facts (or, for the cover, an off-site reference) that don't belong in the shared catalog; the cover image itself is fetched and stored as a volume asset instead.

Variables

View Source
var ErrNoKey = errors.New("no DriveThruRPG key stored: run 'sweetrpg dtrpg login'")

ErrNoKey means no DriveThruRPG application key is stored.

Functions

This section is empty.

Types

type Cover

type Cover struct {
	Data        []byte
	ContentType string
}

Cover is a downloaded cover image ready to upload.

func FetchCover

func FetchCover(ctx context.Context, httpClient *http.Client, url string) (*Cover, error)

FetchCover downloads a product's cover image from DriveThruRPG. Every failure mode (no URL, network error, non-2xx, oversized body, unsupported content type) returns a nil *Cover and a descriptive error - callers treat this as a per-product warning, never a fatal one.

type KeyStore

type KeyStore interface {
	SaveKey(key string) error
	LoadKey() (string, error)
	DeleteKey() error
}

KeyStore persists the DriveThruRPG application key. The seam keeps commands testable and lets a broken keychain surface as a clear refusal instead of a plaintext fallback.

type KeyringStore

type KeyringStore struct{}

KeyringStore persists the key in the OS keychain via go-keyring, under KeychainAccount - the one slot shared by every consumer of the DriveThruRPG login.

func (KeyringStore) DeleteKey

func (KeyringStore) DeleteKey() error

func (KeyringStore) LoadKey

func (KeyringStore) LoadKey() (string, error)

func (KeyringStore) SaveKey

func (KeyringStore) SaveKey(key string) error

type Library

type Library struct {
	Products []library.OrderProductItem
	Included []library.IncludedItem
}

Library is the owner's full DriveThruRPG library: every ordered product across all pages, plus the sideloaded Publisher/Product resources they reference.

type MemoryKeyStore

type MemoryKeyStore struct {
	// contains filtered or unexported fields
}

MemoryKeyStore is an in-process KeyStore for tests.

func (*MemoryKeyStore) DeleteKey

func (m *MemoryKeyStore) DeleteKey() error

func (*MemoryKeyStore) LoadKey

func (m *MemoryKeyStore) LoadKey() (string, error)

func (*MemoryKeyStore) SaveKey

func (m *MemoryKeyStore) SaveKey(key string) error

type Product

type Product struct {
	ProductID     string // dtrpg_product_id; the idempotency key
	Title         string
	PublisherName string
	CoverURL      string // source image to fetch and store as the volume cover
	Archived      bool
	Volume        catvo.VolumeVO
}

Product is one DriveThruRPG ordered product mapped to a catalog volume, keeping the fields the importer classifies and acts on alongside the volume payload.

func MapProducts

func MapProducts(lib Library) []Product

MapProducts converts a fetched library into catalog volumes. Mapping is conservative: only title, short description, and category tags land in volume fields; ISBN, purchase date, cover URL, and the raw DTRPG identifiers ride in properties where a wrong guess can't pollute a typed field.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session is an authenticated DriveThruRPG library client. The JWT it holds is never persisted; it dies with the process, and each run re-exchanges the stored application key for a fresh one.

func NewSession

func NewSession(ctx context.Context, appKey, baseURL string) (*Session, error)

NewSession exchanges appKey for a DriveThruRPG session and returns a library client bound to it. baseURL overrides the production API endpoint (tests point it at a fixture server); pass "" for production.

func (*Session) FetchLibrary

func (s *Session) FetchLibrary(ctx context.Context, pageSize uint32, onPage func(page int, fetched int)) (Library, error)

FetchLibrary pages through the authenticated user's order_products, requesting filter categories so tags can be mapped. Rate-limit responses carrying Retry-After are waited out before retrying the same page.

The archived query parameter is deliberately left unset. An earlier version unconditionally passed archived=true on the (SDK-doc-comment) assumption that it additively includes archived items alongside active ones; against a real ~1700-title account that returned only 40 - evidently an archived-only filter, not "everything". Leaving the parameter off returns the account's full, unfiltered order_products, and each item's own Archived field (already read downstream in mapping.go) still drives per-product archived/active classification - no server-side filtering needed for that at all.

onPage, when non-nil, is called after each page is fetched with the page number just retrieved and the running product total - callers use it for progress output; pass nil to skip it.

Jump to

Keyboard shortcuts

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