diff --git a/internal/kb/kbArticleList.json b/internal/kb/kbArticleList.json index 682d96fb..4ec2b51a 100644 --- a/internal/kb/kbArticleList.json +++ b/internal/kb/kbArticleList.json @@ -4,8 +4,7 @@ "url": "https://proton.me/support/automatically-start-bridge", "title": "Automatically start Bridge", "keywords": [ - "start", - "automatically", + "automatic", "login", "start", "boot" @@ -53,7 +52,8 @@ "title": "Proton Mail Bridge connection issues with Thunderbird, Outlook, and Apple Mail", "keywords": [ "connect", - "error", + "SSL", + "STARTTLS", "client", "program", "Outlook", @@ -68,7 +68,8 @@ "keywords": [ "pgp", "gpg", - "encrypt" + "encrypt", + "crypto" ] }, { @@ -205,7 +206,6 @@ "Outlook", "configure", "setup", - "program", "application", "setup", "IMAP", diff --git a/internal/kb/suggester.go b/internal/kb/suggester.go index e5eeef94..0fa31dfb 100644 --- a/internal/kb/suggester.go +++ b/internal/kb/suggester.go @@ -52,14 +52,20 @@ func GetArticleList() (ArticleList, error) { return articles, err } -// GetSuggestions return a list of up to 3 suggestions for KB articles matching the given user input. +// GetSuggestions returns a list of up to 3 suggestions for the built-in list of KB articles matching the given user input. func GetSuggestions(userInput string) (ArticleList, error) { - userInput = strings.ToUpper(userInput) articles, err := GetArticleList() if err != nil { return ArticleList{}, err } + return GetSuggestionsFromArticleList(userInput, articles) +} + +// GetSuggestionsFromArticleList returns a list of up to 3 suggestions for the given list of KB articles matching the given user input. +func GetSuggestionsFromArticleList(userInput string, articles ArticleList) (ArticleList, error) { + userInput = strings.ToUpper(userInput) + for _, article := range articles { for _, keyword := range article.Keywords { if strings.Contains(userInput, strings.ToUpper(keyword)) { @@ -78,7 +84,7 @@ func GetSuggestions(userInput string) (ArticleList, error) { return articles, nil } -// GetArticleIndex retrieve the index of an article from its url. if the article is not found, ErrArticleNotFound is returned. +// GetArticleIndex retrieves the index of an article from its url. if the article is not found, ErrArticleNotFound is returned. func GetArticleIndex(url string) (uint64, error) { articles, err := GetArticleList() if err != nil { diff --git a/internal/kb/suggester_test.go b/internal/kb/suggester_test.go index e4373e2f..97debcd9 100644 --- a/internal/kb/suggester_test.go +++ b/internal/kb/suggester_test.go @@ -18,7 +18,6 @@ package kb import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -42,16 +41,39 @@ func Test_ArticleList(t *testing.T) { func Test_GetSuggestions(t *testing.T) { suggestions, err := GetSuggestions("Thunderbird is not working, error during password") require.NoError(t, err) - require.True(t, len(suggestions) <= 3) - for _, article := range suggestions { - fmt.Printf("Score: %v - %#v\n", article.Score, article.Title) - } - + count := len(suggestions) + require.True(t, (count > 0) && (count <= 3)) suggestions, err = GetSuggestions("Supercalifragilisticexpialidocious Sesquipedalian Worcestershire") require.NoError(t, err) require.Empty(t, suggestions) } +func Test_GetSuggestionsFromArticleList(t *testing.T) { + articleList := ArticleList{} + suggestions, err := GetSuggestionsFromArticleList("Thunderbird", articleList) + require.NoError(t, err) + require.Empty(t, suggestions) + + articleList = ArticleList{ + &Article{ + Index: 0, + URL: "https://proton.me", + Title: "Proton home page", + Keywords: []string{"proton"}, + }, + &Article{ + Index: 1, + URL: "https://mozilla.org", + Title: "Mozilla home page", + Keywords: []string{"mozilla"}, + }, + } + suggestions, err = GetSuggestionsFromArticleList("PRoToN", articleList) + require.NoError(t, err) + require.Len(t, suggestions, 1) + require.Equal(t, suggestions[0].URL, "https://proton.me") +} + func Test_GetArticleIndex(t *testing.T) { index1, err := GetArticleIndex("https://proton.me/support/bridge-for-linux") require.NoError(t, err) diff --git a/utils/kb-suggester/kb-suggester.go b/utils/kb-suggester/kb-suggester.go index 9ae878ac..6a6b1787 100644 --- a/utils/kb-suggester/kb-suggester.go +++ b/utils/kb-suggester/kb-suggester.go @@ -18,40 +18,120 @@ package main import ( + "encoding/json" + "errors" "fmt" "io" + "log" "os" + "path/filepath" "github.com/ProtonMail/proton-bridge/v3/internal/kb" + "github.com/urfave/cli/v2" ) -func checkErrors(err error) { - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) +const flagArticles = "articles" +const flagInput = "input" + +func main() { + app := &cli.App{ + Name: "kb-suggester", + Usage: "test bridge KB article suggester", + HideHelpCommand: true, + ArgsUsage: "", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: flagArticles, + Aliases: []string{"a"}, + Usage: "use `articles.json` as the JSON article list", + TakesFile: true, + }, + &cli.StringFlag{ + Name: flagInput, + Aliases: []string{"i"}, + Usage: "read user input from the `userInput` file", + TakesFile: true, + }, + }, + Action: run, + } + + if err := app.Run(os.Args); err != nil { + log.Fatal(err) } } -func main() { - fi, err := os.Stdin.Stat() - checkErrors(err) +func getUserInput(ctx *cli.Context) (string, error) { + inputFile := ctx.String(flagInput) + var bytes []byte + var err error - if (fi.Mode() & os.ModeNamedPipe) == 0 { - fmt.Println("Type your input, Ctrl+D to finish: ") + if len(inputFile) == 0 { + var fi os.FileInfo + if fi, err = os.Stdin.Stat(); err != nil { + return "", err + } + + if (fi.Mode() & os.ModeNamedPipe) == 0 { + fmt.Println("Type your input, Ctrl+D to finish: ") + } + bytes, err = io.ReadAll(os.Stdin) + } else { + bytes, err = os.ReadFile(filepath.Clean(inputFile)) } - bytes, err := io.ReadAll(os.Stdin) - checkErrors(err) + if err != nil { + return "", err + } - suggestions, err := kb.GetSuggestions(string(bytes)) - checkErrors(err) + return string(bytes), nil +} + +func getArticleList(ctx *cli.Context) (kb.ArticleList, error) { + articleFile := ctx.String(flagArticles) + if len(articleFile) == 0 { + return kb.GetArticleList() + } + + bytes, err := os.ReadFile(filepath.Clean(articleFile)) + if err != nil { + return nil, err + } + + var result kb.ArticleList + err = json.Unmarshal(bytes, &result) + return result, err +} + +func run(ctx *cli.Context) error { + if ctx.Args().Len() > 0 { + _ = cli.ShowAppHelp(ctx) + return errors.New("command accept no argument") + } + + articles, err := getArticleList(ctx) + if err != nil { + return err + } + + userInput, err := getUserInput(ctx) + if err != nil { + return err + } + + suggestions, err := kb.GetSuggestionsFromArticleList(userInput, articles) + if err != nil { + return err + } if len(suggestions) == 0 { fmt.Println("No suggestions found") - return + return nil } for _, suggestion := range suggestions { fmt.Printf("Score %v: %v (%v)\n", suggestion.Score, suggestion.Title, suggestion.URL) } + + return nil }