package app import ( "fmt" "os" "path" "go.balki.me/tss/parser" "gopkg.in/yaml.v3" ) type FeedLimit int const NoLimit FeedLimit = -1 type FeedURL []string type FeedCfg struct { // Unique identifier for the feed. Used as filename, so keep it simple Name string `yaml:"name" jsonschema:"required"` // Telegram channel name in the form "@foobar" or "-1004242442" (private) Channel string `yaml:"channel" jsonschema:"required"` // Telegram instant view rhash id, see https://instantview.telegram.org/#publishing-templates Rhash string `yaml:"rhash" jsonschema:"default="` // Feed URL, string or array of strings // If array, all entries are merged, e.g ["https://example.com/blog/tag/foo/feed", "https://example.com/blog/tag/bar/feed" ] URL FeedURL `yaml:"url" jsonschema:"required,oneof_type=string;array"` // Cron expression, see https://pkg.go.dev/github.com/robfig/cron#hdr-CRON_Expression_Format Cron string `yaml:"cron" jsonschema:"required"` // Proxy for this feed if different from global Proxy string `yaml:"proxy" jsonschema:"default="` // Rss or Atom feed Type parser.FeedType `yaml:"type" jsonschema:"required,enum=rss,enum=atom"` // Text of the link sent to telegram, Title string `yaml:"title" jsonschema:"default=Link"` // Number of entries to process when the feed is seen for the first time FTL *int `yaml:"first_time_limit" jsonschema:"default="` FirstTimeLimit FeedLimit `yaml:"-"` } type Config struct { // default proxy url that http.Client can understand // or socks5://unix/path/to/sock for socks proxy over unix socket // empty for no proxy Proxy string `yaml:"proxy" jsonschema:"default=,example=socks5://unix/run/tor/socks,example=*myvpsproxy"` // proxy for telegram if different from default // NONE to not use global default TelegramProxy string `yaml:"telegram_proxy" jsonschema:"default="` // Auth token for telegram in the form of 424242424:AB02132skdcjlisjkjflewjlfkjslkfHHXX TelegramAuthToken string `yaml:"telegram_auth_token" jsonschema:"required"` //TODO: read from credential file // Base directory to write feed data and schedule file DataDir string `yaml:"data_dir" jsonschema:"default=./"` // Path to the file where last success time for each feed is saved LastSuccessPath string `yaml:"last_loaded_path" jsonschema:"default=/last_success.yml"` // Path to the directory where feed content is saved DbDir string `yaml:"db_dir" jsonschema:"default=/feed_data"` // Number of entries to process when the feed is seen for the first time // -1 means no limit FTL *int `yaml:"first_time_limit" jsonschema:"default=-1"` // Feed config Feeds []FeedCfg `yaml:"feeds" jsonschema:"required"` } func ParseConfig(configPath string) (*Config, error) { cfg, err := os.ReadFile(configPath) if err != nil { return nil, err } c := Config{} err = yaml.Unmarshal(cfg, &c) if err != nil { return nil, err } if c.DataDir == "" { c.DataDir = "." } if c.LastSuccessPath == "" { c.LastSuccessPath = path.Join(c.DataDir, "last_success.yml") } if c.DbDir == "" { c.DbDir = path.Join(c.DataDir, "feed_data") } err = os.MkdirAll(c.DbDir, 0755) if err != nil { return nil, err } err = os.MkdirAll(c.DataDir, 0755) if err != nil { return nil, err } if c.Proxy != "" { if c.TelegramProxy == "" { c.TelegramProxy = c.Proxy } for i := range c.Feeds { feedCfg := &c.Feeds[i] if feedCfg.Proxy == "" { feedCfg.Proxy = c.Proxy } } } if c.TelegramProxy == "NONE" { c.TelegramProxy = "" } for i := range c.Feeds { feedCfg := &c.Feeds[i] if feedCfg.Proxy == "NONE" { feedCfg.Proxy = "" } if feedCfg.FTL != nil { feedCfg.FirstTimeLimit = FeedLimit(*feedCfg.FTL) } else if c.FTL != nil { feedCfg.FirstTimeLimit = FeedLimit(*c.FTL) } else { feedCfg.FirstTimeLimit = NoLimit } } return &c, nil } func (fu *FeedURL) UnmarshalYAML(node *yaml.Node) error { switch node.Kind { case yaml.SequenceNode: return node.Decode((*[]string)(fu)) case yaml.ScalarNode: *fu = []string{""} return node.Decode(&(*fu)[0]) } return fmt.Errorf("unexpected node type: %s, at %d:%d", node.ShortTag(), node.Line, node.Column) }