diff options
Diffstat (limited to 'direct_client.go')
| -rw-r--r-- | direct_client.go | 386 |
1 files changed, 0 insertions, 386 deletions
diff --git a/direct_client.go b/direct_client.go deleted file mode 100644 index 61824b1..0000000 --- a/direct_client.go +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package kamusku - -import ( - "bytes" - "encoding/gob" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "net/http/cookiejar" - "net/url" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/shuLhan/share/lib/debug" - libhttp "github.com/shuLhan/share/lib/http" - "github.com/shuLhan/share/lib/net/html" - "golang.org/x/net/publicsuffix" -) - -const ( - cookieFile = "cookie" - configDir = "kbbi" - maxPageNumber = 501 -) - -// -// directClient for KBBI web using HTTP. -// -type directClient struct { - baseDir string - cookieURL *url.URL - cookies []*http.Cookie - httpc *http.Client -} - -// -// newDirectClient create and initialize new client that connect directly to -// KBBI official website. -// -func newDirectClient() (cl *directClient, err error) { - cookieURL, err := url.Parse(baseURL) - if err != nil { - return nil, fmt.Errorf("newDirectClient: %w", err) - } - - jarOpt := &cookiejar.Options{ - PublicSuffixList: publicsuffix.List, - } - - jar, err := cookiejar.New(jarOpt) - if err != nil { - return nil, fmt.Errorf("newDirectClient: %w", err) - } - - cl = &directClient{ - cookieURL: cookieURL, - httpc: &http.Client{ - Jar: jar, - Timeout: defTimeout, - }, - } - - err = cl.loadCookies() - if err != nil { - return nil, fmt.Errorf("newDirectClient: %w", err) - } - - if cl.cookies != nil { - jar.SetCookies(cookieURL, cl.cookies) - } - - return cl, nil -} - -// -// CariDefinisi dari daftar kata. -// -func (cl *directClient) CariDefinisi(ins []string) ( - res DefinisiResponse, err error, -) { - res = make(DefinisiResponse, len(ins)) - - for _, in := range ins { - _, ok := res[in] - if ok { - continue - } - - kata := &Kata{} - res[in] = kata - - entriURL := baseURL + entriPath + in - httpRes, err := cl.httpc.Get(entriURL) - if err != nil { - kata.err = err - continue - } - - defer httpRes.Body.Close() - - body, err := ioutil.ReadAll(httpRes.Body) - if err != nil { - kata.err = err - continue - } - - if debug.Value >= 2 { - fmt.Printf(">>> HTML body for %s:\n%s", entriURL, body) - } - - err = kata.parseHTMLEntri(in, body) - if err != nil { - kata.err = err - } - - if len(kata.Definisi) == 0 && len(kata.Pesan) == 0 { - kata.Pesan = "Entri tidak ditemukan" - } - } - - return res, nil -} - -// -// ListKataDasar list all of the root words in dictionary. -// -func (cl *directClient) ListKataDasar() (kataDasar DaftarKata, err error) { - params := url.Values{ - paramNameMasukan: []string{paramValueDasar}, - paramNameMasukanLengkap: []string{paramValueDasar}, - } - - urlPage := baseURL + "/Cari/Jenis?" - - kataDasar = make(DaftarKata) - - for pageNumber := 1; pageNumber <= maxPageNumber; pageNumber++ { - params.Set(paramNamePage, strconv.Itoa(pageNumber)) - - req, err := http.NewRequest(http.MethodGet, urlPage+params.Encode(), nil) - if err != nil { - return kataDasar, err - } - - res, err := cl.httpc.Do(req) - if err != nil { - return kataDasar, fmt.Errorf("ListKataDasar: page %d: %w", - pageNumber, err) - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return kataDasar, fmt.Errorf("ListKataDasar: page %d: %w", - pageNumber, err) - } - - got, err := cl.parseHTMLKataDasar(body) - if err != nil { - return kataDasar, fmt.Errorf("ListKataDasar: page %d: %w", - pageNumber, err) - } - if len(got) == 0 { - break - } - - kataDasar.merge(got) - - log.Printf("ListKataDasar: halaman %d, jumlah kata %d, total kata %d", - pageNumber, len(got), len(kataDasar)) - } - - return kataDasar, nil -} - -// -// isAuthenticated will return true if the client already login; otherwise it -// will return false. -// -func (cl *directClient) isAuthenticated() bool { - return len(cl.cookies) > 0 -} - -// -// login authenticate the client using username and password. -// -func (cl *directClient) login(surel, sandi string) (err error) { - tokenLogin, err := cl.preLogin() - if err != nil { - return fmt.Errorf("Login: %w", err) - } - - params := url.Values{ - paramNameRequestVerificationToken: []string{tokenLogin}, - paramNamePosel: []string{surel}, - paramNameKataSandi: []string{sandi}, - paramNameIngatSaya: []string{paramValueFalse}, - } - - reqBody := strings.NewReader(params.Encode()) - - req, err := http.NewRequest(http.MethodPost, loginURL, reqBody) - if err != nil { - return fmt.Errorf("Login: %w", err) - } - - req.Header.Set(libhttp.HeaderContentType, libhttp.ContentTypeForm) - - res, err := cl.httpc.Do(req) - if err != nil { - return fmt.Errorf("Login: %w", err) - } - - defer res.Body.Close() - - resBody, err := ioutil.ReadAll(res.Body) - if err != nil { - return fmt.Errorf("Login: %w", err) - } - - if res.StatusCode >= http.StatusBadRequest { - return fmt.Errorf("login: %d %s", res.StatusCode, resBody) - } - - cl.cookies = cl.httpc.Jar.Cookies(cl.cookieURL) - cl.setCookies() - cl.saveCookies() - - return nil -} - -// -// setCookies for HTTP request that need an authentication. -// -func (cl *directClient) setCookies() { - cl.httpc.Jar.SetCookies(cl.cookieURL, cl.cookies) -} - -func (cl *directClient) parseHTMLKataDasar(htmlBody []byte) ( - kataDasar DaftarKata, err error, -) { - iter, err := html.Parse(bytes.NewReader(htmlBody)) - if err != nil { - return nil, err - } - - kataDasar = make(DaftarKata) - - for node := iter.Next(); node != nil; node = iter.Next() { - if !node.IsElement() { - continue - } - if node.Data != tagNameAnchor { - continue - } - hrefValue := node.GetAttrValue(attrNameHref) - if !strings.HasPrefix(hrefValue, entriPath) { - continue - } - k := strings.TrimSpace(node.FirstChild.Data) - kataDasar[k] = struct{}{} - } - - return kataDasar, nil -} - -// -// parseHTMLLogin get the token at the form login. -// -func (cl *directClient) parseHTMLLogin(htmlBody []byte) ( - token string, err error, -) { - iter, err := html.Parse(bytes.NewReader(htmlBody)) - if err != nil { - return "", err - } - - for node := iter.Next(); node != nil; node = iter.Next() { - if !node.IsElement() { - continue - } - if node.Data != tagNameInput { - continue - } - - token := node.GetAttrValue(attrNameValue) - if len(token) > 0 { - return token, nil - } - } - - return "", fmt.Errorf("token login not found") -} - -// -// preLogin initialize the client to get the first cookie. -// -func (cl *directClient) preLogin() (token string, err error) { - req, err := http.NewRequest(http.MethodGet, loginURL, nil) - if err != nil { - return "", err - } - - res, err := cl.httpc.Do(req) - if err != nil { - return "", err - } - - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return "", err - } - - token, err = cl.parseHTMLLogin(body) - if err != nil { - return "", err - } - - return token, nil -} - -// -// loadCookies load the KBBI cookies from file. -// -func (cl *directClient) loadCookies() (err error) { - cl.baseDir, err = os.UserConfigDir() - if err != nil { - return fmt.Errorf("loadCookies: %w", err) - } - - f := filepath.Join(cl.baseDir, configDir, cookieFile) - - _, err = os.Stat(f) - if errors.Is(err, os.ErrNotExist) { - return nil - } - - body, err := ioutil.ReadFile(f) - if err != nil { - return fmt.Errorf("loadCookies: %w", err) - } - - dec := gob.NewDecoder(bytes.NewReader(body)) - - err = dec.Decode(&cl.cookies) - if err != nil { - return fmt.Errorf("loadCookies: %w", err) - } - - return nil -} - -// -// saveCookies store the client cookies to the file for future use. -// -func (cl *directClient) saveCookies() { - err := os.MkdirAll(filepath.Join(cl.baseDir, configDir), 0700) - if err != nil { - log.Println("saveCookies:", err) - } - - f := filepath.Join(cl.baseDir, configDir, cookieFile) - - var buf bytes.Buffer - enc := gob.NewEncoder(&buf) - err = enc.Encode(cl.cookies) - if err != nil { - log.Println("saveCookies: ", err) - } - - err = ioutil.WriteFile(f, buf.Bytes(), 0600) - if err != nil { - log.Println("saveCookies: ", err) - } -} |
