aboutsummaryrefslogtreecommitdiff
path: root/maltoken.go
diff options
context:
space:
mode:
authorVidhu Kant Sharma <vidhukant@vidhukant.com>2023-11-25 20:04:12 +0530
committerVidhu Kant Sharma <vidhukant@vidhukant.com>2023-11-25 20:04:12 +0530
commit5311dee4b19fd60eab0ea5efd5a09f96a5219627 (patch)
tree1fb81f3befe156701a34dba2f9c553e0e728b819 /maltoken.go
first commitv1.0.0
Diffstat (limited to 'maltoken.go')
-rw-r--r--maltoken.go110
1 files changed, 110 insertions, 0 deletions
diff --git a/maltoken.go b/maltoken.go
new file mode 100644
index 0000000..d73ea79
--- /dev/null
+++ b/maltoken.go
@@ -0,0 +1,110 @@
+/*
+maltoken - A go package for the MyAnimeList OAuth mechanism
+Copyright © 2023 Vidhu Kant Sharma <vidhukant@vidhukant.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package maltoken
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "html"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+)
+
+var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+var SuccessHTML = "<p>Success! You may close this tab now.</p>"
+var BadRequestHTML = "<p>Invalid request.</p>"
+var ErrHTML = "<p>MyAnimeList responded with an error: %s</p>"
+
+func codeChallenge() string {
+ rand.Seed(time.Now().UnixNano())
+ b := make([]rune, 128)
+ for i := range b {
+ b[i] = letterRunes[rand.Intn(len(letterRunes))]
+ }
+ return string(b)
+}
+
+func GetChallengeLink(clientId string) (string, string) {
+ challenge := codeChallenge()
+ return challenge, "https://myanimelist.net/v1/oauth2/authorize?response_type=code&client_id=" + clientId + "&code_challenge=" + challenge
+}
+
+func Listen(clientId, verifier string, port int) (map[string]interface{}, error) {
+ var res map[string]interface{}
+ var e error
+
+ mux := http.NewServeMux()
+ server := &http.Server{
+ Addr: fmt.Sprintf(":%d", port),
+ Handler: mux,
+ }
+
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ code, codeExists := r.URL.Query()["code"]
+ if !codeExists {
+ fmt.Fprintf(w, BadRequestHTML)
+ return
+ }
+
+ resp, err := http.PostForm("https://myanimelist.net/v1/oauth2/token", url.Values {
+ "client_id": {clientId},
+ "code_verifier": {verifier},
+ "grant_type": {"authorization_code"},
+ "code": {code[0]},
+ })
+ if err != nil {
+ // error while making post request
+ e = err
+ go server.Shutdown(context.Background())
+ return
+ }
+
+ json.NewDecoder(resp.Body).Decode(&res)
+
+ if res["error"] != nil {
+ // MyAnimeList returned error
+ errMsg := fmt.Sprintf("%v (%v)", res["message"], res["error"])
+ e = errors.New(errMsg)
+
+ if strings.Contains(ErrHTML, "%s") {
+ fmt.Fprintf(w, ErrHTML, html.EscapeString(errMsg))
+ } else {
+ fmt.Fprintf(w, ErrHTML)
+ }
+
+ go server.Shutdown(context.Background())
+ return
+ }
+
+ fmt.Fprintf(w, SuccessHTML)
+ go server.Shutdown(context.Background())
+ })
+
+ err := server.ListenAndServe()
+ if err != nil && err != http.ErrServerClosed {
+ e = err
+ }
+
+ return res, e
+}