aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVidhu Kant Sharma <bokuwakanojogahoshii@yahoo.com>2022-02-05 16:52:20 +0000
committerGitHub <noreply@github.com>2022-02-05 16:52:20 +0000
commit787ff0ea0262385cb43f549ad970fd7cf78e13d3 (patch)
tree555b38f89250f0fa2f343966ff45a4a7089f1cfd
parente38b4ae7575df9be1a89265e66ad3808a4443082 (diff)
parente550d3d71360c4da68c7f9e978ef45f05e68b216 (diff)
Merge pull request #1 from MikunoNaka/anime
Merging anime branch into main, with all the currently available API endpoints implemented and working (not sure if properly)
-rw-r--r--LICENSE4
-rwxr-xr-xanime-searcher-2000bin0 -> 6593031 bytes
-rw-r--r--anime/anime.go252
-rw-r--r--anime/anime.structs.go (renamed from anime/structs.go)46
-rw-r--r--anime/errhandlers.go71
-rw-r--r--anime/general.structs.go28
-rw-r--r--anime/ranking.structs.go41
-rw-r--r--anime/search.structs.go32
-rw-r--r--anime/seasonal.structs.go34
-rw-r--r--anime/suggestedanime.structs.go31
-rw-r--r--anime/util.go89
-rw-r--r--anime/validators.go96
12 files changed, 689 insertions, 35 deletions
diff --git a/LICENSE b/LICENSE
index f288702..29a48b1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
+ mal2go - MyAnimeList V2 API wrapper for Go
+ Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
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
diff --git a/anime-searcher-2000 b/anime-searcher-2000
new file mode 100755
index 0000000..bee2537
--- /dev/null
+++ b/anime-searcher-2000
Binary files differ
diff --git a/anime/anime.go b/anime/anime.go
index 5d054ab..3344016 100644
--- a/anime/anime.go
+++ b/anime/anime.go
@@ -1,40 +1,250 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
package anime
import (
"encoding/json"
+ "errors"
"fmt"
- "io/ioutil"
- "log"
- "net/http"
+ "strconv"
)
-func GetAnimeById(token string, animeId int) Anime {
- client := &http.Client{}
- endpoint := fmt.Sprintf("https://api.myanimelist.net/v2/anime/%d?fields=id,title,main_picture,alternative_titles,start_date,end_date,synopsis,mean,rank,popularity,num_list_users,num_scoring_users,nsfw,created_at,updated_at,media_type,status,genres,my_list_status,num_episodes,start_season,broadcast,source,average_episode_duration,rating,pictures,background,related_anime,related_manga,recommendations,studios,statistics", animeId)
+const BASE_URL string = "https://api.myanimelist.net/v2/anime"
- // generate request
- req, err := http.NewRequest("GET", endpoint, nil)
- if err != nil {
- log.Fatal(err)
+// in MAL documentation this is named Get Anime List
+func SearchAnime(token, searchString string, limit, offset int, fields []string) (AnimeSearch, error) {
+ var searchResults AnimeSearch
+
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ return searchResults, limitsErr
}
- req.Header.Add("Authorization", token)
- // req.Header.Add("Content-Type", "application/json")
- // do request
- res, err := client.Do(req)
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
if err != nil {
- log.Fatal(err)
+ return searchResults, err
}
- defer res.Body.Close()
- body, err := ioutil.ReadAll(res.Body)
- if err != nil {
- log.Fatal(err)
+ // generate endpoint url with custom params
+ endpoint, _ := urlGenerator(
+ BASE_URL,
+ []string{"q", "limit", "offset", "fields"},
+ [][]string{{searchString}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
+ true,
+ )
+
+ // gets data from API and stores it in a struct
+ var animeSearchData AnimeSearchRaw
+ data := requestHandler(token, endpoint)
+ json.Unmarshal([]byte(data), &animeSearchData)
+
+ // Adding all the animes to another list to get formatted results later
+ var animes []Anime
+ for _, element := range animeSearchData.Data {
+ animes = append(animes, element.Anime)
}
- data := string(body)
+ // finally generate AnimeList
+ searchResults = AnimeSearch {
+ Animes: animes,
+ Paging: animeSearchData.Paging,
+ }
+
+ return searchResults, nil
+}
+
+// Each anime has its own ID on MAL
+func GetAnimeById(token string, animeId int, fields []string) (Anime, error) {
var anime Anime
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ return anime, err
+ }
+
+ endpoint, _ := urlGenerator(
+ BASE_URL + "/" + strconv.Itoa(animeId),
+ []string{"fields"},
+ /* it seems to still return all fields from the API.
+ * this might be an issue with MAL itself
+ * TODO: look into this */
+ [][]string{fields},
+ true,
+ )
+
+ data := requestHandler(token, endpoint)
json.Unmarshal([]byte(data), &anime)
- return anime
+ return anime, nil
+}
+
+// Ranking is a list of anime sorted by their rank
+func GetAnimeRanking(token string, rankingType string, limit, offset int, fields []string) (AnimeRanking, error) {
+ var animeRanking AnimeRanking
+
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ return animeRanking, limitsErr
+ }
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ return animeRanking, err
+ }
+
+ // if ranking type is invalid
+ if !isValidRankingType(rankingType) {
+ return animeRanking, errors.New(fmt.Sprintf("GetAnimeRanking: Invalid ranking type specified: \"%s\"", rankingType))
+ }
+
+ endpoint, _ := urlGenerator(
+ BASE_URL + "/ranking",
+ []string{"ranking_type", "limit", "offset", "fields"},
+ [][]string{{rankingType}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
+ true,
+ )
+
+ // gets data from API and stores it in a struct
+ var rankingData RawRanking
+ data := requestHandler(token, endpoint)
+ json.Unmarshal([]byte(data), &rankingData)
+
+ // Adding all the animes in ranking list to a slice
+ var animeRankingTitles []AnimeRankingTitle
+ for _, element := range rankingData.Data {
+ animeRankingTitles = append(
+ animeRankingTitles,
+ AnimeRankingTitle {
+ Anime: element.Anime,
+ RankNum: element.Ranking.Rank,
+ },
+ )
+ }
+
+ // Finally, create the AnimeRanking object
+ animeRanking = AnimeRanking {
+ Titles: animeRankingTitles,
+ Paging: ListPaging {
+ NextPage: rankingData.Paging.NextPage,
+ PrevPage: rankingData.Paging.PrevPage,
+ },
+ }
+
+ return animeRanking, nil
+}
+
+// get list of animes from specified season
+func GetSeasonalAnime(token, year, season, sort string, limit, offset int, fields []string) (SeasonalAnime, error) {
+ var seasonalAnime SeasonalAnime
+
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ return seasonalAnime, limitsErr
+ }
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ return seasonalAnime, err
+ }
+
+ // checks if valid season is specified
+ if !isValidSeason(season) {
+ return seasonalAnime, errors.New(fmt.Sprintf("GetSeasonalAnime: Invalid season specified: \"%s\"", season))
+ }
+
+ // checks if valid sort is specified
+ if !isValidSort(sort) {
+ return seasonalAnime, errors.New(fmt.Sprintf("GetSeasonalAnime: Invalid sort specified: \"%s\"", sort))
+ }
+
+ endpoint, _ := urlGenerator(
+ BASE_URL + fmt.Sprintf("/season/%s/%s", year, season),
+ []string{"sort", "limit", "offset", "fields"},
+ [][]string{{sort}, {strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
+ true,
+ )
+
+ // gets data from API and stores it in a struct
+ var seasonalAnimeData SeasonalAnimeRaw
+ data := requestHandler(token, endpoint)
+ json.Unmarshal([]byte(data), &seasonalAnimeData)
+
+ // Adding all the animes to another list to get formatted results later
+ var animes []Anime
+ for _, element := range seasonalAnimeData.Data {
+ animes = append(animes, element.Anime)
+ }
+
+ // finally generate SeasonalAnime
+ seasonalAnime = SeasonalAnime {
+ Animes: animes,
+ Paging: seasonalAnimeData.Paging,
+ Season: seasonalAnimeData.Season,
+ }
+
+ return seasonalAnime, nil
+}
+
+// get anime suggestions for the user
+func GetSuggestedAnime(token string, limit, offset int, fields []string) (SuggestedAnime, error){
+ var suggestedAnime SuggestedAnime
+
+ // error handling for limit and offset
+ limitsErr := limitsErrHandler(limit, offset)
+ if limitsErr != nil {
+ return suggestedAnime, limitsErr
+ }
+
+ // handle all the errors for the fields
+ fields, err := fieldsErrHandler(fields)
+ if err != nil {
+ return suggestedAnime, err
+ }
+
+ endpoint, _ := urlGenerator(
+ BASE_URL + "/suggestions",
+ []string{"limit", "offset", "fields"},
+ [][]string{{strconv.Itoa(limit)}, {strconv.Itoa(offset)}, fields},
+ true,
+ )
+
+ // gets data from API and stores it in a struct
+ var suggestedAnimeData SuggestedAnimeRaw
+ data := requestHandler(token, endpoint)
+ json.Unmarshal([]byte(data), &suggestedAnimeData)
+
+ // Adding all the animes to another list to get formatted results later
+ var animes []Anime
+ for _, element := range suggestedAnimeData.Data {
+ animes = append(animes, element.Anime)
+ }
+
+ // finally generate RecommendedAnime struct
+ suggestedAnime = SuggestedAnime {
+ Animes: animes,
+ Paging: suggestedAnimeData.Paging,
+ }
+
+ return suggestedAnime, nil
}
diff --git a/anime/structs.go b/anime/anime.structs.go
index bb35ded..1599e6d 100644
--- a/anime/structs.go
+++ b/anime/anime.structs.go
@@ -1,3 +1,19 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
package anime
type AnimePicture struct {
@@ -6,11 +22,11 @@ type AnimePicture struct {
}
type StatusStatistics struct {
- Watching int `json:"watching"`
- Completed int `json:"completed"`
- OnHold int `json:"on_hold"`
- Dropped int `json:"dropped"`
- PlanToWatch int `json:"plan_to_watch"`
+ Watching string `json:"watching"`
+ Completed string `json:"completed"`
+ OnHold string `json:"on_hold"`
+ Dropped string `json:"dropped"`
+ PlanToWatch string `json:"plan_to_watch"`
}
type AnimeStatistics struct {
@@ -31,11 +47,6 @@ type ListStatus struct {
UpdatedAt string `json:"updated_at"`
}
-type Season struct {
- Year int `json:"year"`
- Name string `json:"season"`
-}
-
type Broadcast struct {
Day string `json:"day_of_the_week"`
Time string `json:"start_time"`
@@ -57,11 +68,17 @@ type Recommendation struct {
Num int `json:"num_recommendations"`
}
+type AltTitles struct {
+ Synonyms []string `json:"synonyms"`
+ En string `json:"en"`
+ Ja string `json:"ja"`
+}
+
type Anime struct {
Id int `json:"id"`
Title string `json:"title"`
MainPicture AnimePicture `json:"main_picture"`
- AltTitles []string `json:"alternative_titles"`
+ AltTitles AltTitles `json:"alternative_titles"`
StartDate string `json:"start_date"`
EndDate string `json:"end_date"`
Synopsis string `json:"synopsis"`
@@ -70,7 +87,11 @@ type Anime struct {
Popularity int `json:"popularity"`
NumListUsers int `json:"num_list_users"`
NumScoringUsers int `json:"num_scoring_users"`
- NsfwStatus string `json:"nsfw"` // find out what values are there
+ /* NsfwStatus potential values:
+ * white = sfw
+ * gray = probably nsfw
+ * black = nsfw */
+ NsfwStatus string `json:"nsfw"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
MediaType string `json:"media_type"`
@@ -82,6 +103,7 @@ type Anime struct {
Broadcast Broadcast `json:"broadcast"`
Source string `json:"source"`
DurationSeconds int `json:"average_episode_duration"`
+ // Rating as in R, PG13, etc
Rating string `json:"rating"`
Pictures []AnimePicture `json:"pictures"`
Background string `json:"background"`
diff --git a/anime/errhandlers.go b/anime/errhandlers.go
new file mode 100644
index 0000000..d7f70f9
--- /dev/null
+++ b/anime/errhandlers.go
@@ -0,0 +1,71 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+import (
+ "errors"
+ "fmt"
+)
+
+/* NOTE: MAL still seems to send some fields
+ * even if they aren't requested.
+ * those include Title, Picture, Id, etc */
+// default fields to use when none are specified
+var defaultFields []string = []string{
+ "id", "title", "main_picture",
+ "alternative_titles", "start_date",
+ "end_date", "synopsis", "mean", "rank",
+ "popularity", "num_list_users",
+ "num_scoring_users", "nsfw", "created_at",
+ "updated_at", "media_type", "status",
+ "genres", "my_list_status", "num_episodes",
+ "start_season", "broadcast", "source",
+ "average_episode_duration", "rating",
+ "pictures", "background", "related_anime",
+ "related_manga", "recommendations",
+ "studios", "statistics",
+}
+
+// if fields aren't specified
+func fieldsErrHandler(fields []string) ([]string, error) {
+ if cap(fields) == 0 {
+ // uses all the default fields if none specified
+ return defaultFields, nil
+ }
+
+ // checks if each given field is valid
+ for _, j := range(fields) {
+ if !isValidField(j) {
+ return []string{}, errors.New(fmt.Sprintf("InvalidFieldError: Invalid field specified: \"%s\"", j))
+ }
+ }
+
+ // everything's fine!
+ return fields, nil
+}
+
+// if limit or error specified are above the limit
+func limitsErrHandler(limit, offset int) error {
+ maxOffset := 500 - limit
+ if limit > 500 {
+ return errors.New(fmt.Sprintf("InvalidLimitError: Limit specified too high (%d > 500).", limit))
+ } else if offset > maxOffset {
+ return errors.New(fmt.Sprintf("InvalidOffsetError: Offset specified too high (%d > %d).", offset, maxOffset))
+ }
+ // return nil if no error
+ return nil
+}
diff --git a/anime/general.structs.go b/anime/general.structs.go
new file mode 100644
index 0000000..c49b762
--- /dev/null
+++ b/anime/general.structs.go
@@ -0,0 +1,28 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// contains previous/next page for anime list
+type ListPaging struct {
+ NextPage string `json:"next"`
+ PrevPage string `json:"previous"` // might need checking
+}
+
+type Season struct {
+ Year int `json:"year"`
+ Name string `json:"season"`
+}
diff --git a/anime/ranking.structs.go b/anime/ranking.structs.go
new file mode 100644
index 0000000..28ae860
--- /dev/null
+++ b/anime/ranking.structs.go
@@ -0,0 +1,41 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// this is how the API returns data (looks horrible)
+type RawRanking struct {
+ Data []struct {
+ Anime Anime `json:"node"`
+ Ranking struct {
+ Rank int `json:"rank"`
+ } `json:"ranking"`
+ } `json:"data"`
+
+ Paging ListPaging `json:"paging"`
+}
+
+// each anime has a ranking number
+type AnimeRankingTitle struct {
+ Anime Anime
+ RankNum int
+}
+
+// this is how mal2go returns data
+type AnimeRanking struct {
+ Titles []AnimeRankingTitle
+ Paging ListPaging
+}
diff --git a/anime/search.structs.go b/anime/search.structs.go
new file mode 100644
index 0000000..e59bb44
--- /dev/null
+++ b/anime/search.structs.go
@@ -0,0 +1,32 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// this is how the API returns data (looks horrible)
+type AnimeSearchRaw struct {
+ Data []struct {
+ Anime Anime `json:"node"`
+ } `json:"data"`
+
+ Paging ListPaging `json:"paging"`
+}
+
+// this is how mal2go returns data
+type AnimeSearch struct {
+ Animes []Anime
+ Paging ListPaging
+}
diff --git a/anime/seasonal.structs.go b/anime/seasonal.structs.go
new file mode 100644
index 0000000..06bda42
--- /dev/null
+++ b/anime/seasonal.structs.go
@@ -0,0 +1,34 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// this is how the API returns data (looks horrible)
+type SeasonalAnimeRaw struct {
+ Data []struct {
+ Anime Anime `json:"node"`
+ } `json:"data"`
+
+ Paging ListPaging `json:"paging"`
+ Season Season `json:"season"`
+}
+
+// this is how mal2go returns data
+type SeasonalAnime struct {
+ Animes []Anime
+ Paging ListPaging
+ Season Season
+}
diff --git a/anime/suggestedanime.structs.go b/anime/suggestedanime.structs.go
new file mode 100644
index 0000000..a7b3a60
--- /dev/null
+++ b/anime/suggestedanime.structs.go
@@ -0,0 +1,31 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// this is how the API returns data (looks horrible)
+type SuggestedAnimeRaw struct {
+ Data []struct {
+ Anime Anime `json:"node"`
+ } `json:"data"`
+ Paging ListPaging `json:"paging"`
+}
+
+// this is how mal2go returns data
+type SuggestedAnime struct {
+ Animes []Anime
+ Paging ListPaging
+}
diff --git a/anime/util.go b/anime/util.go
new file mode 100644
index 0000000..f1f3b2a
--- /dev/null
+++ b/anime/util.go
@@ -0,0 +1,89 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+import (
+ "io/ioutil"
+ "log"
+ "net/http"
+ "errors"
+)
+
+// Handles HTTP request with your OAuth token as a Header
+// TODO: Verify that this function is safe to use
+func requestHandler(token string, endpoint string) string {
+ client := &http.Client{}
+
+ // generate request
+ req, err := http.NewRequest("GET", endpoint, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+ req.Header.Add("Authorization", token)
+
+ // do request
+ res, err := client.Do(req)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer res.Body.Close()
+
+ // read body
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ return string(body)
+}
+
+func urlGenerator(baseUrl string, names []string, values [][]string, isPrimary bool) (string, error) {
+ // length of names and values should be same
+ if cap(names) != cap(values) {
+ return "", errors.New("urlGenerator: Error: Length of names and values don't match.")
+ }
+
+ var fields string
+
+ for index, name := range(names) {
+ var data string
+ /* if the data is the first field in URL,
+ * it goes like ?key=value
+ * else it is &nextkey=value */
+ if isPrimary {
+ data = "?" + name + "="
+ } else {
+ data = "&" + name + "="
+ }
+
+ // add values to data variable
+ for i, j := range values[index] {
+ if i > 0 {
+ data = data + "," + j
+ } else {
+ data = data + j
+ }
+ }
+
+ fields = fields + data
+
+ // from now on all other fields will be secondary
+ isPrimary = false
+ }
+
+ return baseUrl + fields, nil
+}
diff --git a/anime/validators.go b/anime/validators.go
new file mode 100644
index 0000000..7f6a7cc
--- /dev/null
+++ b/anime/validators.go
@@ -0,0 +1,96 @@
+/* mal2go - MyAnimeList V2 API wrapper for Go
+ * Copyright (C) 2022 Vidhu Kant Sharma <vidhukant@protonmail.ch>
+
+ * 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 <https://www.gnu.org/licenses/>. */
+
+package anime
+
+// Checks if given rankingType is valid
+func isValidRankingType(rankingType string) bool {
+ switch rankingType {
+ case
+ "all",
+ "airing",
+ "upcoming",
+ "tv",
+ "ova",
+ "movie",
+ "special",
+ "bypopularity",
+ "favorite": return true
+ }
+ return false
+}
+
+// Checks if given rankingType is valid
+func isValidField(field string) bool {
+ switch field {
+ case
+ "id",
+ "title",
+ "main_picture",
+ "alternative_titles",
+ "start_date",
+ "end_date",
+ "synopsis",
+ "mean",
+ "rank",
+ "popularity",
+ "num_list_users",
+ "num_scoring_users",
+ "nsfw",
+ "created_at",
+ "updated_at",
+ "media_type",
+ "status",
+ "genres",
+ "my_list_status",
+ "num_episodes",
+ "start_season",
+ "broadcast",
+ "source",
+ "average_episode_duration",
+ "rating",
+ "pictures",
+ "background",
+ "related_anime",
+ "related_manga",
+ "recommendations",
+ "studios",
+ "statistics": return true
+ }
+ return false
+}
+
+// Checks if given season is valid
+func isValidSeason(season string) bool {
+ switch season {
+ case
+ "winter",
+ "spring",
+ "summer",
+ "fall": return true
+ }
+ return false
+}
+
+// Checks if given sort is valid
+func isValidSort(sort string) bool {
+ switch sort {
+ case
+ "anime_score",
+ "anime_num_list_users": return true
+ }
+ return false
+}