package fa import ( "context" "fmt" "iter" "time" "github.com/PuerkitoBio/goquery" "git.anthrove.art/public/go-fa-api/internal/urls" ) // NoteID identifies a private message (note) thread on FA. type NoteID int64 // NotePreview is a row from the notes inbox listing at /msg/pms/. It carries // just enough to identify the thread and decide whether to fetch the full // note via [Client.GetNote]. type NotePreview struct { ID NoteID Subject string Sender UserRef // from-user as it appears in the inbox list SentAt time.Time Unread bool ThreadURL string // the /msg/pms/1/{id}/#message link FA renders } // Note is a single private message thread, as rendered at /viewmessage/{id}/. // Subject and Body are the headline note; FA shows quoted prior replies // inline within Body rather than as a separate thread, so callers wanting // to programmatically split a conversation will need to walk Body's HTML. type Note struct { ID NoteID Subject string From UserRef To UserRef SentAt time.Time BodyHTML string BodyText string } // Notes iterates the private-message inbox at /msg/pms/. Each yielded // [*NotePreview] is one thread; call [Client.GetNote] with its ID to load // the body. // // FA paginates /msg/pms/ with a similar messagecenter-navigation control // to the submission inbox. // // ListOptions.StartPage is ignored the inbox uses cursor pagination // (follow-the-Next-link), not page-numbered fetches. // // Requires a logged-in client. func (c *Client) Notes(ctx context.Context, opts ListOptions, reqOpts ...Option) iter.Seq2[*NotePreview, error] { return func(yield func(*NotePreview, error) bool) { nextURL := urls.MsgPMs() pagesFetched := 0 for nextURL != "" { if opts.reachedLimit(pagesFetched) { return } var ( items []*NotePreview next string ) err := c.fetch(ctx, nextURL, func(doc *goquery.Document) error { items, next = parseNotesInboxPage(doc) return nil }, reqOpts...) if err != nil { yield(nil, err) return } pagesFetched++ for _, it := range items { if !yield(it, nil) { return } } if next == "" || len(items) == 0 { return } nextURL = next } } } // GetNote fetches a single note (private message) by ID. Requires a // logged-in client. func (c *Client) GetNote(ctx context.Context, id NoteID, opts ...Option) (*Note, error) { if id <= 0 { return nil, fmt.Errorf("fa: GetNote: id must be > 0") } var out *Note err := c.fetch(ctx, urls.ViewMessage(int64(id)), func(doc *goquery.Document) error { n, err := parseNote(id, doc) if err != nil { return err } out = n return nil }, opts...) if err != nil { return nil, err } return out, nil }