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>
91 lines
2.3 KiB
Go
91 lines
2.3 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
type Config struct {
|
|
GroqAPIKey string
|
|
GeminiAPIKey string
|
|
TelegramBotToken string
|
|
TelegramChatID string
|
|
LinkedInClientID string
|
|
LinkedInClientSecret string
|
|
LinkedInAccessToken string
|
|
Workspace string
|
|
CropBottomPx int
|
|
CropRightPx int
|
|
}
|
|
|
|
// Load reads .env (if present) then maps env vars to Config.
|
|
// Never panics — callers check with Validate.
|
|
func Load() *Config {
|
|
_ = godotenv.Load() // silently ignore missing .env
|
|
|
|
ws := os.Getenv("LDPOST_WORKSPACE")
|
|
if ws == "" {
|
|
ws = `C:\Textos-Linkedin`
|
|
}
|
|
|
|
return &Config{
|
|
GroqAPIKey: os.Getenv("GROQ_API_KEY"),
|
|
GeminiAPIKey: os.Getenv("GEMINI_API_KEY"),
|
|
TelegramBotToken: os.Getenv("TELEGRAM_BOT_TOKEN"),
|
|
TelegramChatID: os.Getenv("TELEGRAM_CHAT_ID"),
|
|
LinkedInClientID: os.Getenv("LINKEDIN_CLIENT_ID"),
|
|
LinkedInClientSecret: os.Getenv("LINKEDIN_CLIENT_SECRET"),
|
|
LinkedInAccessToken: os.Getenv("LINKEDIN_ACCESS_TOKEN"),
|
|
Workspace: ws,
|
|
CropBottomPx: envInt("LDPOST_CROP_BOTTOM", 48),
|
|
CropRightPx: envInt("LDPOST_CROP_RIGHT", 48),
|
|
}
|
|
}
|
|
|
|
func envInt(key string, def int) int {
|
|
if v := os.Getenv(key); v != "" {
|
|
if n, err := strconv.Atoi(v); err == nil {
|
|
return n
|
|
}
|
|
}
|
|
return def
|
|
}
|
|
|
|
// Validate checks named field groups and returns a combined error (nil = OK).
|
|
// Accepts: "groq", "gemini", "telegram", "linkedin", "workspace".
|
|
// Returns error listing ALL missing vars — does not panic, safe for dry-run.
|
|
func (c *Config) Validate(fields ...string) error {
|
|
var missing []string
|
|
|
|
check := func(name, val string) {
|
|
if val == "" {
|
|
missing = append(missing, name)
|
|
}
|
|
}
|
|
|
|
for _, f := range fields {
|
|
switch f {
|
|
case "groq":
|
|
check("GROQ_API_KEY", c.GroqAPIKey)
|
|
case "gemini":
|
|
check("GEMINI_API_KEY", c.GeminiAPIKey)
|
|
case "telegram":
|
|
check("TELEGRAM_BOT_TOKEN", c.TelegramBotToken)
|
|
check("TELEGRAM_CHAT_ID", c.TelegramChatID)
|
|
case "linkedin":
|
|
check("LINKEDIN_ACCESS_TOKEN", c.LinkedInAccessToken)
|
|
case "workspace":
|
|
check("LDPOST_WORKSPACE", c.Workspace)
|
|
}
|
|
}
|
|
|
|
if len(missing) == 0 {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("variáveis ausentes: %s", strings.Join(missing, ", "))
|
|
}
|