Skip to content
Completed version - v2.0

Anime Functions

Installations needed

NPM Package Install
npm install aoi.js
npm install jikan4.js
npm install fs

Basic Info

This wiki is all about the use of Jikan package for interacting with the MAL(My Anime List) API and getting the anime, character info from it.

This gives the information as per the result type specified in the custom function and you can make it into an embed like the normal AOI uses. This code is for those who are fans of anime and wanna make such a system but, getting it hard to code. Use this function as I say or you can also edit this if you have experience in DJS and API interacts.

Index config

Before heading to the custom funcs code, we need to config our index.js a little so that instead of cluttering the index with random custom funcs, we do that in a seperate directory(folder). Here, we use a directory named customFunctions.

Here is the thing that you must add in your index, probably towards the end of it.

index.js
const funcsdir = "./customFunctions/";
const fs = require('fs');
files = fs.readdirSync(funcsdir);
files.forEach((file) => {
let func = require(funcsdir+file);
func(bot);
// Replace "bot" with "client" if necessary.
});

Custom Function Code

This is split into two parts, anime and manga. We here discuss the anime part only. Also, the basic custom function which both of em needs and that is, the $getID which gets Anime ID from its name.

So, let’s head to the important part AKA the code!

$getID

This function deals with the fetching of anime/manga ID from its name. This also simplifies the code as we don’t need to fetch ID every time to get it’s info. Instead, we use this function alongside the other to fetch ID as other funcs has an anime ID parameter NOT anime name.

getID.js
const Jikan = require('jikan4.js');
const JIKAN_CLIENT = new Jikan.Client();
// Edit the "bot" as per your config in index.js
// Replace it with "client" if necessary.
module.exports = (bot) => {
bot.functionManager.createFunction({
name: "$getID",
type: "djs",
code: async d => {
const data = d.util.aoiFunc(d);
if (data.err) return d.error(data.err);
const [name, type = 'Anime'] = data.inside.splits;
if (!name) {
return d.aoiError.fnError(d, "custom", {}, "Anime/Manga name not specified");
}
if (!type) return d.aoiError.fnError(d, "custom", {}, "Type not specified.")
let searchResults;
let foundID;
const ty = type.toLowerCase().trim();
const n = name.trim();
if (ty != 'anime' && ty != 'manga') {
return d.aoiError.fnError(d, "custom", {}, "Invalid type specified. Either Manga / Anime only");
}
switch (ty) {
case 'anime':
searchResults = await JIKAN_CLIENT.anime.search(n);
break;
case 'manga':
searchResults = await JIKAN_CLIENT.manga.search(n);
break;
}
const quarterLength = Math.ceil(searchResults.length / 4);
for (let i = 0; i < quarterLength; i++) {
const result = searchResults[i].title.default.toLowerCase();
if (n === result) {
foundID = searchResults[i].id;
} else {
foundID = searchResults[0].id;
}
}
if (!foundID) {
foundID = "NFError";
}
data.result = foundID;
return {
code: d.util.setCode(data),
};
},
});
}

$animeInfo

This function deals with fetching the various information of anime from the API as per the parameter passed. It has a total of 2 parameters.

