# mg mg is a free and open source library for accessing the official MyAnimeList V2 API using Go. All endpoints are supported except forums as of now. If you find any bugs, please write to me at # License Licenced under GNU General Public Licence V3 GNU GPL License: [link](https://mikunonaka.net/mg/tree/LICENSE) Copyright (c) 2022-2023 Vidhu Kant Sharma # Documentation ***for v1.0.1+*** *Documentation might be incomplete or buggy. If you want to submit any changes, or need any help, please feel free to contact me at the given E-mail address.* ## Initializing the mg client There are 2 ways to authenticate with mg: - ClientAuth With ClientAuth, you just need to use your MyAnimeList Client ID to access the read-only resources. You cannot do actions that require logging in. To use ClientAuth, set `client.ClientAuthOnly` to `true`. - MainAuth With MainAuth, you can perform actions like getting recommendations, or editing an anime/manga list. For this, you need to generate a Bearer token; for which a Client ID is still needed. `ClientAuthOnly` must be set to `false` to use MainAuth. Go to to generate your very own Client ID! Check out [this script](https://git.mikunonaka.net/mal-authtoken-generator/about/) to generate an auth token, whilst I work on adding this functionality to mg! It has been deprecated and may or may not work, but it's better than nothing! Also check out [this guide](https://myanimelist.net/blog.php?eid=835707) for more info; or just in case if this script doesn't work! ``` go package main import ( "vidhukant.com/mg" ) func main() { var client mg.Client // either this client.MainAuth = "Bearer " // or this client.ClientAuth = "" client.ClientAuthOnly = true // ... } ``` # Anime ## Searching for an anime `MainAuth` or `ClientAuth` ``` go // ... var animes []mg.Anime params := mg.SearchParamsNew() params.SearchString = "mushishi" // optional; these are the default values params.Limit = 100 // max = 100 params.Offset = 0 params.NSFW = false params.Fields = []string{} err := client.SearchAnime(&animes, params) if err != nil { fmt.Println(err) } for _, anime := range animes { fmt.Println(anime.Title) } // ... ``` ## Getting an anime's information `MainAuth` or `ClientAuth` _searching can be a bit inaccurate, if you know the anime's ID (obtain it with SearchAnime!) and want to get detailed information, use this._ ``` go // ... var anime mg.Anime err := client.GetAnimeById(&anime, /*anime id*/ 457, /*fields*/ []string{}) if err != nil { fmt.Println(err) } // ... ``` ## Getting anime ranking list `MainAuth` or `ClientAuth` Accepted ranking types: | Ranking Type | Description | |----------------------------|-------------------------| | mg.RankingTypeAll | Top Anime Series | | mg.RankingTypeByPopularity | Top Anime by Popularity | | mg.RankingTypeFavorite | Top Favorited Anime | | mg.RankingTypeAiring | Top Airing Anime | | mg.RankingTypeUpcoming | Top Upcoming Anime | | mg.RankingTypeTV | Top Anime TV Series | | mg.RankingTypeOVA | Top Anime OVA Series | | mg.RankingTypeMovie | Top Anime Movies | | mg.RankingTypeSpecial | Top Anime Specials | ## Getting seasonal animes `MainAuth` or `ClientAuth` Accepted seasons: - `mg.SeasonWinter` - `mg.SeasonSpring` - `mg.SeasonSummer` - `mg.SeasonFall` Accepted sorts: - `mg.SeasonSortByAnimeScore` (descending) - `mg.SeasonSortByNumListUsers` (descending) ``` go // ... var seasonals []mg.Anime params := mg.SeasonalParamsNew() params.Year = "2023" // required params.Season = mg.SeasonFall // required // optional; these are the default values params.Sort = mg.SeasonSortByAnimeScore params.Limit = 100 // max = 500 params.Offset = 0 params.NSFW = false params.Fields = []string{} err := client.GetSeasonalAnime(&seasonals, params) if err != nil { fmt.Println(err) } // ... ``` ## Getting suggested animes `MainAuth` only ``` go // ... var suggestedAnimes []mg.Anime params := mg.SuggestedParamsNew() // optional; these are the default values params.Limit = 100 // max = 100 params.Offset = 0 params.NSFW = false params.Fields = []string{} err := client.GetSeasonalAnime(&suggestedAnimes, params) if err != nil { fmt.Println(err) } // ... ``` ## Get any user's anime list `MainAuth` or `ClientAuth` This returns a boolean telling us if there is a next page on this user's list. Simply increment the offset by the limit and create a new request in order to get the next page. In order to get the complete list, repeat the process until `nextPageExists` becomes false. Accepted statuses: - `mg.ListStatusAll` - `mg.ListStatusWatching` - `mg.ListStatusCompleted` - `mg.ListStatusOnHold` - `mg.ListStatusDropped` - `mg.ListStatusPTW` Accepted list sorts: - `mg.SortByListScore` (descending) - `mg.SortByListUpdatedAt` (descending) - `mg.SortByAnimeTitle` (ascending) - `mg.SortByAnimeStartDate` (descending) - `mg.SortByAnimeId` (ascending) (beta according to MAL) ``` go // ... var animes []mg.Anime params := mg.ListParamsNew() // optional; these are the default values params.Username = "@me" // can speciy any public account params.Status = mg.ListStatusAll params.Sort = mg.SortByListScore params.Limit = 100 // max = 1000 params.Offset = 0 params.NSFW = false params.Fields = []string{} nextPageExists, err := client.GetAnimeList(&animes, params) if err != nil { fmt.Println(err) } // ... ``` ## Updating an anime `MainAuth` only Updates an anime in the the currently logged in user's anime list. Adds the anime if it's not in the list. Accepted statuses: - `mg.ListStatusWatching` - `mg.ListStatusCompleted` - `mg.ListStatusOnHold` - `mg.ListStatusDropped` - `mg.ListStatusPTW` Score range: `0` - `10` Priority range: `0` - `2` | Priority | Description | |----------|-------------| | 0 | Low | | 1 | Medium | | 2 | High | RewatchValue range: `0` - `5` | RewatchValue | Description | |--------------|-------------| | 0 | None | | 1 | Very Low | | 2 | Low | | 3 | Medium | | 4 | High | | 5 | Very High | ``` go // ... var res AnimeUpdateResponse // can omit any field but keep at least 1! params := map[string]interface{} { mg.Status: mg.ListStatusCompleted, mg.IsRewatching: false, mg.Score: 10, mg.EpisodesWatched: 26, mg.Priority: 2, mg.TimesRewatched: 2, mg.RewatchValue: 5, mg.Tags: "tags", mg.Comments: "comments", } err := client.UpdateAnime(&res, /*anime id*/ 457, params) if err != nil { fmt.Println(err) } // ... ``` ## Deleting an anime from list `MainAuth` only Deletes the anime from currently logged in user's anime list. Returns an error if anime is already not in the list. ``` go // ... err := client.DeleteAnime(/*anime id*/ 457) if err != nil { fmt.Println(err) } // ... ``` # Manga ## Searching for a manga `MainAuth` or `ClientAuth` _also works for light novels, etc_ ``` go // ... var mangas []mg.Manga carams := mg.SearchParamsNew() carams.SearchString = "empty box and zeroth maria" // optional; these are the default values params.Limit = 100 params.Offset = 0 params.NSFW = false params.Fields = []string{} err := client.SearchManga(&mangas, params) if err != nil { fmt.Println(err) } for _, manga range mangas { fmt.Println(manga.Title) } // ... ``` ## Getting a manga's information `MainAuth` or `ClientAuth` _searching can be a bit inaccurate, if you know the manga's ID (obtain it with SearchManga!) and want to get detailed information, use this._ ``` go // ... var manga mg.Manga err := client.GetMangaById(&manga, /*manga id*/ 55215, /*fields*/ []string{}) if err != nil { fmt.Println(err) } // ... ``` ## Getting manga ranking list `MainAuth` or `ClientAuth` Accepted ranking types: | RankingType | Description | |----------------------------|----------------| | mg.RankingTypeAll | All | | mg.RankingTypeByPopularity | Most Popular | | mg.RankingTypeFavorite | Most Favorited | | mg.RankingTypeManga | Top Manga | | mg.RankingTypeNovel | Top Novels | | mg.RankingTypeOneShot | Top One-shots | | mg.RankingTypeDoujin | Top Doujinshi | | mg.RankingTypeManhwa | Top Manhwa | | mg.RankingTypeManhua | Top Manhua | *RankedManga is just the Manga struct with an extra field `RankNum int`* ``` go // ... var rankingList []mg.RankedManga params := mg.RankingParamsNew() // optional; these are the default values params.RankingType = mg.RankingTypeAll params.Limit = 100 // max = 500 params.Offset = 0 params.NSFW = false params.Fields = []string{} err := client.GetAnimeRanking(&rankingList, params) if err != nil { fmt.Println(err) } // ... ``` ## Get any user's manga list `MainAuth` or `ClientAuth` This returns a boolean telling us if there is a next page on this user's list. Simply increment the offset by the limit and create a new request in order to get the next page. In order to get the complete list, repeat the process until `nextPageExists` becomes false. Accepted statuses: - `mg.ListStatusAll` - `mg.ListStatusReading` - `mg.ListStatusCompleted` - `mg.ListStatusOnHold` - `mg.ListStatusDropped` - `mg.ListStatusPTR` Accepted list sorts: - `mg.SortByListScore` (descending) - `mg.SortByListUpdatedAt` (descending) - `mg.SortByMangaTitle` (ascending) - `mg.SortByMangaStartDate` (descending) - `mg.SortByMangaId` (ascending) (beta according to MAL) ``` go // ... var mangas []mg.Manga params := mg.ListParamsNew() // optional; these are the default values params.Username = "@me" // can speciy any public account params.Status = mg.ListStatusAll params.Sort = mg.SortByListScore params.Limit = 100 // max = 1000 params.Offset = 0 params.NSFW = false params.Fields = []string{} nextPageExists, err := client.GetMangaList(&mangas, params) if err != nil { fmt.Println(err) } // ... ``` ## Updating a manga `MainAuth` only Updates a manga in the the currently logged in user's manga list. Adds the manga if it's not in the list. Accepted statuses: - `mg.ListStatusReading` - `mg.ListStatusCompleted` - `mg.ListStatusOnHold` - `mg.ListStatusDropped` - `mg.ListStatusPTR` Score range: `0` - `10` Priority range: `0` - `2` | Priority | Description | |----------|-------------| | 0 | Low | | 1 | Medium | | 2 | High | RereadValue range: `0` - `5` | RereadValue | Description | |-------------|-------------| | 0 | None | | 1 | Very Low | | 2 | Low | | 3 | Medium | | 4 | High | | 5 | Very High | ``` go // ... var res MangaUpdateResponse // can omit any field but keep at least 1! params := map[string]interface{} { mg.Status: mg.ListStatusReading, mg.IsRereading: false, mg.Score: 10, mg.VolumesRead: 2, mg.ChaptersRead: 20, mg.Priority: 2, mg.TimesReread: 0, mg.RereadValue: 5, mg.Tags: "tags", mg.Comments: "comments", } err := client.UpdateManga(&res, /*manga id*/ 55215, params) if err != nil { fmt.Println(err) } // ... ``` ## Deleting a manga from list `MainAuth` only Deletes the manga from currently logged in user's manga list. Returns an error if manga is already not in the list. ``` go // ... err := client.DeleteManga(/*manga id*/ 55215) if err != nil { fmt.Println(err) } // ... ``` # User ## Get logged in user's details `MainAuth` only MyAnimeList's official API doesn't support getting another user's details. Only the currently logged in user. ``` go // ... var user mg.User // to get user's list statistics or not getStatistics := true err := client.GetSelfInfo(&user, getStatistics) if err != nil { fmt.Println(err) } // ... ```