@irfanshadikrishad/anilist 1.0.0-forbidden.5 → 1.0.0-forbidden.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/helpers/auth.d.ts +2 -30
- package/bin/helpers/auth.js +57 -22
- package/bin/helpers/fetcher.js +8 -7
- package/bin/helpers/lists.js +10 -35
- package/bin/helpers/mutations.d.ts +7 -4
- package/bin/helpers/mutations.js +9 -10
- package/bin/helpers/queries.d.ts +3 -6
- package/bin/helpers/queries.js +3 -13
- package/bin/helpers/truncate.d.ts +6 -0
- package/bin/helpers/truncate.js +10 -0
- package/bin/helpers/types.d.ts +55 -115
- package/bin/helpers/workers.d.ts +4 -2
- package/bin/helpers/workers.js +62 -6
- package/bin/index.js +2 -9
- package/package.json +12 -10
package/bin/helpers/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MediaTitle } from "./types.js";
|
|
1
|
+
import { MediaTitle, User } from "./types.js";
|
|
2
2
|
declare class Auth {
|
|
3
3
|
/**
|
|
4
4
|
* Get access-token from user
|
|
@@ -7,35 +7,7 @@ declare class Auth {
|
|
|
7
7
|
static StoreAccessToken(token: string): Promise<void>;
|
|
8
8
|
static RetriveAccessToken(): Promise<string | null>;
|
|
9
9
|
static Login(clientId: number, clientSecret: string): Promise<void>;
|
|
10
|
-
static Myself(): Promise<
|
|
11
|
-
id: number;
|
|
12
|
-
name: string;
|
|
13
|
-
siteUrl: string;
|
|
14
|
-
options: {
|
|
15
|
-
profileColor: string;
|
|
16
|
-
timezone: string;
|
|
17
|
-
activityMergeTime: string;
|
|
18
|
-
};
|
|
19
|
-
donatorTier: string;
|
|
20
|
-
donatorBadge: string;
|
|
21
|
-
unreadNotificationCount: number;
|
|
22
|
-
createdAt: number;
|
|
23
|
-
updatedAt: number;
|
|
24
|
-
statistics: {
|
|
25
|
-
anime: {
|
|
26
|
-
count: number;
|
|
27
|
-
meanScore: string;
|
|
28
|
-
minutesWatched: string;
|
|
29
|
-
episodesWatched: number;
|
|
30
|
-
};
|
|
31
|
-
manga: {
|
|
32
|
-
count: number;
|
|
33
|
-
meanScore: string;
|
|
34
|
-
chaptersRead: number;
|
|
35
|
-
volumesRead: number;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
}>;
|
|
10
|
+
static Myself(): Promise<User>;
|
|
39
11
|
static isLoggedIn(): Promise<boolean>;
|
|
40
12
|
static Logout(): Promise<void>;
|
|
41
13
|
static MyUserId(): Promise<number>;
|
package/bin/helpers/auth.js
CHANGED
|
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { Cipher } from "@irfanshadikrishad/cipher";
|
|
10
11
|
import fs from "fs";
|
|
11
12
|
import inquirer from "inquirer";
|
|
12
13
|
import fetch from "node-fetch";
|
|
@@ -17,12 +18,14 @@ import { exit } from "process";
|
|
|
17
18
|
import Spinner from "tiny-spinner";
|
|
18
19
|
import { fetcher } from "./fetcher.js";
|
|
19
20
|
import { AniDB, AniList, MyAnimeList } from "./lists.js";
|
|
20
|
-
import { deleteActivityMutation, likeActivityMutation, saveTextActivityMutation, } from "./mutations.js";
|
|
21
|
-
import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery,
|
|
21
|
+
import { deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, likeActivityMutation, saveTextActivityMutation, toggleFollowMutation, } from "./mutations.js";
|
|
22
|
+
import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, followingActivitiesQuery, globalActivitiesQuery, specificUserActivitiesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
|
|
23
|
+
import { responsiveOutput } from "./truncate.js";
|
|
22
24
|
import { activityBy, aniListEndpoint, getTitle, redirectUri, timestampToTimeAgo, } from "./workers.js";
|
|
23
25
|
const home_dir = os.homedir();
|
|
24
26
|
const save_path = path.join(home_dir, ".anilist_token");
|
|
25
27
|
const spinner = new Spinner();
|
|
28
|
+
const vigenere = new Cipher.Vigenere("anilist");
|
|
26
29
|
class Auth {
|
|
27
30
|
/**
|
|
28
31
|
* Get access-token from user
|
|
@@ -56,7 +59,7 @@ class Auth {
|
|
|
56
59
|
console.warn("\nNo token provided. Nothing to store.");
|
|
57
60
|
return;
|
|
58
61
|
}
|
|
59
|
-
fs.writeFileSync(save_path, token, { encoding: "utf8" });
|
|
62
|
+
fs.writeFileSync(save_path, vigenere.encrypt(token), { encoding: "utf8" });
|
|
60
63
|
}
|
|
61
64
|
catch (error) {
|
|
62
65
|
console.error(`\nError storing access token: ${error.message}`);
|
|
@@ -67,7 +70,7 @@ class Auth {
|
|
|
67
70
|
return __awaiter(this, void 0, void 0, function* () {
|
|
68
71
|
try {
|
|
69
72
|
if (fs.existsSync(save_path)) {
|
|
70
|
-
return fs.readFileSync(save_path, { encoding: "utf8" });
|
|
73
|
+
return vigenere.decrypt(fs.readFileSync(save_path, { encoding: "utf8" }));
|
|
71
74
|
}
|
|
72
75
|
else {
|
|
73
76
|
return null;
|
|
@@ -183,7 +186,7 @@ Statistics (Manga):
|
|
|
183
186
|
console.log(`\nRecent Activities:`);
|
|
184
187
|
if (activities.length > 0) {
|
|
185
188
|
activities.map(({ status, progress, media, createdAt }) => {
|
|
186
|
-
|
|
189
|
+
responsiveOutput(`${timestampToTimeAgo(createdAt)}\t${status} ${progress ? `${progress} of ` : ""}${getTitle(media === null || media === void 0 ? void 0 : media.title)}`);
|
|
187
190
|
});
|
|
188
191
|
}
|
|
189
192
|
return user;
|
|
@@ -596,14 +599,14 @@ Statistics (Manga):
|
|
|
596
599
|
if (like === null || like === void 0 ? void 0 : like.data) {
|
|
597
600
|
likedCount++;
|
|
598
601
|
}
|
|
599
|
-
|
|
602
|
+
responsiveOutput(`${(like === null || like === void 0 ? void 0 : like.data) ? "✅" : "❌"} ${activityBy(activ, likedCount)}`);
|
|
600
603
|
}
|
|
601
604
|
catch (error) {
|
|
602
605
|
console.error(`Activity possibly deleted. ${error.message}`);
|
|
603
606
|
}
|
|
604
607
|
}
|
|
605
608
|
else {
|
|
606
|
-
|
|
609
|
+
responsiveOutput(`"🔵" ${activityBy(activ, likedCount)}`);
|
|
607
610
|
}
|
|
608
611
|
// avoiding rate-limit
|
|
609
612
|
yield new Promise((resolve) => {
|
|
@@ -655,14 +658,14 @@ Statistics (Manga):
|
|
|
655
658
|
});
|
|
656
659
|
// const ToggleLike = like?.data?.ToggleLike
|
|
657
660
|
likedCount++;
|
|
658
|
-
|
|
661
|
+
responsiveOutput(`${(like === null || like === void 0 ? void 0 : like.data) ? "✅" : "❌"} ${activityBy(activ, likedCount)}`);
|
|
659
662
|
}
|
|
660
663
|
catch (error) {
|
|
661
664
|
console.error(`Activity possibly deleted. ${error.message}`);
|
|
662
665
|
}
|
|
663
666
|
}
|
|
664
667
|
else {
|
|
665
|
-
|
|
668
|
+
responsiveOutput(`🔵 ${activityBy(activ, likedCount)}`);
|
|
666
669
|
}
|
|
667
670
|
// avoiding rate-limit
|
|
668
671
|
yield new Promise((resolve) => {
|
|
@@ -722,14 +725,14 @@ Statistics (Manga):
|
|
|
722
725
|
activityId: activ.id,
|
|
723
726
|
});
|
|
724
727
|
likedCount++;
|
|
725
|
-
|
|
728
|
+
responsiveOutput(`${(like === null || like === void 0 ? void 0 : like.data) ? "✅" : "❌"} ${activityBy(activ, likedCount)}`);
|
|
726
729
|
}
|
|
727
730
|
catch (error) {
|
|
728
731
|
console.error(`Activity possibly deleted. ${error.message}`);
|
|
729
732
|
}
|
|
730
733
|
}
|
|
731
734
|
else {
|
|
732
|
-
|
|
735
|
+
responsiveOutput(`🔵 ${activityBy(activ, likedCount)}`);
|
|
733
736
|
}
|
|
734
737
|
// Avoiding rate limit
|
|
735
738
|
yield new Promise((resolve) => {
|
|
@@ -763,7 +766,9 @@ Statistics (Manga):
|
|
|
763
766
|
let hasNextPage = true;
|
|
764
767
|
let page = 1;
|
|
765
768
|
let liked = 0;
|
|
769
|
+
// ------------------------
|
|
766
770
|
// Fetch all following users
|
|
771
|
+
// ------------------------
|
|
767
772
|
spinner.start(`Gathering following information...`);
|
|
768
773
|
while (hasNextPage) {
|
|
769
774
|
spinner.update(`Fetched page ${page}...`);
|
|
@@ -782,10 +787,28 @@ Statistics (Manga):
|
|
|
782
787
|
spinner.stop(`Got ${allFollowingUsers.length} following user.`);
|
|
783
788
|
// Extract the IDs of all following users
|
|
784
789
|
const followingUserIds = allFollowingUsers.map((user) => user.id);
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
790
|
+
// --------------------
|
|
791
|
+
// APPROXIMATE TIME
|
|
792
|
+
// --------------------
|
|
793
|
+
const totalActivities = followingUserIds.length * perPage;
|
|
794
|
+
const perActivityTimeInSec = 1;
|
|
795
|
+
const rateLimitTimeInSec = 60;
|
|
796
|
+
const batchSize = 29;
|
|
797
|
+
const batches = Math.floor(totalActivities / batchSize);
|
|
798
|
+
const remaining = totalActivities % batchSize;
|
|
799
|
+
const processingTime = batches * batchSize * perActivityTimeInSec +
|
|
800
|
+
remaining * perActivityTimeInSec;
|
|
801
|
+
const waitTime = (batches - 1) * rateLimitTimeInSec;
|
|
802
|
+
const totalWaitTimeInSec = processingTime + (batches > 0 ? waitTime : 0);
|
|
803
|
+
const hours = Math.floor(totalWaitTimeInSec / 3600);
|
|
804
|
+
const minutes = Math.floor((totalWaitTimeInSec % 3600) / 60);
|
|
805
|
+
const seconds = totalWaitTimeInSec % 60;
|
|
806
|
+
const time = `${String(hours).padStart(2, "0")}h ${String(minutes).padStart(2, "0")}m ${String(seconds).padStart(2, "0")}s`;
|
|
807
|
+
console.log(`\nTotal following: ${followingUserIds.length}\nApproximately ${totalActivities} to like.\nWill take around ${time}`);
|
|
808
|
+
// -------------------
|
|
809
|
+
// Traverse the array and
|
|
810
|
+
// fetch users' activities one by one
|
|
811
|
+
// -------------------
|
|
789
812
|
let userNumber = 0;
|
|
790
813
|
for (const userId of followingUserIds) {
|
|
791
814
|
userNumber++;
|
|
@@ -808,7 +831,7 @@ Statistics (Manga):
|
|
|
808
831
|
const like = yield fetcher(likeActivityMutation, {
|
|
809
832
|
activityId: activ.id,
|
|
810
833
|
});
|
|
811
|
-
|
|
834
|
+
responsiveOutput(`${(like === null || like === void 0 ? void 0 : like.data) ? "✅" : "❌"} ${activityBy(activ, i + 1)}`);
|
|
812
835
|
if (like === null || like === void 0 ? void 0 : like.data) {
|
|
813
836
|
liked++;
|
|
814
837
|
}
|
|
@@ -818,7 +841,7 @@ Statistics (Manga):
|
|
|
818
841
|
}
|
|
819
842
|
}
|
|
820
843
|
else {
|
|
821
|
-
|
|
844
|
+
responsiveOutput(`🔵 ${activityBy(activ, i + 1)}`);
|
|
822
845
|
}
|
|
823
846
|
// Avoid rate-limiting
|
|
824
847
|
yield new Promise((resolve) => setTimeout(resolve, 1200));
|
|
@@ -844,9 +867,10 @@ Statistics (Manga):
|
|
|
844
867
|
name: "activityType",
|
|
845
868
|
message: "Select activity type:",
|
|
846
869
|
choices: [
|
|
847
|
-
{ name: "Following", value: 1 },
|
|
848
|
-
{ name: "
|
|
849
|
-
{ name: "
|
|
870
|
+
{ name: "Following • v1", value: 1 },
|
|
871
|
+
{ name: "Following • v2", value: 2 },
|
|
872
|
+
{ name: "Global", value: 3 },
|
|
873
|
+
{ name: "Specific User", value: 4 },
|
|
850
874
|
],
|
|
851
875
|
pageSize: 10,
|
|
852
876
|
},
|
|
@@ -855,10 +879,21 @@ Statistics (Manga):
|
|
|
855
879
|
case 1:
|
|
856
880
|
yield this.LikeFollowing();
|
|
857
881
|
break;
|
|
858
|
-
case 2:
|
|
859
|
-
yield
|
|
882
|
+
case 2: {
|
|
883
|
+
const { count } = yield inquirer.prompt([
|
|
884
|
+
{
|
|
885
|
+
type: "number",
|
|
886
|
+
name: "count",
|
|
887
|
+
message: "Likes to give:",
|
|
888
|
+
},
|
|
889
|
+
]);
|
|
890
|
+
yield this.LikeFollowingActivityV2(count);
|
|
860
891
|
break;
|
|
892
|
+
}
|
|
861
893
|
case 3:
|
|
894
|
+
yield this.LikeGlobal();
|
|
895
|
+
break;
|
|
896
|
+
case 4:
|
|
862
897
|
yield this.LikeSpecificUser();
|
|
863
898
|
break;
|
|
864
899
|
default:
|
package/bin/helpers/fetcher.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import fetch from "node-fetch";
|
|
11
11
|
import { Auth } from "./auth.js";
|
|
12
|
-
import { aniListEndpoint } from "./workers.js";
|
|
12
|
+
import { aniListEndpoint, handleRateLimitRetry } from "./workers.js";
|
|
13
13
|
/**
|
|
14
14
|
* Sends a GraphQL request to the AniList API.
|
|
15
15
|
*
|
|
@@ -28,9 +28,11 @@ function fetcher(query, variables) {
|
|
|
28
28
|
const headers = {
|
|
29
29
|
"content-type": "application/json",
|
|
30
30
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const token = (yield Auth.isLoggedIn())
|
|
32
|
+
? yield Auth.RetriveAccessToken()
|
|
33
|
+
: null;
|
|
34
|
+
if (token)
|
|
35
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
34
36
|
const request = yield fetch(aniListEndpoint, {
|
|
35
37
|
method: "POST",
|
|
36
38
|
headers: headers,
|
|
@@ -41,9 +43,8 @@ function fetcher(query, variables) {
|
|
|
41
43
|
return response;
|
|
42
44
|
}
|
|
43
45
|
else if (request.status === 429) {
|
|
44
|
-
|
|
45
|
-
yield
|
|
46
|
-
return yield fetcher(query, variables); // Retry the request
|
|
46
|
+
yield handleRateLimitRetry(60);
|
|
47
|
+
return yield fetcher(query, variables);
|
|
47
48
|
}
|
|
48
49
|
else {
|
|
49
50
|
console.error(`\n${request.status} ${((_b = (_a = response === null || response === void 0 ? void 0 : response.errors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) || "Unknown error"}.`);
|
package/bin/helpers/lists.js
CHANGED
|
@@ -16,9 +16,10 @@ import { Auth } from "./auth.js";
|
|
|
16
16
|
import { fetcher } from "./fetcher.js";
|
|
17
17
|
import { addAnimeToListMutation, addMangaToListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from "./mutations.js";
|
|
18
18
|
import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
|
|
19
|
+
import { responsiveOutput } from "./truncate.js";
|
|
19
20
|
import { AniListMediaStatus, } from "./types.js";
|
|
20
21
|
import { Validate } from "./validation.js";
|
|
21
|
-
import { anidbToanilistMapper, formatDateObject, getDownloadFolderPath, getNextSeasonAndYear, getTitle, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, selectFile, simpleDateFormat, timestampToTimeAgo, } from "./workers.js";
|
|
22
|
+
import { anidbToanilistMapper, formatDateObject, getDownloadFolderPath, getNextSeasonAndYear, getTitle, logUserDetails, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, selectFile, simpleDateFormat, timestampToTimeAgo, } from "./workers.js";
|
|
22
23
|
class AniList {
|
|
23
24
|
static importAnime() {
|
|
24
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -35,8 +36,7 @@ class AniList {
|
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
37
38
|
let count = 0;
|
|
38
|
-
const batchSize = 1;
|
|
39
|
-
const delay = 1100; // delay to avoid rate-limiting
|
|
39
|
+
const batchSize = 1;
|
|
40
40
|
for (let i = 0; i < importedData.length; i += batchSize) {
|
|
41
41
|
const batch = importedData.slice(i, i + batchSize);
|
|
42
42
|
yield Promise.all(batch.map((anime) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -63,8 +63,6 @@ class AniList {
|
|
|
63
63
|
console.error(`\nError saving ${anime === null || anime === void 0 ? void 0 : anime.id}: ${error.message}`);
|
|
64
64
|
}
|
|
65
65
|
})));
|
|
66
|
-
// Avoid rate-limiting: Wait before sending the next batch
|
|
67
|
-
yield new Promise((resolve) => setTimeout(resolve, delay));
|
|
68
66
|
}
|
|
69
67
|
console.log(`\nTotal ${count} anime(s) imported successfully.`);
|
|
70
68
|
}
|
|
@@ -88,9 +86,7 @@ class AniList {
|
|
|
88
86
|
return;
|
|
89
87
|
}
|
|
90
88
|
let count = 0;
|
|
91
|
-
const batchSize = 1;
|
|
92
|
-
const delay = 1100; // 2 seconds delay to avoid rate-limit
|
|
93
|
-
// Process in batches
|
|
89
|
+
const batchSize = 1;
|
|
94
90
|
for (let i = 0; i < importedData.length; i += batchSize) {
|
|
95
91
|
const batch = importedData.slice(i, i + batchSize);
|
|
96
92
|
yield Promise.all(batch.map((manga) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -115,8 +111,6 @@ class AniList {
|
|
|
115
111
|
console.error(`\nError saving ${manga === null || manga === void 0 ? void 0 : manga.id}: ${err.message}`);
|
|
116
112
|
}
|
|
117
113
|
})));
|
|
118
|
-
// Avoid rate-limit by adding delay after processing each batch
|
|
119
|
-
yield new Promise((resolve) => setTimeout(resolve, delay));
|
|
120
114
|
}
|
|
121
115
|
console.log(`\nTotal ${count} manga(s) imported successfully.`);
|
|
122
116
|
}
|
|
@@ -620,7 +614,7 @@ class AniList {
|
|
|
620
614
|
}
|
|
621
615
|
static getUserByUsername(username) {
|
|
622
616
|
return __awaiter(this, void 0, void 0, function* () {
|
|
623
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m
|
|
617
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
624
618
|
try {
|
|
625
619
|
const response = yield fetcher(userQuery, { username });
|
|
626
620
|
if (!((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.User)) {
|
|
@@ -642,26 +636,11 @@ class AniList {
|
|
|
642
636
|
});
|
|
643
637
|
const followersCount = ((_j = (_h = (_g = req_followers === null || req_followers === void 0 ? void 0 : req_followers.data) === null || _g === void 0 ? void 0 : _g.Page) === null || _h === void 0 ? void 0 : _h.pageInfo) === null || _j === void 0 ? void 0 : _j.total) || 0;
|
|
644
638
|
const followingCount = ((_m = (_l = (_k = req_following === null || req_following === void 0 ? void 0 : req_following.data) === null || _k === void 0 ? void 0 : _k.Page) === null || _l === void 0 ? void 0 : _l.pageInfo) === null || _m === void 0 ? void 0 : _m.total) || 0;
|
|
645
|
-
|
|
646
|
-
console.log(`Name:\t\t${user.name}`);
|
|
647
|
-
console.log(`Site URL:\t${user.siteUrl}`);
|
|
648
|
-
console.log(`Donator Tier:\t${user.donatorTier}`);
|
|
649
|
-
console.log(`Donator Badge:\t${user.donatorBadge}`);
|
|
650
|
-
console.log(`Account Created:\t${user.createdAt ? new Date(user.createdAt * 1000).toUTCString() : "N/A"}`);
|
|
651
|
-
console.log(`Account Updated:\t${user.updatedAt ? new Date(user.updatedAt * 1000).toUTCString() : "N/A"}`);
|
|
652
|
-
console.log(`Blocked:\t${user.isBlocked}`);
|
|
653
|
-
console.log(`Follower:\t${user.isFollower}`);
|
|
654
|
-
console.log(`Following:\t${user.isFollowing}`);
|
|
655
|
-
console.log(`Profile Color:\t${(_o = user.options) === null || _o === void 0 ? void 0 : _o.profileColor}`);
|
|
656
|
-
console.log(`Timezone:\t${((_p = user.options) === null || _p === void 0 ? void 0 : _p.timezone) ? (_q = user.options) === null || _q === void 0 ? void 0 : _q.timezone : "N/A"}`);
|
|
657
|
-
console.log(`\nFollowers:\t${followersCount}`);
|
|
658
|
-
console.log(`Following:\t${followingCount}`);
|
|
659
|
-
console.log(`\nStatistics (Anime)\n\tCount: ${((_s = (_r = user.statistics) === null || _r === void 0 ? void 0 : _r.anime) === null || _s === void 0 ? void 0 : _s.count) || 0}\tEpisodes Watched: ${((_u = (_t = user.statistics) === null || _t === void 0 ? void 0 : _t.anime) === null || _u === void 0 ? void 0 : _u.episodesWatched) || 0}\tMinutes Watched: ${((_w = (_v = user.statistics) === null || _v === void 0 ? void 0 : _v.anime) === null || _w === void 0 ? void 0 : _w.minutesWatched) || 0}`);
|
|
660
|
-
console.log(`Statistics (Manga)\n\tCount: ${((_y = (_x = user.statistics) === null || _x === void 0 ? void 0 : _x.manga) === null || _y === void 0 ? void 0 : _y.count) || 0}\tChapters Read: ${((_0 = (_z = user.statistics) === null || _z === void 0 ? void 0 : _z.manga) === null || _0 === void 0 ? void 0 : _0.chaptersRead) || 0}\tVolumes Read: ${((_2 = (_1 = user.statistics) === null || _1 === void 0 ? void 0 : _1.manga) === null || _2 === void 0 ? void 0 : _2.volumesRead) || 0}`);
|
|
639
|
+
logUserDetails(user, followersCount, followingCount);
|
|
661
640
|
if (activities.length > 0) {
|
|
662
641
|
console.log(`\nRecent Activities:`);
|
|
663
642
|
activities.forEach(({ status, progress, media, createdAt }) => {
|
|
664
|
-
|
|
643
|
+
responsiveOutput(`${timestampToTimeAgo(createdAt)}\t${status} ${progress ? `${progress} of ` : ""}${getTitle(media === null || media === void 0 ? void 0 : media.title)}`);
|
|
665
644
|
});
|
|
666
645
|
}
|
|
667
646
|
else {
|
|
@@ -894,8 +873,6 @@ class MyAnimeList {
|
|
|
894
873
|
count++;
|
|
895
874
|
console.log(`[${count}] ${entryId} ✅`);
|
|
896
875
|
}
|
|
897
|
-
// Rate limit each API call to avoid server overload
|
|
898
|
-
yield new Promise((resolve) => setTimeout(resolve, 1100));
|
|
899
876
|
}
|
|
900
877
|
else {
|
|
901
878
|
console.error(`Could not retrieve AniList ID for MAL ID ${malId}`);
|
|
@@ -1119,10 +1096,8 @@ class AniDB {
|
|
|
1119
1096
|
const entryId = (_b = (_a = saveResponse === null || saveResponse === void 0 ? void 0 : saveResponse.data) === null || _a === void 0 ? void 0 : _a.SaveMediaListEntry) === null || _b === void 0 ? void 0 : _b.id;
|
|
1120
1097
|
if (entryId) {
|
|
1121
1098
|
count++;
|
|
1122
|
-
|
|
1099
|
+
responsiveOutput(`[${count}]\t${entryId} ✅\t${anidbId}\t${anilistId}\t(${ownEpisodes}/${totalEpisodes})\t${status}–>${getStatus(status, ownEpisodes)}`);
|
|
1123
1100
|
}
|
|
1124
|
-
// Rate limit each API call to avoid server overload
|
|
1125
|
-
// await new Promise((resolve) => setTimeout(resolve, 1100))
|
|
1126
1101
|
}
|
|
1127
1102
|
catch (error) {
|
|
1128
1103
|
console.error(`Error processing AniDB ID ${anidbId}: ${error.message}`);
|
|
@@ -1136,9 +1111,9 @@ class AniDB {
|
|
|
1136
1111
|
});
|
|
1137
1112
|
}
|
|
1138
1113
|
}
|
|
1139
|
-
|
|
1114
|
+
responsiveOutput(`\nAccuracy: ${(((animeList.length - missed.length) / animeList.length) * 100).toFixed(2)}%\tTotal Processed: ${iteration}\tMissed: ${missed.length}`);
|
|
1140
1115
|
if (missed.length > 0) {
|
|
1141
|
-
|
|
1116
|
+
responsiveOutput(`Exporting missed entries to JSON file, Please add them manually.`);
|
|
1142
1117
|
yield saveJSONasJSON(missed, "anidb-missed");
|
|
1143
1118
|
}
|
|
1144
1119
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
declare const addAnimeToListMutation = "\nmutation($mediaId: Int, $status: MediaListStatus) {\n SaveMediaListEntry(mediaId: $mediaId, status: $status) { id status }\n}\n";
|
|
2
|
-
declare const addMangaToListMutation = "\n mutation($mediaId: Int, $status: MediaListStatus) {\n SaveMediaListEntry(mediaId: $mediaId, status: $status) {\n id
|
|
3
|
-
declare const deleteActivityMutation = "\nmutation($id: Int!) {
|
|
4
|
-
declare const saveTextActivityMutation = "\nmutation SaveTextActivity($status: String!) {
|
|
2
|
+
declare const addMangaToListMutation = "\n mutation($mediaId: Int, $status: MediaListStatus) {\n SaveMediaListEntry(mediaId: $mediaId, status: $status) {\n id status media { id title { romaji english } }\n }\n }\n";
|
|
3
|
+
declare const deleteActivityMutation = "\nmutation($id: Int!) { DeleteActivity(id: $id) { deleted } }\n";
|
|
4
|
+
declare const saveTextActivityMutation = "\nmutation SaveTextActivity($status: String!) { SaveTextActivity(text: $status) { id text userId createdAt } }\n";
|
|
5
5
|
declare const saveAnimeWithProgressMutation = "\nmutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean) {\n SaveMediaListEntry(mediaId: $mediaId, progress: $progress, status: $status, hiddenFromStatusLists: $hiddenFromStatusLists) {\n id progress hiddenFromStatusLists\n }\n}\n";
|
|
6
6
|
declare const saveMangaWithProgressMutation = "\nmutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean, $private: Boolean) {\n SaveMediaListEntry( mediaId: $mediaId, progress: $progress, status: $status, hiddenFromStatusLists: $hiddenFromStatusLists, private: $private\n ) { id progress hiddenFromStatusLists private }\n}\n";
|
|
7
7
|
declare const likeActivityMutation = "\nmutation($activityId: Int!) {\n ToggleLike(id: $activityId, type: ACTIVITY) { id }\n}\n";
|
|
8
|
-
|
|
8
|
+
declare const toggleFollowMutation = "mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }";
|
|
9
|
+
declare const deleteMediaEntryMutation = "mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }";
|
|
10
|
+
declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
|
|
11
|
+
export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, likeActivityMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
|
package/bin/helpers/mutations.js
CHANGED
|
@@ -6,21 +6,15 @@ mutation($mediaId: Int, $status: MediaListStatus) {
|
|
|
6
6
|
const addMangaToListMutation = `
|
|
7
7
|
mutation($mediaId: Int, $status: MediaListStatus) {
|
|
8
8
|
SaveMediaListEntry(mediaId: $mediaId, status: $status) {
|
|
9
|
-
id
|
|
10
|
-
status
|
|
11
|
-
media { id title { romaji english } }
|
|
9
|
+
id status media { id title { romaji english } }
|
|
12
10
|
}
|
|
13
11
|
}
|
|
14
12
|
`;
|
|
15
13
|
const deleteActivityMutation = `
|
|
16
|
-
mutation($id: Int!) {
|
|
17
|
-
DeleteActivity(id: $id) { deleted }
|
|
18
|
-
}
|
|
14
|
+
mutation($id: Int!) { DeleteActivity(id: $id) { deleted } }
|
|
19
15
|
`;
|
|
20
16
|
const saveTextActivityMutation = `
|
|
21
|
-
mutation SaveTextActivity($status: String!) {
|
|
22
|
-
SaveTextActivity(text: $status) { id text userId createdAt }
|
|
23
|
-
}
|
|
17
|
+
mutation SaveTextActivity($status: String!) { SaveTextActivity(text: $status) { id text userId createdAt } }
|
|
24
18
|
`;
|
|
25
19
|
const saveAnimeWithProgressMutation = `
|
|
26
20
|
mutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean) {
|
|
@@ -40,4 +34,9 @@ mutation($activityId: Int!) {
|
|
|
40
34
|
ToggleLike(id: $activityId, type: ACTIVITY) { id }
|
|
41
35
|
}
|
|
42
36
|
`;
|
|
43
|
-
|
|
37
|
+
const toggleFollowMutation = `mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }`;
|
|
38
|
+
const deleteMediaEntryMutation = `mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }`;
|
|
39
|
+
const deleteMangaEntryMutation = `mutation($id: Int) {
|
|
40
|
+
DeleteMediaListEntry(id: $id) { deleted }
|
|
41
|
+
}`;
|
|
42
|
+
export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, likeActivityMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
|
package/bin/helpers/queries.d.ts
CHANGED
|
@@ -2,10 +2,8 @@ declare const currentUserQuery = "{\n Viewer {\n id name about bans siteUrl
|
|
|
2
2
|
declare const trendingQuery = "query ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n media(sort: TRENDING_DESC, type: ANIME) { id title { romaji english } }\n }\n}";
|
|
3
3
|
declare const popularQuery = "query ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n media(sort: POPULARITY_DESC, type: ANIME) { id title { romaji english } }\n }\n}";
|
|
4
4
|
declare const userQuery = "query ($username: String) {\n User(name: $username) {\n id name siteUrl donatorTier donatorBadge createdAt updatedAt previousNames { name createdAt updatedAt }\n isBlocked isFollower isFollowing options { profileColor timezone activityMergeTime }\n statistics { anime { count episodesWatched minutesWatched } manga { count chaptersRead volumesRead } }\n }\n}";
|
|
5
|
-
declare const currentUserAnimeList = "query ($id: Int) {\n MediaListCollection(userId: $id, type: ANIME) {\n lists { name entries { id progress hiddenFromStatusLists status media { id idMal title { romaji english } status episodes siteUrl } } }\n }\n}\n";
|
|
6
|
-
declare const currentUserMangaList = "query ($id: Int) {\n MediaListCollection(userId: $id, type: MANGA) {\n lists { name entries { id progress hiddenFromStatusLists private status media { id idMal title { romaji english } status chapters } } }\n }\n}\n";
|
|
7
|
-
declare const deleteMediaEntryMutation = "mutation($id: Int!) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
|
|
8
|
-
declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
|
|
5
|
+
declare const currentUserAnimeList = "query ($id: Int) {\n MediaListCollection(userId: $id, type: ANIME) {\n lists { name entries { id progress hiddenFromStatusLists status media { id idMal title { romaji english native userPreferred } status episodes siteUrl format } } }\n }\n}\n";
|
|
6
|
+
declare const currentUserMangaList = "query ($id: Int) {\n MediaListCollection(userId: $id, type: MANGA) {\n lists { name entries { id progress hiddenFromStatusLists private status media { id idMal title { romaji english native userPreferred } status chapters } } }\n }\n}\n";
|
|
9
7
|
declare const upcomingAnimesQuery = "query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {\n Page(perPage: $perPage) {\n media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {\n id title { romaji english native userPreferred } season seasonYear startDate { year month day }\n episodes description genres\n }\n }\n}";
|
|
10
8
|
declare const animeDetailsQuery = "query ($id: Int) {\n Media(id: $id) {\n id idMal title { romaji english native userPreferred } episodes nextAiringEpisode { id }\n duration startDate { year month day } endDate { year month day } countryOfOrigin description isAdult status season format genres siteUrl\n stats { scoreDistribution { score amount } statusDistribution { status amount } }\n }\n}";
|
|
11
9
|
declare const userActivityQuery = "query ($id: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $id, type_in: [ANIME_LIST, MANGA_LIST], sort: ID_DESC) {\n ... on ListActivity { id status progress createdAt media { id title { romaji english } } }\n }\n }\n}";
|
|
@@ -24,6 +22,5 @@ declare const globalActivitiesQuery = "\nquery ($page: Int, $perPage: Int) {\n
|
|
|
24
22
|
declare const specificUserActivitiesQuery = "\nquery ($page: Int, $perPage: Int, $userId: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n activities(userId: $userId, sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { messenger { name } id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
25
23
|
declare const userFollowingQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
|
|
26
24
|
declare const userFollowersQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
|
|
27
|
-
declare const toggleFollowMutation = "mutation ($userId: Int!) {\n ToggleFollow(userId: $userId) { id name isFollower isFollowing }\n}\n";
|
|
28
25
|
declare const mangaDetailsQuery = "query ($id: Int) {\n Media(id: $id, type: MANGA) {\n id title { romaji english native userPreferred } coverImage { color medium large extraLarge } \n bannerImage description chapters volumes status genres\n startDate { year month day } endDate { year month day }\n }\n}\n";
|
|
29
|
-
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery,
|
|
26
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, followingActivitiesQuery, globalActivitiesQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, specificUserActivitiesQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
package/bin/helpers/queries.js
CHANGED
|
@@ -26,22 +26,16 @@ const userQuery = `query ($username: String) {
|
|
|
26
26
|
}`;
|
|
27
27
|
const currentUserAnimeList = `query ($id: Int) {
|
|
28
28
|
MediaListCollection(userId: $id, type: ANIME) {
|
|
29
|
-
lists { name entries { id progress hiddenFromStatusLists status media { id idMal title { romaji english } status episodes siteUrl } } }
|
|
29
|
+
lists { name entries { id progress hiddenFromStatusLists status media { id idMal title { romaji english native userPreferred } status episodes siteUrl format } } }
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
`;
|
|
33
33
|
const currentUserMangaList = `query ($id: Int) {
|
|
34
34
|
MediaListCollection(userId: $id, type: MANGA) {
|
|
35
|
-
lists { name entries { id progress hiddenFromStatusLists private status media { id idMal title { romaji english } status chapters } } }
|
|
35
|
+
lists { name entries { id progress hiddenFromStatusLists private status media { id idMal title { romaji english native userPreferred } status chapters } } }
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
|
-
const deleteMediaEntryMutation = `mutation($id: Int!) {
|
|
40
|
-
DeleteMediaListEntry(id: $id) { deleted }
|
|
41
|
-
}`;
|
|
42
|
-
const deleteMangaEntryMutation = `mutation($id: Int) {
|
|
43
|
-
DeleteMediaListEntry(id: $id) { deleted }
|
|
44
|
-
}`;
|
|
45
39
|
const upcomingAnimesQuery = `query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {
|
|
46
40
|
Page(perPage: $perPage) {
|
|
47
41
|
media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {
|
|
@@ -173,10 +167,6 @@ const userFollowersQuery = `query ($userId: Int!, $page: Int) {
|
|
|
173
167
|
}
|
|
174
168
|
}
|
|
175
169
|
`;
|
|
176
|
-
const toggleFollowMutation = `mutation ($userId: Int!) {
|
|
177
|
-
ToggleFollow(userId: $userId) { id name isFollower isFollowing }
|
|
178
|
-
}
|
|
179
|
-
`;
|
|
180
170
|
const mangaDetailsQuery = `query ($id: Int) {
|
|
181
171
|
Media(id: $id, type: MANGA) {
|
|
182
172
|
id title { romaji english native userPreferred } coverImage { color medium large extraLarge }
|
|
@@ -185,4 +175,4 @@ const mangaDetailsQuery = `query ($id: Int) {
|
|
|
185
175
|
}
|
|
186
176
|
}
|
|
187
177
|
`;
|
|
188
|
-
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery,
|
|
178
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, followingActivitiesQuery, globalActivitiesQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, specificUserActivitiesQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import cliTruncate from "cli-truncate";
|
|
2
|
+
import process from "process";
|
|
3
|
+
/**
|
|
4
|
+
* Truncate text (No line break)
|
|
5
|
+
* @param str - Long text to trancate
|
|
6
|
+
*/
|
|
7
|
+
function responsiveOutput(str) {
|
|
8
|
+
console.log(cliTruncate(str, process.stdout.columns - 5));
|
|
9
|
+
}
|
|
10
|
+
export { responsiveOutput };
|
package/bin/helpers/types.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
type Error = {
|
|
2
|
+
message: string;
|
|
3
|
+
}[];
|
|
1
4
|
interface DeleteMangaResponse {
|
|
2
5
|
data?: {
|
|
3
6
|
DeleteMediaListEntry?: {
|
|
4
7
|
deleted?: boolean;
|
|
5
8
|
};
|
|
6
9
|
};
|
|
7
|
-
errors?:
|
|
8
|
-
message: string;
|
|
9
|
-
}[];
|
|
10
|
+
errors?: Error;
|
|
10
11
|
}
|
|
11
12
|
declare enum AniListMediaStatus {
|
|
12
13
|
CURRENT = "CURRENT",
|
|
@@ -48,9 +49,7 @@ interface MalIdToAnilistIdResponse {
|
|
|
48
49
|
title: MediaTitle;
|
|
49
50
|
};
|
|
50
51
|
};
|
|
51
|
-
errors?:
|
|
52
|
-
message: string;
|
|
53
|
-
}[];
|
|
52
|
+
errors?: Error;
|
|
54
53
|
}
|
|
55
54
|
interface saveAnimeWithProgressResponse {
|
|
56
55
|
data?: {
|
|
@@ -60,9 +59,7 @@ interface saveAnimeWithProgressResponse {
|
|
|
60
59
|
hiddenFromStatusLists: boolean;
|
|
61
60
|
};
|
|
62
61
|
};
|
|
63
|
-
errors?:
|
|
64
|
-
message: string;
|
|
65
|
-
}[];
|
|
62
|
+
errors?: Error;
|
|
66
63
|
}
|
|
67
64
|
declare enum MALAnimeStatus {
|
|
68
65
|
ON_HOLD = "On-Hold",
|
|
@@ -84,9 +81,7 @@ interface AnimeList {
|
|
|
84
81
|
lists: MediaList[];
|
|
85
82
|
};
|
|
86
83
|
};
|
|
87
|
-
errors?:
|
|
88
|
-
message: string;
|
|
89
|
-
}[];
|
|
84
|
+
errors?: Error;
|
|
90
85
|
}
|
|
91
86
|
interface MediaWithProgress {
|
|
92
87
|
malId?: number;
|
|
@@ -125,9 +120,7 @@ interface SaveTextActivityResponse {
|
|
|
125
120
|
createdAt: number;
|
|
126
121
|
};
|
|
127
122
|
};
|
|
128
|
-
errors?:
|
|
129
|
-
message: string;
|
|
130
|
-
}[];
|
|
123
|
+
errors?: Error;
|
|
131
124
|
}
|
|
132
125
|
interface MediaListCollectionResponse {
|
|
133
126
|
data?: {
|
|
@@ -135,9 +128,7 @@ interface MediaListCollectionResponse {
|
|
|
135
128
|
lists: MediaList[];
|
|
136
129
|
};
|
|
137
130
|
};
|
|
138
|
-
errors?:
|
|
139
|
-
message: string;
|
|
140
|
-
}[];
|
|
131
|
+
errors?: Error;
|
|
141
132
|
}
|
|
142
133
|
interface List {
|
|
143
134
|
name: string;
|
|
@@ -151,39 +142,9 @@ interface MediaList {
|
|
|
151
142
|
}
|
|
152
143
|
interface Myself {
|
|
153
144
|
data?: {
|
|
154
|
-
Viewer:
|
|
155
|
-
id: number;
|
|
156
|
-
name: string;
|
|
157
|
-
siteUrl: string;
|
|
158
|
-
options: {
|
|
159
|
-
profileColor: string;
|
|
160
|
-
timezone: string;
|
|
161
|
-
activityMergeTime: string;
|
|
162
|
-
};
|
|
163
|
-
donatorTier: string;
|
|
164
|
-
donatorBadge: string;
|
|
165
|
-
unreadNotificationCount: number;
|
|
166
|
-
createdAt: number;
|
|
167
|
-
updatedAt: number;
|
|
168
|
-
statistics: {
|
|
169
|
-
anime: {
|
|
170
|
-
count: number;
|
|
171
|
-
meanScore: string;
|
|
172
|
-
minutesWatched: string;
|
|
173
|
-
episodesWatched: number;
|
|
174
|
-
};
|
|
175
|
-
manga: {
|
|
176
|
-
count: number;
|
|
177
|
-
meanScore: string;
|
|
178
|
-
chaptersRead: number;
|
|
179
|
-
volumesRead: number;
|
|
180
|
-
};
|
|
181
|
-
};
|
|
182
|
-
};
|
|
145
|
+
Viewer: User;
|
|
183
146
|
};
|
|
184
|
-
errors?:
|
|
185
|
-
message: string;
|
|
186
|
-
}[];
|
|
147
|
+
errors?: Error;
|
|
187
148
|
}
|
|
188
149
|
interface DateMonthYear {
|
|
189
150
|
day?: number | null;
|
|
@@ -208,9 +169,7 @@ interface AnimeDetails {
|
|
|
208
169
|
siteUrl: string;
|
|
209
170
|
};
|
|
210
171
|
};
|
|
211
|
-
errors?:
|
|
212
|
-
message: string;
|
|
213
|
-
}[];
|
|
172
|
+
errors?: Error;
|
|
214
173
|
}
|
|
215
174
|
interface SaveMediaListEntryResponse {
|
|
216
175
|
data?: {
|
|
@@ -219,9 +178,7 @@ interface SaveMediaListEntryResponse {
|
|
|
219
178
|
status: string;
|
|
220
179
|
};
|
|
221
180
|
};
|
|
222
|
-
errors?:
|
|
223
|
-
message: string;
|
|
224
|
-
}[];
|
|
181
|
+
errors?: Error;
|
|
225
182
|
}
|
|
226
183
|
interface MediaListEntry {
|
|
227
184
|
id?: number;
|
|
@@ -245,55 +202,51 @@ type UserActivitiesResponse = {
|
|
|
245
202
|
activities: Activity[];
|
|
246
203
|
};
|
|
247
204
|
};
|
|
248
|
-
errors?:
|
|
249
|
-
message: string;
|
|
250
|
-
}[];
|
|
205
|
+
errors?: Error;
|
|
251
206
|
};
|
|
252
207
|
type UserResponse = {
|
|
253
208
|
data?: {
|
|
254
|
-
User:
|
|
255
|
-
id: number;
|
|
256
|
-
name: string;
|
|
257
|
-
siteUrl: string;
|
|
258
|
-
donatorTier: string;
|
|
259
|
-
donatorBadge: string;
|
|
260
|
-
createdAt: number;
|
|
261
|
-
updatedAt: number;
|
|
262
|
-
isBlocked: boolean;
|
|
263
|
-
isFollower: boolean;
|
|
264
|
-
isFollowing: boolean;
|
|
265
|
-
options: {
|
|
266
|
-
profileColor: string;
|
|
267
|
-
timezone: string;
|
|
268
|
-
};
|
|
269
|
-
statistics: {
|
|
270
|
-
anime: {
|
|
271
|
-
count: number;
|
|
272
|
-
episodesWatched: number;
|
|
273
|
-
minutesWatched: number;
|
|
274
|
-
};
|
|
275
|
-
manga: {
|
|
276
|
-
count: number;
|
|
277
|
-
chaptersRead: number;
|
|
278
|
-
volumesRead: number;
|
|
279
|
-
};
|
|
280
|
-
};
|
|
281
|
-
};
|
|
209
|
+
User: User;
|
|
282
210
|
};
|
|
283
|
-
errors?:
|
|
284
|
-
|
|
285
|
-
|
|
211
|
+
errors?: Error;
|
|
212
|
+
};
|
|
213
|
+
type Avatar = {
|
|
214
|
+
large?: string;
|
|
215
|
+
medium?: string;
|
|
286
216
|
};
|
|
287
217
|
type User = {
|
|
288
218
|
id: number;
|
|
289
|
-
name
|
|
290
|
-
avatar
|
|
291
|
-
|
|
292
|
-
|
|
219
|
+
name?: string;
|
|
220
|
+
avatar?: Avatar;
|
|
221
|
+
bannerImage?: string;
|
|
222
|
+
isFollower?: boolean;
|
|
223
|
+
isFollowing?: boolean;
|
|
224
|
+
siteUrl?: string;
|
|
225
|
+
donatorTier?: string;
|
|
226
|
+
donatorBadge?: string;
|
|
227
|
+
createdAt?: number;
|
|
228
|
+
updatedAt?: number;
|
|
229
|
+
isBlocked?: boolean;
|
|
230
|
+
unreadNotificationCount?: number;
|
|
231
|
+
options?: {
|
|
232
|
+
profileColor?: string;
|
|
233
|
+
timezone?: string;
|
|
234
|
+
activityMergeTime?: number;
|
|
235
|
+
};
|
|
236
|
+
statistics?: {
|
|
237
|
+
anime?: {
|
|
238
|
+
count?: number;
|
|
239
|
+
episodesWatched?: number;
|
|
240
|
+
minutesWatched?: number;
|
|
241
|
+
meanScore?: number;
|
|
242
|
+
};
|
|
243
|
+
manga?: {
|
|
244
|
+
count?: number;
|
|
245
|
+
chaptersRead?: number;
|
|
246
|
+
volumesRead?: number;
|
|
247
|
+
meanScore?: number;
|
|
248
|
+
};
|
|
293
249
|
};
|
|
294
|
-
bannerImage: string;
|
|
295
|
-
isFollower: boolean;
|
|
296
|
-
isFollowing: boolean;
|
|
297
250
|
};
|
|
298
251
|
type UserFollower = {
|
|
299
252
|
data?: {
|
|
@@ -308,9 +261,7 @@ type UserFollower = {
|
|
|
308
261
|
followers: User[];
|
|
309
262
|
};
|
|
310
263
|
};
|
|
311
|
-
errors?:
|
|
312
|
-
message: string;
|
|
313
|
-
}[];
|
|
264
|
+
errors?: Error;
|
|
314
265
|
};
|
|
315
266
|
type UserFollowing = {
|
|
316
267
|
data?: {
|
|
@@ -325,9 +276,7 @@ type UserFollowing = {
|
|
|
325
276
|
following: User[];
|
|
326
277
|
};
|
|
327
278
|
};
|
|
328
|
-
errors?:
|
|
329
|
-
message: string;
|
|
330
|
-
}[];
|
|
279
|
+
errors?: Error;
|
|
331
280
|
};
|
|
332
281
|
type AnimeSearchResponse = {
|
|
333
282
|
data?: {
|
|
@@ -342,9 +291,7 @@ type AnimeSearchResponse = {
|
|
|
342
291
|
}[];
|
|
343
292
|
};
|
|
344
293
|
};
|
|
345
|
-
errors?:
|
|
346
|
-
message: string;
|
|
347
|
-
}[];
|
|
294
|
+
errors?: Error;
|
|
348
295
|
};
|
|
349
296
|
type ToggleFollowResponse = {
|
|
350
297
|
data?: {
|
|
@@ -355,9 +302,7 @@ type ToggleFollowResponse = {
|
|
|
355
302
|
isFollowing: boolean;
|
|
356
303
|
};
|
|
357
304
|
};
|
|
358
|
-
errors?:
|
|
359
|
-
message: string;
|
|
360
|
-
}[];
|
|
305
|
+
errors?: Error;
|
|
361
306
|
};
|
|
362
307
|
type DeleteMediaListResponse = {
|
|
363
308
|
data?: {
|
|
@@ -365,9 +310,7 @@ type DeleteMediaListResponse = {
|
|
|
365
310
|
deleted: boolean;
|
|
366
311
|
};
|
|
367
312
|
};
|
|
368
|
-
errors?:
|
|
369
|
-
message: string;
|
|
370
|
-
}[];
|
|
313
|
+
errors?: Error;
|
|
371
314
|
};
|
|
372
315
|
type Activity = {
|
|
373
316
|
id: number;
|
|
@@ -432,9 +375,6 @@ type SpecificUserActivitiesResponse = {
|
|
|
432
375
|
message: string;
|
|
433
376
|
}[];
|
|
434
377
|
};
|
|
435
|
-
type Error = {
|
|
436
|
-
message: string;
|
|
437
|
-
}[];
|
|
438
378
|
type CoverImage = {
|
|
439
379
|
color: string;
|
|
440
380
|
medium: string;
|
package/bin/helpers/workers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DateMonthYear, MALAnimeStatus, MALMangaStatus, MediaWithProgress, TheActivity } from "./types.js";
|
|
1
|
+
import { DateMonthYear, MALAnimeStatus, MALMangaStatus, MediaWithProgress, TheActivity, User } from "./types.js";
|
|
2
2
|
declare const aniListEndpoint = "https://graphql.anilist.co";
|
|
3
3
|
declare const redirectUri = "https://anilist.co/api/v2/oauth/pin";
|
|
4
4
|
declare function getTitle(title: {
|
|
@@ -47,4 +47,6 @@ declare function activityBy(activity: TheActivity, count?: number): string;
|
|
|
47
47
|
*/
|
|
48
48
|
declare function saveToPath(data_type: string, file_format: string): Promise<string>;
|
|
49
49
|
declare function simpleDateFormat(date: DateMonthYear): string;
|
|
50
|
-
|
|
50
|
+
declare function handleRateLimitRetry(retryCount: number): Promise<void>;
|
|
51
|
+
declare function logUserDetails(user: User, followersCount: number, followingCount: number): void;
|
|
52
|
+
export { activityBy, anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
|
package/bin/helpers/workers.js
CHANGED
|
@@ -27,12 +27,14 @@ import { homedir } from "os";
|
|
|
27
27
|
import Papa from "papaparse";
|
|
28
28
|
import { join } from "path";
|
|
29
29
|
import process from "process";
|
|
30
|
+
import Spinner from "tiny-spinner";
|
|
30
31
|
import { Auth } from "./auth.js";
|
|
31
32
|
import { fetcher } from "./fetcher.js";
|
|
32
33
|
import { animeSearchQuery } from "./queries.js";
|
|
33
34
|
import { MALAnimeStatus, MALMangaStatus, } from "./types.js";
|
|
34
35
|
const aniListEndpoint = `https://graphql.anilist.co`;
|
|
35
36
|
const redirectUri = "https://anilist.co/api/v2/oauth/pin";
|
|
37
|
+
const spinner = new Spinner();
|
|
36
38
|
function getTitle(title) {
|
|
37
39
|
return (title === null || title === void 0 ? void 0 : title.english) || (title === null || title === void 0 ? void 0 : title.romaji) || "???";
|
|
38
40
|
}
|
|
@@ -382,22 +384,23 @@ const anidbToanilistMapper = (romanjiName, year, englishName) => __awaiter(void
|
|
|
382
384
|
});
|
|
383
385
|
function activityBy(activity, count) {
|
|
384
386
|
var _a, _b, _c, _d;
|
|
387
|
+
const countStr = `[${count ? count : "?"}]`.padEnd(6);
|
|
385
388
|
if ((_a = activity === null || activity === void 0 ? void 0 : activity.messenger) === null || _a === void 0 ? void 0 : _a.name) {
|
|
386
|
-
return
|
|
389
|
+
return `${countStr}${activity.messenger.name} >> messaged ${activity.recipient.name}`;
|
|
387
390
|
}
|
|
388
391
|
else if ((_c = (_b = activity === null || activity === void 0 ? void 0 : activity.media) === null || _b === void 0 ? void 0 : _b.title) === null || _c === void 0 ? void 0 : _c.userPreferred) {
|
|
389
392
|
if (activity.progress) {
|
|
390
|
-
return
|
|
393
|
+
return `${countStr}${activity.user.name} >> ${activity.status} ${activity.progress} of ${activity.media.title.userPreferred}`;
|
|
391
394
|
}
|
|
392
395
|
else {
|
|
393
|
-
return
|
|
396
|
+
return `${countStr}${activity.user.name} >> ${activity.status} ${activity.media.title.userPreferred}`;
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
else if ((_d = activity === null || activity === void 0 ? void 0 : activity.user) === null || _d === void 0 ? void 0 : _d.name) {
|
|
397
|
-
return
|
|
400
|
+
return `${countStr}${activity.user.name}`;
|
|
398
401
|
}
|
|
399
402
|
else {
|
|
400
|
-
return
|
|
403
|
+
return `${countStr}???`;
|
|
401
404
|
}
|
|
402
405
|
}
|
|
403
406
|
/**
|
|
@@ -417,4 +420,57 @@ function simpleDateFormat(date) {
|
|
|
417
420
|
}
|
|
418
421
|
return `${date === null || date === void 0 ? void 0 : date.day}/${date === null || date === void 0 ? void 0 : date.month}/${date === null || date === void 0 ? void 0 : date.year}`;
|
|
419
422
|
}
|
|
420
|
-
|
|
423
|
+
function handleRateLimitRetry(retryCount) {
|
|
424
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
425
|
+
let seconds = Math.pow(2, retryCount) * 1000;
|
|
426
|
+
const maxWait = 60 * 1000;
|
|
427
|
+
seconds = Math.min(seconds, maxWait);
|
|
428
|
+
spinner.start(`Rate limit reached. Retrying in ${seconds / 1000} sec...`);
|
|
429
|
+
let remainingTime = seconds / 1000;
|
|
430
|
+
const interval = setInterval(() => {
|
|
431
|
+
remainingTime--;
|
|
432
|
+
spinner.update(`Rate limit reached. Retrying in ${remainingTime} sec...`);
|
|
433
|
+
if (remainingTime <= 0)
|
|
434
|
+
clearInterval(interval);
|
|
435
|
+
}, 1000);
|
|
436
|
+
yield new Promise((resolve) => setTimeout(resolve, seconds));
|
|
437
|
+
clearInterval(interval);
|
|
438
|
+
spinner.stop();
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function formatDate(timestamp) {
|
|
442
|
+
return timestamp ? new Date(timestamp * 1000).toUTCString() : "N/A";
|
|
443
|
+
}
|
|
444
|
+
function logUserDetails(user, followersCount, followingCount) {
|
|
445
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
446
|
+
console.log("\n📌 User Information:");
|
|
447
|
+
console.table({
|
|
448
|
+
"ID": user.id,
|
|
449
|
+
"Name": user.name,
|
|
450
|
+
"Site URL": user.siteUrl,
|
|
451
|
+
"Donator Tier": user.donatorTier,
|
|
452
|
+
"Donator Badge": user.donatorBadge,
|
|
453
|
+
"Account Created": formatDate(user.createdAt),
|
|
454
|
+
"Account Updated": formatDate(user.updatedAt),
|
|
455
|
+
"Blocked": user.isBlocked,
|
|
456
|
+
"isFollower": user.isFollower,
|
|
457
|
+
"isFollowing": user.isFollowing,
|
|
458
|
+
"Profile Color": ((_a = user.options) === null || _a === void 0 ? void 0 : _a.profileColor) || "N/A",
|
|
459
|
+
"Timezone": ((_b = user.options) === null || _b === void 0 ? void 0 : _b.timezone) || "N/A",
|
|
460
|
+
"Followers": followersCount,
|
|
461
|
+
"Following": followingCount,
|
|
462
|
+
});
|
|
463
|
+
console.log("\n📊 Anime Statistics:");
|
|
464
|
+
console.table({
|
|
465
|
+
"Count": ((_d = (_c = user.statistics) === null || _c === void 0 ? void 0 : _c.anime) === null || _d === void 0 ? void 0 : _d.count) || 0,
|
|
466
|
+
"Episodes Watched": ((_f = (_e = user.statistics) === null || _e === void 0 ? void 0 : _e.anime) === null || _f === void 0 ? void 0 : _f.episodesWatched) || 0,
|
|
467
|
+
"Minutes Watched": ((_h = (_g = user.statistics) === null || _g === void 0 ? void 0 : _g.anime) === null || _h === void 0 ? void 0 : _h.minutesWatched) || 0,
|
|
468
|
+
});
|
|
469
|
+
console.log("\n📖 Manga Statistics:");
|
|
470
|
+
console.table({
|
|
471
|
+
"Count": ((_k = (_j = user.statistics) === null || _j === void 0 ? void 0 : _j.manga) === null || _k === void 0 ? void 0 : _k.count) || 0,
|
|
472
|
+
"Chapters Read": ((_m = (_l = user.statistics) === null || _l === void 0 ? void 0 : _l.manga) === null || _m === void 0 ? void 0 : _m.chaptersRead) || 0,
|
|
473
|
+
"Volumes Read": ((_p = (_o = user.statistics) === null || _o === void 0 ? void 0 : _o.manga) === null || _p === void 0 ? void 0 : _p.volumesRead) || 0,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
export { activityBy, anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
|
package/bin/index.js
CHANGED
|
@@ -213,16 +213,9 @@ cli
|
|
|
213
213
|
cli
|
|
214
214
|
.command("autolike")
|
|
215
215
|
.alias("al")
|
|
216
|
-
.option("-2, --v2", "Like the activities", false)
|
|
217
|
-
.option("-c, --count <number>", "Number of activities to like", "25")
|
|
218
216
|
.description("Autolike following or global activities.")
|
|
219
|
-
.action((
|
|
220
|
-
|
|
221
|
-
yield Auth.LikeFollowingActivityV2(count);
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
yield Auth.AutoLike();
|
|
225
|
-
}
|
|
217
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
218
|
+
yield Auth.AutoLike();
|
|
226
219
|
}));
|
|
227
220
|
cli
|
|
228
221
|
.command("social")
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@irfanshadikrishad/anilist",
|
|
3
3
|
"description": "Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts",
|
|
4
4
|
"author": "Irfan Shadik Rishad",
|
|
5
|
-
"version": "1.0.0-forbidden.
|
|
5
|
+
"version": "1.0.0-forbidden.7",
|
|
6
6
|
"main": "./bin/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"types": "./bin/index.d.ts",
|
|
@@ -55,25 +55,27 @@
|
|
|
55
55
|
"license": "MPL-2.0",
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@babel/preset-env": "^7.26.9",
|
|
58
|
-
"@eslint/js": "^9.
|
|
58
|
+
"@eslint/js": "^9.23.0",
|
|
59
59
|
"@types/jest": "^29.5.14",
|
|
60
|
-
"@types/node": "^22.13.
|
|
60
|
+
"@types/node": "^22.13.14",
|
|
61
61
|
"@types/papaparse": "^5.3.15",
|
|
62
62
|
"@types/xml2js": "^0.4.14",
|
|
63
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
64
|
-
"eslint": "^9.
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^8.28.0",
|
|
64
|
+
"eslint": "^9.23.0",
|
|
65
65
|
"globals": "^16.0.0",
|
|
66
66
|
"jest": "^29.7.0",
|
|
67
|
-
"prettier": "^3.5.
|
|
67
|
+
"prettier": "^3.5.3",
|
|
68
68
|
"prettier-plugin-organize-imports": "^4.1.0",
|
|
69
|
-
"ts-jest": "^29.
|
|
69
|
+
"ts-jest": "^29.3.0",
|
|
70
70
|
"ts-node": "^10.9.2",
|
|
71
|
-
"typescript": "^5.
|
|
71
|
+
"typescript": "^5.8.2"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
+
"@irfanshadikrishad/cipher": "^1.0.7",
|
|
75
|
+
"cli-truncate": "^4.0.0",
|
|
74
76
|
"commander": "^13.1.0",
|
|
75
|
-
"fast-xml-parser": "^5.0.
|
|
76
|
-
"inquirer": "^12.
|
|
77
|
+
"fast-xml-parser": "^5.0.9",
|
|
78
|
+
"inquirer": "^12.5.0",
|
|
77
79
|
"jsonrepair": "^3.12.0",
|
|
78
80
|
"node-fetch": "^3.3.2",
|
|
79
81
|
"open": "^10.1.0",
|