Jobmaker-LdPost/shared/history/history.go
Ricardo Carneiro ea532659b0 feat: pipeline inicial ldpost-squad (6 agentes)
Pipeline completo de publicação no LinkedIn:
evaluator → redator → editor → art → director → publisher

- Seed com 37 posts em _sugestoes.md
- Sorteio de formato com N=3 bloqueados (format-history)
- Reciclagem mensal de posts com rotação de formato
- Revisão via Telegram com chat livre (Gemini 2.5 Flash)
- Publicação via LinkedIn API (OAuth2)
- Makefile com targets para Windows/Linux/ARM64

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-03 18:55:39 -03:00

93 lines
2.2 KiB
Go

package history
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
const (
histRelPath = "_history/format-history.json"
maxEntries = 20
)
type HistoryEntry struct {
Slug string `json:"slug"`
Formato string `json:"formato"`
Data string `json:"data"` // YYYY-MM-DD
}
type FormatHistory struct {
History []HistoryEntry `json:"history"`
}
// LoadHistory reads _history/format-history.json from workspacePath.
// Returns empty history if file doesn't exist.
func LoadHistory(workspacePath string) (*FormatHistory, error) {
path := filepath.Join(workspacePath, histRelPath)
data, err := os.ReadFile(path)
if os.IsNotExist(err) {
return &FormatHistory{}, nil
}
if err != nil {
return nil, fmt.Errorf("ler format-history: %w", err)
}
var h FormatHistory
if err := json.Unmarshal(data, &h); err != nil {
return nil, fmt.Errorf("parsear format-history: %w", err)
}
return &h, nil
}
// SaveHistory writes _history/format-history.json atomically.
func SaveHistory(workspacePath string, h *FormatHistory) error {
dir := filepath.Join(workspacePath, "_history")
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("criar _history/: %w", err)
}
data, err := json.MarshalIndent(h, "", " ")
if err != nil {
return fmt.Errorf("serializar history: %w", err)
}
path := filepath.Join(workspacePath, histRelPath)
tmp := path + ".tmp"
if err := os.WriteFile(tmp, data, 0644); err != nil {
return fmt.Errorf("escrever tmp: %w", err)
}
if err := os.Rename(tmp, path); err != nil {
os.Remove(tmp)
return fmt.Errorf("rename tmp: %w", err)
}
return nil
}
// LastN returns the last n formats used (most recent first).
func (h *FormatHistory) LastN(n int) []string {
var out []string
for i, e := range h.History {
if i >= n {
break
}
out = append(out, e.Formato)
}
return out
}
// AddEntry prepends a new entry and trims to maxEntries.
func (h *FormatHistory) AddEntry(slug, formato string) {
entry := HistoryEntry{
Slug: slug,
Formato: formato,
Data: time.Now().Format("2006-01-02"),
}
h.History = append([]HistoryEntry{entry}, h.History...)
if len(h.History) > maxEntries {
h.History = h.History[:maxEntries]
}
}