128 lines
2.9 KiB
Go
128 lines
2.9 KiB
Go
package user
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Config struct {
|
|
Name string `yaml:"name"`
|
|
Description string `yaml:"description"`
|
|
Provider string `yaml:"provider"`
|
|
Model string `yaml:"model"`
|
|
APIKeyEnv string `yaml:"api_key_env"`
|
|
AvatarColor string `yaml:"avatar_color"`
|
|
Preferences Preferences `yaml:"preferences"`
|
|
}
|
|
|
|
type Preferences struct {
|
|
Language string `yaml:"language"`
|
|
Tone string `yaml:"tone"`
|
|
}
|
|
|
|
type User struct {
|
|
Config Config
|
|
Profile string // content from PROFILE.md
|
|
Dir string // users/<name>/
|
|
}
|
|
|
|
const DefaultUser = "default"
|
|
|
|
func Load(dir string) (*User, error) {
|
|
userDir := filepath.Join(dir, DefaultUser)
|
|
|
|
userMD, err := os.ReadFile(filepath.Join(userDir, "USER.md"))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read USER.md: %w", err)
|
|
}
|
|
|
|
cfg, err := parseFrontmatter(userMD)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse USER.md: %w", err)
|
|
}
|
|
|
|
profile, _ := os.ReadFile(filepath.Join(userDir, "PROFILE.md"))
|
|
|
|
if cfg.Provider == "" {
|
|
cfg.Provider = "deepseek"
|
|
}
|
|
if cfg.APIKeyEnv == "" {
|
|
cfg.APIKeyEnv = "DEEPSEEK_API_KEY"
|
|
}
|
|
if cfg.AvatarColor == "" {
|
|
cfg.AvatarColor = "#5865F2"
|
|
}
|
|
|
|
return &User{Config: cfg, Profile: string(profile), Dir: userDir}, nil
|
|
}
|
|
|
|
func parseFrontmatter(data []byte) (Config, error) {
|
|
var cfg Config
|
|
if !bytes.HasPrefix(data, []byte("---")) {
|
|
return cfg, fmt.Errorf("missing frontmatter")
|
|
}
|
|
parts := bytes.SplitN(data, []byte("---"), 3)
|
|
if len(parts) < 3 {
|
|
return cfg, fmt.Errorf("invalid frontmatter")
|
|
}
|
|
return cfg, yaml.Unmarshal(parts[1], &cfg)
|
|
}
|
|
|
|
func (u *User) SaveProfile(content string) error {
|
|
return os.WriteFile(filepath.Join(u.Dir, "PROFILE.md"), []byte(content), 0644)
|
|
}
|
|
|
|
func (u *User) SaveConfig(cfg Config) error {
|
|
u.Config = cfg
|
|
var buf bytes.Buffer
|
|
buf.WriteString("---\n")
|
|
enc := yaml.NewEncoder(&buf)
|
|
enc.Encode(cfg)
|
|
buf.WriteString("---\n")
|
|
return os.WriteFile(filepath.Join(u.Dir, "USER.md"), buf.Bytes(), 0644)
|
|
}
|
|
|
|
func (u *User) BuildUserXML() string {
|
|
var xml strings.Builder
|
|
xml.WriteString("<user_info>\n")
|
|
xml.WriteString(fmt.Sprintf(" <name>%s</name>\n", u.Config.Name))
|
|
xml.WriteString(fmt.Sprintf(" <description>%s</description>\n", u.Config.Description))
|
|
if u.Profile != "" {
|
|
xml.WriteString(" <profile>\n")
|
|
xml.WriteString(u.Profile)
|
|
xml.WriteString(" </profile>\n")
|
|
}
|
|
xml.WriteString("</user_info>")
|
|
return xml.String()
|
|
}
|
|
|
|
func (u *User) GetProvider() string {
|
|
return u.Config.Provider
|
|
}
|
|
|
|
func (u *User) GetModel() string {
|
|
return u.Config.Model
|
|
}
|
|
|
|
func (u *User) GetAPIKeyEnv() string {
|
|
return u.Config.APIKeyEnv
|
|
}
|
|
|
|
func (u *User) GetName() string {
|
|
return u.Config.Name
|
|
}
|
|
|
|
func (u *User) GetAvatarColor() string {
|
|
return u.Config.AvatarColor
|
|
}
|
|
|
|
func Exists(dir string) bool {
|
|
_, err := os.Stat(filepath.Join(dir, DefaultUser, "USER.md"))
|
|
return err == nil
|
|
}
|