diff --git a/.vscode/launch.json b/.vscode/launch.json index 0c1f443..3b48105 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,7 @@ "request": "launch", "program": "${workspaceFolder}/cobra", // https://www.blogger.com/edit-profile.g - "args": ["publish", "blogger", "${input:PostURL}", "blogger"], + "args": ["publish", "blogger", "${input:PostURL}", "markdown"], // "envFile": "${workspaceFolder}/cobra/.env", "console": "integratedTerminal" } diff --git a/cobra/cmd/platforms.go b/cobra/cmd/platforms.go index 3bbda1e..5fff617 100644 --- a/cobra/cmd/platforms.go +++ b/cobra/cmd/platforms.go @@ -2,37 +2,43 @@ package cmd import ( "fmt" + "os" + "path/filepath" "strings" + "github.com/gosimple/slug" + md "github.com/JohannesKaufmann/html-to-markdown" "github.com/charmbracelet/log" "github.com/go-resty/resty/v2" "github.com/slashtechno/cross-blogger/cobra/pkg/oauth" + "github.com/spf13/afero" ) type Destination interface { - Push() error + Push(PostData, PlatformOptions) error GetName() string + GetType() string } type Source interface { - Pull(SourceOptions) (PostData, error) + Pull(PlatformOptions) (PostData, error) GetName() string GetType() string } -type SourceOptions struct { +type PlatformOptions struct { AccessToken string BlogId string - Filepath string PostUrl string } type PostData struct { Title string - html string - markdown string + Html string + Markdown string // Other fields that are probably needed are canonical URL, publish date, and description + CanonicalUrl string } // type PlatformParent struct { @@ -98,10 +104,9 @@ func (b Blogger) getBlogId(accessToken string) (string, error) { } return id.(string), nil } -func (b Blogger) Pull(options SourceOptions) (PostData, error) { +func (b Blogger) Pull(options PlatformOptions) (PostData, error) { log.Info("Blogger pull called", "options", options) postPath := strings.Replace(options.PostUrl, b.BlogUrl, "", 1) - client := resty.New() resp, err := client.R().SetHeader("Authorization", fmt.Sprintf("Bearer %s", options.AccessToken)).SetResult(&map[string]interface{}{}).Get("https://www.googleapis.com/blogger/v3/blogs/" + options.BlogId + "/posts/bypath?path=" + postPath) if err != nil { @@ -120,19 +125,24 @@ func (b Blogger) Pull(options SourceOptions) (PostData, error) { if !ok { return PostData{}, fmt.Errorf("content not found in response or is not a string") } + canonicalUrl, ok := result["url"].(string) + if !ok { + return PostData{}, fmt.Errorf("url not found in response or is not a string") + } // Convert the HTML to Markdown markdown, err := md.NewConverter("", true, nil).ConvertString(html) if err != nil { return PostData{}, err } return PostData{ - Title: title, - html: html, - markdown: markdown, + Title: title, + Html: html, + Markdown: markdown, + CanonicalUrl: canonicalUrl, }, nil } -func (b Blogger) Push() error { +func (b Blogger) Push(data PostData, options PlatformOptions) error { log.Error("not implemented") return nil } @@ -146,11 +156,56 @@ type Markdown struct { func (m Markdown) GetName() string { return m.Name } func (m Markdown) GetType() string { return "markdown" } -func (m Markdown) Push() error { - log.Error("not implemented") + +// Push the data to the contentdir with the title as the filename using gosimple/slug. +// The markdown file should have YAML frontmatter compatible with Hugo. +func (m Markdown) Push(data PostData, options PlatformOptions) error { + // Create the file, if it exists, log an error and return + fs := afero.NewOsFs() + slug := slug.Make(data.Title) + // Clean the slug to remove any characters that may cause issues with the filesystem + slug = filepath.Clean(slug) + filePath := filepath.Join(m.ContentDir, slug+".md") + // Create parent directories if they don't exist + dirPath := filepath.Dir(filePath) + if _, err := fs.Stat(dirPath); os.IsNotExist(err) { + errDir := fs.MkdirAll(dirPath, 0755) + if errDir != nil { + log.Error("failed to create directory", "directory", dirPath) + return errDir + } + } + // Check if the file already exists + if _, err := fs.Stat(filePath); err == nil { + log.Error("file already exists", "file", filePath) + return nil + } + // Create the file + file, err := fs.Create(filePath) + if err != nil { + return err + } + // After the function returns, close the file + defer file.Close() + // Create the frontmatter + frontmatter := struct { + Title string `yaml:"title"` + CanonicalUrl string `yaml:"canonicalUrl"` + }{ + Title: data.Title, + CanonicalUrl: data.CanonicalUrl, + } + // Convert the frontmatter to YAML + content := fmt.Sprintf("---\n%s---\n\n%s", frontmatter, data.Markdown) + log.Debug("Writing content", "content", content, "file", filePath) + _, err = file.WriteString(content) + if err != nil { + return err + } return nil + } -func (m Markdown) Pull(options SourceOptions) (PostData, error) { +func (m Markdown) Pull(options PlatformOptions) (PostData, error) { log.Info("Markdown pull called", "options", options) return PostData{}, nil } diff --git a/cobra/cmd/publish.go b/cobra/cmd/publish.go index 6098c69..93151d1 100644 --- a/cobra/cmd/publish.go +++ b/cobra/cmd/publish.go @@ -82,7 +82,7 @@ var publishCmd = &cobra.Command{ // } // Pull the data from the source - var options SourceOptions + var options PlatformOptions switch source.GetType() { case "blogger": // Convert source to Blogger @@ -121,7 +121,7 @@ var publishCmd = &cobra.Command{ if err != nil { log.Fatal(err) } - options = SourceOptions{ + options = PlatformOptions{ AccessToken: accessToken, BlogId: blogId, PostUrl: args[1], @@ -135,6 +135,19 @@ var publishCmd = &cobra.Command{ log.Fatal(err) } log.Info("Post data", "data", postData) + + // For each destination, push the data + for _, destination := range destinationSlice { + switch destination.GetType() { + case "markdown": + options := PlatformOptions{} + err := destination.Push(postData, options) + if err != nil { + log.Fatal(err) + } + + } + } }, } diff --git a/cobra/output_markdown/hello-world.md b/cobra/output_markdown/hello-world.md new file mode 100644 index 0000000..09cd64d --- /dev/null +++ b/cobra/output_markdown/hello-world.md @@ -0,0 +1,4 @@ +--- +{Hello, world! http://itsfrommars.blogspot.com/2022/08/hello-world.html}--- + +I created this post from the Google API. Hello, world! \ No newline at end of file diff --git a/go.mod b/go.mod index 7f48dfe..e231a88 100644 --- a/go.mod +++ b/go.mod @@ -8,9 +8,11 @@ require ( github.com/charmbracelet/log v0.4.0 github.com/go-resty/resty/v2 v2.13.1 github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c + github.com/gosimple/slug v1.14.0 github.com/imdario/mergo v0.3.16 github.com/sirupsen/logrus v1.9.2 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 + github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/subosito/gotenv v1.6.0 @@ -28,6 +30,7 @@ require ( github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect @@ -42,7 +45,6 @@ require ( github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/atomic v1.9.0 // indirect diff --git a/go.sum b/go.sum index 0e7a507..1936e75 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,10 @@ github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c h1:iyaGYbCmcYK github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es= +github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=