animeInfo.js
const Jikan = require('jikan4.js');
const JIKAN_CLIENT = new Jikan.Client();
// Edit the "bot" as per your config in index.js
// Replace it with "client" if necessary.
module.exports = (bot) => {
bot.functionManager.createFunction({
name: "$animeInfo",
type: "djs",
code: async d => {
const data = d.util.aoiFunc(d);
if (data.err) return d.error(data.err);
const [animeID, res] = data.inside.splits;
if (!animeID) return d.aoiError.fnError(d, "custom", {}, "Anime ID not provided!")
if (!res) return d.aoiError.fnError(d, "custom", {}, "No result type specified.")
const aID = animeID.trim()
const resu = res.trim().toLowerCase()
const type = ['title', 'synopsis', 'synopsis2', 'image', 'ratings', 'genre', 'url', 'episodes', 'recommendations', 'year', 'trailer', 'studio'];
if (!type.includes(resu)) return d.aoiError.fnError(d, "custom", {}, "Invalid result type.");
const anime = await JIKAN_CLIENT.anime.get(aID);
if (!anime) return d.aoiError.fnError(d, "custom", {}, "Invalid anime ID.");
const stats = await JIKAN_CLIENT.anime.getStatistics(aID);
let genres = anime.genres.map(genre => genre.name).join(', ');
const rec = await JIKAN_CLIENT.anime.getRecommendations(aID);
if (!genres || genres.trim() === '') {
genres = 'No genre information.';
}
let synopsis = '';
let synopsis2 = '\n';
//SPLITS SYNOPSIS IF TOO LONG INTO 2-3 PARAGRAPHS.
if (anime.synopsis) {
if (anime.synopsis.length > 1024) {
const midPoint = anime.synopsis.lastIndexOf('.', 1024);
if (midPoint !== -1) {
const synopsisFirstPart = anime.synopsis.substring(0, midPoint + 1);
const synopsisSecondPart = anime.synopsis.substring(midPoint + 1);
synopsis = synopsisFirstPart;
synopsis2 = synopsisSecondPart;
}
} else {
synopsis = anime.synopsis;
}
} else {
synopsis = 'Synopsis not found!';
}
let ratings = '';
if (stats.scores) {
let totalScore = 0;
let totalVotes = 0;
for (const obj of stats.scores) {
totalScore += obj.score * obj.votes;
totalVotes += obj.votes;
}
const averageScore = totalScore / totalVotes;
ratings = `Average score based off ${totalVotes.toLocaleString()} votes: ${averageScore.toFixed(2) + ' / 10'}`;
} else {
ratings = 'Rating information not found.';
}
//DISPLAY RECOMMENDED TITLES
let recList = [];
let recListString = '';
if (rec.length > 2) {
recList.push(rec[0].entry.title);
recList.push(rec[1].entry.title);
recListString = recList.map(item => item).join(', ');
} else {
recListString = 'No recommendations.';
}
//SYNOPSIS, URL, EPISODES, GENRES, RATINGS, ETC.
const SYNOPSIS = synopsis;
const SYNOPSIS2 = synopsis2;
const URL = anime.url ?? 'URL not found.';
const EPISODES = anime.episodes?.toLocaleString() ?? 'Episodes info not found.';
const GENRES = genres;
const RATINGS = ratings;
const TITLE = anime.title.default;
const IMAGE = anime.image.webp.default;
const YEAR = anime.year ?? 'Year information not found.';
const TRAILER = anime?.trailer?.embedUrl ?? 'Trailer not found.';
const STUDIO = anime.studios[0]?.name ?? 'Studio information not found.';
const RECOMMENDATIONS = recListString
let result = '';
switch(resu) {
case 'title':
result = TITLE;
break;
case 'synopsis':
result = SYNOPSIS;
break;
case 'synopsis2':
result = SYNOPSIS2;
break;
case 'url':
result = URL;
break;
case 'genre':
result = GENRES;
break;
case 'image':
result = IMAGE;
break;
case 'ratings':
result = RATINGS;
break;
case 'episodes':
result = EPISODES;
break;
case 'recommendations':
result = RECOMMENDATIONS;
break;
case 'year':
result = YEAR;
break;
case 'studio':
result = STUDIO;
break;
case 'trailer':
result = TRAILER;
break;
}
data.result = result;
return {
code: d.util.setCode(data),
};
},
})
}

$getCharInfo

This function deals with getting the character info of a specific character in a specific anime passed as parameters or arguments. It has a total of 3 parameters.

