anitrack/AniListFunctions.go
2024-07-24 09:15:52 -04:00

350 lines
7.6 KiB
Go

package AniList
import (
"AniTrack"
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/wailsapp/wails/v2/pkg/runtime"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"sync"
)
type AniListJWT struct {
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
type AniListUser struct {
Data struct {
Viewer struct {
ID int `json:"id"`
Name string `json:"name"`
} `json:"Viewer"`
} `json:"data"`
}
type AniListCurrentUserWatchList struct {
Data struct {
Page struct {
PageInfo struct {
Total int `json:"total"`
PerPage int `json:"perPage"`
CurrentPage int `json:"currentPage"`
LastPage int `json:"lastPage"`
HasNextPage bool `json:"hasNextPage"`
} `json:"pageInfo"`
MediaList []struct {
ID int `json:"id"`
MediaID int `json:"mediaId"`
Media struct {
ID int `json:"id"`
IDMal int `json:"idMal"`
Title struct {
Romaji string `json:"romaji"`
English string `json:"english"`
Native string `json:"native"`
} `json:"title"`
Description string `json:"description"`
CoverImage struct {
Large string `json:"large"`
} `json:"coverImage"`
Season string `json:"season"`
SeasonYear int `json:"seasonYear"`
Episodes int `json:"episodes"`
} `json:"media"`
Status string `json:"status"`
Notes string `json:"notes"`
Progress int `json:"progress"`
Score int `json:"score"`
Repeat int `json:"repeat"`
User struct {
Statistics struct {
Anime struct {
Count int `json:"count"`
Statuses []struct {
Status string `json:"status"`
Count int `json:"count"`
} `json:"statuses"`
} `json:"anime"`
} `json:"statistics"`
} `json:"user"`
} `json:"mediaList"`
} `json:"Page"`
} `json:"data"`
}
var jwt AniListJWT
func AniListQuery(body interface{}, login bool) (json.RawMessage, string) {
reader, _ := json.Marshal(body)
response, err := http.NewRequest("POST", "https://graphql.anilist.co", bytes.NewBuffer(reader))
if err != nil {
log.Printf("Failed at response, %s\n", err)
}
if login && (AniListJWT{}) != jwt {
response.Header.Add("Authorization", "Bearer "+jwt.AccessToken)
} else if login {
return nil, "Please login to AniList to make this request"
}
response.Header.Add("Content-Type", "application/json")
response.Header.Add("Accept", "application/json")
client := &http.Client{}
res, reserr := client.Do(response)
if reserr != nil {
log.Printf("Failed at res, %s\n", err)
}
defer res.Body.Close()
returnedBody, err := io.ReadAll(res.Body)
//var post interface{}
//err = json.Unmarshal(returnedBody, &post)
//if err != nil {
// log.Printf("Failed at unmarshal, %s\n", err)
//}
//
//return post
return returnedBody, ""
}
func (a *main.App) GetAniListItem(aniId int) any {
type Variables struct {
ID int `json:"id"`
ListType string `json:"listType"`
}
body := struct {
Query string `json:"query"`
Variables Variables `json:"variables"`
}{
Query: `
query ($id: Int!, $listType: MediaType) {
Media (id: $id, type: $listType) {
id
idMal
title {
romaji
english
}
description
coverImage {
medium
large
extraLarge
color
}
tags {
id
name
description
category
rank
isGeneralSpoiler
isMediaSpoiler
isAdult
}
}
}
`,
Variables: Variables{
ID: aniId,
ListType: "ANIME",
},
}
returnedBody, _ := AniListQuery(body, false)
var post interface{}
err := json.Unmarshal(returnedBody, &post)
if err != nil {
log.Printf("Failed at unmarshal, %s\n", err)
}
return post
}
func (a *main.App) AniListSearch(query string) any {
type Variables struct {
Search string `json:"search"`
ListType string `json:"listType"`
}
body := struct {
Query string `json:"query"`
Variables Variables `json:"variables"`
}{
Query: `
query ($search: String!, $listType: MediaType) {
Page (page: 1, perPage: 100) {
pageInfo {
total
currentPage
lastPage
hasNextPage
perPage
}
media (search: $search, type: $listType) {
id
idMal
title {
romaji
english
}
coverImage {
extraLarge
color
}
}
}
}
`,
Variables: Variables{
Search: query,
ListType: "ANIME",
},
}
returnedBody, _ := AniListQuery(body, false)
var post interface{}
err := json.Unmarshal(returnedBody, &post)
if err != nil {
log.Printf("Failed at unmarshal, %s\n", err)
}
return post
}
var ctxShutdown, cancel = context.WithCancel(context.Background())
func (a *main.App) AniListLogin() {
getAniListCodeUrl := "https://anilist.co/api/v2/oauth/authorize?client_id=" + os.Getenv("ANILIST_APP_ID") + "&redirect_uri=" + os.Getenv("ANILIST_CALLBACK_URI") + "&response_type=code"
runtime.BrowserOpenURL(a.ctx, getAniListCodeUrl)
serverDone := &sync.WaitGroup{}
serverDone.Add(1)
handleAniListCallback(serverDone)
serverDone.Wait()
}
func handleAniListCallback(wg *sync.WaitGroup) {
srv := &http.Server{Addr: ":6734"}
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
select {
case <-ctxShutdown.Done():
fmt.Println("Shutting down...")
return
default:
}
content := r.FormValue("code")
if content != "" {
jwt = getAniListAuthorizationToken(content)
fmt.Println("Shutting down...")
cancel()
err := srv.Shutdown(context.Background())
if err != nil {
log.Println("server.Shutdown:", err)
}
} else {
_, err := fmt.Fprintf(w, "Getting code failed.")
if err != nil {
return
}
}
})
go func() {
defer wg.Done()
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
fmt.Println("Shutting down...")
}()
}
func getAniListAuthorizationToken(content string) AniListJWT {
apiUrl := "https://anilist.co/api/v2/oauth/token"
resource := "/api/v2/oauth/token"
data := url.Values{}
data.Set("grant_type", "authorization_code")
data.Set("client_id", os.Getenv("ANILIST_APP_ID"))
data.Set("client_secret", os.Getenv("ANILIST_SECRET_TOKEN"))
data.Set("redirect_uri", os.Getenv("ANILIST_CALLBACK_URI"))
data.Set("code", content)
u, _ := url.ParseRequestURI(apiUrl)
u.Path = resource
urlStr := u.String()
response, err := http.NewRequest("POST", urlStr, strings.NewReader(data.Encode()))
if err != nil {
log.Printf("Failed at response, %s\n", err)
}
response.Header.Add("content-type", "application/x-www-form-urlencoded")
response.Header.Add("Content-Type", "application/json")
response.Header.Add("Accept", "application/json")
client := &http.Client{}
res, reserr := client.Do(response)
if reserr != nil {
log.Printf("Failed at res, %s\n", err)
}
defer res.Body.Close()
returnedBody, err := io.ReadAll(res.Body)
var post AniListJWT
err = json.Unmarshal(returnedBody, &post)
if err != nil {
log.Printf("Failed at unmarshal, %s\n", err)
}
return post
}
func (a *main.App) GetAniListLoggedInUserId() AniListUser {
if (AniListJWT{}) == jwt {
a.AniListLogin()
}
body := struct {
Query string `json:"query"`
}{
Query: `
query {
Viewer {
id
name
}
}
`,
}
user, _ := AniListQuery(body, true)
var post AniListUser
err := json.Unmarshal(user, &post)
if err != nil {
log.Printf("Failed at unmarshal, %s\n", err)
}
fmt.Println("UserInfo: ", post)
return post
}
func (a *main.App) GetAniListUserWatchingList() {
}