Skip to content

Commit

Permalink
Merge pull request #49 from Butterstroke/development
Browse files Browse the repository at this point in the history
Release 1.12.0
  • Loading branch information
AurelicButter authored Apr 27, 2022
2 parents 7dd7b45 + 0447b1d commit a47da80
Show file tree
Hide file tree
Showing 13 changed files with 437 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A simple, lightweight Node.js wrapper for the AniList API.
## Using Anilist-node
To install: `npm install anilist-node`

You may need a token for some features (ie checking favourites). A token only needs to be generated once in order to use. To start, head to [Anilist's Developer Page](https://anilist.co/settings/developer) and click "Create New Client". Note the client id. Then, copy paste this URL `https://anilist.co/api/v2/oauth/authorize?client_id={clientID}&response_type=token`, replacing the `{clientID}` with your client ID. It will ask you to log in and then provide you with the token to use.
You may need a token for some features (ie checking favourites). A token only needs to be generated once in order to use. To start, head to [Anilist's Developer Page](https://anilist.co/settings/developer) and click "Create New Client". Note the client id and place this URL into your client redirect `https://anilist.co/api/v2/oauth/pin`. This URL will allow you to get your token when authorizing. Then, copy paste this URL `https://anilist.co/api/v2/oauth/authorize?client_id={clientID}&response_type=token`, replacing the `{clientID}` with your client ID. It will ask you to log in and then provide you with the token to use.

>***NOTE: Please store your token securely and privately! This gives access to your AniList account. It is your responsibility to maintain your token.***
Expand Down
24 changes: 24 additions & 0 deletions lib/docDefinition.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
* @property { Boolean } studios.isAnimationStudio - Check if the studio is the animation studio for the anime.
* @property { String[] } synonyms - The media's alternative titles
* @property { Object[] } tags - Tags that describes the media
* @property {Number} tags.id - The ID of the tag
* @property { String } tags.name - The name of the tag
* @property { Boolean } tags.isMediaSpoiler - Checks if the tag is a spoiler for the media.
* @property { MediaTitle } title - The manga's titles
Expand Down Expand Up @@ -255,6 +256,7 @@
* @property { String } status - The current status of the media
* @property { String[] } synonyms - The media's alternative titles
* @property { Object[] } tags - Tags that describes the media
* @property {Number} tags.id - The ID of the tag
* @property { String } tags.name - The name of the tag
* @property { Boolean } tags.isMediaSpoiler - Checks if the tag is a spoiler for the media.
* @property { MediaTitle } title - The manga's titles
Expand Down Expand Up @@ -784,3 +786,25 @@
* @property {String} categories.name - Name of the forum category
* @property {MediaRelation[]} mediaCategories - List of related media entries for the thread
*/

/**
* @typedef MediaTag
* @property {Number} id - The ID of the tag
* @property {String} name - Name of the tag
* @property {String} description - What the tag is about
* @property {String} category - The category that the tag belongs to
* @property {Boolean} isAdult - True if tag is meant for adult content
*/

/**
* @typedef ThreadComment
* @property {Number} id - The ID of the thread comment
* @property {UserRelation} user - The author of the comment
* @property {String} comment - The content of the comment
* @property {Boolean} isLiked - [Requires Login] True if current user liked
* @property {Number} createdAt - The timestamp it was created at
* @property {Number} updatedAt - The timestamp it was updated at
* @property {UserRelation[]} likes - An array of users who liked the post
* @property {ThreadComment[]} childComments - Comments in reply to this comment
* @property {Boolean} isLocked - True if users can no longer interact with comment.
*/
18 changes: 14 additions & 4 deletions lib/fetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ module.exports = {
* @returns { Object } Returns a customized object containing all of the data fetched.
*/
send: async function (query, variables) {
if (!query || !variables) {
throw new Error("Query or variables are not given!");
if (!query) {
throw new Error("Query is not given!");
}

if (query.startsWith("mutation") && this.key === null) {
Expand All @@ -132,12 +132,16 @@ module.exports = {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify({ query: query, variables: variables }),
signal: controller.signal
};
if (this.key) {
options.headers.Authorization = `Bearer ${this.key}`;
}
if (variables) {
options.body = JSON.stringify({ query: query, variables: variables });
} else {
options.body = JSON.stringify({ query: query });
}

const response = await fetch("https://graphql.anilist.co", options)
.catch((error) => {
Expand All @@ -149,7 +153,13 @@ module.exports = {
clearTimeout(requestTimeout);
});

let json = await response.json();
let json;

try {
json = await response.json();
} catch (err) {
throw Error(err);
}

if (Object.keys(json).length < 0) {
throw new Error("ERROR: AniList API is down. Please refer to official channels for more information.");
Expand Down
56 changes: 56 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,33 @@ class AniList {
);
}

/**
* [Requires Login] Favourite/Unfavourite a studio
* @param {Number} id - Required. The ID tied to the AniList entry.
* @returns {Boolean} Returns true if added, false otherwise.
* @since 1.12.0
*/
async favouriteStudio(id) {
if (!id || typeof id !== "number") {
throw new Error("AniList ID is not provided!");
}

const data = await this.util.send(
`mutation ($studioID: Int) {
ToggleFavourite(studioId: $studioID) {
studios (page: 1, perPage: 25) {
nodes { id }
} } }`,
{ studioID: id }
);

return data.ToggleFavourite.studios.nodes.some((e) => {
if (e.id === id) {
return true;
}
});
}

/**
* Searches AniList based on a specific term.
* @param {String} type - Required. Either anime, manga, character, staff, studio, or user.
Expand Down Expand Up @@ -160,6 +187,35 @@ class AniList {
{ search: term, page: page, perPage: amount }
);
}

/**
* Grabs all possible genres
* @return { String[] }
* @since 1.12.0
*/
genres() {
return this.__util.send("query { GenreCollection }", null).then((data) => {
return data.GenreCollection;
});
}

/**
* Grabs all possible media tags
* @return { MediaTag[] }
* @since 1.12.0
*/
mediaTags() {
return this.__util
.send(
`query { MediaTagCollection {
id name description category isAdult
} }`,
null
)
.then((data) => {
return data.MediaTagCollection;
});
}
}

module.exports = AniList;
58 changes: 56 additions & 2 deletions lib/media.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Media {
season seasonYear duration countryOfOrigin isLicensed source hashtag trailer { id site }
updatedAt coverImage { large:extraLarge medium:large small:medium color }
bannerImage genres synonyms averageScore meanScore favourites
popularity trending tags { name isMediaSpoiler } relations { nodes { id title { english native romaji userPreferred } type } }
popularity trending tags { id name isMediaSpoiler } relations { nodes { id title { english native romaji userPreferred } type } }
characters { nodes { id name { english: full } } } staff { nodes { id name { english: full } } } studios { nodes { id name isAnimationStudio } }
isFavourite isAdult isLocked nextAiringEpisode { timeUntilAiring airingAt episode } airingSchedule { nodes { airingAt timeUntilAiring episode } }
trends { nodes { date trending popularity inProgress } } externalLinks { url }
Expand All @@ -46,6 +46,33 @@ class Media {
);
}

/**
* [Requires Login] Favourite/Unfavourite an anime
* @param {Number} id - Required. The ID tied to the AniList entry.
* @returns {Boolean} Returns true if added, false otherwise.
* @since 1.12.0
*/
async favouriteAnime(id) {
if (!id || typeof id !== "number") {
throw new Error("AniList ID is not provided!");
}

const data = await this.util.send(
`mutation ($mediaID: Int) {
ToggleFavourite(animeId: $mediaID) {
anime (page: 1, perPage: 25) {
nodes { id }
} } }`,
{ mediaID: id }
);

return data.ToggleFavourite.anime.nodes.some((e) => {
if (e.id === id) {
return true;
}
});
}

/**
* Fetch a manga entry by its AniList ID.
* @param { Number } id - Required. The ID tied to the AniList entry.
Expand All @@ -64,7 +91,7 @@ class Media {
`query ($id: Int) { Media (id: $id, type: MANGA) { id idMal title { romaji english native userPreferred }
description format status startDate { year month day } endDate { year month day } chapters volumes countryOfOrigin isLicensed updatedAt
coverImage { large:extraLarge medium:large small:medium color } bannerImage genres synonyms averageScore meanScore siteUrl autoCreateForumThread modNotes
popularity trending tags { name isMediaSpoiler } relations { nodes { id title { english native romaji userPreferred } type } }
popularity trending tags { id name isMediaSpoiler } relations { nodes { id title { english native romaji userPreferred } type } }
characters { nodes { id name { english: full } } } staff { nodes { id name { english: full } } } isFavourite isAdult isLocked
trends { nodes { date trending popularity inProgress } } externalLinks { url } rankings { rank type context year season }
mediaListEntry { id status } reviews { nodes { id score summary body } }
Expand All @@ -73,6 +100,33 @@ class Media {
{ id: id }
);
}

/**
* [Requires Login] Favourite/Unfavourite a manga
* @param {Number} id - Required. The ID tied to the AniList entry.
* @returns {Boolean} Returns true if added, false otherwise.
* @since 1.12.0
*/
async favouriteManga(id) {
if (!id || typeof id !== "number") {
throw new Error("AniList ID is not provided!");
}

const data = await this.util.send(
`mutation ($mediaID: Int) {
ToggleFavourite(mangaId: $mediaID) {
manga (page: 1, perPage: 25) {
nodes { id }
} } }`,
{ mediaID: id }
);

return data.ToggleFavourite.manga.nodes.some((e) => {
if (e.id === id) {
return true;
}
});
}
}

module.exports = Media;
54 changes: 54 additions & 0 deletions lib/people.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ class People {
);
}

/**
* [Requires Login] Favourite/Unfavourite a character
* @param {Number} id - Required. The ID tied to the AniList entry.
* @returns {Boolean} Returns true if added, false otherwise.
* @since 1.12.0
*/
async favouriteChar(id) {
if (!id || typeof id !== "number") {
throw new Error("AniList ID is not provided!");
}

const data = await this.util.send(
`mutation ($charID: Int) {
ToggleFavourite(characterId: $charID) {
characters (page: 1, perPage: 25) {
nodes { id }
} } }`,
{ charID: id }
);

return data.ToggleFavourite.characters.nodes.some((e) => {
if (e.id === id) {
return true;
}
});
}

/**
* Fetch a staff entry by its AniList ID or their name.
* @param { Number|String } id - Required. The ID can either be the AniList ID or the staff's name.
Expand All @@ -50,6 +77,33 @@ class People {
queryVars[0]
);
}

/**
* [Requires Login] Favourite/Unfavourite a staff entry
* @param {Number} id - Required. The ID tied to the AniList entry.
* @returns {Boolean} Returns true if added, false otherwise.
* @since 1.12.0
*/
async favouriteStaff(id) {
if (!id || typeof id !== "number") {
throw new Error("AniList ID is not provided!");
}

const data = await this.util.send(
`mutation ($staffID: Int) {
ToggleFavourite(staffId: $staffID) {
staff (page: 1, perPage: 25) {
nodes { id }
} } }`,
{ staffID: id }
);

return data.ToggleFavourite.staff.nodes.some((e) => {
if (e.id === id) {
return true;
}
});
}
}

module.exports = People;
46 changes: 46 additions & 0 deletions lib/thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,52 @@ class Thread {
return data.Thread;
});
}

/**
* [Require Login] Delete a thread
* @param {Number} id - The AniList thread ID to delete
*
* @returns {Boolean} Returns true if successful
* @since 1.12.0
*/
async delete(id) {
if (typeof id !== "number") {
throw new Error("ID is not a number type.");
}

return this.util.send(`mutation ($id: Int) { DeleteThread(id: $id) { deleted } }`, { id: id }).then((data) => {
return data.DeleteThread.deleted;
});
}

/**
* Get thread comments for a thread
* @param {Number} id - The AniList thread ID
* @param {Number} page - The page number
* @param {Number} perPage - How many entries per page
*
* @returns {ThreadComment[]}
* @since 1.12.0
*/
getComments(id, page = 1, perPage = 25) {
if (typeof id !== "number") {
throw new Error("ID is not a number type.");
}

return this.util
.send(
`query ($threadID: Int, $page: Int, $perPage: Int) {
Page(page:$page, perPage:$perPage) {
threadComments(threadId: $threadID) {
id user { id name } comment isLiked createdAt updatedAt
likes { id name } childComments isLocked
} } }`,
{ threadID: id, page: page, perPage: perPage }
)
.then((data) => {
return data.threadComments;
});
}
}

module.exports = Thread;
Loading

0 comments on commit a47da80

Please sign in to comment.