Repository: j178/leetgo (language: python)

Issue:
make rate limit time configurable
Now, it is hard-coded 10 seconds for every one, but for premium user, they can test or submit more frequently.


Discussion / hints:
or if we find out that current user is premium, we reduce the wait time automatically.

Fix patch:
diff --git a/cmd/debug.go b/cmd/debug.go
index 544187b3..2cab49b9 100644
--- a/cmd/debug.go
+++ b/cmd/debug.go
@@ -36,11 +36,11 @@ var whoamiCmd = &cobra.Command{
 	RunE: func(cmd *cobra.Command, args []string) error {
 		cred := leetcode.CredentialsFromConfig()
 		c := leetcode.NewClient(leetcode.WithCredentials(cred))
-		name, err := whoami(c)
+		user, err := c.GetUserStatus()
 		if err != nil {
 			return err
 		}
-		cmd.Println(name)
+		cmd.Println(user.Whoami(c))
 		return nil
 	},
 }
diff --git a/cmd/submit.go b/cmd/submit.go
index 51d8465d..c32ae003 100644
--- a/cmd/submit.go
+++ b/cmd/submit.go
@@ -2,7 +2,6 @@ package cmd
 
 import (
 	"fmt"
-	"time"
 
 	"github.com/hashicorp/go-hclog"
 	"github.com/spf13/cobra"
@@ -37,9 +36,14 @@ leetgo submit w330/
 			return err
 		}
 
