enhancements:

- english README (german version will be put to the homepage)
- better commandline options
- enhanced logging capabilities and error handling
- config file support
- support to backup one or more singular ads
- add id to adlisting
- added manual page
- fixed config file reading
- fixed typo
This commit is contained in:
2023-12-15 14:50:40 +01:00
parent c2f378be05
commit 1b55d887bc
12 changed files with 621 additions and 65 deletions

147
main.go
View File

@@ -20,36 +20,75 @@ package main
import (
"errors"
"fmt"
"os"
"github.com/lmittmann/tint"
flag "github.com/spf13/pflag"
"log/slog"
"os"
"runtime/debug"
)
const VERSION string = "0.0.1"
const Useragent string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
const VERSION string = "0.0.2"
const Useragent string = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
const Baseuri string = "https://www.kleinanzeigen.de"
const Listuri string = "/s-bestandsliste.html"
const Defaultdir string = "."
const Usage string = `This is kleingebaeck, the kleinanzeigen.de backup tool.
Usage: kleingebaeck [-dvVhmoc] [<ad-listing-url>,...]
Options:
--user,-u <uid> Backup ads from user with uid <uid>.
--debug, -d Enable debug output.
--verbose,-v Enable verbose output.
--output-dir,-o <dir> Set output dir (default: current directory)
--manual,-m Show manual.
--config,-c <file> Use config file <file> (default: ~/.kleingebaeck).
If one or more <ad-listing-url>'s are specified, only backup those,
otherwise backup all ads of the given user.`
const LevelNotice = slog.Level(2)
func main() {
os.Exit(Main())
}
func Main() int {
logLevel := &slog.LevelVar{}
opts := &tint.Options{
Level: logLevel,
AddSource: false,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// Remove time from the output
if a.Key == slog.TimeKey {
return slog.Attr{}
}
return a
},
}
logLevel.Set(LevelNotice)
var handler slog.Handler = tint.NewHandler(os.Stdout, opts)
logger := slog.New(handler)
slog.SetDefault(logger)
showversion := false
showhelp := false
showmanual := false
enabledebug := false
configfile := ""
dir := Defaultdir
enableverbose := false
uid := 0
configfile := os.Getenv("HOME") + "/.kleingebaeck.hcl"
dir := ""
flag.BoolVarP(&enabledebug, "debug", "d", false, "debug mode")
flag.BoolVarP(&showversion, "version", "v", false, "show version")
flag.BoolVarP(&enableverbose, "verbose", "v", false, "be verbose")
flag.BoolVarP(&showversion, "version", "V", false, "show version")
flag.BoolVarP(&showhelp, "help", "h", false, "show usage")
flag.BoolVarP(&showmanual, "manual", "m", false, "show manual")
flag.IntVarP(&uid, "user", "u", uid, "user id")
flag.StringVarP(&dir, "output-dir", "o", dir, "where to store ads")
flag.StringVarP(&configfile, "config", "c",
os.Getenv("HOME")+"/.kleingebaeck", "config file")
flag.StringVarP(&configfile, "config", "c", configfile, "config file")
flag.Parse()
@@ -58,39 +97,93 @@ func Main() int {
return 0
}
/*
if showhelp {
fmt.Println(Usage)
return 0
}
if showhelp {
fmt.Println(Usage)
return 0
conf, err := ParseConfigfile(configfile)
if err != nil {
return Die(err)
}
if enableverbose || conf.Verbose {
logLevel.Set(slog.LevelInfo)
}
if enabledebug {
// we're using a more verbose logger in debug mode
buildInfo, _ := debug.ReadBuildInfo()
opts := &tint.Options{
Level: logLevel,
AddSource: true,
}
if enabledebug {
calc.ToggleDebug()
}
logLevel.Set(slog.LevelDebug)
var handler slog.Handler = tint.NewHandler(os.Stdout, opts)
debuglogger := slog.New(handler).With(
slog.Group("program_info",
slog.Int("pid", os.Getpid()),
slog.String("go_version", buildInfo.GoVersion),
),
)
slog.SetDefault(debuglogger)
}
if showmanual {
man()
return 0
}
slog.Debug("config", "conf", conf)
*/
if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) {
err := os.Mkdir(dir, os.ModePerm)
if showmanual {
err := man()
if err != nil {
return Die(err)
}
return 0
}
if len(flag.Args()) == 1 {
Start(flag.Args()[0], dir)
if len(dir) == 0 {
if len(conf.Outdir) > 0 {
dir = conf.Outdir
} else {
dir = Defaultdir
}
}
// prepare output dir
err = Mkdir(dir)
if err != nil {
return Die(err)
}
// directly backup ad listing[s]
if len(flag.Args()) >= 1 {
for _, uri := range flag.Args() {
err := Scrape(uri, dir)
if err != nil {
return Die(err)
}
}
return 0
}
// backup all ads of the given user (via config or cmdline)
if uid == 0 && conf.User > 0 {
uid = conf.User
}
if uid > 0 {
err := Start(fmt.Sprintf("%d", uid), dir)
if err != nil {
return Die(err)
}
} else {
return Die(errors.New("invalid or no user id specified"))
}
return 0
}
func Die(err error) int {
fmt.Println(err)
slog.Error("Failure", "error", err.Error())
return 1
}