package main import ( "encoding/json" "fmt" "io" "log" "net/http" "net/url" "strconv" "strings" ) func MALHelper(method string, malUrl string, body url.Values) (json.RawMessage, string, error) { client := &http.Client{} req, err := http.NewRequest(method, malUrl, strings.NewReader(body.Encode())) if err != nil { message, _ := json.Marshal(struct { Message string `json:"message"` }{ Message: "Failed to create request: " + err.Error(), }) return message, "", fmt.Errorf("failed to create request: %w", err) } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Authorization", "Bearer "+myAnimeListJwt.AccessToken) resp, err := client.Do(req) if err != nil { message, _ := json.Marshal(struct { Message string `json:"message"` }{ Message: "Network error: " + err.Error(), }) return message, "", fmt.Errorf("network error: %w", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, resp.Status, fmt.Errorf("failed to read response: %w", err) } if resp.Status == "401 Unauthorized" { refreshMyAnimeListAuthorizationToken() return MALHelper(method, malUrl, body) } if resp.Status != "200 OK" && resp.Status != "201 Created" && resp.Status != "202 Accepted" { return respBody, resp.Status, fmt.Errorf("API returned status: %s", resp.Status) } return respBody, resp.Status, nil } func (a *App) GetMyAnimeList(count int) (MALWatchlist, error) { limit := strconv.Itoa(count) user := a.GetMyAnimeListLoggedInUser() malUrl := "https://api.myanimelist.net/v2/users/" + user.Name + "/animelist?fields=list_status&status=watching&limit=" + limit var malList MALWatchlist respBody, resStatus, err := MALHelper("GET", malUrl, nil) if err != nil { return malList, fmt.Errorf("failed to get MAL watchlist: %w", err) } if resStatus == "200 OK" { err := json.Unmarshal(respBody, &malList) if err != nil { log.Printf("Failed to unmarshal json response, %s\n", err) return malList, fmt.Errorf("failed to parse response: %w", err) } } return malList, nil } func (a *App) MyAnimeListUpdate(anime MALAnime, update MALUploadStatus) (MalListStatus, error) { if update.NumTimesRewatched >= 1 { update.IsRewatching = true } else { update.IsRewatching = false } body := url.Values{} body.Set("status", update.Status) body.Set("is_rewatching", strconv.FormatBool(update.IsRewatching)) body.Set("score", strconv.Itoa(update.Score)) body.Set("num_watched_episodes", strconv.Itoa(update.NumWatchedEpisodes)) body.Set("num_times_rewatched", strconv.Itoa(update.NumTimesRewatched)) body.Set("comments", update.Comments) malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(anime.Id) + "/my_list_status" var status MalListStatus respBody, respStatus, err := MALHelper("PATCH", malUrl, body) if err != nil { return status, fmt.Errorf("failed to update MAL entry: %w", err) } if respStatus == "200 OK" { err := json.Unmarshal(respBody, &status) if err != nil { log.Printf("Failed to unmarshal json response, %s\n", err) return status, fmt.Errorf("failed to parse response: %w", err) } } return status, nil } func (a *App) GetMyAnimeListAnime(id int) (MALAnime, error) { malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,genres,created_at,updated_at,media_type,status,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,recommendations,studios,statistics" var malAnime MALAnime respBody, respStatus, err := MALHelper("GET", malUrl, nil) if err != nil { return malAnime, fmt.Errorf("failed to get MAL anime: %w", err) } if respStatus == "200 OK" { err := json.Unmarshal(respBody, &malAnime) if err != nil { log.Printf("Failed to unmarshal json response, %s\n", err) return malAnime, fmt.Errorf("failed to parse response: %w", err) } } return malAnime, nil } func (a *App) DeleteMyAnimeListEntry(id int) (bool, error) { malUrl := "https://api.myanimelist.net/v2/anime/" + strconv.Itoa(id) + "/my_list_status" _, respStatus, err := MALHelper("DELETE", malUrl, nil) if err != nil { return false, fmt.Errorf("failed to delete MAL entry: %w", err) } if respStatus == "200 OK" { return true, nil } else { return false, fmt.Errorf("delete failed with status: %s", respStatus) } }