-		limiter := utils.NewRateLimiter(10 * time.Second)
+		user, err := c.GetUserStatus()
+		if err != nil {
+			return err
+		}
+		limiter := newLimiter(user)
 
 		for _, q := range qs {
+			hclog.L().Info("submitting solution", "question", q.TitleSlug, "user", user.Whoami(c))
 			result, err := submitSolution(cmd, q, c, gen, limiter)
 			if err != nil {
 				hclog.L().Error("failed to submit solution", "question", q.TitleSlug, "err", err)
@@ -67,8 +71,6 @@ func submitSolution(
 		return nil, fmt.Errorf("failed to get solution code: %w", err)
 	}
 
-	user, _ := whoami(c)
-	hclog.L().Info("submitting solution", "question", q.TitleSlug, "user", user)
 	spin := newSpinner(cmd.ErrOrStderr())
 	spin.Suffix = " Submitting solution..."
 	spin.Reverse()
diff --git a/leetcode/models.go b/leetcode/models.go
index 310ca84d..e078fb58 100644
--- a/leetcode/models.go
+++ b/leetcode/models.go
@@ -2,6 +2,7 @@ package leetcode
 
 import (
 	"fmt"
+	"net/url"
 	"strings"
 
 	"github.com/fatih/color"
@@ -17,6 +18,11 @@ type UserStatus struct {
 	IsPremium       bool   `json:"isPremium"`
 }
 
+func (u *UserStatus) Whoami(c Client) string {
+	uri, _ := url.Parse(c.BaseURI())
+	return u.Username + "@" + uri.Host
+}
+
 type InterpretSolutionResult struct {
 	InterpretExpectedId string `json:"interpret_expected_id"`
 	InterpretId         string `json:"interpret_id"`


Test patch:
diff --git a/cmd/contest.go b/cmd/contest.go
index 672a2b91..f5221ca1 100644
--- a/cmd/contest.go
+++ b/cmd/contest.go
@@ -148,7 +148,11 @@ leetgo contest left w330
 		if err != nil {
 			return err
 		}
-		user, _ := whoami(c)
+		user, err := c.GetUserStatus()
+		if err != nil {
+			user = &leetcode.UserStatus{}
+		}
+
 		if !contest.HasFinished() && !contest.Registered {
 			register := true
 			if !viper.GetBool("yes") {
@@ -156,7 +160,7 @@ leetgo contest left w330
 					Message: fmt.Sprintf(
 						"Register for %s as %s?",
 						colorBlue.Sprint(contest.Title),
-						colorCyan.Sprint(user),
+						colorCyan.Sprint(user.Whoami(c)),
 					),
 				}
 				err := survey.AskOne(&prompt, &register)
@@ -169,7 +173,7 @@ leetgo contest left w330
 				if err != nil {
 					return err
 				}
-				hclog.L().Info("registered", "contest", contest.Title, "user", user)
+				hclog.L().Info("registered", "contest", contest.Title, "user", user.Whoami(c))
 			} else {
 				return nil
 			}
@@ -229,14 +233,17 @@ var unregisterCmd = &cobra.Command{
 		if contest.HasFinished() {
 			return fmt.Errorf("contest %s has finished", contest.Title)
 		}
-		user, _ := whoami(c)
+		user, err := c.GetUserStatus()
+		if err != nil {
+			return err
+		}
 		unregister := true
 		if !viper.GetBool("yes") {
 			prompt := survey.Confirm{
 				Message: fmt.Sprintf(
 					"Unregister from %s as %s?",
 					colorBlue.Sprint(contest.Title),
-					colorCyan.Sprint(user),
+					colorCyan.Sprint(user.Whoami(c)),
 				),
 			}
 			err = survey.AskOne(&prompt, &unregister)
@@ -249,7 +256,7 @@ var unregisterCmd = &cobra.Command{
 			if err != nil {
 				return err
 			}
-			hclog.L().Info("unregistered", "contest", contest.Title, "user", user)
+			hclog.L().Info("unregistered", "contest", contest.Title, "user", user.Whoami(c))
 		}
 
 		return nil
diff --git a/cmd/test.go b/cmd/test.go
index 97a5f786..0ae48610 100644
--- a/cmd/test.go
+++ b/cmd/test.go
@@ -3,7 +3,6 @@ package cmd
 import (
 	"fmt"
 	"io"
-	"net/url"
 	"strings"
 	"time"
 
@@ -79,8 +78,12 @@ leetgo test w330/`,
 			return fmt.Errorf("local test not supported for %s", cfg.Code.Lang)
 		}
 
-		testLimiter := utils.NewRateLimiter(10 * time.Second)
-		submitLimiter := utils.NewRateLimiter(10 * time.Second)
+		user, err := c.GetUserStatus()
+		if err != nil {
+			user = &leetcode.UserStatus{}
+		}
+		testLimiter := newLimiter(user)
+		submitLimiter := newLimiter(user)
 
 		for _, q := range qs {
 			localPassed, remotePassed := true, true
@@ -92,6 +95,7 @@ leetgo test w330/`,
 				}
 			}
 			if runRemotely {
+				hclog.L().Info("running test remotely", "question", q.TitleSlug, "user", user.Whoami(c))
 				result, err := runTestRemotely(cmd, q, c, gen, testLimiter)
 				if err != nil {
 					hclog.L().Error("failed to run test remotely", "question", q.TitleSlug, "err", err)
@@ -140,8 +144,6 @@ func runTestRemotely(
 	}
 	casesStr := strings.Join(cases, "\n")
 
-	user, _ := whoami(c)
-	hclog.L().Info("running test remotely", "question", q.TitleSlug, "user", user)
 	spin := newSpinner(cmd.ErrOrStderr())
 	spin.Suffix = " Running test..."
 	spin.Reverse()
@@ -189,6 +191,13 @@ func waitResult(c leetcode.Client, submissionId string) (
 	}
 }
 
+func newLimiter(user *leetcode.UserStatus) *utils.RateLimiter {
+	if user.IsPremium {
+		return utils.NewRateLimiter(1 * time.Second)
+	}
+	return utils.NewRateLimiter(10 * time.Second)
+}
+
 func newSpinner(w io.Writer) *spinner.Spinner {
 	spin := spinner.New(
 		spinner.CharSets[11],
@@ -199,24 +208,3 @@ func newSpinner(w io.Writer) *spinner.Spinner {
 	)
 	return spin
 }
-
-var userCache map[leetcode.Client]string
-
-func init() {
-	userCache = make(map[leetcode.Client]string)
-}
-
-func whoami(c leetcode.Client) (string, error) {
-	if userCache[c] == "" {
-		u, err := c.GetUserStatus()
-		if err != nil {
-			return "", err
-		}
-		if !u.IsSignedIn {
-			return "", leetcode.ErrUserNotSignedIn
-		}
-		uri, _ := url.Parse(c.BaseURI())
-		userCache[c] = u.Username + "@" + uri.Host
-	}
-	return userCache[c], nil
-}