animeCharacterInfo.js
const Jikan = require('jikan4.js');
const JIKAN_CLIENT = new Jikan.Client();
function getFirstName(characterName, databaseNames) {
let res = false;
const nameParts = databaseNames.split(',').map(part => part.trim().toLowerCase());
if (nameParts.includes(characterName.toLowerCase())) {
res = true;
};
return res;
};
function getDescription(characterDescription) {
let description;
if (!characterDescription) {
return description = 'Description not found.';
} else {
if (characterDescription.length > 1024) {
const midPoint = characterDescription.lastIndexOf('.', 1024);
if (midPoint !== -1) {
const descriptionFirstPart = characterDescription.substring(0, midPoint + 1);
description = descriptionFirstPart;
}
} else {
description = characterDescription
}
}
return description;
}
// Edit the "bot" as per your config in index.js
// Replace it with "client" if necessary.
module.exports = (bot) => {
bot.functionManager.createFunction({
name: "$animeCharInfo",
type: "djs",
code: async d => {
const data = d.util.aoiFunc(d);
if (data.err) return d.error(data.err);
const [animeID, character, res] = data.inside.splits;
if (!animeID) return d.aoiError.fnError(d, "custom", {}, "Anime ID not provided.");
if (!character) return d.aoiError.fnError(d, "custom", {}, "Character not provided.");
if (!res) return d.aoiError.fnError(d, "custom", {}, "Enter the result type.")
const aID = animeID.trim();
const characterName = character.trim().toLowerCase();
const resu = res.trim().toLowerCase();
const type = ["name", "url", "voiceactor", "image", "nicknames", "role", "description", "anime"];
if (!type.includes(resu)) return d.aoiError.fnError(d, "custom", {}, "Invalid result type entered.")
const anime = await JIKAN_CLIENT.anime.get(aID);
if (!anime) return d.aoiError.fnError(d, "custom", {}, "Anime not found.");
const ch = await JIKAN_CLIENT.anime.getCharacters(aID);
const animeName = anime.title.default;
let description = '';
let characterObj;
let characterFound = false;
let characterArr = [];
let result = '';
for (let i = 0; i < ch.length; i++) {
if (getFirstName(characterName, (ch[i].character.name).toLowerCase())) {
characterArr.push(ch[i]);
characterFound = true;
}
}
if (!characterFound) {
result = "ACNFError"
}
let i = 0;
if (characterFound) {
characterObj = await JIKAN_CLIENT.characters.getFull(characterArr[i].character.id);
if (characterObj) {
description = getDescription(characterObj.about);
} else {
description = 'Description not found.';
}
let nicknames = '';
if (characterObj.nicknames[0] === undefined) {
nicknames = 'None';
} else {
nicknames = characterObj.nicknames.map(item => item).join(', ');
}
let va = '';
if (characterArr[i]?.voiceActors[0] === undefined) {
va = 'No information found.';
} else {
va = characterArr[i]?.voiceActors[0]?.person.name;
}
//last calling of vars
const CHARACTER = characterArr[i].character.name;
const VOICEACTOR = va;
const URL = characterArr[i].character.url;
const ANIME = animeName;
const ROLE = characterArr[i]?.role ?? 'Role information not found.';
const DESCRIPTION = description;
const IMAGE = characterArr[i].character.image.webp.default;
const NICKNAMES = nicknames;
switch(resu) {
case 'name':
result = CHARACTER;
break;
case 'url':
result = URL;
break;
case 'image':
result = IMAGE;
break;
case 'nicknames':
result = NICKNAMES;
break;
case 'anime':
result = ANIME;
break;
case 'description':
result = DESCRIPTION;
break;
case 'role':
result = ROLE;
break;
case 'voiceactor':
result = VOICEACTOR;
break;
}
}
data.result = result;
return {
code: d.util.setCode(data),
};
}
})
}

And these are the codes you must add in your /customFunctions/ or whatever you call it directory.

Syntax & Tables

Now, let’s know the syntax and other thing on how to use the above functions.

Syntax Table

The syntax is:

$getID[name;type?]
OptionTypeOptionalExample
NameStringfalseAny anime/manga name as per type
TypeStringtrueSpecify anime(default) or manga

Use like: $getID[$message/anime_name;type(if not anime)]


Result Type Table

Now, let’s head towards the table for the result. Or rather, what you must put in the result parameter in both cases.

OptionOutput
titleReturns the name of anime
synopsisReturns the synopsis of anime
synopsis2Returns the remaining synopsis if more than 1024 character else, null
urlReturns the URL of anime in MAL website
genreReturns the genre of anime
imageReturns image of anime
ratingsReturns the rating of anime
episodesReturns episode count of anime
recommendationsReturns similar anime as the passed one
yearReturns release year of anime
studioReturns anime studio
trailerReturns the trailer link of anime

Error Table

There are two errors which will be most common when making a command out of it and that should be caught. They are Not Found (Anime/Manga) and Anime Character Not Found. I have made error catching for this which can be caught using $onlyIf which makes sure that you won’t see unnecessary AOIErrors while making embeds.

ErrorCatchFunction
Not Found (Anime/Manga)NFError$getID
Anime Character Not FoundACNFError$animeCharInfo

Usage:

  • $onlyIf[$getID[anime_name]!=NFError;]
  • $onlyIf[$animeCharInfo[$getID[anime_name];character;type]!=ACNFError;]

Important reference

Credits