diff --git a/cmd/aoctl/puzzle.go b/cmd/aoctl/puzzle.go index 7343302..a21aa48 100644 --- a/cmd/aoctl/puzzle.go +++ b/cmd/aoctl/puzzle.go @@ -19,26 +19,40 @@ THE SOFTWARE. package aoctl import ( - "fmt" + "fmt" + "strconv" + "time" - "github.com/spf13/cobra" - "github.com/dolfolife/aoctl/pkg/aoc" + "github.com/dolfolife/aoctl/pkg/aoc" + "github.com/spf13/cobra" ) + var puzzleCmd = &cobra.Command{ - Use: "puzzle", - Aliases: []string{"p", "pzzl", "pz"}, - Short: "Get the puzzles for a given day", - Args: cobra.ExactArgs(3), - Run: func(cmd *cobra.Command, args []string) { - day := args[0] - year := args[1] - cookie := args[2] - puzzles := aoc.GetPuzzles(day, year, cookie) - fmt.Println(puzzles[0]) - fmt.Println(puzzles[1]) - }, + Use: "puzzle", + Aliases: []string{"p", "pzzl", "pz"}, + Short: "Get the puzzles for a given day", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + day := cmd.Flags().Lookup("day").Value.String() + year := cmd.Flags().Lookup("year").Value.String() + puzzles := aoc.GetPuzzles(day, year) + fmt.Println(puzzles[0]) + fmt.Println(puzzles[1]) + }, } func init() { - rootCmd.AddCommand(puzzleCmd) + rootCmd.AddCommand(puzzleCmd) + + puzzleCmd.Flags().StringP("day", "d", "1", "Day of the Advent of Code problems to fetch; By default it will be day 1") + puzzleCmd.Flags().StringP("year", "y", getCurrentYear(), "Year of the Advent of Code; by default uses the last Advent of Code Year") +} + +func getCurrentYear() string { + year, month, _ := time.Now().Date() + if month < 12 { + return strconv.Itoa(year - 1) + } + + return strconv.Itoa(year) } diff --git a/cmd/aoctl/session.go b/cmd/aoctl/session.go new file mode 100644 index 0000000..bf9bb19 --- /dev/null +++ b/cmd/aoctl/session.go @@ -0,0 +1,45 @@ +/* +Copyright © 2024 Rodolfo Sanchez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package aoctl + +import ( + "github.com/dolfolife/aoctl/pkg/aoc" + "github.com/spf13/cobra" +) + +var sessionCmd = &cobra.Command{ + Use: "session", + Aliases: []string{"initialize"}, + Short: "Initialize the Advent of Code project in the path specifed", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + session := cmd.Flags().Lookup("session").Value.String() + + aoc.SetSessionId(session) + }, +} + +func init() { + rootCmd.AddCommand(sessionCmd) + + sessionCmd.Flags().StringP("session", "s", "", "Session Cookie from the Advent of Code site") +} diff --git a/docs/how-to-guides.md b/docs/how-to-guides.md new file mode 100644 index 0000000..36f0322 --- /dev/null +++ b/docs/how-to-guides.md @@ -0,0 +1,26 @@ +## How to Guides + +These are easy to follow guides to solve an specific problem you might have. + +### Start a new project + +In this guide we will create a new project and add your first solution for a [Advent of Code](https://adventofcode.com/) site. + +#### Initialize the project + +To start a new working folder for advent of code use the command [`init`](https://github.com/dolfolife/aoctl?tab=readme-ov-file#init). + +> By default it will create a new folder named `adventofcode` but you can change that with the parameter `--path` to initialize the project with a specific name. +```bash +aoctl init -p aoc-solutions +``` + +Next, we need to add our user session to the project by editing the `.env` file. To fetch this user session you can check the `session` cookie in the cookies section of the browser inspect console. + +> The session cookie is a httpOnly cookie, so it can't be fetch using `document.cookie` object + +To add the session to the project use the `session` cmd + +```bash +aoctl session -s +``` diff --git a/go.mod b/go.mod index 9b7a47d..35f2e8d 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/dolfolife/aoctl go 1.20 require ( + github.com/joho/godotenv v1.5.1 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 go.hein.dev/go-version v0.1.0 diff --git a/go.sum b/go.sum index e88b093..d4ab301 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= diff --git a/pkg/aoc/aoc.go b/pkg/aoc/aoc.go index 907e25f..ab299f4 100644 --- a/pkg/aoc/aoc.go +++ b/pkg/aoc/aoc.go @@ -9,64 +9,53 @@ import ( "github.com/dolfolife/aoctl/pkg/puzzle" ) -type AoCConfig struct { - ProjectPath string -} +func InitializeProject(path string) error { + fullPath := filepath.Join(os.Getenv("PWD"), path) -func GetAoCConfig() AoCConfig { - return AoCConfig{ - ProjectPath: os.Getenv("PWD"), - } -} + log.Printf("....Initializing Advent of Code project in %s....", fullPath) -func InitializeProject(path string) error { - fullPath := filepath.Join(os.Getenv("PWD"), path) - - log.Printf("....Initializing Advent of Code project in %s....", fullPath) - - err := os.Mkdir(fullPath, os.ModePerm) - - if err != nil { - log.Fatalf("Error creating directory: %s", err) - } - - cookieFile := filepath.Join(path, ".aoc") - roFile, err := os.OpenFile(cookieFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - log.Fatalf("Error creating cookie file: %s", err) - } - - defer roFile.Close() - - _, err = roFile.WriteString(` + err := os.Mkdir(fullPath, os.ModePerm) + + if err != nil { + log.Fatalf("Error creating directory: %s", err) + } + + cookieFile := filepath.Join(path, ".env") + roFile, err := os.OpenFile(cookieFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + log.Fatalf("Error creating cookie file: %s", err) + } + + defer roFile.Close() + + _, err = roFile.WriteString(` # Use the .aoc file to set the environment variables for your project -session= +AOC_SESSION= `) - - if err != nil { - log.Fatalf("Error writing to the aoc file: %s", err) - } - - readmeFile := filepath.Join(path, "README.md") - roFile, err = os.OpenFile(readmeFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - log.Fatalf("Error creating README.md file: %s", err) - } - - defer roFile.Close() - roFile.WriteString(` + + if err != nil { + log.Fatalf("Error writing to the aoc file: %s", err) + } + + readmeFile := filepath.Join(path, "README.md") + roFile, err = os.OpenFile(readmeFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + log.Fatalf("Error creating README.md file: %s", err) + } + + defer roFile.Close() + roFile.WriteString(` # Advent of Code `) + gitIgnoreFile := filepath.Join(path, ".gitignore") + roFile, err = os.OpenFile(gitIgnoreFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) + if err != nil { + log.Fatalf("Error creating .gitignore file: %s", err) + } + defer roFile.Close() - gitIgnoreFile := filepath.Join(path, ".gitignore") - roFile, err = os.OpenFile(gitIgnoreFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - log.Fatalf("Error creating .gitignore file: %s", err) - } - defer roFile.Close() - - if _, err = roFile.WriteString(` + if _, err = roFile.WriteString(` # Advent of Code use the .aoc file for storing session information which you should # not track in source control. .aoc @@ -74,31 +63,32 @@ session= return err } - log.Println("Project initialized") - return nil + log.Println("Project initialized") + return nil } +func GetPuzzles(day string, year string) []puzzle.Puzzle { + + aocConfig := GetAoCConfig() + puzzleURL, err := url.JoinPath("https://adventofcode.com/", year, "/day/", day) + + if err != nil { + log.Fatalf("Error creating url: %s", err) + } -func GetPuzzles(day string, year string, cookie string) []puzzle.Puzzle { - puzzleURL, err := url.JoinPath("https://adventofcode.com/", year, "/day/", day) - - if err != nil { - log.Fatalf("Error creating url: %s", err) - } - - body := getBodyFromUrl(puzzleURL, cookie) - - inputURL, err := url.JoinPath(puzzleURL, "/input") - - if err != nil { - log.Fatalf("Error creating url: %s", err) - } - - rawInput := getBodyFromUrl(inputURL, cookie) - - response, err := ParsePuzzles(day, year, body, rawInput) - if err != nil { - log.Fatalf("Error parsing puzzles: %s", err) - } - return response + body := getBodyFromUrl(puzzleURL, aocConfig.SessionId) + + inputURL, err := url.JoinPath(puzzleURL, "/input") + + if err != nil { + log.Fatalf("Error creating url: %s", err) + } + + rawInput := getBodyFromUrl(inputURL, aocConfig.SessionId) + + response, err := ParsePuzzles(day, year, body, rawInput) + if err != nil { + log.Fatalf("Error parsing puzzles: %s", err) + } + return response } diff --git a/pkg/aoc/config.go b/pkg/aoc/config.go new file mode 100644 index 0000000..85f6101 --- /dev/null +++ b/pkg/aoc/config.go @@ -0,0 +1,42 @@ +package aoc + +import ( + "fmt" + "log" + "os" + + "github.com/joho/godotenv" +) + +type AoCConfig struct { + ProjectPath string + SessionId string +} + +func GetAoCConfig() AoCConfig { + + err := godotenv.Load() + + if err != nil { + log.Fatal("error loading the .env file") + } + + return AoCConfig{ + SessionId: os.Getenv("AOC_SESSION"), + ProjectPath: os.Getenv("PWD"), + } +} + +func SetSessionId(id string) { + env, err := godotenv.Unmarshal(fmt.Sprintf("AOC_SESSION=%v", id)) + + if err != nil { + log.Fatal("error writing the session into the environment") + } + + err = godotenv.Write(env, "./.env") + + if err != nil { + log.Fatal("error writing the session into the environment") + } +}